.afterme Open Container Format
Specification v1.0 · AES-256-GCM · PBKDF2 · Open format
Overview
The .afterme file format is an open, documented container format designed for long-term archival and secure storage of sensitive personal documents. It prioritizes user ownership, platform independence, and future-proof access. Even if the After Me application or company ceases to exist, users with the correct credentials can always access their data using standard open-source tools.
This specification is public and stable. Any changes to the format will be versioned and documented.
File Structure
An .afterme file is a standard ZIP archive containing:
.afterme (ZIP Archive) ├── README.txt # Human-readable instructions for manual recovery ├── manifest.json # Metadata about the vault contents (unencrypted) ├── vault.enc # The core encrypted data payload (AES-256-GCM) └── key.enc # Content encryption key, wrapped with access credential
README.txt
The README.txt is a plain text file included in every .afterme archive. It is human-readable without any tools and is designed to guide a non-technical recipient through the recovery process even if the After Me application and website no longer exist. It contains no sensitive information.
Template contents:
============================================================
AFTER ME — VAULT RECOVERY INSTRUCTIONS
============================================================
This file contains an encrypted personal document vault
created with After Me (myafterme.co.uk).
TO OPEN THIS VAULT YOU NEED:
1. This .afterme file
2. The printed QR Key Card that was given to you
OPTION 1 — USE THE APP (easiest)
- Download After Me from the App Store or Google Play
- Tap "I Have a Legacy Kit" on the welcome screen
- Scan the QR Key Card with your camera
- Select this .afterme file when prompted
- The vault will open automatically
OPTION 2 — MANUAL RECOVERY (no app required)
- The QR code encodes a plain text Access Key string
- Scan the QR code with any QR reader to reveal this string
- The open format specification is published at:
myafterme.co.uk/format-spec
- Any developer can write a decoder in under 100 lines of
code using only standard tools (Python, Node.js, etc.)
- The specification uses only open standards:
AES-256-GCM and PBKDF2-HMAC-SHA256
THIS VAULT DOES NOT REQUIRE AFTER ME TO EXIST.
The cryptographic format is fully documented and open.
Your documents are yours — always.
============================================================
The README.txt is always present and always plain text regardless of format version. It is the first thing a recipient should read.
manifest.json
Non-sensitive metadata. Schema:
{
"version": "1.0",
"created_at": "2023-10-27T10:00:00Z",
"vault_id": "uuid-string",
"owner_name": "John Doe",
"document_count": 15,
"categories": ["Identity", "Finance", "Legal"],
"encryption_algo": "AES-256-GCM",
"kdf_algo": "PBKDF2-HMAC-SHA256"
}
Privacy Note
The owner_name field is stored unencrypted intentionally. It exists so that survivors who receive an .afterme file can identify whose vault it is before attempting decryption. This is important for scenarios where a family member may possess multiple .afterme files from different relatives.
The owner_name field is optional. If the user omits it, the field should be absent from the JSON or set to null. Implementations must not require this field to be present.
Privacy recommendation: If maximum privacy is required, omit this field entirely. Anyone with access to the .afterme file can read the manifest without decrypting the vault — the owner's name would be visible. Privacy-conscious users should leave the owner name blank.
vault.enc
Encrypted payload. Algorithm: AES-256-GCM. Key: 32 bytes. Nonce/IV: 12 bytes. Auth tag: 16 bytes.
Wire format (byte-level layout):
[IV (12 bytes)][Auth Tag (16 bytes)][Ciphertext]
| Offset | Length | Field |
|---|---|---|
| 0 | 12 | IV (nonce) |
| 12 | 16 | GCM authentication tag |
| 28 | variable | Ciphertext (encrypted JSON payload) |
key.enc
Content Encryption Key (CEK) wrapped with the user's access credential (e.g., QR code key). Wrapping: AES-256-GCM. Key derivation: PBKDF2-HMAC-SHA256 with 600,000 iterations, 32-byte salt.
Binary format (byte-level layout):
[Salt (32 bytes)][IV (12 bytes)][Auth Tag (16 bytes)][Encrypted CEK (32 bytes)]
| Offset | Length | Field |
|---|---|---|
| 0 | 32 | PBKDF2 salt |
| 32 | 12 | IV (nonce) for AES-256-GCM key wrap |
| 44 | 16 | GCM authentication tag |
| 60 | 32 | Encrypted Content Encryption Key |
Total file size: 92 bytes (fixed).
vault.enc, the authentication tag precedes the ciphertext (the encrypted CEK). See Implementation Notes for library-specific guidance.QR Code Access Key
The Access Key is the secret credential used to derive the Key Encryption Key (KEK) via PBKDF2. It is delivered to the user as a QR code.
- Format: A high-entropy random string. The current implementation generates a 48-character string from a 75-symbol alphabet (alphanumeric characters and selected printable ASCII symbols, excluding whitespace and control characters), yielding approximately 298 bits of entropy. The minimum floor for any conforming implementation is 32 characters from a similarly sized alphabet. Implementations should use a cryptographically secure pseudorandom number generator (CSPRNG) — the reference implementation uses an OpenSSL-backed native PRNG via the
react-native-quick-cryptomodule. - Encoding: The QR code payload is the Access Key encoded as a plain UTF-8 string. No additional framing, URL scheme, or binary encoding is applied.
- Error Correction: QR codes must use error correction level H (High), which tolerates up to ~30% damage. This is critical because the QR code may be printed on paper and stored for years or decades before use.
- Display: The Access Key is never displayed as raw text in the app UI. Users interact with it exclusively through the QR code.
Encryption Details
KDF: PBKDF2, HMAC-SHA256, 600,000 iterations, 32-byte random salt.
Content Encryption: AES-256-GCM, 32-byte key, 12-byte nonce, 16-byte tag.
Recovery Process
- Unzip the
.aftermefile. - Read
README.txtfor instructions. - Locate the decoder tool or write one based on this spec.
- Provide the QR code string (Access Key).
- Decoder reads the salt (first 32 bytes) from
key.enc. - Decoder derives KEK from Access Key via PBKDF2-HMAC-SHA256 (600,000 iterations).
- Decoder reads IV, Auth Tag, and Encrypted CEK from
key.enc; decrypts to obtain CEK. - Decoder reads IV and Auth Tag from the start of
vault.enc; decrypts remaining ciphertext using CEK. - Parses the resulting JSON and extracts the documents.
Implementation Notes
Tag-Before-Ciphertext Convention
The .afterme v1.0 wire format places the GCM authentication tag before the ciphertext in both vault.enc and key.enc. This is a deliberate design choice baked into the reference implementation and all existing encrypted files.
Many cryptographic libraries expect the tag appended after the ciphertext (e.g., Python's cryptography AESGCM, Go's crypto/cipher, WebCrypto). When working with such libraries, implementors must rearrange the bytes:
Reading (decryption)
- Parse the wire format:
[IV][Tag][Ciphertext] - Rearrange for the library: pass
[Ciphertext][Tag](concatenated) as the "ciphertext" input, with the IV/nonce separately.
Writing (encryption)
- The library returns
[Ciphertext][Tag](concatenated) or separate values. - Rearrange to the wire format: write
[IV][Tag][Ciphertext]to the file.
Example: Python (cryptography library)
# Decrypting vault.enc:
data = open("vault.enc", "rb").read()
iv = data[:12]
tag = data[12:28]
ciphertext = data[28:]
# AESGCM.decrypt() expects ciphertext+tag appended:
plaintext = AESGCM(cek).decrypt(iv, ciphertext + tag, None)
# Decrypting key.enc:
data = open("key.enc", "rb").read()
salt = data[:32]
iv = data[32:44]
tag = data[44:60]
encrypted_cek = data[60:92]
cek = AESGCM(kek).decrypt(iv, encrypted_cek + tag, None)
Example: Node.js (crypto module)
// Decrypting vault.enc:
const data = fs.readFileSync('vault.enc');
const iv = data.subarray(0, 12);
const tag = data.subarray(12, 28);
const ciphertext = data.subarray(28);
const decipher = crypto.createDecipheriv('aes-256-gcm', cek, iv);
decipher.setAuthTag(tag);
const plaintext = Buffer.concat([
decipher.update(ciphertext), decipher.final()
]);
crypto.createDecipheriv for GCM accepts the tag separately via setAuthTag(), so no rearrangement is needed — simply slice the tag and ciphertext from the wire format and pass them independently.Implementing a Decoder
This format is deliberately built on open, standardised cryptography so that anyone can write a decoder in well under 100 lines of code, in any major language, using only tools that ship with that language. We do not host a single canonical decoder, because doing so would create a dependency on us — exactly what this format is designed to avoid.
To open an .afterme file you only need:
- A standard ZIP extractor (built into every operating system).
- An AES-256-GCM implementation (NIST SP 800-38D — included in Python
cryptography, Node.jscrypto, Gocrypto/cipher, Javajavax.crypto, .NETSystem.Security.Cryptography, OpenSSL CLI, and WebCrypto). - A PBKDF2-HMAC-SHA256 implementation (RFC 8018 — same universal availability).
- The Access Key from the QR card and this specification.
The Implementation Notes above include working Python and Node.js decryption snippets. The complete recovery procedure is described in Recovery Process. A competent developer should be able to recover an .afterme file from this page alone, with no further reference required.
Future Compatibility
- The
versionfield inmanifest.jsondetermines the parsing logic, including the encryption algorithm, KDF parameters, and the wire format byte layout convention. - New encryption algorithms will be added as new version identifiers.
- Future versions may adopt a different tag placement convention; implementations should always check the version field before assuming a wire format.
- The
README.txtwill always be present and backward-compatible (plain text).
Last updated: May 2026. This specification is maintained at https://www.myafterme.co.uk/format-spec