Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
claude/adoring-hopper-5x74bqclaude/affectionate-feynman-ykrf1hclaude/architecture-audit-design-wxprenclaude/build-status-update-3MXsfclaude/charming-meitner-mllb5rclaude/compare-gate-gluecron-s4mFQclaude/confident-faraday-tikcwbclaude/continue-work-XMTlIclaude/crontech-gluecron-deploy-7MIECclaude/crontech-platform-setup-SeKfwclaude/design-2026claude/ecstatic-ptolemy-jMdigclaude/enhance-github-integration-QNHdGclaude/fix-aa-loop-issue-PonMQclaude/fix-actions-and-processclaude/fix-desktop-errors-XqoW8claude/fix-red-workflowsclaude/fix-website-access-6FKJNclaude/gatetest-integration-hardeningclaude/github-audit-improvements-bDFr9claude/gluecron-launch-status-FoMRlclaude/hopeful-lamport-olfCTclaude/issue-to-pr-and-protectionsclaude/jolly-heisenberg-2sg1Qclaude/launch-preparation-QmTb6claude/new-session-xk1l7claude/plan-platform-architecture-kkN4yclaude/platform-analysis-roadmap-1nUGLclaude/platform-launch-assessment-8dWV8claude/polish-platform-release-AeDrUclaude/resume-previous-work-KzyLwclaude/review-crontech-handoff-qYEVqclaude/review-project-completeness-lHhS2claude/review-readme-docs-ulqPKclaude/serene-edison-rj87weclaude/setup-multi-repo-dev-BCwNQclaude/ship-fixes-and-tests-Jvz1cclaude/site-audit-competitive-pctlwgclaude/site-migration-vercel-XstpKclaude/standalone-product-repos-XHFTDcopilot/feat-smart-empty-states-keyboard-first-enhancementcopilot/feat-smart-morning-digest-review-context-restorecopilot/fix-and-process-workflowscopilot/update-ai-powered-code-reviewfeat/debt-mapfeat/push-policy-codeowners-hardeningfeat/smart-digest-contextfeat/stage-impactfeat/t1-secret-migrationfeat/u-polishfeat/w-self-hostfeat/w2-claude-configfix/agent-journey-orphan-sweepgatetest/auto-fix-1776586424172gatetest/auto-fix-1776586534814gatetest/auto-fix-1776590685143gatetest/auto-fix-1776590808199mainops/redeploy-retriggerstyle/dxt-cta-themeworktree-agent-a3377aad30d55da26worktree-agent-a7ef607b7ee1d6c74
GATETEST_HOOK.md4.2 KB · 182 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# GateTest ↔ GlueCron integration

GlueCron exposes **two** inbound paths for GateTest to report scan results back.
Use the primary path for normal traffic; the backup path exists so you always
have a way in if the shared secret is misconfigured.

Base URL: `https://<your-gluecron-host>` (e.g. `https://gluecron.com`)

---

## Primary: shared-secret hook

**URL:** `POST /api/hooks/gatetest`

### Auth (pick one)

**Option A — Bearer token**
```
Authorization: Bearer <GATETEST_CALLBACK_SECRET>
```
or if the GateTest side can't set `Authorization`:
```
X-GateTest-Token: <GATETEST_CALLBACK_SECRET>
```

**Option B — HMAC-SHA256 over raw body**
```
X-GateTest-Signature: sha256=<hex(hmac_sha256(GATETEST_HMAC_SECRET, rawBody))>
```

Both are compared with a timing-safe equality check.

### Request body (JSON)

```json
{
  "repository": "owner/name",
  "sha": "0123abcd0123abcd0123abcd0123abcd01234567",
  "ref": "refs/heads/main",
  "pullRequestNumber": 42,
  "status": "passed",
  "summary": "12 tests passed, 0 failed, 3.4s",
  "details": { "...": "arbitrary JSON, persisted as-is" },
  "durationMs": 3400
}
```

| Field | Required | Notes |
|---|---|---|
| `repository` | ✅ | `"owner/name"` |
| `sha` | ✅ | full 40-char commit SHA |
| `status` | ✅ | `"passed"` \| `"failed"` \| `"error"` \| `"success"` |
| `ref` | optional | defaults to `refs/heads/main` |
| `pullRequestNumber` | optional | links the gate run to a PR |
| `summary` | optional | shown on the gates UI |
| `details` | optional | arbitrary JSON, stored verbatim |
| `durationMs` | optional | for metrics |

### Response

```json
{ "ok": true, "gateRunId": "uuid-of-the-inserted-row" }
```

On failure:
- `400` — invalid JSON or missing required fields
- `401` — missing / invalid credentials
- `404` — repository not known to GlueCron
- `500` — DB error (retry-safe, idempotent on sha + gate_name)

### Side effects on a `failed` status
1. Row inserted in `gate_runs`
2. In-app notification to the repo owner (kind `gate_failed`)
3. Entry in `audit_log` (action `gate_callback`)

---

## Backup: personal access token

**URL:** `POST /api/v1/gate-runs`

### Auth
Standard GlueCron PAT, created at `/settings/tokens`:
```
Authorization: Bearer glc_<64-hex-chars>
```
Alternative header if Bearer isn't possible:
```
X-API-Token: glc_<64-hex-chars>
```

### Request body
Identical to the primary path, plus optional `gateName`:
```json
{
  "repository": "owner/name",
  "sha": "...",
  "status": "passed",
  "gateName": "GateTest",
  "summary": "...",
  "details": { }
}
```

### Authorisation rules
- The PAT's owner must match the repo's owner (MVP rule — will expand with orgs/teams in Block B).
- Token is rejected if expired.
- Successful use updates `api_tokens.last_used_at`.

### Listing prior runs
```
GET /api/v1/gate-runs?repository=owner/name&limit=20
Authorization: Bearer glc_...
```

Returns:
```json
{ "ok": true, "runs": [ {...}, ... ] }
```

---

## Liveness probe (no auth)

```
GET /api/hooks/ping
```
Returns:
```json
{
  "ok": true,
  "service": "gluecron",
  "hooks": ["gatetest", "gatetest/recent", "api/v1/gate-runs (backup)"],
  "timestamp": "2026-04-14T..."
}
```

Use this in GateTest's connectivity check before trying an authenticated call.

---

## Configuring the secrets

On the GlueCron host (Railway / Fly / Docker) set either or both:
```
GATETEST_CALLBACK_SECRET=<long random bearer token>
GATETEST_HMAC_SECRET=<long random HMAC key>
```

Generate with:
```bash
openssl rand -hex 32
```

Share the same value with GateTest. **If neither is set, the callback endpoint
refuses all requests** — there is no anonymous write path by design.

---

## Example curl (primary path)

```bash
curl -X POST "https://gluecron.com/api/hooks/gatetest" \
  -H "Authorization: Bearer $GATETEST_CALLBACK_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "repository": "alice/webapp",
    "sha": "9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b",
    "ref": "refs/heads/main",
    "status": "failed",
    "summary": "2 tests failed in src/auth.test.ts",
    "durationMs": 4812
  }'
```

Response:
```json
{ "ok": true, "gateRunId": "b7f3e2a1-..." }
```

The repo owner immediately sees a red notification; the run shows up at
`/<owner>/<repo>/gates`.