Files
ModelForge-Public/docs/architecture/adr/0053-huggingface-control-plane-secret-slot.md

52 lines
4.0 KiB
Markdown

# ADR-0053: Bounded Hugging Face control-plane secret lifecycle
Status: accepted design; implementation and qualification in progress.
Only `huggingface_control_plane` is API-managed. It supplies the control-plane Hugging Face
discovery and repository metadata adapter. Node-agent downloads retain their independently
deployed credential; no operation here acknowledges node credential activation. Database,
migration, operator/recovery, backup encryption and Redis credentials remain deployment-managed.
Deployment mode remains until the first explicit set. Thereafter no missing, revoked, restored,
undecryptable or inactive managed state falls back to deployment or ambient HF credentials.
Set persists a new encrypted candidate and switches to managed mode; an existing active version
continues serving during rotation. Test calls only the fixed HTTPS Hugging Face whoami-v2 endpoint
with bounded time/body, no redirects or environment proxies, and redacted results. It proves token
authentication, not repository entitlement or node downloads. Activation requires a successful
test within five minutes for the exact candidate version and current revision. Each mutation
uses CAS and atomic attributable audit. Network testing occurs outside the mutation transaction;
the result cannot bless a concurrently replaced/revoked candidate. Revocation disables all managed
versions; recovery requires setting a new candidate, testing and activating it.
Use AES-256-GCM from the existing cryptography dependency, fresh 96-bit nonces and associated data
binding slot, version and key ID. `MODELFORGE_SECRET_SLOT_ROOT_KEY` is strictly a base64 32-byte key,
supplied externally, independent of backup encryption and operator credentials. Its explicit ID
is stored on every version. This bounded first implementation supports one mounted/deployment
key at a time; replacing that key does not silently rewrap history. Retain old external keys for
historical restore, or set/test/activate a new version before retiring the prior deployment key.
The root key cannot be managed through the slot API. No plaintext reads, exports or audit fields.
State and encrypted versions are authoritative. Test results and active admission are current
truth, cleared by supported restore and independently checked before READY. Restored candidates
require a fresh test and explicit activation. Old-schema restore permits only the exact initial
empty state introduced by migration 0028. A history-bearing downgrade requires verified backup
recovery. Retired ciphertext remains historical; provider revocation cannot be undone locally.
New provider instances resolve one snapshot when upstream access starts. Persisted history and
node job progress, completion and cleanup do not require upstream credential resolution; a revoked
slot must not strand an existing job's bookkeeping. Activation acknowledges this
local resolver, not remote downloads or running requests. Provider failure never becomes a
successful test. Missing root-key disables the managed consumer without blocking deployment
administrator access needed for recovery. SQLite qualification is not PostgreSQL race/restore
qualification; live HF account checks and isolated RC provisioning remain external gates.
Persistence contract: migration 0028 introduces two authoritative tables and two current-truth
admission tables. The runtime role can insert encrypted versions but can update only their
`revoked_at` column; it cannot rewrite version numbers, key IDs, nonces, ciphertext or creation
timestamps, nor delete/truncate historical rows. The recovery owner retains the isolated
maintenance authority required by supported restore. Metadata fingerprints explicitly exclude
ciphertext and nonces; encrypted archive authentication and AES-GCM validate secret contents.
`SECRET_STRICT` retains all secret metadata while delegating the existing verified reconciliation
marker exception to `MANAGED_STRICT`. Earlier schemas accept only the exact uninitialized seed
and empty version history introduced by 0028, never arbitrary newly introduced secret state.