- Status: Accepted
- Date: 2026-09-02
- Scope: cross-repository admin-web architecture for
noema,contextual-orchestrator, andkeyverse
The owner asked for admin web UIs across three repositories
(noema, contextual-orchestrator, keyverse) and for mutual
integration so keyverse — currently a Keycloak-fronting central Identity
Provider — can also be used as a Keyvault (secrets/credential management,
analogous to Azure Key Vault or HashiCorp Vault), later expanded by the
owner to two further Keyverse capabilities: service-to-service ABAC/RBAC,
and a "login credential store" for service-account/machine credentials.
Direct repository research (cloned fresh, not assumed) found:
contextual-orchestratoralready runs a real, serving/adminoperator console (admin.py, inline stdlib HTML/JS, eight Figma-grounded screens) with no per-model LLM timeout control — the exact gapdocs/product-goal-directive.md§8 already names. Anadmin_ui/React+Storybook scaffold exists but is confirmed (by direct inspection, matching that repo's own planning ADR 0036, superseded) to be the unmodified Vite demo output — no admin-web work in flight there. This was the readiest of the three repos: it already had a serving console, an established KV/audit pattern (credentials.py,model_groupfamily), and an explicit product requirement to build against.keyversehad no encrypted secrets store (kv_store.py'sidp_config_entriesis its own internal, unencrypted config — never a generic secrets product surface) and no frontend of any kind. PR #103 (open, Draft) already implements most of the requested service ABAC/RBAC capability (authorization_plane.py,org_authorization.py, ADRs 0010–0012) but is not currently mergeable.noemais a Cloudflare Worker OIDC/credential-exchange broker with only/health,/ready,/exchangeand Durable-Object-only internal state — no admin-readable HTTP surface exists to build a console on top of today. The least ready of the three.
Per this repo's own scoping guidance for genuinely multi-week product work, the correct first iteration is the smallest real, honestly-scoped slice per repo — not three parallel half-built admin webs.
- Keyverse is the shared SSO provider for every admin web in this
ecosystem. It is already the org's central IdP; admins authenticate
to each product's admin console via Keyverse OIDC rather than a
per-repo local admin credential. This is itself the "상호 연계"
(mutual integration) the owner asked for, independent of the Keyvault
question. Design only in this iteration —
contextual-orchestrator's/adminstill uses its existing shared-bearer-token session model (/admin/session); wiring Keyverse OIDC in is the next concrete step for that console, tracked as an explicit open item rather than silently deferred. - Each repo's admin web stays a thin frontend over that repo's own
backend API, not a shared cross-repo frontend package — there is no
second consumer of shared UI primitives yet (matching
contextual-orchestrator's own ADR 0033 reasoning for why Storybook/ component tooling stays deferred there specifically). - Keyverse's Keyvault is a bounded context separate from its IdP
identity/config modules, sharing only the KV storage pattern
(Protocol + in-memory/SQLite backends) already proven in that repo,
not any shared table.
contextual-orchestrator's existingCredentialBackendProtocol (pluggable backends, KV-not-env discipline) is the natural adapter target for a futureKeyverseCredentialBackend— the motivating first consumer, not implemented in this pass. Full reasoning:keyverseADR-0014. - Service ABAC/RBAC is not rebuilt here. Keycloak's built-in
Authorization Services (UMA 2.0) exist but are unconfigured in this
deployment and do not natively cover the hierarchical org-path
inheritance CWL's Orgmetra-owned org tree requires; PR #103 already
implements that hierarchy. Recommendation: reconcile and land PR #103
rather than duplicate it. Full reasoning:
keyverseADR-0015. - "Login credential store" is Keyvault plus per-service
Anti-Corruption Layers, not a fourth Keyverse module. Centralizing
secret storage in Keyverse while each consuming service keeps its
own credential-taxonomy knowledge (via its own Protocol adapter, e.g.
contextual-orchestrator'sCredentialBackend) avoids growing Keyverse into a service that must change whenever any consumer's credential schema changes. Full reasoning:keyverseADR-0016. - The first implemented slice is
contextual-orchestrator's per-model LLM timeout admin surface (view/set/clear/restore, units, priority/ inheritance, validation, audit history, API contract — the exact §8 requirement), extending the existing/adminconsole in place per its own ADR 0033/0042.keyverse's Keyvault (write/read/delete/list APIs, encryption at rest via Fernet, audit logging) is implemented alongside it as the second slice, since it was independently ready and directly answers the Keyvault half of the owner's request.noemagets no code change this iteration — it has no admin-relevant state to expose yet; the honest next step there is deciding what operational state (OIDC exchange health/rate, App-token issuance evidence) is worth exposing before building a console around it.
- No repo gained a half-built parallel admin frontend; each shipped either a real, tested slice or an explicit, evidenced "not yet, and here is why" record.
- Cross-repo SSO and the Keyvault-as-credential-backend consolidation are both real, next, concretely-scoped follow-ups — not vague future work — recorded here and in the two repos' own ADRs so the next iteration does not have to re-derive this research.
keyversePR #103 (service authorization) is now more clearly the blocking dependency for capability #2 of the owner's three-capability Keyverse request; this ADR does not change its status, only records that a competing implementation was deliberately not built.
- Build out
admin_ui/(React+Storybook) forcontextual-orchestratorinstead of extendingadmin.py. Rejected: contradicts that repo's own operative ADR 0033, and no revisit trigger from that ADR is met by this work. - Build a from-scratch policy engine for Keyverse service ABAC/RBAC. Rejected: PR #103 already implements the actual (hierarchical, org-path-aware) requirement; a second implementation would duplicate ~2,000 lines of already-written, already-tested domain logic.
- Centralize per-service credential semantics inside Keyverse. Rejected: violates this org's minimal-Shared-Kernel/Anti-Corruption-Layer DDD convention and would couple Keyverse's deploy cadence to every consuming service's credential taxonomy.
- Force a code change into all three repos this iteration regardless of
readiness. Rejected per this org's own genuinely-multi-week scoping
guidance:
noemahad no admin-relevant surface to build against yet, and forcing one would have meant fabricating state or shipping a console with nothing real to show.
Decision item 6 above named contextual-orchestrator#1010 (per-model LLM
timeout admin surface) as this iteration's first implemented slice. That PR
was subsequently closed unmerged by the repo owner the same day (2026-09-02,
closed_at 05:10:46Z — after this ADR PR was opened at 03:40:12Z), on a
categorical objection independent of this ADR's design: "the current manual
timeout-setting semantics must not become production authority," plus four
distinct unresolved correctness findings in the PR's live-enforcement wiring
(local queue path ignores the override, passthrough/tool requests bypass it,
failed persistence can leave the live timeout mutated, and admin-refresh races
can misreport/stale audit state). A subsequent repair-policy recheck (recorded
on the PR and in docs/product-technical-gap-baseline.md) confirmed this
closure is valid under the org's repair-not-close policy's "explicit user
instruction" ground, and that the PR's delta is preserved (not orphaned) on
its own closed branch for selective future reuse once a research-/standard-backed
timeout allocator exists to host it — not revived as-is.
This ADR's own architecture decisions (1–5) are unaffected — they concern
the SSO/Keyvault/ABAC-RBAC/credential-store shape, not the timeout-surface
implementation. Only decision item 6's specific claim that the timeout slice
was "implemented" is now stale. keyverse#129 (Keyvault, this iteration's
second slice) is unaffected by this and remains open. Left as an update rather
than rewriting the original decision record, so the historical reasoning
trail (what was true when each decision was made) stays intact.
contextual-orchestratorplanning ADR 0033 (admin console UI tooling boundary), 0036 (superseded React/Storybook proposal), 0042 (per-model timeout admin surface — this iteration'scontextual-orchestratorslice, subsequently closed unmerged; see Update above).keyverseADR-0014 (Keyvault bounded context), ADR-0015 (service authorization plane), ADR-0016 (login credential store).docs/product-technical-gap-baseline.md, 2026-09-02 entry (repair-policy recheck ofcontextual-orchestrator#1010's closure).docs/product-goal-directive.md§8 (LLM/orchestration; the per-model timeout admin requirement this ADR's first slice attempted to close).