docs(org): pre-stage A5 living-docs for merged core+server+CLI-admin (item-CRUD/extension TODO)

Pre-stages the A5 living-docs sweep for the already-merged A (relicario-core org
module) + C (relicario-server pre-receive hook) + CLI admin/rotate/status-audit
work, so the final A5 sweep (after Dev-B B9-B14 merges) is fast.

Adds org sections to docs/FORMATS.md (org repo wire formats + wrapped-key blob
layout), docs/CRYPTO.md (ECIES X25519 wrap/unwrap, no-Argon2id contrast, rotate
re-encryption), docs/SECURITY.md (signature-verifying hook, owner-only elevation,
audit vocabulary, honest limitations), DESIGN.md (org-master-key secrets row +
server org mode + deps), core/cli ARCHITECTURE.md (org module + org_session), and
an Unreleased CHANGELOG entry.

B item-CRUD (org add/get/list/edit/rm/restore/purge + main.rs wiring) and extension
parity are left as explicit TODO. STATUS/ROADMAP mark-shipped and
extension/ARCHITECTURE are deferred to the full A5 (track not yet landed; Dev-D
deferred). All cited code constants pinned with file:line per living-docs discipline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJo44YM3UbBjro2fG6NrKy
This commit is contained in:
adlee-was-taken
2026-06-20 14:39:08 -04:00
parent 519e503cbd
commit ed50735e91
7 changed files with 459 additions and 0 deletions

View File

@@ -74,6 +74,117 @@ Without device authentication, access control is transport-layer only:
Device registration is optional but recommended for shared vaults.
## Org vault security
An org vault is a separate git repository alongside the personal vault. It
uses ed25519 commit-signing and a server-side pre-receive hook to make
least-privilege access control server-enforced, not advisory.
### Org device-key authentication
Every org member registers an ed25519 device key. The key appears in
`members.json` as an OpenSSH public-key string alongside the member's role
and collection grants. Fingerprint matching is done via
`relicario_core::fingerprint`, which normalises the OpenSSH format so that
whitespace and comment differences do not create phantom mismatches.
Org access requires two things at once: a wrapped key blob (`keys/<member-id>.enc`)
and the device private key that can unwrap it. There is no org passphrase —
removing a member's blob and rotating the org master key is sufficient to
revoke access (see **Key rotation** below). Device keys are completely
separate from the personal vault's KDF inputs; revoking org access does not
affect the member's personal vault.
### Pre-receive hook enforcement
`relicario-server generate-org-hook` (`crates/relicario-server/src/main.rs:511`)
emits a hook script that calls `relicario-server verify-org-commit` for
every pushed commit. Unsigned or structurally invalid commits are rejected
before they land.
`verify_org_commit` (`main.rs:286`) performs four checks in order:
1. **Signature verification** — a temporary `allowed_signers` file is
constructed from the current `members.json`; `git verify-commit --raw`
is run and the resulting SHA-256 fingerprint is matched back to a
`members.json` entry. A commit not signed by a *current* member is
rejected outright.
2. **Path-level write authorisation** — each modified path is classified by
`classify_path` (`crates/relicario-server/src/lib.rs:19`) into
`ProtectedJson` (owner/admin write only), `CollectionItem` (the
`items/<slug>/…` prefix; write allowed only if the slug appears in the
signer's `collections` grant array), or `Unrestricted`. The write is
authorised if and only if the signer's role and grants satisfy the
classification. Item blobs are authorised by the leading path segment
alone — the ciphertext is never decrypted by the hook.
3. **Owner-only elevation guard** (`enforce_owner_only_elevation`,
`main.rs:438`) — only a member whose *pre-commit* (parent) role is Owner
may introduce a new member at Owner or Admin level, or promote an
existing member to either. Checking the pre-commit role means an Admin
cannot self-promote in the same commit that writes the escalated
`members.json`; there is no epoch in which the transition is
self-authorised.
4. **Schema monotonicity** (`enforce_schema_monotonicity`, `main.rs:521`)
`schema_version` values in org JSON containers may not decrease.
Merge commits are rejected. A genesis commit (no parents) is allowed
only when it is signed by the sole Owner it introduces.
### Key rotation
`relicario org rotate-key` generates a fresh 256-bit org master key,
re-wraps it for every current member, and re-encrypts every
`items/<slug>/<id>.enc` blob and the manifest under the new key in a single
signed commit tagged `Relicario-Action: key-rotate`. A revoked member's
wrapped blob is simply not written during rotation, so they hold a blob that
decrypts to a stale key — they cannot read items encrypted under the new
key.
### Audit action vocabulary
The `relicario org audit` command attributes actions to their verified
signer (not to the commit author or trailer value). Each event records two
actors: the **verified** actor resolved from the signing key (authoritative)
and the actor **claimed** by the `Relicario-Actor` trailer (advisory). When the
claimed actor disagrees with the verified signer, the event is flagged
`TAMPERED`. Trailers are advisory metadata; the trustworthy actor is always
the cryptographically verified signer.
Actions live in two groups:
- **Live (merged A + C streams):** `member-add`, `member-remove`,
`member-role-change`, `collection-create`, `collection-grant`,
`collection-revoke`, `key-rotate`, `org-init`, `ownership-transfer`,
`org-delete`.
- **TODO (pending Dev-B B9B13):** `item-create`, `item-update`,
`item-delete`, `item-restore`, `item-purge` — the emitter code lands with
the item-CRUD command stream.
### Honest limitations
The following are deliberate design boundaries, not oversights:
- **Shared org master key — reads are not cryptographically scoped per
collection.** The pre-receive hook scopes *writes* by collection path
and the CLI filters the manifest to each member's grants, but a single
org key opens all collection blobs. A member with any grant can, outside
the CLI, decrypt items from collections they are not granted. For true
cryptographic separation, use a separate org vault per access boundary.
Per-collection subkeys are a phase-2 non-goal.
- **No read audit.** Git records writes only. A member who reads blobs
directly leaves no server-visible trace.
- **No "hide value."** There is no mechanism to show a member that an item
exists without revealing its field values on decrypt.
- **`delete-org` is a local tombstone in phase 1.** The schema-monotonicity
check causes the hook to reject protected-file deletion, so an
`org-delete` action cannot be pushed to a hook-protected remote. The
deletion is recorded locally only until a future phase addresses it.
## Configuration env vars
Relicario reads the following environment variables. Each is a trust