rein-openweights acquires its OpenRouter credential directly (OpenBao/ops-warden), consistent with rein-aharness's existing credential-holder principle — glas-harness does not broker LLM-provider credentials. Confirmed llm-connect itself never brokers credentials either (resolve_api_key() only reads an already-materialized key from explicit/env/file), so routing through llm-connect vs. a leaner wrapper doesn't change this. Composable reins as middleware (monitoring/eval/optimization) is recorded as a separate, deliberately deferred question in the same ADR — one candidate capability isn't evidence of a recurring pattern yet. GLAS-WP-0001-T06 done; T05 (bootstrap rein-openweights) unblocked. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.1 KiB
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.mdalready 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.mdstates 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:
- 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. - Scope discipline. glas-harness has zero live end-to-end proof yet
(
GLAS-WP-0001-T04is 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. - 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-openweightsdepends onllm-connect's Core +OpenRouterAdapteronly — not the full package surface — confirmed against the actual layer boundaries above, not assumed.GLAS-WP-0001-T06is done.GLAS-WP-0001-T05(bootstraprein-openweights) is unblocked.
Part 2 — open, deferred: composable reins as middleware
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/ReinWrapperconcept distinct fromReinincontract.py, with an explicit chain-of-responsibility arounddispatch_tool. - A decision on whether
llm-connectitself 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 withllm-connect's own INTENT.md boundary against owning application-level workflow).