docs: rewrite README to the docs standard; polish docs/
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
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# 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 bw `2026.7.0` (inside an
|
||||
alpine container; no host Node/wget), builds `reachableceo-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), then `docker run`
|
||||
with named volume `tsys-bw-cli-state` mounted at `/root/.config/Bitwarden
|
||||
CLI` so 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 account
|
||||
`coo@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 logout`s 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
|
||||
|
||||
1. **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?
|
||||
2. **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?
|
||||
3. **Ref syntax**: is `bw:<item-name>` (password field) sufficient for
|
||||
v1, or do you want `#field` / `#totp` / collection-scoped refs now?
|
||||
4. **Session lifetime**: unlock per `harness once` run only, or persist
|
||||
BW_SESSION (0600, TTL) across runs to avoid unlock cost each start?
|
||||
5. **Secret inventory + names**: confirm exact vault item names for
|
||||
Redmine and LiteLLM keys so `key_ref` values can be pinned in
|
||||
harness.toml.example.
|
||||
6. **Scope of port**: absorb KNELCredsManager's `~/.creds/*.env`
|
||||
contract too, or strictly the `bw:` refs used by the harness?
|
||||
7. **Subprocess injection**: any consumers needing secrets as env vars
|
||||
for child processes (LiteLLM?), vs HTTP-header-only as today?
|
||||
Reference in New Issue
Block a user