@shieldfive/crypto reached 1.0.0 on 2026-09-21. The release changes no
wire format and no function relative to 1.0.0-rc.6; what it changes is the
promise. From 1.0.0 the public API follows semantic versioning, and the v1
wire format stays frozen: a file written today decrypts on every future v1
release.
This post records what the release covers, what it does not claim, and what happened during the ten weeks of release candidates.
What 1.0.0 means, and what it does not
Two separate axes, tracked separately:
- API stability. Signatures in the public surface do not change in a
minor or patch release. One exception is stated in the changelog:
@shieldfive/crypto/vaultis experimental and may change in a minor release until it is declared stable. It was three days old at release and backs a feature still being evaluated. - Audit status. Unchanged. The library has not had a paid third-party
cryptographic audit, and
1.0.0does not claim one. It has had an internal review with findings fixed, and it publishes positive and adversarial test vectors so an independent implementation can check it without trusting us.
A version number is not a security property. 1.0.0 says the interface will
hold still; whether the construction is sound is a question the vectors, the
specs and, eventually, an external audit answer.
The wire format
Four suites, one self-describing header:
| ID | Name | Status |
|---|---|---|
0x01 | aes-256-gcm-v1 | Read path for files written before 0x04 |
0x02 | xchacha20-poly1305-v1 | libsodium, 192-bit nonces |
0x03 | pq-hybrid-xchacha-mlkem1024-v1 | Default: XChaCha20-Poly1305 + ML-KEM-1024 |
0x04 | aes-256-gcm-v2 | Current AES-GCM write path, 8-byte nonce prefix |
Suite 0x03 stays confidential against an adversary who breaks either the
classical or the post-quantum primitive; only a break of both compromises the
file. Chunk keys and nonce prefixes mix in a per-file file_id, so
truncation, reordering and cross-file splicing fail at the AEAD rather than at
an application-layer check.
The release-candidate period
rc.1 shipped on 2026-07-14 and froze the v1 format and the TypeScript API.
The rule for the following ten weeks was that a release candidate could add
API or fix behaviour but not remove or change an existing signature. Six
candidates followed that rule:
rc.1: published adversarial test vectors (tests/vectors/adversarial-vectors.json), tampered v1 files that a conforming decoder must reject, across all four suites, each with a valid control. Bad magic, suite confusion, reserved flags, header and header-MAC tampering, chunk ciphertext and tag tampering, reordering, truncation and cross-file splice. The positive known-answer vectors show a decoder works; these show it fails closed.rc.2andrc.3: theinboundmodule behind document requests. An account-less sender encrypts to a recipient's published X25519 and ML-KEM-1024 keys, so only the recipient can decrypt.rc.3adds an Ed25519 signature over the firm's key bundle so a sender can detect a swapped firm key. It does not defeat a server that also serves malicious verifying code; the changelog says so.rc.4: two fixes from an external whitebox review, both low severity. The blob API now accepts and reports a signed file's trailing signature block instead of rejecting it. Suites0x01,0x02and0x04now reject a non-zero reserved pad, as0x03already did; the encoder always wrote zeros, so no existing file is affected.rc.5andrc.6:@shieldfive/crypto/vault, one reviewed implementation of the formats the web app stores (key-chain wraps, name envelopes, the upload proof) plus the key wraps for AI-assistant access grants. Before this, a second client (the MCP server) would have needed its own hand-written copy of each format, and two hand-written copies are how clients silently diverge. Its vectors come from an independent implementation (@noble/hashesArgon2id plus raw Web Crypto), not from the library itself.
One change in the pre-release line was breaking, and it landed before the
freeze. In beta.4 the optional sender-attribution signature was redefined
to commit to every chunk's full ciphertext instead of only the AEAD tags,
because a holder of the content key can forge a tag. The signing feature was
unused in production, so no signed files existed under the old transcript.
Where to check it
- Source, specs and vectors: github.com/shieldfive/crypto
- Package:
@shieldfive/cryptoon npm, published with provenance - Full history:
CHANGELOG.mdin the repository - The review that covers this library:
/security/internal-review
The external audit remains the open item. When it happens, it will start from the frozen format, the specs and the two vector sets rather than from a moving target, which is the practical reason to freeze first.