Skip to content

pkvault seal/unseal: encrypt any file with the vault identity#3

Merged
ddyy merged 1 commit into
mainfrom
seal-unseal
Jul 18, 2026
Merged

pkvault seal/unseal: encrypt any file with the vault identity#3
ddyy merged 1 commit into
mainfrom
seal-unseal

Conversation

@ddyy

@ddyy ddyy commented Jul 18, 2026

Copy link
Copy Markdown
Owner

What

pkvault seal <file> / pkvault unseal <file.pkfile> — local file encryption as a vault utility, per the DIRECTIONS scope decision (single files, existing recipients, no folder sync, no watch modes).

  • Works anywhere on disk: a repo only upgrades the default recipient set (--recipient flags → manifest → yourself). The chosen set is always echoed — the one footgun the defaulting introduces is silently sealing to the wrong audience, so it's never silent.
  • Write-only sealing: needs no unlock unless self-sealing (public recipients only).
  • Agent-mediated unsealing: new op:"unseal-file" unwraps the 16-byte per-file key inside the agent; the identity scalar still never crosses the socket. @pksuite/crypto gains an unwrapFileKey alternative in openFileEnvelope for this holder pattern → 0.2.0.
  • SPEC-FILE §7.1 (new): canonical single-file serialization — header ‖ chunks in index order, no trailing bytes, .pkfile.
  • Streaming chunked I/O, atomic temp+rename with finalization inside the atomic window, 0600 plaintext outputs, overwrite refusal without --force.

Tests

  • crypto: 21/21 (new: unwrapFileKey validation + copy semantics)
  • pkvault: 226/226 (new: 7-test seal/unseal suite + agent e2e proving one ceremony per unseal, zero for seal)

No new derivation strings — the pksuite/file labels were registered when file-v1 landed.

Clean rebuild of the seal-unseal branch from main containing only the
pkvault/crypto/spec work (the previous branch had accidentally swept
in unrelated pklinks files from a shared working tree).

pkvault seal <file> / unseal <file.pkfile> wrap the PKFILE1 envelope
(SPEC-FILE new §7.1: canonical single-file serialization, header then
chunks in index order, no trailing bytes, .pkfile). Works anywhere on
disk — a repo only upgrades the default recipient set: --recipient
flags → manifest → yourself, with the chosen set always echoed.
Sealing uses only public recipients (no unlock, write-only); unsealing
resolves the per-file key through the agent's new op:"unseal-file" —
the identity scalar never crosses the socket. @pksuite/crypto gains an
unwrapFileKey alternative to identity in openFileEnvelope (copies in,
copies out) and bumps to 0.2.0.

Durability and data-loss hardening (review rounds 1–2):
- input/output aliases detected by device/inode (case-insensitive
  filesystems, hard links) and refused even with --force, both commands
- the crypto state is created after the input opens, geometry comes
  from fstat of the opened descriptor, and disposal runs before close
  on every failure path — a close error can never skip key disposal
- all writes loop via txn.writeAllSync (short writes)
- no-force installs use link(2): a destination created after the
  precheck fails E_EXISTS instead of being replaced by rename
- destination directory fsynced after install (POSIX durability)
- finalization inside the atomic window; 0600 plaintext outputs;
  input-mutation detection while sealing; no temp litter on failure

Tests: crypto 21/21; pkvault 228/228 including hard-link and
case-folded alias refusals and a race test that reaches the actual
link(2) window via an injected destination.
@ddyy
ddyy merged commit 9b2a661 into main Jul 18, 2026
2 checks passed
@ddyy
ddyy deleted the seal-unseal branch July 18, 2026 17:25
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