# 4. Keys, Identity, and Capabilities ## 4.1 Identity model An **identity** is an Ed25519 keypair — the **root key**. Its public key *is* the identity: `omp:id:ed25519:`. There is no registry, no certificate authority, and no server-side account; possession of the root private key is the identity (Milestone 1 threat model, "user-owned root of trust"). Properties: 1. The root key is the **genesis authority** of the identity's log: the first operation of every log MUST be authored by the root key (`author` = the key form of the log's identity ID, `seq` = 1, `prev` = null, `deps` = []). A log whose earliest operation violates this is not a valid OMP log (`ERR_AUTHZ`). 2. The root key holds **all capabilities implicitly** over its own log: it never needs a grant, and its authority cannot be revoked from inside the log. (Root-key loss or compromise is identity loss; mitigations — offline storage, social recovery — are operational guidance from Milestone 1, not wire format.) 3. The root key SHOULD be kept offline/cold and used only to authorize **device keys**. ## 4.2 Device and agent keys Day-to-day operations are authored by **operational keys** (`omp:key:ed25519:`): one per phone, laptop, home server, or delegated agent. An operational key gets authority via an ordinary `permission-grant`: - Device authorization: caps `["author"]` (optionally `["author","delegate"]` for a primary device that may enroll others), scope typically `{"predicates":["*"],"provenance":"full"}`. - Third-party write-back (e.g. a delegated assistant allowed to log its inference calls): caps `["author"]` with `scope.ops = ["inference-call"]`, plus `["infer","read"]` with a narrow predicate scope. There is exactly one authorization mechanism — grants — for both the user's own devices and third parties. This is intentional: it keeps the operation set at seven and makes the revocation story identical everywhere. ## 4.3 Capabilities | Cap | Meaning | |---|---| | `read` | May receive the projection of claims matching `scope` (claim bodies; provenance per `scope.provenance`). Enforced at sync/projection time by the node that holds the data. | | `author` | May append operations to the log, restricted to `scope.ops` types if present. Claims authored under a grant MUST have predicates matching `scope.predicates` (`ERR_AUTHZ`). | | `delegate` | May issue sub-grants (§4.7). | | `infer` | May execute inference over claims matching `scope` and (if also holding suitable `author`) record the `inference-call`. An `inference-call` whose authority is a grant MUST set `body.grant` to that grant's op ID, and its `inputs` MUST all match the grant's scope (`ERR_AUTHZ`). | Capability checks on **log validity** (the `author` cap, stage 11 of the pipeline) are fully objective and MUST be enforced identically by every verifier. Capability checks on **data egress** (`read`, `infer`) are enforced by whichever node holds the data when it decides what to project to a grantee; they are stated here so all implementations enforce the same policy, but a malicious holder of plaintext can obviously exfiltrate — the threat model (Milestone 1 §5) is explicit that capabilities bound *authorized* flows and provide *auditability*, not DRM. ## 4.4 Grant evaluation A grant `G` **covers** an action by key `K` iff: 1. `G.grantee` is `K` (or the identity form of `K`, for root-of-another-identity grantees); 2. the required cap ∈ `G.caps`; 3. the action's predicates match `G.scope.predicates` (pattern semantics in `02-…` §2.6), and for authoring, the op type ∈ `G.scope.ops` when present; 4. `G` is not revoked at the relevant causal position (§4.6); 5. `G` is not expired: if `expires_at` is set, enforcing nodes MUST refuse *egress* (read/infer projections) after that instant by their local clock, and SHOULD refuse to accept *new* operations whose `ts` is later than `expires_at`. Expiry is deliberately advisory for log validity (wall clocks are untrusted); **revocation is the mechanical kill-switch**, expiry is hygiene. ## 4.5 Who may issue and revoke - **Issue:** the root key; or a key holding an active grant with cap `delegate` whose scope is a superset of the new grant's scope (predicate patterns: every pattern in the child scope must be matched-or-contained by some parent pattern; caps: child caps ⊆ parent caps minus nothing — `delegate` itself may only be passed on if `max_depth` permits, decremented by 1 in the child) (`ERR_AUTHZ`). - **Revoke:** the root key; the grant's original author; or any key that could currently issue that grant under §4.5 (`ERR_AUTHZ` otherwise). - Revocation is **permanent**. A `revocation` whose target is already revoked is valid and idempotent. ## 4.6 The causal authorization rule This is the rule that makes multi-node revocation deterministic. For an operation `O` authored by non-root key `K` requiring cap `c`: > `O` is **authorized** iff there exists a grant `G` covering (`K`, `c`, `O`'s action) > such that `G ≺ O` (G is a causal ancestor of O via `prev`/`deps`), and there is **no** > revocation `R` of `G` with `R ≺ O`. Consequences: 1. A device MUST include (directly or transitively, normally via `deps`) the grant that authorizes it — its first operation naturally lists the enrollment grant in `deps`. 2. Once a revocation has propagated to a device, **nothing that device signs afterward can be valid**: any new operation either includes the revocation in its causal history (and fails the rule) or pretends not to have seen it — but then honest replicators that *have* seen `R` still accept `O` only if `O ∥ R`. See next point. 3. **Concurrent operations** (`O ∥ R`, the revoked device raced the revocation) ARE accepted: rejecting them would make validity depend on arrival order and break convergence. Replicators MUST surface such operations as `WARN_POST_REVOCATION_CONCURRENT` so the UI can show the user exactly what slipped through during the race, and SHOULD treat claims so authored as candidates for refutation. 4. Because the rule quantifies only over `ancestors(O)` — a fixed set determined by `O`'s own hashes — every replicator, forever, computes the same verdict for `O`. Authorization is monotone: it can never be retroactively invalidated, which is what permits stage-11 caching (`03-…` §3.2). Rationale recorded for implementers: the alternative ("revocation retroactively invalidates concurrent ops") was rejected because it makes acceptance non-monotone, forces cascading un-acceptance of downstream operations, and still cannot stop an attacker who withholds the revocation from a victim node. The chosen rule bounds the damage window to the propagation delay, makes it *visible*, and keeps merge a CRDT. ## 4.7 Delegation chains A sub-grant issued under cap `delegate` forms a chain `root → G₁ → G₂ → …`. Normative rules: 1. Chain depth is bounded by each link's `max_depth` (child's effective remaining depth = `min(parent_remaining − 1, child.max_depth or 0)`); exceeding it is `ERR_AUTHZ`. 2. Sub-grant scope MUST be a subset of the parent scope (§4.5) — narrowing only. 3. Revoking any link kills the whole suffix of the chain for the causal future: an action under `G₂` is authorized only if *every* link in its chain satisfies §4.6. 4. Each link MUST be causally present (`≺`) in any operation relying on the chain. ## 4.8 Key rotation and compromise recovery - **Routine rotation:** issue a grant to the new device key, then revoke the old key's grant. Old operations remain valid (monotonicity); the old key simply cannot author beyond the revocation's causal future. - **Device compromise:** revoke immediately from any other authorized device; the revocation propagates with normal sync priority (sync layer SHOULD flood revocations first — guidance for Milestone 6). Concurrent-window operations surface as warnings (§4.6.3) for user triage and refutation. - **Equivocation by a key** (`03-…` §3.4) SHOULD be treated as compromise. - **Root compromise** is out of band by design: nothing inside a log can outrank its root. Milestone 1 §7 covers identity migration (new identity, cross-signed export); the wire format intentionally contains no "super-root". ## 4.9 What grantees can verify A grantee receiving a projection (claims without evidence) can verify, offline: - each received operation's signature, ID, and canonical form; - that claim provenance hashes commit to specific (undisclosed) evidence — so if evidence is later disclosed (e.g. to an auditor), it provably matches; - the grant chain authorizing its own access, and that it is causally fresh up to the heads it was given. It cannot verify completeness (that nothing in-scope was withheld) — deliberate: the user may always under-share. This asymmetry is a Milestone 1 design decision restated here because it constrains the wire format: nothing in this format creates an obligation to disclose.