feat: glaz-policy-store-client crate + lookup error taxonomy
What
Adds the Policy Store REST client as a new workspace crate, glaz-policy-store-client, together with the two glaz-govern seam changes it builds on: LookupError gains InvalidResponse and Rejected (three failure classes, three different owners — store on-call, integration config, transport), and MAX_POLICIES_PER_TRIGGER rises to 100, matching the store's Govern::Policy::EVALUATION_LIMIT. Inert: nothing consumes the crate in this MR, so there is zero behavior change for any host.
Why
STEP 3a of the 19.3 delivery plan (gitlab-org/gitlab#608189 (closed)) for gitlab-org/gitlab#607650: the engine needs to fetch real policies from the Policy Store's merged endpoints (gitlab-org/gitlab!249148 (merged) + !249439). A crate rather than a cargo feature — settled in !139 (closed)'s review — so the boundary is the dependency graph: glaz-govern stays free of HTTP by construction (cargo tree -p glaz-govern carries no ureq), and a future embedded Policy Store can never link a fetching layer that would call itself.
Carved out of !139 (closed) on review request (!139 (closed), comment 3736928080); !139 (closed) is stacked on this branch and switches glaz-module/FFI onto the client. All content here already went through four review rounds there.
Changes
glaz-policy-store-client(new crate):StoreConfig(builder;Debug-redacted auth header, 500 ms/2 s default timeouts pinned by test — including behaviorally against a stalled server) andStorePolicyLookupimplementingglaz-govern'sPolicyLookup. Fail-closed throughout: fixed-label errors (never ureq'sDisplay, which embeds the URL and can echo the token), no redirects, every served entity validated against the requested key, unrecognized lifecycle/mode is schema drift,audit/warnskipped likedisabled(one audit policy must not brick the org's gate), >100 entities and >8 MiB / non-UTF-8 bodies rejected, and a scoped policy without transpiledscope_regofails closed. Blocking ureq 3 with platform trust roots. The entity'spolicy_regocarries executable Rego, transpiled by the store from the policy's structuredrules(RuleProgramMerger); the store serializesnullwhen transpilation fails, and such a policy fails the lookup closed.glaz-govern: theLookupErrortaxonomy (#[non_exhaustive], mirroringLookupKey) and the cap alignment.deny.toml: per-crateCDLA-Permissive-2.0exceptions for the webpki root-store data crates only — any other CDLA-licensed dependency still fails the gate.
Out of scope / unchanged
- Consumption — module/FFI wiring, explicit construction, fixture retirement, conformance: !139 (closed) (stacked on this MR).
- Caching / hot-path strategy: #17, decision to be recorded on gitlab-org/gitlab#604407.
- The bounded engine-facing store route (
Govern::Policy.evaluation_candidateshas no API route yet) — the remaining store-side activation blocker (the entity now serves executablepolicy_rego), tracked in !139 (closed).
Testing
-
cargo test --workspacepasses — 268 tests; the crate's 25-test stub-server suite (single-request localTcpListener, no new dev-dependencies) runs in the default build -
cargo clippy --workspace --all-targetsis clean -
cargo fmt --all --checkis clean
Author checklist
- Title follows Conventional Commits and breaking changes are flagged (additive — no host-visible change)
- Docs / comments updated where behaviour changed
- No unrelated changes swept in