Skip to content

Reopen an encrypted signer record with the seal it was written under - #10

Closed
Parad0x-Labs wants to merge 2 commits into
mainfrom
mission/signer-seal-reopen
Closed

Parad0x-Labs wants to merge 2 commits into
mainfrom
mission/signer-seal-reopen

Conversation

@Parad0x-Labs

Copy link
Copy Markdown
Owner

Root cause

The CI pollution-matrix child session (tests/gauntlet/test_hermetic_boundary.py::test_pollution_matrix_targets_stay_green_around_children, both iterations) failed with cryptography.exceptions.InvalidTag at network/signer.py _load_encrypted_seed, taking all 96 tests/test_public_hive_bridge.py reads with it (those 96 are the child session's own echoed summary — one real failure).

Mechanism, reproduced locally: an unattended boot (no VOOL_KEY_PASSPHRASE) seals the node-signing seed under the account-file fallback (protection: account_file). The loader then tried VOOL_KEY_PASSPHRASE FIRST on any encrypted record, so the next boot carrying an operator passphrase tried the wrong key against the fallback's ciphertext and raised InvalidTag — bricking every default-home signer read in that process. The module's own comment states the intended contract ("a record sealed by our unattended fallback reopens without any operator env — otherwise the second boot would hard-fail"); the implementation just checked the env first and defeated it.

Repair (at the owner)

network/signer.py: the encrypted-record branch now reads the record's protection stamp — account_file records reopen with the account-file secret (missing secret file raises a clean, actionable RuntimeError instead of crypto garbage), user_passphrase records demand VOOL_KEY_PASSPHRASE and never silently fall back to an unrelated account-file secret. key_storage_class() reuses the same _record_protection reader instead of its inline copy.

Evidence

  • Local reproduction: unattended mint, then env-passphrase reader — InvalidTag before the fix, same peer id reopened after.
  • New contract tests: test_machine_protected_record_reopens_even_with_a_passphrase_env (the CI shape) and test_passphrase_record_never_falls_back_to_the_account_file (the mirror).
  • Cumulative: tests/test_signer_key_storage.py 16 passed; victim + matrix tests/test_public_hive_bridge.py tests/test_must_keep_is_a_guarantee_not_a_sort_key.py tests/gauntlet/test_hermetic_boundary.py 81 passed; neighbor tests/test_tiered_context_loader.py 32 passed; ruff clean.
  • Which shard-3 process minted the unattended record on the runner is not separately identified; the repair is reader-side and makes ANY mix of attended/unattended boots reopen correctly, which is the module's documented contract.

Contributed by sls_0x.

sls_0x added 2 commits September 21, 2026 07:42
The loader tried VOOL_KEY_PASSPHRASE first against any encrypted-seed
record, so an account-file-sealed record (written by an unattended boot)
hit InvalidTag on the next boot that carried an operator passphrase —
observed as the CI pollution-matrix child session failing all 96
default-home signer reads. The record's protection stamp now decides
which seal reopens it, and a user-passphrase record never falls back to
the account-file secret. key_storage_class() reuses the same reader.
network.signer freezes its record paths at first import, so a whole
pytest process shared ONE keys directory -- resolved against the default
home whenever no test had set VOOL_HOME yet. Any test that overrides the
passphrase in-process re-seals that shared record, and the next
suite-passphrase reader dies with InvalidTag: measured on CI as the
demand-ownership '(InvalidTag:)' demand failures and the pollution-matrix
child session, both surviving the loader-side seal fix alone. The
keychain pin fixture now repoints the signer paths into a per-test
directory and resets the cached keypair, so each test seals its own
record with whatever passphrase it uses and the default home is never
written by the suite again.
@Parad0x-Labs

Copy link
Copy Markdown
Owner Author

Added a second, deeper half after watching this PR's CI run: the loader fix alone did not clear the CI InvalidTag clusters, because network.signer freezes its record paths at first import — one shared keys directory per pytest process, re-sealed by any in-process passphrase override (measured: demand-ownership '(InvalidTag:)' failures and the pollution-matrix child survived the loader change). tests/conftest.py's keychain pin now repoints the signer paths into a per-test directory and resets the cached keypair. Local cumulative: signer storage 16 + demand-ownership 19 + hive-bridge/matrix/demand in one session 85 + rig-served pair 2 — all green; ruff clean.

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