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>
This commit is contained in:
102
FORMATS.md
Normal file
102
FORMATS.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# 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`
|
||||
|
||||
```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`
|
||||
|
||||
```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`
|
||||
|
||||
```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`.
|
||||
Reference in New Issue
Block a user