Skip to content

fix(storage): let OverlayBackend's InventoryStore capability show through#96

Open
bootc wants to merge 1 commit into
mainfrom
fix/overlay-inventory-store-capability
Open

fix(storage): let OverlayBackend's InventoryStore capability show through#96
bootc wants to merge 1 commit into
mainfrom
fix/overlay-inventory-store-capability

Conversation

@bootc

@bootc bootc commented Jul 5, 2026

Copy link
Copy Markdown
Member

Summary

  • OverlayBackend (used whenever ca_cert_file/ca_key_file is configured) wraps its base backend in a plain field rather than embedding it, so it never satisfied InventoryStore itself even when the SQL backend beneath it does.
  • StorageService picks its inventory-integrity scheme (O(1) hash chain vs. whole-blob HMAC) via a type assertion on s.backend, so wrapping a SQL backend silently downgraded it to the whole-blob scheme.
  • This only becomes visible once two callers disagree on the scheme for the same database: openvox-ca-ctl migrate has no reason to set ca_cert_file/ca_key_file (it never touches per-subject keys), so it writes a hash-chain baseline via RebuildInventoryHMAC. A server configured with ca_key_file then opens the same database through OverlayBackend, falls back to a whole-blob HMAC, and fails to start with inventory file integrity check failed: HMAC mismatch (possible tampering).
  • Fix: add OverlayBackend.Unwrap() and an asInventoryStore() helper that walks through wrapper backends before the capability check, used at every call site that previously asserted s.backend.(InventoryStore) directly.

Reproduced end-to-end against real Redis + PostgreSQL containers driving the actual openvox-ca/openvox-ca-ctl binaries (same error text as reported), then distilled into a regression test.

Test plan

  • New regression test InventoryMigrationRoundTripOverlayDestination in internal/storage/sql_inventory_test.go: migrates into a bare SQL backend, then opens the same database through an OverlayBackend (mirroring a ca_key_file server) and calls InitHMAC — fails with ErrInventoryTampered before the fix, passes after.
  • go test ./... passes.
  • Manually reproduced and fixed against real Redis + PostgreSQL in Docker.

🤖 Generated with Claude Code

…ough

OverlayBackend (used whenever ca_cert_file/ca_key_file is configured)
wraps its base backend in a plain field rather than embedding it, so it
never satisfied the InventoryStore interface itself even when the SQL
backend beneath it does. StorageService picks its inventory-integrity
scheme (O(1) hash chain vs. whole-blob HMAC) via a type assertion on
s.backend, so wrapping a SQL backend silently downgraded it to the
whole-blob scheme.

This only surfaces once two callers disagree on the scheme for the same
database: `openvox-ca-ctl migrate` has no reason to set ca_cert_file/
ca_key_file (it never touches per-subject keys), so it writes a
hash-chain baseline via RebuildInventoryHMAC. A server configured with
ca_key_file then opens the same database through OverlayBackend, falls
back to a whole-blob HMAC, and fails to start with
"inventory file integrity check failed: HMAC mismatch (possible
tampering)".

Add OverlayBackend.Unwrap() and an asInventoryStore() helper that walks
through wrapper backends before the capability check, and use it at
every call site that previously asserted s.backend.(InventoryStore)
directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@bootc
bootc requested a review from trevor-vaughan July 8, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant