--- id: review-001-publish-schema-validation-robust name: Fix /publish schema validation fail-open + per-request recompilation (post-remediation) status: pending depends_on: [] scope: narrow risk: high impact: component level: implementation tags: [gateway, review-001, follow-up] --- ## Description Follow-up to review-001-gateway-publish-semantics (GW-01): the landed fix compiles the op's `publish_schema` **on every `/publish` request** (`src/gateway/routes.rs:254-268`) and, when compilation fails, logs a warning and proceeds with **no validation** — chunks flow to the handler unvalidated. Two remediation-introduced problems: - **Fail-open on compile failure** — this reintroduces, conditionally, the exact transport-dependent invariant GW-01 closed: a handler that registered a `publish_schema` (or a remote import whose schema is un-compilable by jsonschema 0.46's dialect) silently receives arbitrary JSON over the HTTP path that the wire path would have aborted. A registry with one broken schema becomes an unvalidated ingest path with only a `tracing::warn` as the tell. - **Per-request compile cost** — `jsonschema::options().build()` on the hot path is CPU + allocation per publish call; a hostile or merely busy caller multiplies it. Fix direction (implementer's choice on mechanism): compile **once per registration** — cache the compiled `Validator` (or the compile error) against the op in the gateway state, invalidated on re-registration — and make compile failure **loud and closed**: the request path surfaces a server-fault (500, `INTERNAL`-class with the compile error logged at error level), never a silent skip. Consider whether adapters can reject un-compilable `publish_schema` values at import/registration time so the failure lands before any traffic (adapter-side validation where the schema originates), with the gateway cache as defense in depth. ## Acceptance Criteria - [ ] Compiled validator (or error) cached per registration; no per-request recompile (test: compile count / perf shape not asserted, but code path is registration-keyed) - [ ] Un-compilable `publish_schema` → request fails loudly (500/INTERNAL, error logged), chunks never flow unvalidated (test) - [ ] Schema re-registration (hot reload) picks up the new schema (test) - [ ] A `publish_schema`-registered Pub op still rejects an invalid chunk (existing GW-01 gate stays green) - [ ] `cargo test` and `cargo clippy --all-targets -- -D warnings` pass ## References - docs/reviews/001-initial-implementation-review.md (Part C, GW-01) - tasks/gateway/review-001-gateway-publish-semantics.md (the landed fix + rationale) - src/gateway/routes.rs:254-268 (the fail-open) ## Notes > Agent fills during implementation. Found in the post-remediation > sweep — not a Review 001 finding itself. Fail-open is the priority > half; caching is the efficiency half. Registration-side (adapter) > rejection at import is the preferred end state if alkcall's > registration surface allows observing `publish_schema` at > HandlerRegistration time. ## Summary > Filled on completion.