Files
relicario/FORMATS.md
adlee-was-taken 1758edd5c8 docs: add STATUS/ROADMAP/FORMATS and update CLAUDE.md planning guidance
Introduce three new ALLCAPS.md tracking files:
- STATUS.md: living doc of in-flight work and what shipped in v0.5.0
- ROADMAP.md: full roadmap extracted from CLAUDE.md + expanded with all specced work
- FORMATS.md: wire-format quick-reference (.enc blobs, params.json, devices.json, etc.)

Update CLAUDE.md to replace the single-spec "Design spec" section with a
"Planning & design specs" section that instructs checking docs/superpowers/specs/
and docs/superpowers/plans/ before any planning or implementation work.
Also add the rule to update STATUS.md after every dev iteration, and replace
the stale v0.5.0-in-progress roadmap paragraph with references to the new files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 19:44:30 -04:00

4.5 KiB

Relicario Wire Formats

Quick-reference for the load-bearing binary and JSON formats. Check this file before touching serialization, versioning, or storage layout code. Full diagrams and invariants live in the per-crate ARCHITECTURE.md files.

Encrypted blob (.enc files)

Every encrypted file — manifest.enc, settings.enc, items/<id>.enc, attachments/<item-id>/<aid>.enc — uses the layout produced by relicario_core::crypto::encrypt (crypto.rs):

┌─────────┬────────────────────────┬──────────────────┬──────────────────┐
│ version │         nonce          │    ciphertext     │    auth tag      │
│ 1 byte  │       24 bytes         │    N bytes        │    16 bytes      │
│  0x02   │ random per write       │ XChaCha20 stream  │ Poly1305 MAC     │
└─────────┴────────────────────────┴──────────────────┴──────────────────┘
  • VERSION_BYTE = 0x02 (crypto.rs:59). Any blob starting with 0x01 is rejected with UnsupportedFormatVersion { found: 0x01, expected: 0x02 }.
  • Minimum valid blob length: 41 bytes (1 + 24 + 0 + 16).
  • Nonces are always fresh from OsRng — no caller-supplied nonces.
  • Full diagram: docs/ARCHITECTURE.md § "Encrypted File Format".

.relicario/params.json

{
  "format_version": 2,
  "aead": "xchacha20-poly1305",
  "salt_path": ".relicario/salt",
  "kdf": {
    "argon2_m": 65536,
    "argon2_t": 3,
    "argon2_p": 4
  }
}

Parsed via ParamsFile { kdf: KdfParams } in session.rs. The kdf nesting is intentional — format_version, aead, and salt_path co-exist for forward-compat probing. Do not flatten. Production defaults: m=65536 (64 MiB), t=3, p=4. Tests use m=256, t=1, p=1.

.relicario/salt

32 raw bytes. Not secret. Generated once at vault init via OsRng. Feeds Argon2id as the KDF salt.

Manifest (manifest.enc)

Decrypts to JSON matching the Manifest struct (manifest.rs).

  • Schema version: MANIFEST_SCHEMA_VERSION = 2 (manifest.rs:12). v1 manifests (pre-typed-items) fail to parse and are not supported.
  • ManifestEntry fields: id, title, tags, favorite, group, icon_hint, modified, trashed_at, attachment_summaries.
  • The manifest is rebuilt from scratch on every upsert — it can never drift from the source-of-truth item files.
  • Supports case-insensitive title/tag search without decrypting any item.

.relicario/devices.json

[
  { "name": "laptop", "public_key": "<hex-encoded ed25519 public key>" }
]

An empty array ([]) puts the pre-receive hook in bootstrap mode (all pushes accepted). Both devices.json and revoked.json must be empty for bootstrap mode to activate — a non-empty revoked.json alone forces strict verification.

.relicario/revoked.json

[
  { "name": "old-laptop", "public_key": "<hex>", "revoked_at": 1746000000 }
]

Commits by public_key at or after revoked_at (Unix seconds) are rejected by the pre-receive hook. Commits before revoked_at remain valid (they were authorized at the time).

Item IDs and Field IDs

Kind Length Entropy Source
ItemId 16 hex chars 64 bits OsRng
FieldId 16 hex chars 64 bits OsRng
AttachmentId 16 hex chars content-addressed first 8 bytes of SHA-256(plaintext)

AttachmentId is content-addressed — identical plaintexts deduplicate in git automatically.

.relbak backup format

A zstd-compressed tar archive containing a bare git clone of the vault. Designed for relicario backup export/restore.

Full spec: docs/superpowers/specs/2026-04-27-relicario-import-export-design.md.

ItemCore JSON (internal)

ItemCore uses #[serde(tag = "type")] — the outer JSON object gets a "type" discriminator key. No *Core struct may have a field named "type" (use "kind" instead — see CardKind, TotpKind).

Full item type inventory: crates/relicario-core/ARCHITECTURE.md § "Module map".

KDF input construction

The password fed to Argon2id is length-prefixed to prevent extension attacks:

u64_be(len(passphrase)) || passphrase_bytes || u64_be(32) || image_secret

NFC-normalized before hashing. Covered in crypto.rs:229-236 and tested in tests/format_v2.rs:44-54.