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

4.0 KiB

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.