# Maintained AI-context metadata for `kvasir` (docs/ai-context*.md).
# Facts derivable from the code (crate list, deps, API, components) are NOT
# duplicated here — only what an AI agent cannot infer from the tree.
config_precedence = [
"Compiled defaults: gRPC-web door at {origin}/grpc; PAGE_SIZE 100; search limit 200.",
"Build-time env (option_env!): HOLGER_GRPC_WEB_URL overrides the door URL; HOLGER_OIDC_ISSUER / HOLGER_OIDC_CLIENT_ID prefill the SSO settings.",
"localStorage (operator settings): OIDC issuer + client id, saved from the login view — override the build-time prefill.",
"sessionStorage (per-tab credential): the bearer token — set by either login form, attached to every call by the one interceptor in grpc.rs.",
]
reasoning_warnings = [
"proto/holger.proto is VENDORED — the copy in the holger main repo (nordisk/holger on git.gunnar.rs) is the source of truth. Refresh with `cp ../holger/proto/holger.proto proto/` and commit the diff; build.rs regenerates stubs from the local copy only.",
"tonic here is 0.14 (codegen-only, no transport) while holger's server is tonic 0.13 — DELIBERATE skew; the gRPC-web wire protocol is independent of either side's tonic version. Do not 'fix' by downgrading.",
"The crate is `holger-ui-web` but the repo is `holger-ui` — the holger main repo already has an egui crate named `holger-ui` (server/ui); the names must never collide in a workspace or patch table.",
"The gRPC-web door mounts under /grpc (dev: Trunk proxy; prod: reverse proxy) because Trunk matches whole path segments — a bare /holger.v1. prefix never matches the real first segment holger.v1.AdminService. A production host must ALSO fall back to index.html for unknown paths (SPA routing).",
"CORS is deliberately NOT opened on the holger server — deployments are same-origin behind a proxy. HOLGER_GRPC_WEB_URL pointing at a foreign origin requires CORS at that proxy instead.",
"The theme uppercases h3/dt via CSS text-transform — element.innerText reflects the TRANSFORMED text, so text assertions in browser-driven tests must match case-insensitively (this bit our own regression suite).",
"Property state never refetches: SetPropertyResponse returns the coordinate's full map and the table renders THAT. The SBOM resource, by contrast, refetches after attach — the hosted bytes are the truth, not an optimistic echo.",
"The search checksum axis forces the server to hash candidate artifacts — it is the one expensive query axis; the placeholder says so and the UI must keep saying so.",
"The bearer token lives in sessionStorage (XSS-readable by design tradeoff, documented in grpc.rs); there are no cookies and thus no CSRF surface. Log out removes it; a reload keeps it; closing the tab drops it.",
"OIDC PKCE callbacks are only honored when THIS tab has a pending attempt, and the verifier is consumed one-shot on success OR failure — a forged callback burns the attempt. The IdP must allow the console origin as redirect URI and for CORS on its token endpoint.",
"Every mutation surface shows the server's refusal VERBATIM (RBAC, promote seal, quarantine) — the console adds no client-side authorization and must not start rephrasing server messages.",
]
[output.compact]
max_tokens = 20000
[output.full]
max_tokens = 80000
[repository]
purpose = "Web operator console for holger (the airgap-first artifact registry): a Leptos/WASM SPA speaking gRPC-web straight to the server's tonic-web door. proto/holger.proto is the entire contract — no REST layer, no BFF."
system_role = "Browser client covering holger's full service surface: repositories, browse, search, artifact detail (download/SBOM/properties), upload, promote, replicate, and auth (bearer paste + OIDC PKCE)."
language = "Rust → wasm32-unknown-unknown (Leptos 0.8 CSR, trunk build)"
binaries = ["holger-ui-web (wasm; served as static files from dist/)"]
runtime_components = [
"grpc.rs: the one place that knows the server URL, builds the seven generated service clients, and attaches the bearer credential via a tonic interceptor.",
"app.rs: the routed views — every view is a shareable URL that fetches what it needs from its address alone.",
"oidc.rs: OIDC authorization-code + PKCE (S256 via SubtleCrypto); the resulting access token lands in the same store as a pasted one.",
"Trunk dev loop: trunk serve on :8090 proxying /grpc → a local holger-server on :50051.",
]
non_goals = [
"No REST/BFF layer — the browser speaks gRPC-web to holger directly; adding a middle tier reverses the design.",
"No client-side authorization — roles are the server's RBAC mapping; the console only displays refusals.",
"No font/JS CDN — the console must render identically inside an airgap.",
"Not the egui operator app — that is `holger-ui` (server/ui) in the holger main repo; this repo is the web console only.",
]
[[crate_summary]]
name = "holger-ui-web"
summary = "[web-console] The whole SPA: generated gRPC-web clients + routed Leptos views + OIDC PKCE. Single crate; the Vetra theme lives in index.html as plain CSS custom properties."