Skip to content

Commit ff9f2be

Browse files
fix(hcg-policy): cover wired POST /cartridge/:name/sse (Phase E §1.5) (#165)
## Summary Re-verifies `config/gateway-policy-boj-example.yaml` against the live `BojRest.Router` for HCG tier-2 rollout (Phase E §1.5 prereq, `standards#100`). The wired `POST /cartridge/:name/sse` route — `elixir/lib/boj_rest/router.ex` line 130, called out in ADR-0013 §6 and the STATE entry 2026-05-18 ("boj-rest SSE: POST /cartridge/:name/sse on the same single Cowboy listener + trust-gated dispatch") — was absent from the example policy. Silent surface drift since contract v1.0; exactly the risk Phase A flagged. - Adds `cartridge-sse-post` rule (regex `^/cartridge/[A-Za-z0-9_.-]+/sse$`, POST, `authenticated`) alongside `cartridge-invoke-post`. Same `BojRest.Router.check_trust/3` gate, same per-cartridge `auth.method` requirement, streaming envelope around a single dispatch. - Updates the example-policy "Surface source" header to record the 2026-05-28 re-verification and point at the new rule. - Ticks the §1.5 surface-coverage checkbox on `docs/integration/hcg-tier2-rollout-runbook.md` with the re-verification date; flags live-policy promotion as the remaining work before §3.1. The unrelated top-level `/sse` GET rule (declared-not-yet-wired, openapi.yaml only) is left in place — different path, different verb, different surface. ### What this PR does NOT do - Does **not** close `standards#100`. Phase E still has: gateway smoke-test against this policy (§1.5 next box); live-policy promotion (`config/gateway-policy-boj.yaml`); §1.4 `!OWNER:` block; Phase D-4 baseline (`bench/baseline.json _status` still `"scaffold-placeholder"` upstream — gates the perf-regression CI alert); staging soak (§2.3); production rollout (§3); §6.4 Trustfile flip. - Does **not** change runtime behaviour (config-only). ## Test plan - [x] Policy YAML re-parses (`python3 -c "import yaml; yaml.safe_load(open('config/gateway-policy-boj-example.yaml'))"`); 27 → 28 routes; `global_verbs` still `[GET, POST]`. - [x] Both `sse-get` (top-level, declared-not-yet-wired) and `cartridge-sse-post` (per-cartridge, wired) appear as distinct rules under the parsed `governance.routes` list. - [x] All seven routes wired in `BojRest.Router` (`/.well-known/boj-node-pubkey`, `/health`, `/menu`, `/cartridges`, `/cartridge/:name`, `/cartridge/:name/invoke`, `/cartridge/:name/sse`) now have a matching policy entry. - [ ] Future (separate session, Phase E §2.1): stand the gateway up against this policy, confirm `POST /cartridge/:name/sse` with `X-Trust-Level: authenticated` proxies through and with `X-Trust-Level: untrusted` returns 403. Out of scope for this PR; that flips the §1.5 smoke-test checkbox, not this one. Refs hyperpolymath/standards#100 Refs hyperpolymath/standards#91 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01EEtktiR4ynY2dMPC1nD7rv)_ Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 47c4517 commit ff9f2be

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

config/gateway-policy-boj-example.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
# declared in openapi.yaml but not yet wired in router.ex are governed anyway
2424
# (contract §8) so they are not silently exposed when implemented.
2525
#
26+
# Re-verified 2026-05-28 against `BojRest.Router` for HCG tier-2 rollout
27+
# (Phase E §1.5 prereq, standards#100). Recorded drift since contract v1.0:
28+
# the wired `POST /cartridge/:name/sse` route (router.ex line 130, ADR-0013
29+
# §6, STATE entry 2026-05-18) was absent from the example policy. Added below
30+
# as `cartridge-sse-post` alongside `cartridge-invoke-post`. The unrelated
31+
# top-level `/sse` GET rule (declared-not-yet-wired, openapi.yaml only) is
32+
# left in place — different path, different verb, different surface.
33+
#
2634
# ---------------------------------------------------------------------------
2735
# DEFAULT-DENY NOTE (must be confirmed in the Phase A manual verification)
2836
# ---------------------------------------------------------------------------
@@ -120,6 +128,22 @@ governance:
120128
check_trust/3 requires internal/authenticated for cartridges whose
121129
auth.method is not `none`."
122130

131+
- path: "^/cartridge/[A-Za-z0-9_.-]+/sse$"
132+
verbs: [POST]
133+
exposure: "authenticated"
134+
name: "cartridge-sse-post"
135+
narrative: "Tool dispatch into a cartridge over Server-Sent Events
136+
(text/event-stream: open → result|error → done; ADR-0013 §6, router.ex
137+
line 130). Same `BojRest.Router.check_trust/3` gate as
138+
cartridge-invoke-post, same per-cartridge auth.method requirement —
139+
just a streaming response envelope around one dispatch. The handler
140+
is bounded (single tool invocation framed by open/done), not
141+
long-lived, so the proxy-timeout / circuit-breaker risk flagged on
142+
sse-get is narrower here: bracketed by tool latency, not by stream
143+
duration. Phase D benchmarks should still cover this path before the
144+
live policy promotes it (Phase E §1.5 surface-drift verification,
145+
standards#100)."
146+
123147
- path: "/graphql"
124148
verbs: [POST]
125149
exposure: "authenticated"

docs/integration/hcg-tier2-rollout-runbook.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ These cannot be inferred from the code/contract; the owner must fill them before
8181

8282
- [ ] Gateway Containerfile built and signed as a `.ctp` bundle via cerro-torre (plan §E1).
8383
- [ ] `container/gateway-deploy.k9.ncl` exists in the gateway repo (plan §E1).
84-
- [ ] Gateway policy file in place: `config/gateway-policy-boj-example.yaml`, covering all BoJ surface routes (`/.well-known/boj-node-pubkey`, `/health`, `/menu`, `/cartridges`, `/cartridge/:name`, `/cartridge/:name/invoke`, plus any added since contract v1.0 — re-verify against `BojRest.Router` at rollout time).
84+
- [x] Gateway policy file in place: `config/gateway-policy-boj-example.yaml`, covering all BoJ surface routes (`/.well-known/boj-node-pubkey`, `/health`, `/menu`, `/cartridges`, `/cartridge/:name`, `/cartridge/:name/invoke`, `/cartridge/:name/sse`, plus any added since contract v1.0). Re-verified 2026-05-28 against `BojRest.Router`; the `POST /cartridge/:name/sse` route (router.ex line 130, wired since the SSE landing — ADR-0013 §6, STATE entry 2026-05-18) was the only drift since contract v1.0 and is now governed by the `cartridge-sse-post` rule alongside `cartridge-invoke-post`. The live policy file (`config/gateway-policy-boj.yaml`, per the example header) is still to be promoted from this example before §3.1.
8585
- [ ] Gateway has been smoke-tested in isolation with the policy, returning expected allow/deny on each route.
8686

8787
---

0 commit comments

Comments
 (0)