feat(indexer): protected-main auto-deploy and /health git SHA #1276

Closed
opened 2026-09-17 02:21:45 +00:00 by PlasticDigits · 3 comments

Summary

Protected-branch land of this repo already rebuilds the frontend Coolify application (auto-deploy on). The indexer application for the same Forgejo path does not: auto-deploy is off, so merged tips stay off the live indexer until a manual deploy. GET /health returns only {"status":"ok"} (indexer/src/api/mod.rs), so operators cannot attest which git SHA is serving without scraping Coolify logs (forbidden: never scrape SOURCE SHA lines).

CAC grouped drain maps one Coolify uuid per owner/repo (COOLIFY_APP_MAP). Frontend and indexer are two apps on one repo; drain therefore cannot be the only redeploy path for indexer. Product attestation belongs on indexer /health. CAC public /health must stay liveness-only with no SHA (#348 / leftover glance #352 — different process).

This is not a +1 on:

Issue Why it is not this ticket
code/cl8y-dex-terraclassic indexer fee /api/v1/health/fee-discount Different probe. Do not overload it with git SHA.
PlasticDigits/cl8y-agent-control #348 / #352 CAC controller health/SOURCE stamp. Do not put SHA on CAC /health.
code/cl8y-dex-terraclassic #706 Frontend stale Vite chunks after deploy. Complementary UX.

Do not publish Coolify tokens, app uuids, hosts, or /status JSON. Do not scrape Coolify logs for SOURCE SHA.

Current codebase

  • indexer/src/api/mod.rs health() → {"status":"ok"} only; route GET /health.
  • indexer/tests/api_health.rs asserts status == "ok" only.
  • indexer/src/api/fee_discount_health.rs separate LCD probe (keep).
  • Coolify: frontend auto-deploy on; indexer auto-deploy off; git_commit_sha may be the literal branch token rather than the baked commit (build must record a real hex).
  • CAC drain_deploy_url + app_uuid_for_path: one uuid per forgejo path (crates/cl8y-agent-core/src/coolify.rs, merge_drain.rs).

Why this is needed

Indexer schema/API landings (fee hops, quote scale, protocol events) are invisible on the live indexer until someone deploys by hand. /health cannot prove the serving binary matches main. Frontend already auto-deploys; indexer must match that production bar.

Constraints / guardrails

  • /health stays unauthenticated, fast, and liveness. Add at most a hex git_sha (or commit) field; no occupancy, pins, tokens, helper paths, or inventory.
  • Reject non-hex / HEAD / secret-shaped values (same idea as CAC drain SHA parser): omit the field rather than advertise HEAD.
  • Do not fetch Coolify from the indexer process.
  • Do not add a second public /status.
  • Fee-discount health route stays LCD-only.
  • Auto-deploy is the indexer Coolify app’s protected-branch flag, not a CAC token in this repo.
  • Numeric overflow on volume_aggregator is out of scope (not approved).

Relevant files

  • indexer/src/api/mod.rs
  • indexer/tests/api_health.rs
  • indexer/tests/security.rs (health status)
  • Coolify application settings for the indexer (operator; not in git)
  • Optional: nixpacks / Dockerfile ARG for GIT_COMMIT / SOURCE_COMMIT
  1. Enable protected-branch auto-deploy on the indexer Coolify application (same git source as frontend). Document in-repo that indexer follows main like frontend.
  2. Bake a real commit hex into the indexer image (SOURCE_COMMIT / build-arg). If the platform would record the literal HEAD token, set include-source-commit (or equivalent) so the env is a 40-char hex.
  3. GET /health JSON: status=ok plus git_sha when the value matches ^[0-9a-f]{7,40}$ (lowercase); otherwise omit.
  4. First-pass model: grok-high (build stamp + public health; Coolify flag is operator).

If CAC must deploy two uuids per path, that is a separate PlasticDigits/cl8y-agent-control issue (map shape). Do not block indexer auto-deploy on that.

Acceptance criteria

  • Indexer Coolify app auto-deploys on protected-branch push (operator checkbox + documented).
  • Live GET /health includes a hex git_sha matching the baked commit after a main land (not HEAD).
  • Tests: ok-only when env missing; hex present when valid; HEAD / empty / secret-shaped omitted.
  • /api/v1/health/fee-discount unchanged.
  • No Coolify uuid or token in source or issue comments.

Test plan (all paths)

  • Health 200 with missing env; valid 40-char; 7-char prefix; HEAD; empty; uppercase hex normalized or omitted (pick one, test it).
  • Fee-discount health still 200/fail-closed as today.
  • Security tests: health still 200, no extra headers required.

Test plan (attack / hack / abuse)

  • Unauthenticated client must not learn inventory, tokens, or Coolify uuids from /health.
  • Injected env that looks like a bearer / Sanctum N| must be omitted.
  • Path traversal / host override via health JSON: not applicable (static object).
  • Do not make /health wait on LCD or DB (DoS); fee-discount stays the slow probe.

Verification

Indexer unit/api tests. Operator glance after land: frontend and indexer serve the new tip; GET /health git_sha matches git rev-parse HEAD on that merge (prefix OK). No Coolify log scrape.

First-pass model: grok-high

## Summary Protected-branch land of this repo already rebuilds the **frontend** Coolify application (auto-deploy on). The **indexer** application for the same Forgejo path does not: auto-deploy is off, so merged tips stay off the live indexer until a manual deploy. `GET /health` returns only `{"status":"ok"}` (`indexer/src/api/mod.rs`), so operators cannot attest which git SHA is serving without scraping Coolify logs (forbidden: never scrape SOURCE SHA lines). CAC grouped drain maps **one** Coolify uuid per `owner/repo` (`COOLIFY_APP_MAP`). Frontend and indexer are two apps on one repo; drain therefore cannot be the only redeploy path for indexer. Product attestation belongs on indexer `/health`. CAC public `/health` must stay liveness-only with **no** SHA ([#348](https://git.cl8y.com/PlasticDigits/cl8y-agent-control/issues/348) / leftover glance [#352](https://git.cl8y.com/PlasticDigits/cl8y-agent-control/issues/352) — different process). This is **not** a +1 on: | Issue | Why it is not this ticket | | --- | --- | | code/cl8y-dex-terraclassic indexer fee `/api/v1/health/fee-discount` | Different probe. Do not overload it with git SHA. | | PlasticDigits/cl8y-agent-control #348 / #352 | CAC controller health/SOURCE stamp. Do not put SHA on CAC `/health`. | | code/cl8y-dex-terraclassic #706 | Frontend stale Vite chunks after deploy. Complementary UX. | Do not publish Coolify tokens, app uuids, hosts, or `/status` JSON. Do not scrape Coolify logs for SOURCE SHA. ## Current codebase - `indexer/src/api/mod.rs` `health()` → `{"status":"ok"}` only; route `GET /health`. - `indexer/tests/api_health.rs` asserts `status == "ok"` only. - `indexer/src/api/fee_discount_health.rs` separate LCD probe (keep). - Coolify: frontend auto-deploy on; indexer auto-deploy off; `git_commit_sha` may be the literal branch token rather than the baked commit (build must record a real hex). - CAC `drain_deploy_url` + `app_uuid_for_path`: one uuid per forgejo path (`crates/cl8y-agent-core/src/coolify.rs`, `merge_drain.rs`). ## Why this is needed Indexer schema/API landings (fee hops, quote scale, protocol events) are invisible on the live indexer until someone deploys by hand. `/health` cannot prove the serving binary matches `main`. Frontend already auto-deploys; indexer must match that production bar. ## Constraints / guardrails - `/health` stays unauthenticated, fast, and **liveness**. Add at most a hex `git_sha` (or `commit`) field; no occupancy, pins, tokens, helper paths, or inventory. - Reject non-hex / `HEAD` / secret-shaped values (same idea as CAC drain SHA parser): omit the field rather than advertise `HEAD`. - Do not fetch Coolify from the indexer process. - Do not add a second public `/status`. - Fee-discount health route stays LCD-only. - Auto-deploy is the indexer Coolify app’s protected-branch flag, not a CAC token in this repo. - Numeric overflow on volume_aggregator is **out of scope** (not approved). ## Relevant files - `indexer/src/api/mod.rs` - `indexer/tests/api_health.rs` - `indexer/tests/security.rs` (health status) - Coolify application settings for the indexer (operator; not in git) - Optional: nixpacks / Dockerfile ARG for `GIT_COMMIT` / `SOURCE_COMMIT` ## Recommended direction 1. Enable protected-branch auto-deploy on the indexer Coolify application (same git source as frontend). Document in-repo that indexer follows `main` like frontend. 2. Bake a real commit hex into the indexer image (`SOURCE_COMMIT` / build-arg). If the platform would record the literal `HEAD` token, set include-source-commit (or equivalent) so the env is a 40-char hex. 3. `GET /health` JSON: `status=ok` plus `git_sha` when the value matches `^[0-9a-f]{7,40}$` (lowercase); otherwise omit. 4. First-pass model: grok-high (build stamp + public health; Coolify flag is operator). If CAC must deploy **two** uuids per path, that is a **separate** PlasticDigits/cl8y-agent-control issue (map shape). Do not block indexer auto-deploy on that. ## Acceptance criteria - [ ] Indexer Coolify app auto-deploys on protected-branch push (operator checkbox + documented). - [ ] Live `GET /health` includes a hex `git_sha` matching the baked commit after a main land (not `HEAD`). - [ ] Tests: ok-only when env missing; hex present when valid; `HEAD` / empty / secret-shaped omitted. - [ ] `/api/v1/health/fee-discount` unchanged. - [ ] No Coolify uuid or token in source or issue comments. ## Test plan (all paths) - Health 200 with missing env; valid 40-char; 7-char prefix; `HEAD`; empty; uppercase hex normalized or omitted (pick one, test it). - Fee-discount health still 200/fail-closed as today. - Security tests: health still 200, no extra headers required. ## Test plan (attack / hack / abuse) - Unauthenticated client must not learn inventory, tokens, or Coolify uuids from `/health`. - Injected env that looks like a bearer / Sanctum `N|` must be omitted. - Path traversal / host override via health JSON: not applicable (static object). - Do not make `/health` wait on LCD or DB (DoS); fee-discount stays the slow probe. ## Verification Indexer unit/api tests. Operator glance after land: frontend **and** indexer serve the new tip; `GET /health` `git_sha` matches `git rev-parse HEAD` on that merge (prefix OK). No Coolify log scrape. **First-pass model:** grok-high
Author
Owner

cl8y-agent-control: queued design_author job 6c0376c1-1fdb-4b6d-8377-005d871f4e6f (not executed; no Hetzner VM).

cl8y-agent-control: queued `design_author` job `6c0376c1-1fdb-4b6d-8377-005d871f4e6f` (not executed; no Hetzner VM).
Author
Owner

Merge complete: PR #1282 landed on main (5d11185e).

Met (code): optional git_sha on GET /health (hex 7–40, omit HEAD/secrets); Dockerfile runtime GIT_SHA/SOURCE_COMMIT; colliding sqlx versions split to 20260916120000 USDT + 20260916120001 protocol-fee hops; make verify-issue-1276 was green on the PR.

Not met (operator leftover): Coolify protected-main auto-deploy, include-source-commit bake, and live /health git_sha matching tip. This issue auto-closed from the PR "Closes" line; leftover is tracked in a new issue. Do not infer the checkbox from HTTP.

Merge complete: PR #1282 landed on main (`5d11185e`). **Met (code):** optional `git_sha` on `GET /health` (hex 7–40, omit HEAD/secrets); Dockerfile runtime `GIT_SHA`/`SOURCE_COMMIT`; colliding sqlx versions split to `20260916120000` USDT + `20260916120001` protocol-fee hops; `make verify-issue-1276` was green on the PR. **Not met (operator leftover):** Coolify protected-main auto-deploy, include-source-commit bake, and live `/health` `git_sha` matching tip. This issue auto-closed from the PR "Closes" line; leftover is tracked in a new issue. Do not infer the checkbox from HTTP.
Author
Owner

Follow-up leftover: #1284 (Coolify auto-deploy + live git_sha).

Follow-up leftover: #1284 (Coolify auto-deploy + live git_sha).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#1276
No description provided.