# ADR-002: Credential brokering for rein-openweights; composable reins deferred - Status: accepted (part 1), open (part 2) - Date: 2026-07-26 - Deciders: Bernd Worsch ## Context `GLAS-WP-0001-T06` asked who acquires the OpenRouter credential `rein-openweights` needs: glas-harness (mirroring how it brokers sand-boxer sandbox handles) or the rein itself (mirroring how `rein-aharness` already holds its own OpenBao AppRole lane for IMAP/mail credentials). Discussion surfaced a prior question: should `rein-openweights` talk to OpenRouter through `llm-connect`, or through a leaner, independent wrapper that both `llm-connect` and `rein-openweights` depend on — keeping `llm-connect`'s monitoring/evaluation/optimization features optional rather than bundled in? Checking `llm-connect` directly resolved that half cleanly: - Its own `ARCHITECTURE-LAYERS.md` already separates Core (frozen adapter ABC + models) from Functional (independently shippable per-provider adapters, diagnostics, replay, budget tracking — all explicitly optional: *"Keeps base install lightweight... optional dep, not runtime dep"*) from Configuration (TOML resolution, key lookup). - `resolve_api_key()` (`llm_connect/config.py`) only reads an **already-materialized** key — explicit argument → env var → file. It never talks to OpenBao/ops-warden. `llm-connect/INTENT.md` states this as an explicit boundary: *"not intended to... manage secrets, credentials, or organizational access policies."* So `llm-connect` was never a credential broker regardless of which adapter path `rein-openweights` uses — the earlier concern that "using llm-connect" imports a credential model doesn't hold. `rein-openweights` can depend on just Core + the `OpenRouterAdapter` Functional module and skip diagnostics/replay/budget, the same way any other consumer can today. ## Decision (part 1 — resolved) **Option B: `rein-openweights` acquires its OpenRouter credential directly** (OpenBao/ops-warden), the same way `rein-aharness` already does for its own credentials. glas-harness does not broker LLM-provider credentials. Rationale, in order of weight: 1. Consistency with the existing family precedent. `rein-aharness`'s own INTENT.md states *"the harness is the only credential holder and only policy enforcement point."* Having glas-harness broker for one rein but not the other bakes an inconsistency into the family from its second member. 2. Scope discipline. glas-harness has zero live end-to-end proof yet (`GLAS-WP-0001-T04` is a unit-tested wiring, not a live run) — growing it into a secrets broker ahead of validated need repeats the premature-generalization mistake ADR-001 was written to avoid on the sandbox side. 3. Credential lifecycle doesn't map cleanly onto sand-boxer's create/destroy session lifecycle the way a sandbox handle does — the analogy that made Option A attractive was weaker than it first looked. Accepted cost: `rein-openweights` duplicates OpenBao wiring rather than inheriting it from glas-harness. Revisit centralizing it if a third rein repeats the same need — decide from evidence, not in advance. ## Consequences (part 1) - `rein-openweights` (`REIN-OW-WP-0001-T04`) implements its own credential acquisition; it does not wait on or call into glas-harness for this. - `rein-openweights` depends on `llm-connect`'s Core + `OpenRouterAdapter` only — not the full package surface — confirmed against the actual layer boundaries above, not assumed. - `GLAS-WP-0001-T06` is done. `GLAS-WP-0001-T05` (bootstrap `rein-openweights`) is unblocked. ## Part 2 — resolved in ADR-004: composable reins stay deferred See `docs/adr/ADR-004-composable-reins-stay-deferred.md` — the deferral below held even after two more observability additions landed; both turned out simpler as direct implementations, not middleware. ## Part 2 (original framing, for context) Separately, the idea surfaced that monitoring/evaluation/optimization (capabilities `llm-connect` already stages as optional Functional modules) could eventually become their own **composable reins** — cross-cutting capabilities that wrap or observe another rein's `dispatch_tool` calls, rather than each being a complete alternative harness backend the way `rein-aharness`/`rein-openweights` are. This is a real extension to `docs/harness-contract.md`: the current `Rein` ABC assumes one rein *is* the whole session, with no wrapping/composition concept (no middleware chain, no way for a "monitoring rein" to sit between glas-harness and the base rein's calls). **Left open deliberately** — it does not block `rein-openweights` bootstrap and there is only one candidate capability (llm-connect's existing optional modules) to generalize from, not evidence of a recurring pattern. Revisit if/when a second capability (evaluation, or a distinct optimization concern) wants the same treatment. If pursued, it would need: - A `Middleware`/`ReinWrapper` concept distinct from `Rein` in `contract.py`, with an explicit chain-of-responsibility around `dispatch_tool`. - A decision on whether `llm-connect` itself gets decomposed into reins/middleware, or stays a shared library that both base reins and middleware reins happen to depend on (the leaner option, and the one consistent with `llm-connect`'s own INTENT.md boundary against owning application-level workflow).