.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
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.
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, minimum 32 characters, composed of alphanumeric characters and symbols (printable ASCII, excluding whitespace control characters).
- 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.Open-Source Decoder
We publish a reference decoder so your family can open your vault without our app. Download from our GitHub repository or implement your own using this specification.
Download Open-Source Decoder →Decoder repository: github.com/afterme/decoder
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: March 2026. This specification is maintained at https://afterme.app/format-spec