One-Shot: Seal & Unseal

Seal and Unseal encrypt a single blob with a password. The defining trait is that the output is self-describing: the salt and the KDF parameters travel inside it, so Unseal reconstructs the exact key-derivation function from the bytes alone.

blob, err := secretbox.Seal(plaintext, password)
plain, err := secretbox.Unseal(blob, password)

Use SealWith to pick non-default primitives:

blob, err := secretbox.SealWith(plaintext, password,
    secretbox.NewArgon2id(secretbox.Argon2idParams{Time: 4, Memory: 128 * 1024, Threads: 4}),
    secretbox.ChaCha20Poly1305{},
)

What Seal does

Unseal reverses it: parse header → reconstruct KDF + cipher → derive key from the embedded salt → AEAD open. A wrong password or any tampered byte fails the authentication tag and returns ErrDecrypt — the two are indistinguishable on purpose.

Blob layout

Every Seal blob begins with a fixed 16-byte header, followed by the salt and the cipher's nonce-prefixed output.

FieldBytesNotes
Magic4"SBX1" — version + format guard
KDF id11 = Argon2id
Cipher id11 = AES-256-GCM, 2 = XChaCha20-Poly1305
Argon2 time4big-endian uint32
Argon2 memory4big-endian uint32, KiB
Threads1parallelism
Salt length1length of the following salt
Saltnrandom per call
Bodyrestnonce ‖ ciphertext, framed by the cipher
Tip

Because parameters are embedded, you can raise Argon2idParams over time without a migration: old blobs still carry the old cost and Unseal honors it. New blobs get the new cost.

Caution

A blob is only as secret as its password. Seal adds no per-user secret of its own — for shared secrets or rotation, use a Vault.