Loading
feat(cmd): wire the crypto fallback-unwrap counter
What
Step 3 (final) of the S04-C encryption wiring plan: the fallback-unwrap counter.
gitlab_artifact_registry_crypto_fallback_unwraps_total(labelsrecord_root_key_uri,active_root_key_uri) declared at the composition root incmd/artifact-registry/metrics_crypto.go, registered unconditionally at boot beside the oci/npm/storage owners.- The encryption tier builds the hook closure over the boot-derived known root-key URI set and threads it through the manager-factory seam; record and active URIs outside the known set coerce to the literal
unknownbefore becoming label values. - Cardinality budgets (5 per label) documented at the registration site and in
internal/metrics/cardinality.go; catalog row indocs/dev/observability.mdcarrying the counter's one-directional meaning (non-zero proves a rotation sweep unfinished; zero proves nothing — the S04-A zero-live-rows verification remains the authority before removing a root key). - Splits
wire_encryption_test.goat the plan's named failure-path seam (pure move — identicalgo test -listbefore and after) and repairs the adjacent metric-exposition and panic-source comments the new counter made inconsistent.
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)) |
| AC-2 | Single loader TTL | Owned by Step 2 (!1297 (merged)) |
| AC-3 | No database client | Owned by Step 1 (!1295 (merged)) |
| AC-4 | Secret-read failure | Owned by Step 2 (!1297 (merged)) |
| AC-5 | Malformed secret | Owned by Step 2 (!1297 (merged)) |
| AC-6 | Retained-key GetKey increments with both URIs; out-of-set record URI lands under unknown |
TestWireEncryption_FallbackUnwrapIncrementsCounter, TestWireEncryption_TamperedRecordURICoercesToUnknown, TestWireEncryption_ConflictAdoptionIncrementsCounterOnce, TestWireEncryption_WiresFallbackUnwrapHook, TestFallbackUnwrapHook_Coercion |
| AC-7 | Wiring cardinality audit passes with the counter registered | TestWiring_CardinalityAuditPasses, TestWiring_CryptoCounterRegisteredWithoutEncryption, in-test live-series audit |
| AC-8 | Sweeper shutdown paths | Owned by Step 2 (!1297 (merged)); goleak suite kept green here |
| AC-9 | -race |
Race lanes + TestFallbackUnwrapHook_ConcurrentCalls |
| AC-10 | Boot-outcome log | Owned by Steps 1–2 (!1295 (merged), !1297 (merged)) |
Error cases
| # | Condition | Tests |
|---|---|---|
| E-1 – E-5, E-7 | Boot-failure and skip conditions | Owned by Steps 1–2 (!1295 (merged), !1297 (merged)) |
| E-6 | Empty key slice | Defense in depth, unreachable; owned by internal/crypto |
| E-8 | Unknown record URI: counter increments under unknown, never the raw value |
TestWireEncryption_TamperedRecordURICoercesToUnknown (end to end), TestFallbackUnwrapHook_Coercion (both directions, plus the out-of-set active arm and the nil-set arm) |
Security considerations
| # | Concern | Tests |
|---|---|---|
| S-1 | Root-key handling and zeroization | Owned by Step 2 (!1297 (merged)) |
| S-2 | Fail-closed enabled path | Owned by Steps 1–2 (!1295 (merged), !1297 (merged)) |
| S-3 | Label coercion bounds attacker-influenced values; both labels coerced against the boot-derived set | TestWireEncryption_TamperedRecordURICoercesToUnknown, TestFallbackUnwrapHook_Coercion (record and active arms), TestFallbackUnwrapHook_ConcurrentCalls |
| S-4 | Key-material confinement limits | S04-A properties |
| 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 | Counter registers unconditionally but increments only on an active stack (TestWiring_CryptoCounterRegisteredWithoutEncryption) |
Notes
- Was stacked on !1297 (merged) (Step 2, merged); targets
main. Size: 937+/311− across 10 files; both sides are inflated by the failure-suite move intowire_encryption_failure_test.go, whose function bodies are byte-identical to their old locations on currentmain. Tests dominate ~3:1. - E2e scenario impact: none — boot wiring and metrics only, no client-visible behavior.
Related to https://gitlab.com/gitlab-org/ops/artifact-registry/-/work_items/491
Edited by Suleimi Ahmed