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) and StorePolicyLookup implementing glaz-govern's PolicyLookup. Fail-closed throughout: fixed-label errors (never ureq's Display, 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/warn skipped like disabled (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 transpiled scope_rego fails closed. Blocking ureq 3 with platform trust roots. The entity's policy_rego carries executable Rego, transpiled by the store from the policy's structured rules (RuleProgramMerger); the store serializes null when transpilation fails, and such a policy fails the lookup closed.
  • glaz-govern: the LookupError taxonomy (#[non_exhaustive], mirroring LookupKey) and the cap alignment.
  • deny.toml: per-crate CDLA-Permissive-2.0 exceptions 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_candidates has no API route yet) — the remaining store-side activation blocker (the entity now serves executable policy_rego), tracked in !139 (closed).

Testing

  • cargo test --workspace passes — 268 tests; the crate's 25-test stub-server suite (single-request local TcpListener, no new dev-dependencies) runs in the default build
  • cargo clippy --workspace --all-targets is clean
  • cargo fmt --all --check is 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
Edited by Artur Fedorov

Merge request reports

Loading
Loading