feat(cmd): activate the encryption tier at the composition root

What

Step 2 of the S04-C encryption wiring plan: the present-block activation of the encryption tier.

  • Root-key load chain (infra.Secrets.Getcrypto.DecodeKeyscrypto.NewLocalProviderFromKeys), with the known root-key URI set derived before the decoded slices are zeroized.
  • Secrets-provider resolution: nil infrastructure config resolves through LabKit's DefaultConfig(); a non-nil config with a nil Secrets provider fails boot.
  • Store and manager constructed off the single loader TTL; the manager's sweep-goroutine cleanup registers as a shutdown component and wireServices invokes it on its error path, so no boot path leaks the sweeper (including the panic arm).
  • The row encryptor constructed over the manager; both exposed on the wiring bundle as the enablement signal.
  • Wired-outcome boot log with the active root-key URI fingerprint (never key material).
  • Paired docs: the two fips.md corrections and the new docs/dev/column-encryption.md consumer guide (consumption + the consumer-table contract; migration excluded).

Carried dependency

The first commit (chore: apply the S04-A row-encryptor delta pending its merge) carries open MR !1266 (merged)'s content so this tier compiles ahead of that MR's merge; it drops at rebase once !1266 (merged) lands on main. Reviewing after !1266 (merged) merges shows this MR's own diff only.

Spec coverage

Spec: docs/specs/S04-c-column-level-encryption-wiring.md

Acceptance criteria

# Criterion Tests
AC-1 Absent block: skip path Owned by Step 1 (!1295 (merged)); suite kept green here
AC-2 Single loader TTL to both constructors TestWireEncryption_PresentBlockActivates
AC-3 Present block + no client: boot fails Owned by Step 1 (!1295 (merged))
AC-4 Secret-read failure names encryption/root-key, no key material TestWireEncryption_SecretReadFailureFailsBoot
AC-5 Malformed secret: ErrKeySizeMismatch / ErrNoKeysFound / decode error TestWireEncryption_MalformedSecretFailsBoot (three arms)
AC-6 Fallback-unwrap increment + coercion Not in this MR — owned by Step 3 (!1298 (merged))
AC-7 Cardinality audit Not in this MR — owned by Step 3 (!1298 (merged))
AC-8 Sweeper exits on graceful shutdown and failed boot TestWireEncryption_SweeperStopsOnGracefulShutdown, ...WhenLaterTierFails, ...WhenLaterTierPanics (goleak)
AC-9 -race Standard race lanes; goleak trio runs under -race
AC-10 Boot-outcome log Wired half: TestLogEncryptionTierWired + the wired-record assertions (URI fingerprint present, key material absent). Skip half owned by Step 1 (!1295 (merged))

Error cases

# Condition Tests
E-1 Absent block skip Owned by Step 1 (!1295 (merged))
E-2 No database client Owned by Step 1 (!1295 (merged))
E-3 Secret read fails, error names the key TestWireEncryption_SecretReadFailureFailsBoot
E-4 Zero usable keys TestWireEncryption_MalformedSecretFailsBoot
E-5 Wrong size / invalid base64 TestWireEncryption_MalformedSecretFailsBoot
E-6 Empty key slice Spec-noted defense in depth, unreachable after a successful decode; owned by internal/crypto
E-7 TTL outside bounds Loader-owned; the tier never sees the value
E-8 Unknown record URI coercion Not in this MR — owned by Step 3 (!1298 (merged))
Nil Secrets on a non-nil config (Design rule, no AC) TestWireEncryption_NilSecretsProviderFailsBoot

Security considerations

# Concern Tests
S-1 Root key read once, decoded slices zeroized, never in config/logs/errors TestWireEncryption_DerivesKnownURIsBeforeZeroize (both arms), NotContains assertions in the secret-failure and wired-log tests
S-2 Enabled path fails closed AC-4/AC-5 tests + TestWireEncryption_LaterTierFailureJoinsCleanupError
S-3 Label coercion Not in this MR — owned by Step 3 (!1298 (merged))
S-4 Key-material confinement limits S04-A properties; not owned by this spec's MRs
S-5 FIPS posture Service-wide compliance work, outside S04
S-6 &8 review gates before enablement Process gate (#513)
S-7 Inert until enabled Skip-path suite kept green; no fixture carries the block

Notes

  • Stacked on !1295 (merged) (Step 1); Draft until it merges. Size: ~550–650 LOC net of the carried delta, per the plan's pre-justified split (code half inside the ceiling; ~250 doc lines pair with the tier per the documentation-pairing convention).
  • E2e scenario impact: none — boot wiring only, no client-visible behavior.

Related to https://gitlab.com/gitlab-org/ops/artifact-registry/-/work_items/491

Edited by Suleimi Ahmed

Merge request reports

Loading
Loading