README: verified quickstart (Docker builder, dry-run, live demo), mermaid
architecture, CLI/config/routing reference tables, status table from
REPORT.md, correct ukrrs repo URLs. docs/: Status front-matter lines on
all PORTING-NOTES (secrets notes now tracked). DESIGN: table of contents
with anchor links; sections untouched.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
6.5 KiB
Porting notes: KNELSecretsManager (reference/KNELSecretsManager)
Scope for MOPAC harness: Bitwarden secret retrieval for bw: key refs
(reserved in internal/config/keys.go:34-35, errors until "phase 3").
DESIGN.md: Go bitwarden-go wrapper replaces KNELSecretsManager; secrets
never in logs. Line refs from study 2026-08-28, drift possible.
Status: 2026-08-28 — complete; open questions for Charles pending (section 3), feeds ukrrs/mopac-bitwarden-go.
1. Current surface
Two generations ship in the repo; ADR-002 (docs/ADR-002-ContainerBW.md)
supersedes the standalone script with a containerized bw wrapper.
Legacy secrets-manager.sh (v2.0) — single bash script, CLI only:
install | get <name> | list | test, -c/--config FILE. Every invocation
redoes the full lifecycle: source plaintext config (BW_SERVER_URL,
BW_CLIENTID, BW_CLIENTSECRET, BW_PASSWORD) -> bw config server ->
bw logout -> bw login --apikey -> bw unlock --raw -> bw get password <name> (secrets-manager.sh:119-159). Exit codes 10/20/30/40/50/60/70
(config/bw-missing/install/server/session/secret/login). Logs to
/tmp/secrets-manager.sh.log (names, not values). Designed to be vendored
into shell frameworks; Makefile lint/test/vendor-test targets.
Current container wrapper (production since 2026-08-13, ADR-002):
bin/bw-install.sh— downloads pre-compiled bw2026.7.0(inside an alpine container; no host Node/wget), buildsreachableceo-bw-native: 2026.7.0(debian:bookworm-slim + ca-certificates,docker/bw-native/ Dockerfile), installs wrapper to~/.local/bin/bw+ entrypoint to~/.local/share/bw/entrypoint.sh.bin/bw-cli.sh(host) — loads~/.config/bw/env(single-quoted values, mode 600; the only secrets-on-disk allowed org-wide), thendocker runwith named volumetsys-bw-cli-statemounted at/root/.config/Bitwarden CLIso login state persists across calls.bin/bw-entrypoint.sh(container) — idempotent:bw config server->bw login --apikey(no TOTP; API key minted from an authenticated web session) ->bw unlock --passwordfile(password briefly in /tmp) ->bw sync->exec bw "$@"with BW_SESSION.- Interface is transparent
bw:status | list items/collections | get password|totp|item <name> | generate. Lookup is by item name; the default secret is the item's password field (e.g.APIKEY-pushover). - Backend: Vaultwarden at
https://pwvault.turnsys.com, machine accountcoo@turnsys.com, API-key auth + master-password unlock.
Consumers: nothing in the MOPAC harness consumes it yet — sole planned
integration is the bw: key-ref prefix (internal/config/keys.go,
asserted by config_test.go:126,130; harness.toml.example:5). First
real secrets needed: Redmine API key + LiteLLM key ([redmine] key_ref,
[litellm] key_ref). Siblings still on Bitwarden: KNELCredsManager
(~/.creds/*.env feeder) and mcp-bitwarden-wrapper.sh in
KNEL-AIMiddleware.
Weaknesses to fix in the port: plaintext master password + API secret
on disk; full login/unlock/sync per call (slow, racy logout in legacy);
secret values echoed to stdout and captured by callers into env; docker
required per host; "native" bw binary is secretly a Node SEA (ADR-002
caveat); legacy script bw logouts any concurrent session.
2. Replacement design sketch (Go)
Shape: internal/secrets package + thin MOPAC/tools/bitwarden-go
CLI (per DESIGN.md:88) so both harness and humans share one resolver.
Config gains ResolveKeyRef case for bw:REF (keys.go already reserves
it; keep redact() semantics).
bw execution: exec bw from PATH (the ADR-002 wrapper makes it
transparent whether bw is native or containerized). Treat as opaque
subprocess: bw get password <name> --session <tok> --nointeraction,
JSON mode where useful (bw get item <name>), 30s timeout, capture
stdout into a []byte not a logged string.
Machine credentials (bootstrapping only): prefer env
BW_CLIENTID/BW_CLIENTSECRET/BW_PASSWORD; fallback ~/.config/bw/env
parsed by hand (single-quoted values, require mode 0600). Never in
harness.toml, never in logs. Server URL from env BW_SERVER with
Vaultwarden default.
Unlock strategy: once per process — config server (idempotent),
bw login --apikey (skip if bw status says logged in), bw unlock --passwordenv BW_PASSWORD --raw -> session key held in memory only;
single bw sync after login. On session-expired error mid-run: re-unlock
once, then fail. Never bw logout (breaks concurrent clients).
Lookup by key: bw:<item-name> -> password field (matches current
usage). Optional bw:<name>#<field> and bw:<name>#totp if non-password
fields are needed. Not-found => typed ErrSecretNotFound; auth failures
=> ErrUnlockFailed (mirrors exit codes 60/50/70 for callers).
Caching: per-run in-memory map[ref]string + mutex (multi-vertical
workers may resolve concurrently); negative results cached too. Session
token cached for process lifetime, zeroed on exit. No disk cache of
values; optional disk persistence of BW_SESSION is an open question.
Never-log rules (extends keys.go:70-78): log refs only as
bw:<name>****; error strings never embed values; subprocess stderr from
bw is scrubbed/dropped (bw can echo fragments); secrets never written to
REPORT.md, spans, or crash dumps; go test uses fake bw stub, never the
real vault.
3. Open questions for Charles
- Substrate: keep shelling out to
bw(containerized per ADR-002, Docker on every harness host) or implement the Vaultwarden REST API directly in Go (truly zero Node/bw, but reimplements crypto + sync)? DESIGN.md:84 says "replaces Node bw CLI dependency" — which side of that line do you want? - Machine account: dedicated service account for the harness instead
of
coo@turnsys.com, with a vault collection scoped to only the secrets the harness needs? - Ref syntax: is
bw:<item-name>(password field) sufficient for v1, or do you want#field/#totp/ collection-scoped refs now? - Session lifetime: unlock per
harness oncerun only, or persist BW_SESSION (0600, TTL) across runs to avoid unlock cost each start? - Secret inventory + names: confirm exact vault item names for
Redmine and LiteLLM keys so
key_refvalues can be pinned in harness.toml.example. - Scope of port: absorb KNELCredsManager's
~/.creds/*.envcontract too, or strictly thebw:refs used by the harness? - Subprocess injection: any consumers needing secrets as env vars for child processes (LiteLLM?), vs HTTP-header-only as today?