docs: slim CLAUDE.md 278?99 lines; ops detail moves to docs/ops/OPERATIONS.md #3524
2 changed files+184−266
ModifiedCLAUDE.md+87−266View fileUnifiedSplit
@@ -1,278 +1,99 @@
11# gluecron
22
3AI-native code intelligence platform — git hosting, automated CI, and push-time gate enforcement.
4
5## Source of truth: Gluecron (self-canonical, no GitHub mirror)
6
7This repository self-hosts on Gluecron itself. The one canonical remote is
8`https://gluecron.com/ccantynz/Gluecron.com.git`. **Gluecron is self-canonical:
9there is no GitHub mirror or fallback.** Durability comes from the offsite
10backup (`scripts/backup-offsite.sh` → rclone), not from GitHub. A fresh host
11re-seeds from a restored backup (`scripts/restore.sh`), never from GitHub.
12(Cutover status + operator steps: `docs/CUTOVER_RUNBOOK.md`.)
13
14**For any AI session working in this repo:**
15
16- Use the Gluecron MCP server. It is declared in `.mcp.json` at the
17 repo root (project scope — this is the file Claude Code actually
18 loads MCP servers from, on both web/cloud sessions and the CLI; the
19 old `mcpServers` block in `.claude/settings.json` was never read).
20 Tools: `gluecron_create_pr`, `gluecron_merge_pr`,
21 `gluecron_create_issue`, `gluecron_comment_pr`, etc. The surface is
22 **60 tools**, not 15: `src/lib/mcp-tools.ts` defines 15 and
23 `src/lib/mcp-tools-expanded.ts` another 45, merged in `defaultTools()`
24 (`mcp-tools.ts:1648`). Verified against the live `tools/list` on
25 2026-08-03 — all 60 carry MCP `ToolAnnotations` (26 `readOnlyHint`,
26 7 `destructiveHint`), which the Anthropic connector directory requires.
27- Push the feature branch straight to the canonical remote —
28 `git push https://x:${GLUECRON_PAT}@gluecron.com/ccantynz/Gluecron.com.git HEAD:<branch>`
29 (requires `gluecron.com` in the environment's network allowlist) —
30 then open the PR with `gluecron_create_pr`. There is no GitHub fallback;
31 if gluecron.com is unreachable, stop and surface it rather than pushing
32 to GitHub.
33- Do NOT call any `mcp__github__*` write tool. The settings file's
34 `permissions.deny` already blocks them; if you encounter a deny
35 error, switch to the equivalent `gluecron_*` tool.
36- The `.claude/skills/gluecron-pr`, `gluecron-issue`, and
37 `gluecron-review` skills are the highest-leverage entry points for
38 routine work. Invoke them when the user mentions a PR, an issue,
39 or wants a second AI review pass.
40- Auth: the `GLUECRON_PAT` env var holds the user's personal access
41 token. If it's missing, prompt the user to generate one at
42 `/settings/tokens`. **`repo` scope is enough for almost everything** —
43 including `gluecron_merge_pr`, despite what this line used to say. Of the
44 60 tools exactly two call `requireScope(ctx, "admin", …)`:
45 `gluecron_delete_repo` and `gluecron_create_agent_session`
46 (`src/lib/mcp-tools-expanded.ts:250,2169`). The 15 tools in
47 `mcp-tools.ts` — create/merge PR, issues, comments — contain no scope
48 check at all and gate on repo ACLs alone.
49
50 **Scope is not the same as access.** `resolveRepoAccess`
51 (`src/middleware/repo-access.ts`) resolves owner → accepted
52 `repo_collaborators` row → public-read fallback. Being a *site* admin
53 does not grant write on someone else's repo, so a PAT must come from an
54 account that owns the target repo or is an accepted write collaborator
55 on it. A token with `admin` scope from the wrong account still fails.
56
57**Deploys are self-driven, via the timer — not the git hook.** Push to the
58canonical Gluecron remote and `gluecron-update.timer` (60s) runs
59`scripts/auto-update.sh`, which fetches `origin main` in `/opt/gluecron` and
60rebuilds on change. Expect the live site to reflect a push within ~60-90
61seconds. Watch `/admin/deploys` for the step stream.
62
63This document previously claimed `src/hooks/post-receive.ts` fires
64`scripts/self-deploy.sh` directly, in ~25 seconds. That path exists in code
65but **does not run**: the hook only dispatches when `process.env.SELF_HOST_REPO`
66matches, and while `SELF_HOST_REPO` is set in the host env file it is absent
67from the container the app actually runs in, so the condition is never true.
68No systemd unit invokes the script either — its log's last write was
692026-07-13. `scripts/self-deploy.sh` carries the same finding in its own
70header, verified on the box on 2026-07-29.
71
72The distinction matters during an incident: if a push has not gone out, check
73`systemctl list-timers 'gluecron-*'` first. A disabled timer stops deploys
74silently, and the self-deploy log will look untouched either way — because it
75always is.
76
77**Server:** `gluecron.com` → `66.42.121.161` (Vultr VPS, not Fly.io or Hetzner —
78both are decommissioned). Working tree lives at `/opt/gluecron` on that box.
79
80**Operator access (AI sessions included):** the box is on the owner's
81Tailscale tailnet as `jarvis` (`100.109.131.122`) with **Tailscale SSH
82enabled** — from an owner machine, `ssh root@100.109.131.122` just works
83(no key setup). Public-internet SSH (port 22) is password-only and the git
84SSH server port (2222) is not exposed; always go via Tailscale.
85
86**Runtime layout on the box (verified 2026-07-12):**
87- App runs via `docker compose -f docker-compose.standalone.yml` in
88 `/opt/gluecron` (services: `gluecron`, `postgres`, `autoheal`).
89- **Database is Neon** (managed Postgres, `ep-solitary-fog-...aws.neon.tech`).
90 The app's `DATABASE_URL` (in `/opt/gluecron/.env`) points at Neon, so ALL
91 reads/writes and migrations go there — Neon provides PITR. NOTE: the compose
92 also starts a local `postgres` (pgvector/pg16) container, but the app IGNORES
93 it (it's leftover/unused dead weight, like the caddy service). Do NOT back up
94 or query the local container thinking it's production — target Neon via the
95 app's `DATABASE_URL`. (Verified 2026-07-13.)
96- Bare repos live in the docker volume `gluecron_git-repos`
97 (host path `/var/lib/docker/volumes/gluecron_git-repos/_data`), mounted
98 into the container at `/data/repos`. The host-side `/data/repos` dir is
99 NOT the live store — always go through the volume path.
100- Auto-deploy: `gluecron-update.timer` (60s) runs `scripts/auto-update.sh`
101 — fetches `origin main` in `/opt/gluecron` and rebuilds on change.
102 `origin` = the canonical Gluecron repo. If the timer is disabled, deploys
103 silently stop — check `systemctl list-timers 'gluecron-*'` first when the
104 live build looks stale (compare `/api/version` builtAt vs latest commit).
105- Manual deploy: `cd /opt/gluecron && git fetch <src> main && git reset
106 --hard FETCH_HEAD && docker compose -f docker-compose.standalone.yml up
107 -d --build`, then `... exec -T gluecron bun run db:migrate`.
108
109**To deploy:**
110```bash
111git push gluecron main
112# or first-time setup:
113git remote add gluecron https://x:${GLUECRON_PAT}@gluecron.com/ccantynz/Gluecron.com.git
114```
115
116**Do NOT use Vercel** — it has been disconnected.
117
118**GitHub reality check (2026-07-12):** the old GitHub repo
119(`ccantynz-alt/Gluecron.com`) is NOT frozen and is still writable — the
120cutover runbook (docs/CUTOVER_RUNBOOK.md) was designed but its operator
121phases were never fully executed. GitHub remains the emergency deploy
122source (`git fetch https://github.com/ccantynz-alt/Gluecron.com.git main`
123on the box) until offsite backups are armed and a restore drill has
124passed. Do not delete or archive the GitHub repo before that. Prefer the
125canonical Gluecron remote for normal work; keep GitHub in sync until the
126runbook's Phase 4 criteria are actually met.
3AI-native code intelligence platform — git hosting, automated CI, and
4push-time gate enforcement. Self-hosts on Gluecron itself.
1275
1286## READ FIRST — every session
1297
130**`BUILD_BIBLE.md` is mandatory reading for every Claude agent before any code changes.**
131
132It contains:
133- Agent policy (do-not-undo rule, continuous-build rule)
134- GitHub parity scorecard (what's shipped vs missing)
135- Numbered build plan (Blocks A–H)
136- Locked components that cannot be altered without owner permission
137- Session workflow
138
139Do not skip it. Do not refactor locked files. Do not stop mid-block.
140
141## Stack
142
143- **Runtime:** Bun
144- **Framework:** Hono (with JSX for server-rendered views)
145- **Database:** Drizzle ORM + Neon (managed Postgres) in prod
146- **Git:** Smart HTTP protocol via git CLI subprocesses
147
148## Development
8**`BUILD_BIBLE.md` is mandatory reading before any code changes.** It holds
9the agent policy (do-not-undo, continuous-build), the parity scorecard, the
10numbered build plan, locked components, and the authoritative feature map
11(§2). Do not skip it. Do not refactor locked files. Do not stop mid-block.
12
13## Source of truth & workflow
14
15- **Canonical remote:** `https://gluecron.com/ccantynz/Gluecron.com.git`.
16 Gluecron is self-canonical — no GitHub mirror for normal work. Push the
17 feature branch straight there
18 (`git push https://x:${GLUECRON_PAT}@gluecron.com/ccantynz/Gluecron.com.git HEAD:<branch>`),
19 then open the PR with `gluecron_create_pr`. If gluecron.com is
20 unreachable, stop and surface it — do not push to GitHub.
21- **Never call `mcp__github__*` write tools** (settings deny them). Use the
22 equivalent `gluecron_*` tool.
23- GitHub (`ccantynz-alt/Gluecron.com`) is still the emergency deploy source
24 until the cutover runbook's Phase 4 criteria are met — do not delete or
25 archive it. Details: `docs/ops/OPERATIONS.md`, `docs/CUTOVER_RUNBOOK.md`.
26
27## MCP server (Gluecron)
28
29- Declared in `.mcp.json` at the repo root (project scope — the file Claude
30 Code actually loads; the old block in `.claude/settings.json` was never
31 read).
32- **60 tools**, not 15: `src/lib/mcp-tools.ts` defines 15,
33 `src/lib/mcp-tools-expanded.ts` another 45, merged in `defaultTools()`.
34- **Auth:** `GLUECRON_PAT` env var. If missing, generate at
35 `/settings/tokens` (pick "never expires"). `repo` scope is enough for
36 almost everything, including `gluecron_merge_pr`; only
37 `gluecron_delete_repo` and `gluecron_create_agent_session` require
38 `admin`.
39- **Scope ≠ access:** write access resolves via repo ownership /
40 accepted collaborator rows (`src/middleware/repo-access.ts`). An `admin`
41 PAT from the wrong account still fails on someone else's repo.
42- Skills `gluecron-pr`, `gluecron-issue`, `gluecron-review`
43 (`.claude/skills/`) are the entry points for routine PR/issue/review
44 work — invoke them when the user mentions any of those.
45
46## Deploys & production (short version — details in docs/ops/OPERATIONS.md)
47
48- **Deploy = push to canonical remote + wait ~60–90s.** The
49 `gluecron-update.timer` (60s) runs `scripts/auto-update.sh`, which
50 rebuilds on change. Watch `/admin/deploys`.
51- The `post-receive.ts` → `self-deploy.sh` path exists in code but **never
52 fires** in prod — do not trust it or its log. If a push hasn't shipped,
53 check `systemctl list-timers 'gluecron-*'` first.
54- **Server:** `gluecron.com` → `66.42.121.161` (Vultr). SSH via Tailscale:
55 `ssh root@100.109.131.122` (node `jarvis`). App runs in Docker at
56 `/opt/gluecron` (`docker-compose.standalone.yml`).
57- **Database is Neon** (via `DATABASE_URL` in `/opt/gluecron/.env`). The
58 local `postgres` container in the compose file is **unused dead weight**
59 — never back up or query it as production.
60- Bare repos: docker volume `gluecron_git-repos` → `/data/repos` in the
61 container. The host-side `/data/repos` is NOT the live store.
62- **Vercel disconnected, Fly.io decommissioned** — do not use either.
63
64## Stack & development
65
66Bun + Hono (server-rendered JSX) + Drizzle ORM + Neon Postgres; git via
67Smart HTTP CLI subprocesses.
14968
15069```bash
15170bun install # install dependencies
152bun dev # start dev server (hot reload)
71bun dev # dev server (hot reload)
15372bun test # run tests
15473bun run db:migrate # run database migrations
15574```
15675
157## Architecture
158
159> Note (2026-06-10): the tree below is illustrative, not exhaustive — the
160> platform has grown to ~190 route files in `src/routes/` and 161 tables in
161> `src/db/schema.ts`. `BUILD_BIBLE.md` §2 is the authoritative feature map.
162
163```
164src/
165 index.ts Entry point (Bun server)
166 app.tsx Hono app composition + error handlers
167 lib/
168 config.ts Environment config (getters, reads env at access time)
169 auth.ts Password hashing (bcrypt), session tokens
170 highlight.ts Syntax highlighting (highlight.js, 40+ languages)
171 markdown.ts Markdown rendering (GFM + syntax highlighting)
172 db/
173 schema.ts Drizzle schema (all tables)
174 index.ts Lazy DB connection (proxy pattern)
175 migrate.ts Migration runner
176 git/
177 repository.ts Git operations (tree, blob, commits, diff, branches, blame, search, raw)
178 protocol.ts Smart HTTP protocol (pkt-line, service RPC)
179 hooks/
180 post-receive.ts GateTest + optional deploy webhook on push
181 middleware/
182 auth.ts softAuth + requireAuth middleware
183 routes/
184 git.ts Git HTTP endpoints (clone/push)
185 api.ts REST API (repo CRUD, setup)
186 auth.tsx Register, login, logout (web + API)
187 web.tsx Web UI (file browser, commits, diffs, search, blame, raw)
188 issues.tsx Issue tracker (CRUD, comments, close/reopen)
189 pulls.tsx Pull requests (create, review, merge, close)
190 editor.tsx Web file editor (create/edit via git plumbing)
191 compare.tsx Branch comparison (diff + commit list)
192 settings.tsx User settings (profile, SSH keys)
193 repo-settings.tsx Repository settings (description, visibility, delete)
194 webhooks.tsx Webhook management + delivery engine
195 fork.ts Repository forking
196 explore.tsx Explore/discover public repos
197 tokens.tsx Personal access tokens
198 contributors.tsx Contributor list + commit activity graph
199 views/
200 layout.tsx HTML shell + CSS design tokens + auth-aware nav
201 Light-first ("Natural Light": warm paper, evergreen
202 accent #1f5f57, no purple). Dark is opt-in via the
203 `theme` cookie. Both token blocks are complete —
204 do not let light fall through to dark values.
205 error-beacon.ts Browser error capture -> POST /api/client-errors.
206 Injected by layout.tsx AND by every view that
207 renders its own document; see the coverage test.
208 components.tsx UI components (file table, commit list, diff viewer, etc.)
209```
210
211## Database Schema
212
213- `users` — accounts with bcrypt password hashing
214- `sessions` — cookie-based auth sessions (30 day expiry)
215- `repositories` — repos with fork tracking, star/fork/issue counts
216- `stars` — user-repo star relationships
217- `issues` — issue tracker with open/closed state
218- `issue_comments` — threaded comments on issues
219- `labels` + `issue_labels` — issue categorization
220- `pull_requests` — PRs with base/head branches, open/closed/merged state
221- `pr_comments` — PR comments with AI review flag + file/line annotations
222- `activity_feed` — event log for repos
223- `webhooks` — registered webhook URLs with HMAC secret + event filtering
224- `api_tokens` — personal access tokens with SHA-256 hashing
225- `repo_topics` — repository tags for discoverability
226- `ssh_keys` — user SSH public keys
227
228## Integrations
229
230- **GateTest (optional):** third-party security scanner. When `GATETEST_URL` is set, `git push` POSTs to it; inbound results accepted at `POST /api/hooks/gatetest`.
231- **Outbound deploy webhook (optional):** when `VAPRON_DEPLOY_URL` is set (legacy `CRONTECH_DEPLOY_URL` honored), pushes to the default branch POST to Vapron (formerly Crontech).
232- **Webhooks:** POST to user-registered URLs on push/issue/PR/star events with HMAC signatures.
233
234## Environment Variables
235
236See `.env.example` for required variables. Key ones:
237- `DATABASE_URL` — PostgreSQL connection string (prod: Neon; set in `/opt/gluecron/.env`)
238- `GIT_REPOS_PATH` — directory for bare git repos (default: `./repos`)
239- `PORT` — HTTP port (default: 3000)
240
241## Deployment
242
243- **Production server:** `66.42.121.161` (`gluecron.com`) — Vultr VPS. The app
244 runs in Docker, not as a bare Bun process under systemd: systemd's role is
245 the `gluecron-update.timer` that drives deploys.
246- **Deploy trigger:** `git push gluecron main` → `gluecron-update.timer` (60s)
247 → `scripts/auto-update.sh` → `git fetch origin main` in `/opt/gluecron` →
248 `docker compose -f docker-compose.standalone.yml up -d --build` →
249 `compose exec gluecron bun run db:migrate`.
250- **Not the deploy trigger:** `post-receive.ts` → `scripts/self-deploy.sh`.
251 See the note under "Deploys are self-driven" above — that path is gated on
252 `SELF_HOST_REPO`, which is absent from the app container, so it never fires.
253- **Fly.io:** decommissioned (fly.toml kept for reference, not active).
254- **Vercel:** disconnected — do not reconnect.
255- **Database:** Neon (managed Postgres) via `DATABASE_URL`; has built-in PITR.
256- **Logs:** `journalctl -u gluecron-update` for the deploy itself, and
257 `docker compose -f docker-compose.standalone.yml logs gluecron` for the app.
258 `/var/log/gluecron-self-deploy.log` belongs to the dormant path above and
259 has not been written since 2026-07-13 — an empty log there means nothing.
260 Live step stream at `/admin/deploys`.
261
262## Skills available for this project
263
264Claude Code skill bundle for the Gluecron MCP write surface lives in
265`.claude/skills/`. The install script (`scripts/install.sh`) copies these
266into `~/.claude/skills/` so they are available across all projects:
267
268- **`gluecron-pr`** — open, list, fetch, comment on, merge, or close pull
269 requests on a Gluecron-hosted repository.
270- **`gluecron-issue`** — create, list, comment on, close, or reopen issues
271 on a Gluecron-hosted repository.
272- **`gluecron-review`** — act as a secondary AI code reviewer on a
273 Gluecron PR; complements the built-in `src/lib/ai-review.ts` pass.
274
275All three skills drive the K1 MCP write tools defined in
276`src/lib/mcp-tools.ts` (`gluecron_create_issue`, `gluecron_create_pr`,
277`gluecron_merge_pr`, etc.). They auto-invoke when the active repo's
278origin URL contains `gluecron.com` or matches `$GLUECRON_HOST`.
76Env vars: see `.env.example`. Key: `DATABASE_URL`, `GIT_REPOS_PATH`
77(default `./repos`), `PORT` (default 3000).
78
79## Architecture pointers
80
81The platform is large (~190 route files in `src/routes/`, 161 tables in
82`src/db/schema.ts`) — `BUILD_BIBLE.md` §2 is the authoritative feature map
83and `src/db/schema.ts` the authoritative schema. Orientation:
84
85- `src/app.tsx` — Hono app composition + error handlers;
86 `src/index.ts` — entry point.
87- `src/git/` — git operations + Smart HTTP protocol;
88 `src/routes/git.ts` — clone/push endpoints.
89- `src/routes/` — one file per surface (web UI, issues, pulls, editor,
90 settings, webhooks, tokens, …).
91- `src/views/layout.tsx` — HTML shell + design tokens. **Light-first
92 ("Natural Light"): warm paper, evergreen `#1f5f57`, no purple.** Dark is
93 opt-in via the `theme` cookie; both token blocks must stay complete so
94 light never falls through to dark values.
95- `src/views/error-beacon.ts` — browser error capture →
96 `POST /api/client-errors`; injected by `layout.tsx` AND every view that
97 renders its own document (see the coverage test).
98- `src/middleware/auth.ts` — softAuth/requireAuth;
99 `src/middleware/repo-access.ts` — repo ACL resolution.
Addeddocs/ops/OPERATIONS.md+97−0View fileUnifiedSplit
@@ -0,0 +1,97 @@
1# Gluecron Operations Reference
2
3Day-to-day operational reality of the production box. This is the detailed
4companion to the short facts in `CLAUDE.md` — read this before touching the
5server, the database, or the deploy pipeline. First-time bootstrap lives in
6`DEPLOYMENT_RUNBOOK.md`; cutover status in `../CUTOVER_RUNBOOK.md`.
7
8## Server
9
10- `gluecron.com` → `66.42.121.161` (Vultr VPS). Fly.io and Hetzner are both
11 decommissioned; Vercel is disconnected — do not reconnect any of them.
12- Working tree: `/opt/gluecron`. App runs via
13 `docker compose -f docker-compose.standalone.yml` (services: `gluecron`,
14 `postgres`, `autoheal`).
15- **Operator access (AI sessions included):** the box is on the owner's
16 Tailscale tailnet as `jarvis` (`100.109.131.122`) with Tailscale SSH
17 enabled — from an owner machine, `ssh root@100.109.131.122` just works.
18 Public port 22 is password-only and git SSH (2222) is not exposed;
19 always go via Tailscale.
20
21## Database — Neon, NOT the local container
22
23The app's `DATABASE_URL` (in `/opt/gluecron/.env`) points at **Neon**
24(managed Postgres, `ep-solitary-fog-...aws.neon.tech`). ALL reads, writes,
25and migrations go there; Neon provides PITR.
26
27The compose file also starts a local `postgres` (pgvector/pg16) container,
28but the app **ignores it** — it is leftover dead weight, like the caddy
29service. Do NOT back up or query the local container thinking it's
30production — target Neon via the app's `DATABASE_URL`. (Verified 2026-07-13.)
31
32## Git storage
33
34Bare repos live in the docker volume `gluecron_git-repos`
35(host path `/var/lib/docker/volumes/gluecron_git-repos/_data`), mounted into
36the container at `/data/repos`. The host-side `/data/repos` dir is NOT the
37live store — always go through the volume path.
38
39## Deploys — the timer, not the git hook
40
41**What actually ships:** push to the canonical Gluecron remote →
42`gluecron-update.timer` (60s) runs `scripts/auto-update.sh` → fetches
43`origin main` in `/opt/gluecron` → `docker compose ... up -d --build` →
44`compose exec -T gluecron bun run db:migrate`. Expect the live site to
45reflect a push within ~60–90 seconds. Step stream at `/admin/deploys`.
46
47**The dead path:** `src/hooks/post-receive.ts` → `scripts/self-deploy.sh`
48exists in code but does not run. The hook only dispatches when
49`process.env.SELF_HOST_REPO` matches, and that var is set in the host env
50file but absent from the app container, so the condition is never true. No
51systemd unit invokes the script either — its log
52(`/var/log/gluecron-self-deploy.log`) was last written 2026-07-13 and an
53empty/stale log there means nothing. (Verified on the box 2026-07-29;
54`scripts/self-deploy.sh` carries the same finding in its header.)
55
56**When a push hasn't gone out:** check
57`systemctl list-timers 'gluecron-*'` first. A disabled timer stops deploys
58silently. Compare `/api/version` builtAt vs the latest commit to confirm
59staleness. Also check `git status -sb` locally before blaming prod.
60
61**Manual deploy:**
62
63```bash
64cd /opt/gluecron
65git fetch <src> main && git reset --hard FETCH_HEAD
66docker compose -f docker-compose.standalone.yml up -d --build
67docker compose -f docker-compose.standalone.yml exec -T gluecron bun run db:migrate
68```
69
70**Logs:** `journalctl -u gluecron-update` for the deploy itself;
71`docker compose -f docker-compose.standalone.yml logs gluecron` for the app.
72
73## GitHub reality (2026-07-12)
74
75The old GitHub repo (`ccantynz-alt/Gluecron.com`) is NOT frozen and is
76still writable — the cutover runbook's operator phases were never fully
77executed. GitHub remains the **emergency deploy source**
78(`git fetch https://github.com/ccantynz-alt/Gluecron.com.git main` on the
79box) until offsite backups are armed and a restore drill has passed. Do not
80delete or archive the GitHub repo before the runbook's Phase 4 criteria are
81met. Normal work uses the canonical Gluecron remote only; never push
82day-to-day work to GitHub.
83
84Durability comes from backups, not GitHub: daily `scripts/backup.sh` +
85weekly restore drill work today; `scripts/backup-offsite.sh` (rclone)
86exists but is not yet wired to a schedule. A fresh host re-seeds from a
87restored backup (`scripts/restore.sh`), never from GitHub.
88
89## Integrations
90
91- **GateTest (optional):** when `GATETEST_URL` is set, `git push` POSTs to
92 it; inbound results accepted at `POST /api/hooks/gatetest`.
93- **Outbound deploy webhook (optional):** when `VAPRON_DEPLOY_URL` is set
94 (legacy `CRONTECH_DEPLOY_URL` honored), pushes to the default branch POST
95 to Vapron (formerly Crontech).
96- **User webhooks:** POST to registered URLs on push/issue/PR/star events
97 with HMAC signatures.
098
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts