Protocol Specification
Cyphr Protocol — Draft v0.1
Cyphr¶
Protocol Specification
Version: Draft v0.1 - Work in Progress Authors: Zamicol and nrdxp
Built on Coz v1.0
1. Introduction¶
Cyphr is a self-sovereign identity and authentication protocol. It provides a decentralized authentication layer for the Internet. Briefly, it enables:
- Password-free and email-free authentication via public key cryptography
- Authenticated Atomic Actions (AAA): individually signed, independently verifiable user actions
- Cryptographic primitive agnosticism (via Coz and MultiHash Merkle Root)
- Multi-device key management with revocation
- Data provenance
| Feature | Cyphr | Legacy Passwords/SSO |
| Identity | Public Keys and Merkle Root | Password or Provider |
| Authentication | Authenticated Atomic Action | Bearer Tokens |
| Verification | Signatures and Merkle Trees | Centralized Database |
| Trust | Cryptographic Verification | Trusted Service |
| Recovery | Revoke/Rotation, Social Recovery | Admin-reset or Email |
| State Tracking | Push/Pull - Bidirectional(MSS) | Centralized Service |
Importantly, Cyphr enables an entire identity, both authentication and data, to be represented by a single digest.
2. Core Concepts¶
2.1 Levels and State Tree¶
The principal tree (PT) consists of nodes representing the complete state of a principal (identity) including authentication components and user data.
Principal Tree (PT)
│
├── State Tree (ST) ───────────────── [State]
│ │
│ ├── Auth Tree (AT) ────────────── [Authentication]
│ │ │
│ │ ├── Key Tree (KT) ─────────── [Public Keys]
│ │ │
│ │ └── Rule Tree (RT) ────────── [Permissions & Thresholds]
| │
│ └─── Data Tree (DT) ───────────── [Data Actions]
│
└── Commit Tree (CT) ──────────────── [State Mutations]
The principal root (PR) is a hierarchical structure of cryptographic Merkle roots representing components of the PT calculated for each commit. The first PR is the Principal Genesis (PG).
Principal Root (PR)
│
├── State Root (SR) ───────────────── [State]
│ │
│ ├── Auth Root (AR) ────────────── [Authentication]
│ │ │
│ │ ├── Key Root (KR) ─────────── [Public Keys]
│ │ │
│ │ └── Rule Root (RR) ────────── [Permissions & Thresholds]
| │
│ └─── Data Root (DR) ───────────── [Data Actions]
│
└── Commit Root (CR) ──────────────── [State Mutations]
The commit chain, tracks the principal root over time. Each commit mutates the principal tree (PT) and references the prior principal root (PR) and the forward state tree (ST).
Genesis, State 0 State 1 State 2
+----------------+ +----------------+ +---------------+
| | Commit | | Commit | | (Future)
| PG(SR, CR) | =====> | PR(SR, CR) | =====> | PR(SR, CR) | ==>
| | | | | | | |
+----------------+ +------------V---+ +-----------V---+
^ | ^ |
+ <--(pre)-------------+ + <--(pre)------------+
2.2 Terminology¶
2.2.1 Core Terminology¶
| Term | Abv | Definition |
| Principal | - | An identity in Cyphr, replaces "account" |
| Principal Genesis | PG | The initial, permanent principal identifier |
| Principal Root | PR | Top-level digest. MR(SR, CR?, ...) |
| State Root | SR | Principal non-commit state. MR(AR, DR, ...) |
| Auth Root | AR | Authentication state MR(KR, RR, ...) |
| Key Root | KR | Merkle root of keys MR(tmb₁, tmb₂, ...) |
| Rule Root | RR | Merkle root of rules MR(rule₁, rule₂, ...) |
| Data Root | DR | Merkle root of user data actions |
| Commit Root | CR | MALTR of transactions MALTR(TR₀, TR₁?, ...) |
| Tip | - | The latest PR (digest identifier) |
| Action | - | A signed coz identified by typ, basis of AAA |
| trust anchor | - | Last known valid state for a principal |
PG, PR, SR, AR, KR, RR, DR, and CR are all MultiHash Merkle Root (MHMR) digest values. Each digest identifier corresponds to a tree datastructure: Principal Tree (PT), Auth Tree (AT), Key Tree (KT), Rule Tree (RT), Data Tree (DT) and the Commit Tree (CT) (PT, AT, KT, RT, DT, CT).
Cozies that authorize operations are actions, and as such, "action" is the hypernym of "transaction" and "data action". Concrete resources such as keys, rules, user comments, and binary files are not actions.
2.2.2 Digest¶
A digest is the binary output of a cryptographic hashing algorithm. Digest values are encoded as b64ut ("Base64 URI canonical Truncated", RFC 4648 base64 URL alphabet and encoding method, errors on non-canonical encodings, and no padding). All digest values are opaque bytes; byte sequences are treated as a whole unit without internal structural or meaning.
2.2.3 Identifier¶
All identifiers are cryptographic digest Content IDentifiers (CID's) encoded as
b64ut and provide addressing and integrity of the reference. Following Coz
semantics, all digest identifiers inside a coz must aligned with algorithm
(alg) in pay unless explicitly labeled. For example, the algorithm for the
id of the new key must be SHA256, aligning with alg ES256.
2.2.4 Nonce¶
A nonce is a unique, high-entropy value used to add entropy, obscure content, and/or ensure uniqueness. See section Nonce.
2.2.5 Merkle Root¶
A Merkle tree (MT) is a binary hash tree where each leaf is a hash of data and each non-leaf node is the hash of its two children, culminating in a single Merkle root (MR). CT uses a verifiable data structure, specifically a Merkle, Append-Only, Log Tree, (MALT), with left, dense filling. See section Commit.
2.2.6 Commit¶
A commit is a finalized bundle of transaction cozies that mutate PT and result in a new PR. See section commit.
2.2.7 Implicit Promotion¶
A value is implicitly promoted to the parent without additional hashing when a tree component has only one node. Promotion is recursive; items deep in a tree can be promoted to the root level. For example:
- Single key:
tmbis promoted to KR, then AR, then PR, which equals PG on genesis. - No DR present: AR is promoted to PR.
- Only KR present (no RR): KR is promoted to AR.
2.2.8 Authenticated Atomic Action¶
Authenticated Atomic Action (AAA) is an individual, self-verifiable, discrete operation. AAA supersedes trust traditionally delegated to centralized services. See section Authenticated Atomic Action.
2.2.9 Embedding¶
An embedded node is an external tree reference. Its value may be a tmb,
KR, AR, PR, nonce, or other node value. An embedded principal is a full
Cyphr identity embedded into another principal. See section
Embedding.
2.2.10 Reveal¶
Reveal is the process by which obfuscated structures, i.e. opaque nodes, are made transparent. Public keys must be revealed for verification; embeddings, nonces, and other data structures may also need revealing during commits or other signing operations.
2.2.11 Witnesses¶
A witness is a client that keeps a copy of an external principal's state and communicates state through gossip.
An oracle is a witness with some degree of delegated trust by external clients. For example, a client may delegate some processing to an oracle for state jumping, where the oracle is trusted that the commits in the jump were appropriately processed.
2.2.12 Unrecoverable Principal¶
A principal with no keys capable of meaningfully mutating AT and no viable recovery path within the protocol. See section Unrecoverable.
2.3 Core Protocol Constraints¶
2.3.1 Coz Required Fields¶
Cyphr requires specific fields for Coz messages. All cozies must have the fields:
alg: Following Coz semantics,algis the algorithm of the signing key and a paired hashing algorithm, and also denotes the algorithm for other values contained inpayunless explicitly denoted otherwise.tmb: Thumbprint of the signing key.now: The timestamp of the current time.typ: Denotes the intent of the coz.
2.3.2 Protocol Guarantees¶
- Commits are append-only: Commits are never removed from the chain and implicit forks are prohibited by the protocol. See section Implicit Forks.
- Principal Genesis (PG) is immutable: No operation can change a PG.
2.3.3 AT/DT Duality¶
Auth Tree (AT) and Data Tree (DT) have fundamentally different structural properties. AT has protocol defined rules while DT is a general-purpose data action ledger. While not defined by this protocol, applications (authorities) may impose additional structure on DT.
| Property | Auth Tree (AT) | Data Tree (DT) |
| Mutability | Append-only (immutable history) | Mutable (deletable content) |
| Chain | Linked via pre |
No chain |
| Verification | Replay from genesis | Point-in-time snapshot only |
| State type | Monotonic sequence of commits | Non-monotonic |
| Semantics | Full protocol semantics | None (application-defined) |
3 State¶
3.0 Feature Levels¶
Cyphr has six operational levels. Each level increases complexity and each level builds on the previous level; level 2 has the same semantics as level 1 with the addition of key replacement. Clients may choose to remain compatible with a particular level.
| Level | Description | Components |
| 1 | Single static key | Key (denoted by tmb) |
| 2 | Key replacement | Replaceable key |
| 3 | Multi-key/Commit | Key Tree (KT) |
| 4 | Arbitrary data | Data Tree (DT) |
| 5 | Rules | Rule Tree (RT) |
| 6 | Programmable | VM execution |
Principal levels describe increasing complexity of a principal's state composition and are not an authorization input. Authorization is determined by which state components exist and what rules govern them. A transaction is authorized if and only if all three conditions hold:
- Antecedent Authorization Gate: Every authentication component required to apply an action (signing key, target key, rule, etc.) must already be active in the principal's current state before the transaction is applied.
- Lifecycle gate: The principal's current lifecycle state must permit the operation. For example, a frozen principal rejects mutations; a deleted principal rejects everything. (See section Lifecycle.)
- Capability gate: The principal must have the state components required for the operation. Principal genesis is required for commits. Data actions require DT to exist and data actions require inclusion. Rule operations require RT to exist. For Level 5+, Rule Tree (RT) may define additional constraints; weight thresholds, timelocks, or other conditions that must be satisfied for the transaction to proceed.
3.1 Level 1: Static Key¶
- Single key, never changes
- No commit
tmb== KR == AR == PR- Self-revoke results in permanent lockout (in lieu of sideband intervention)
3.2 Level 2: Key Replacement¶
- Introduces key replacement.
key/replaceswaps current key for new key.
3.3 Level 3: Commit (Multi-Key)¶
- Introduces the Commit Tree (CT) and Principal Genesis (PG).
- Multiple concurrent keys with equal authority
- Initial PR is equal to PG. PR = MR(SR, CR), CR = MR(TMR, TCR)
- Any key can
key/create,key/delete, orkey/revokeany other key - Standard for multi-device users
3.4 Level 4: Arbitrary Data¶
- Introduces Data Tree (DT); Data actions (comments, posts, etc.) are recorded in DT
- Enables Authenticated Atomic Actions (AAA)
3.5 Level 5: Rules¶
- Introduces Rule Tree (RT) for access control with weights (weighted permissions) and timelocks. RR is a component of AR.
- A weight is a numeric value assigned to keys and actions with minimum total weight (threshold) required for execution.
- Keys and actions have a default weight of 1.
- A timelock is a delay before certain actions take effect.
- Enables M-of-N signing, tiered permissions, custom timelocks
3.6 Level 6: VM¶
- Introduces programmable virtual machine (VM) rule execution.
- Rules are executable bytecode stored in RT.
- VM execution produces a deterministic state transition.
- Enables complex conditional logic and programmable policies like smart contracts and complex organizational policies.
3.7 Root Calculation¶
Canonical Root Algorithm:
- Collect component digests (including embedding/nonce if present). If no component exists, a digest is not calculated (empty components are not hashed).
- Sort lexicographically (byte comparison) unless otherwise defined. If sort order is defined, lexical is the tie breaker.
- Implicitly Promote without hashing if only one digest component exists.
- Merkle Root Calculate the Merkle root of a binary Merkle tree. If order is not otherwise given, lexical byte order is used.
Root = MR(d₀, d₁?, ...)
MALT applies a specific, strict implementation of the Canonical Root Algorithm; A MALTR is a MR but not all MR are MALTRs.
3.7.1 Principal Root and Principal Genesis¶
The Principal Root (PR) is the current top-level identity digest of a principal.
For levels 3+, the Principal Genesis (PG) is the first PR computed at creation and remains immutable. Subsequent principal mutations, for example by adding a second key, update only the current PR.
PR = MR(SR, CR?, embedding?, ...)
PG = Genesis Root (Level 3+)
3.7.2 State Root¶
State Root (SR) is calculated as:
SR = MR(AT, DT?, embedding?, ...)
3.7.3 Key Root¶
Key Root (KR) is calculated as:
KR = MR(tmb₀, tmb₁?, embedding?, ...)
3.7.3 Rule Root¶
Rule Root (RR) is calculated as:
RR = MR(rule₀, rule₁?, ...)
3.7.4 Auth Root¶
Auth Root (AR) combines authentication-related trees:
AR = MR(KR, RR?, embedding?) # nil components excluded from sort
3.7.5 Transaction Root¶
Transaction Root (TR) (Level 3+) is the MR of all transactions in a commit structured as MR(TMR, TCR), where TMR is transaction mutation root and TCR is the transaction commit root. See section Commit.
TX = MR(czd₀, czd₁?, ...)
TMR = MR(TX₀, TX₁?, ...)
TCR = MR(czd₀, czd₁?, ...)
TR = MR(TMR, TCR)
3.7.6 Commit Root¶
Commit Root (CR) is the the MALT root (MALTR) of the commit tree (CT). Each node in the CT is a TR.
CR = MALTR(TR₀, TR₁?, ...)
3.7.7 Data Root¶
Data Root (DR) (Level 4+) is the digest of all data action czds. DR is
sorted by now and secondarily czd.
DR = MR(czd₀, czd₁?, ..., embedding?)
4 Commit¶
A commit is an ordered, finalized atomic bundle that mutates PT. A commit
consist of one to many transactions, denoted by typ, and transactions
themselves consist of one to many cozies. Many mutations may occur per commit
and are applied one-by-one using a given order as dictated by the principal.
Unlike other systems, there are no minting fees, gas, or need for a global
ledger.
For example, a commit may have three transactions: one transaction for
key/replace, signed by two keys and consisting of two cozies, one for
key/create, signed by one key and consisting of one coz, and a
commit/create, finalizing the commit.
4.1 Transaction¶
A transaction consists of one or more signed cozies that results in a mutation
of the Principal Tree (PT). All cozies for a particular transaction contain an
identical typ, which defines intent. Clients verify transactions based on the
principal's auth tree (AT).
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"typ": "cyphr.me/key/create",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // Existing key
"id": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M" // New key's tmb
},
"sig": "<b64ut>",
}
The identifier for each transaction is the MR of its czds. The transaction
mutation root, TMR, is the MR of all mutation transactions in a commit. The
transaction commit root, TCR, is the commit czd MR. Unlike TMR, there are no
intermediate TCR roots since there is only one commit transaction per commit.
The wire format for transactions, labeled by the field txs, is a list of
lists, where each list item is a transaction, and each transaction contains one
to many cozies. Transactions and cozies are ordered as specified by the
principal with the condition of the commit transaction appearing last.
Transaction order is suggested by the sequence in txs
(implementation-dependent) and is explicitly denoted by txs_order. Although
inter-transaction coz ordering is not relevant for principal mutation,
transaction order may potentially impact principal mutation.
4.2 Arrow¶
On commit the field arrow is the MR of three components: the prior PR, given
the name pre, the forward SR, given the name fwd, and TMR. arrow is all
principal components, prior, forward, and mutations, excluding the commit
transaction. Commits form a chain via pre. After a commit is finalized, the
new PR is calculated.
A commit cannot refer to itself (a signature cannot sign itself), so instead its
signature covers all principal components except the commit transaction itself.
For the same reason, pre refers to PR while fwd refers to ST; a "forward PR"
isn't calculable inside of a commit as it would create a circular reference.
4.3 Commit Finality¶
Commits are chained by reference to prior principal roots, the forward tree, and are finalized by a commit transaction.
A commit's id is equal to TR, however instead of the field id being used in a
commit transaction, commit/create uses the field arrow since the id of the
commit itself is only calculable after commit and cannot have a cyclic reference
to itself.
{"txs":[[{ // Commit transaction (last entry in `txs`)
"pay":{
"alg": "ES256",
"now": 1623132000,
"typ": "cyphr.me/cyphr/commit/create",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"arrow":"<b64ut>" // Transition arrow: MR(pre, fwd, TMR)
},
"sig": "<b64ut>"
}]]
}
4.4 Commit Tree¶
The Commit Tree (CT) is a MALT. New commits are appended sequentially from the left, maintaining a dense prefix with no gaps, following the growth pattern used in RFC 9162.
Commits are ordered. The Merkle root after incorporating commit N becomes the Commit Root(CR) for that commit (CR_N). Clients obtain inclusion proofs for specific commits and consistency proofs between known roots using standard Merkle path proofs. To prevent client misbehavior, finality may be used as a proof of error (see section Proof of Error).
Commit Root (CR) is the MALTR of all commits. A commit is finalized with a
commit transaction, commit/create with the field "arrow":<MR(pre, fwd, TMR)>. When CT exists (level 3+), PR is the MR of PT including the last commit
(commit id), so that PR = MR(SR, CR).
4.5 JSON Wire Format¶
4.5.1 Plurals¶
For various components, JSON components are labeled. If a plural is possibly valid, the plural is always used ensuring only one payload representation.
Singular:
// For use inside a coz
paysig
Plurals:
keystxs
Prohibited:
key// Use keystx// Use txs
4.5.2 Fields¶
Transaction order, coz order, and intermediate roots are required for
calculating MR's and verification, however depending on JSON implementation this
information isn't always explicitly available. Metadata may be enumerated as a
sibling to txs in the field txs_meta, for example
{"txs":[[{...}]],"txs_meta":...}.
Wire Format:
txs: [tx₀, tx₁?, ...] Ordered list of mutation and commit transactions, which themselves are an unnamed array of related cozies. Commit transaction must be last.txs_meta: An object containing meta fields that describetxs.- TX (Unlabeled): [coz₀, coz₁?, ...] Ordered list of cozies for a particular transaction. The ID of the transaction itself is MR(czd₀, czd₁?, ...)
- TXC (Unlabeled): The commit transaction. Appears last in the wire format.
- coz (Unlabeled): A signed coz consisting minimally of
payandsigwith an identifierczd. keys: [key₀, key₁?, ...] Full public keys referred to by cozies intxs.
4.5.3 JSON Meta Fields¶
arrow:(Principal transition) MR(pre, fwd, TMR) pre:The prior Principal Root (PR), the state being mutate. fwd:The forward State Tree Root (SR), the state after mutation. TX:MR(czd₀, czd₁?, ...) TMR:MR(txm₀?, txm₁?, ...) TCR:MR(txc) TR:MR(TMR, TCR) SR:The state tree root after commit. CR:MALTR(TR₀, TR₁?, ...) CT:MALT(TR₀, TR₁?, ...) txs_order: [TX₀, TX₁?, ...] An array of transaction MR identifiers (TX ids), enumerating transaction order.txs_czds: [czd₀, ...] An arrayczds enumerating cozie order for the entire commit.txs_tree: {"TX₀":[czd₀:, ...], ...} An object with an array of czds in order for the transaction.
4.6 Comparison to ¶
gitIn git, commits are digest of metadata objects, including fields like author and date, but critically parent and commit tree a design similar to Cyphr's.
"arrow":<pre, fwd, TMR>is equivalent to the git tree root, which is referenced in the git commit."pre":<PR>is equivalent to parent in git.preis implemented as a Merkle DAG (instead of a simple binary Merkle tree) wherepreis a list of parents (see section Explicit Fork).
4.7 Data¶
4.7.1 Data Action¶
Data Actions are stateless signed messages representing principal action. Data actions are signed by an authorized key, are not chained, and are recorded in DT. Data actions are not transactions and do not mutate AT. Actions are lightweight for common use cases (comments, posts, etc.).
- DR is computed from action
czds. - Ordered by
nowand if needed lexical as tie-breaker.
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/comment/create",
"msg": "Hello, world!"
},
"sig": "<b64ut>"
}
4.7.2 Data Tree¶
Data Tree (DT) is a binary Merkle Tree that stores user actions. DT allows tree
reorganization, node deletion, and node omission; a broad design allowing
implementations to accommodate diverse applications, although particular
principals may implement more strictly. Tree nodes may represent various
applications. Various data typs may define their own required fields as
defined by an authority.
Data Tree Inclusion: Like all nodes, DT/DR is first set to empty, causing AR to be implicitly promoted to PR. To explicitly include DR into PR, a DR transaction is signed, which updates the value of DR in the PR tree:
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/ds/create",
"id": "<computed new DR = MR(DT)>"
},
"sig": "<b64ut>"
}
4.7.3 DT Organization¶
As a Merkle Tree, DT provides broad flexibility. Nodes may represent Merkle DAGs, Map/Trie-Based Structures (e.g., Sorted Merkle Maps, Merkle Patricia Tries, Verkle Trees), Sparse Merkle Trees, History/Versioned Merkle Trees, or hybrid/pluggable approaches. Principal may construct DT in MALT mode as an append only, verifiable data structure. Clients may maintain subtrees per application or per account, and handle deletion via tombstones or direct removal. DT organization for specific applications is beyond the scope of this document.
4.8 Trust Anchor¶
For a Cyphr client, the last known trusted state for a particular principal
is the trust anchor, and points to an auth root ARₐ. The ordered sequence of
transactions linking two known Auth Roots ARₐ → ARₓ is called the tx_path.
The transactions that must actually be fetched and verified to move from ARₐ to
ARₓ form the tx_patch (Δ). (See also section Checkpoint and State Jumping.)
5. Genesis¶
5.1 Genesis Commit¶
A principal is created through genesis. Levels 1 and 2 are created implicitly, while Levels 3 and above use an explicit genesis commit.
Levels 1 and 2
- Multikey is not supported. The principal exists with a single key.
- No commit or PG exists.
PR==tmbof the single key (via implicit promotion,tmb== KR == AR == PR).
Genesis Commit (Levels 3+) A genesis commit explicitly creates a stateful principal. Genesis uses a bootstrap model, gracefully upgrading from levels 1 and 2 to level 3. genesis)
- The genesis key (the first key) is explicitly added.
- Adding additional keys, rules, or any other AR component requires an additional transaction for each.
principal/createfinalizes the genesis commit. This establishes PG and denotes principal creation.idis equal to the futureSR,fwd.- The commit is finalized with the standard
commit/create. As a component ofarrow,prereferences the genesis key andfwdis equal to the futureSR. - After commit, the Principal immediately has a new PR, where PR = MR(SR, CR)
A principal may reuse authentication components with an nonce embedding for a unique PG.
5.2 Single Key Genesis¶
The following is an example single key genesis. Note that outside of the cozies
is key, which is the unsigned public key material, but tmb is signed within
the coz.
{
"txs": [// Transaction array
// Mutation transactions
[ // TX0
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"typ": "cyphr.me/cyphr/key/create",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // Signing `tmb`
"id": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg" // The `tmb` of the new key. In this case, itself.
},
"sig": "<b64ut>"
}],[{ // TX1
"pay": {
"alg": "ES256",
"now": 1623132000,
"typ": "cyphr.me/cyphr/principal/create",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"id":"U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // ID == PG == SR (No Commit, no CR)
},
"sig": "<b64ut>",
}],[
// Commit transaction
{ // TX2
"pay":{
"alg": "ES256",
"now": 1623132000,
"typ": "cyphr.me/cyphr/commit/create",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"arrow":"<b64ut>"
},
"sig": "<b64ut>"
}]],
"keys": [{ // key public material
"tag": "User Key 0",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"alg": "ES256",
"now": 1623132000,
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g",
}]
}
Accompanying the txs object, clients may also send a txs_meta object:
{"txs_meta":{
"pre": "<b64ut>", // prior (source) PR
"fwd": "<b64ut>", // forward tree, MR(PT)
"TMR": "<b64ut>", // ordered MR(txm)
"TCR": "<b64ut>", // Ordered MR(txc)
"CR": "<b64ut>", // CR = MR(TMR, TCR)
"fwd": "<b64ut>", // Forward principal root.
"txs_order": [TX₀, TX₁, TX₂],
"txs_tree":{
"TX₀": [czd₀],
"TX₁": [czd₀],
"TX₂": [czd₀]
},
// Information from `txs` may also be repeated.
// This may be useful for debugging or other reasons.
"arrow": "<b64ut>"
}}
5.3 Multi-Key Genesis¶
- A genesis key constructs AT by adding itself and other components.
- At genesis there are no prior commits, so AR is promoted to PR via implicit promotion. AR = MR(KR) when only keys are present, or MR(KR, RR) if rules exist. For example, with two keys: AR = MR(tmb₀, tmb₁).
- Finally, the principal is created by
principal/create.
{
"txs": [[{ // TX0: First Key
"pay": {
"alg": "ES256",
"now": 1623132000,
"typ": "cyphr.me/cyphr/key/create",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // Signing `tmb`
"id": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg" // The `tmb` of the new key. In this case, itself.
}}],[{ // TX1: Second Key
"pay": {
"alg": "ES256",
"now": 1623132000,
"typ": "cyphr.me/cyphr/key/create",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // The genesis key
"id": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M" // The second key's `tmb`
},
"sig": "<b64ut>",
}],[{ // TX2: Principal Declaration
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/principal/create",
"id":"<b64ut>" // ID == PG
},
"sig": "<b64ut>",
}],[{ // TX3: Commit (finality)
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/commit/create",
"arrow":"<b64ut>"
},
"sig": "<b64ut>",
}],
],
"keys": [{ // Public keys material
"tag": "User Key 0",
"alg": "ES256",
"now": 1623132000,
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
},{
"tag": "User Key 1",
"alg": "ES256",
"now": 1623132000,
"pub": "iYGklzRf1A1CqEfxXDgrgcKsZca6GZllIJ_WIE4Pve5cJwf0IyZIY79B_AHSTWxNB9sWhYUPToWF-xuIfFgaAQ",
"tmb": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M",
},
],
}
6 Key¶
Cyphr uses Coz which specifies the structure for cryptographic keys. tmb
is the digest of the canonical public key representation using the hash
algorithm associated with alg.
Example private Coz key with standard fields:
{
"tag": "User Key 0", // Optional human label, non-programmatic.
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // Key's thumbprint
"alg": "ES256", // Key algorithm.
"now": 1623132000, // Creation timestamp
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g", // Public component
"prv": "bNstg4_H3m3SlROufwRSEgibLrBuRq9114OvdapcpVA", // Private component, never transmitted
}
Example public key:
{
"tag": "User Key 0",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"alg": "ES256",
"now": 1623132000,
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g"
}
6.1 - Add a Key (Level 3+)¶
key/createThe following example adds a new key to KR for an existing principal. Note that
key is included in the JSON payload, but not the signed payload, as reference
for client. The key may be transmitted through sideband or known previously. The
construction of sending the public key along with the key/create is good
practice. Also note, this example includes txs. For brevity, this example does
not include the commit, and future example include only the transaction, but
transaction should be included in txs.
{
"txs":[[{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // Signing `tmb`
"typ": "cyphr.me/cyphr/key/create",
"id": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M" // New key
},
"sig": "<b64ut>"
}],
{...}// commit transaction
],
"keys": [{
"alg": "ES256",
"now": 1623132000,
"tag": "User Key 1",
"pub": "iYGklzRf1A1CqEfxXDgrgcKsZca6GZllIJ_WIE4Pve5cJwf0IyZIY79B_AHSTWxNB9sWhYUPToWF-xuIfFgaAQ",
"tmb": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M"
}]
}
6.2 - Remove a Key (Level 3+)¶
key/deletekey/delete removes a key from KT without marking it as compromised. Unlike
key/revoke, key/delete does not invalidate the key itself, it only removes
it from KT, which is useful for graceful key retirement (e.g., decommissioning a
device) when the key was never compromised.
If a key is deleted, any action signed with that key after it has been removed from the principal is ignored. Only actions that were signed while the key was still active in KT are interpreted. Past signatures from previous active periods remain valid even after the key is no longer active, provided they were created while the key was in KR.
There is no effective cryptographic difference between a key that was deleted and one that was never added except that a deleted key may have a duration of legitimate past signatures that remain valid, whereas a never-added key never had any legitimate signatures for this principal.
Deleted keys can be re-added later (via key/create), and, if desired, deleted
again afterward. Implementations should store the public key of deleted keys
that signed at least one action so that the client can cryptographically verify
its own chain.
The key active period is the time span during which a key is present and active in the Key Root (KR), and therefore authorized to sign new actions, for the principal. A key may have multiple successive active periods if it is deleted and later re-added (each re-addition starts a new active period).
id:tmbof the key being removed
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/key/delete",
"id": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg"
},
"sig": "<b64ut>"
}
6.3 - Atomic Key Swap (Level 2+)¶
key/replacekey/replace removes the signing key and adds a new key atomically. Maintains
single-key invariant for Level 2 devices.
{
"txs":[[{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // The existing key.
"typ": "cyphr.me/cyphr/key/replace",
"id": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M", // The second key's `tmb`
},
"sig": "<b64ut>"}],
[{...}]// commit transaction
],
"keys": [{
"tag": "User Key 1",
"tmb": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M",
"alg": "ES256",
"now": 1623132000,
"pub": "iYGklzRf1A1CqEfxXDgrgcKsZca6GZllIJ_WIE4Pve5cJwf0IyZIY79B_AHSTWxNB9sWhYUPToWF-xuIfFgaAQ",
}],
}
6.4 - Revoke a Key (Level 1+)¶
key/revokeA revoke is a self-signed declaration that a key is compromised and should never be trusted again. The key signing the revoke message must be the key itself. Revoke is built into the Coz standard:
A revoke is a self-signed declaration that a key is compromised. A Coz key may revoke itself by signing a coz containing the field
rvkwith an integer value greater than0. The integer value1is suitable to denote revocation and the current Unix timestamp is the suggested value.
rvkandnowmust be a positive integer less than 2^53 – 1 (9,007,199,254,740,991), which is the integer precision limit specified by IEEE754 minus one. Revoke checks must error ifrvkis not an integer or larger than 2^53 - 1.
Example Naked Revoke:
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/key/revoke",
"rvk": 1623132000,
"msg": "Private key was uploaded to Github repo: cyphrme/cyphr"
},
"sig": "<b64ut>"
}
Note that a commit is not required for a revoke. This is termed a naked revoke. Third parties may sign the revoke, declaring the key compromised, without any other knowledge of the principal root, and Cyphr must appropriately interpret this event.
A naked revoke, or a revoke without a subsequent delete, puts the principal in
an error state. An uncommitted revoke does not mutate PR. See section "Consensus" and "Recovery"
for error recovery, but in sort, when a principal receives a naked revoke, it
should sign a revoke, a subsequent key/delete to remove the key from, and
commit. A client may include msg detailing why the key was revoked.
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/key/revoke",
"rvk": 1623132000,
"msg": "Private key was uploaded to Github repo: cyphrme/cyphr"
},
"sig": "<b64ut>"
}
7 Action¶
7.1 Authenticated Atomic Action¶
Authenticated Atomic Action (AAA) is a self-contained, cryptographically signed, and independently verifiable message that proves discrete intent.
Applied, AAA is simply a signed coz whose typ corresponds to a meaningful
application-level action (comment, post, vote, bookmark, reaction, like, etc.)
and whose signature is produced by a key currently authorized in the principal's
Key Tree (KT).
AAA supersedes trust traditionally delegated to centralized services. Historically, services depended upon centralized bearer tokens. Third parties, such as other users, have no way to verify user actions without trusting the integrity of the centralized service. There are countless examples of that trust being abused.
AAA precludes such abuse and promotes a new design pattern for authentication. Instead of authenticating to a centralized login service which provides a bearer token for user action, users may sign individual actions directly. Each atomic action may be trustlessly authenticated by anyone, irrespective of centralized services. For example, instead of logging in to make a comment, a user signs a comment directly which is then verifiable by anyone. In this model, centralized services that maintain user identity are irrelevant and should be actively deprecated.
No mutable server-side context is needed to determine whether the action was authorized. Replay, forgery, and repudiation are cryptographically prevented or strongly deterred. Messages remain meaningful after creation and verifiable by third parties as long as the message exists, even if the original service disappears.
7.2 ¶
typCyphr follows a typ grammar system, denoting a cozies' action, consisting
of these core components: auth (authority), act (action), noun, and
verb.
<typ> = <auth>/<action>
<act> = <noun>[/<noun>...]/<verb>
<verb> = create | read | update | upsert | delete
- auth (authority): The first unit. Typically a domain name or a Principal Root.
- act (action): Everything after the authority.
- noun: One or more path units between authority and verb, representing the
resource or subject of the action. Multiple units form a compound noun
(e.g.,
user/image). - verb: The final unit, the operation to perform.
Cyphr recommends that the authority be either a domain or a PG/PR. When a domain is used as authority, that domain should provide a Cyphr identity.
Example: "cyphr.me/user/image/create"
- Auth:
cyphr.me - Act:
user/image/create - Noun:
user/image(compound noun) - Verb:
create
Other Examples:
cyphr.me/cyphr/key/upsertcyphr.me/cyphr/key/revokecyphr.me/cyphr/principal/mergecyphr.me/comment/create
Required fields for verbs
Unless otherwise noted, verbs need an object for their intent denoted by id.
id: The identifier for the noun the verb is acting upon. It is always required fordelete,read,create,update,upsert.
For example, for key/create, tmb is the identifier for keys so id is equal
to tmb.
7.3 Special verbs¶
In addition to the standard CRUD-like verbs (create, read, update,
upsert, delete), Cyphr defines the following special verbs for
protocol-level operations. See the relevant sections for more detail.
key/revoke(Terminal, inherited from Coz)cyphr/key/replace(Atomicity)cyphr/principal/merge(Merge)cyphr/principal/merge-ack(Merge Acknowledgement)
7.4 Authority and ¶
typThe authority defines the acceptance rules for a type. These rules may be
enforced by a consensus mechanism like a blockchain, a VM, a centralized
service, or other processes. Although Cyphr itself agnosticly does not set
permissions outside of the core authentication rules, Cyphr acknowledges
that rules must be implemented by an authority (likecyphr.me).
7.5 Authority and Noun Properties¶
Nouns have properties as set by an authority:
- Creatable - Items that are able to be created, like
comment/create - Updatable - Items that are able to be mutated after the fact.
comment/update - Deletable - Items that can be deleted.
comment/delete - Ownable - Items that reserve some rights, such as mutation, only to owner.
comment - Transferable - Items that are able to be transferred.
For upsert, properties applying to create apply to upsert only on creation,
and properties applying to update apply to upsert only on update.
7.6 Idempotency and Uniqueness Enforcement¶
Cyphr transaction mutations are idempotent. Replaying an already applied coz is ignored and produces no state change.
All create operations in Cyphr enforce uniqueness. If the target item
(e.g., key, rule, principal) already exists, the operation returns error
DUPLICATE.
7.7 Atomic Orthogonality¶
Atomic Orthogonality is the design principle that each meaningful operation must be performed in exactly one canonical, atomic representation with no overlapping alternatives or implicit side effects. Each action remains predictable, independently verifiable, and cleanly composable. Two actions are orthogonal when changing or using one does not restrict, alter, or interfere with the behavior of the other. From programming language theory, orthogonality minimizes surprises and echoes the Unix philosophy of "do one thing and do it well."
Example 1: Principal genesis does not implicitly create the genesis key. All
keys, including the first key, requires a key/create. This ensures key
addition is always auditable and follows the same verification rules, even at
principal initialization.
Example 2: A principal may only be created using principal/create. A
principal/fork/create does not create a principal, it only denotes a fork.
This separation prevents conflation of creation and derivation, ensuring forks
do not bypass genesis rules.
8 Declaration¶
Detailed in this document so far is iterative state mutation. Cyphr also supports a declarative datastructure. Transactional (imperative) and declarative are isomorphic.
8.1 Client Principal JSON Dump¶
The following is a client principal JSON dump, which includes meta values and values that would be secrete only to the client. This represents the client's internal state of a principal, and includes fields useful for client calculation. Witnesses in the same way keep a similar data structure without the secretes.
{
"Principal_Tag":"Example Account",
"PG": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // PG (permanent genesis digest)
"PR": "dYkP9mL2vNx8rQjW7tYfK3cB5nJHs6vPqRtL8xZmA2k=", // Current Principal Root
// Digest Meta
"pre":"U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // Prior Principal Root
"AR":"", // Auth Root
"KR":"", // Key Root
"RR":"", // Rule Root
"DR":"", // Data Root
// The actual Principal Tree, at the point of this commit
"PT":{
"AT":{ // Auth Root
"KT": {
"U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg":{
"tag": "User Key 0",
"alg": "ES256",
"now": 1623132000,
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g",
"prv": "bNstg4_H3m3SlROufwRSEgibLrBuRq9114OvdapcpVA",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg"
},
"CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M":{
"tag": "User Key 1",
"alg": "ES256",
"now": 1623132000,
"pub": "iYGklzRf1A1CqEfxXDgrgcKsZca6GZllIJ_WIE4Pve5cJwf0IyZIY79B_AHSTWxNB9sWhYUPToWF-xuIfFgaAQ",
"prv": "dRlV0LjnJOVfK_hNl_6rjVKutZWTHNL-Vs4_dVZ0bls",
"tmb": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M",
}
},
"CT":[ // Commit tree
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/key/create",
"id": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M",
"arrow": "<b64ut>"
}],
"RT":{}, // Rule Tree (empty)
"DT":{}, // Data Tree (empty)
},
// Other account Meta
"recoveryConfigured": false,
"hasDataActions": false,
"hasRules": false
"past_txs":[
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // genesis key tmb
"typ": "cyphr.me/cyphr/key/create",
"id": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg", // genesis key tmb
},
"sig": "<b64ut>"
}
]
"revoked_keys":[],
"last_signed_now":000001 // Provides the ability to prevent signing too many auth mutations within a certain timeframe.
}
8.2 Declarative Transaction¶
Instead of imperatively mutating or creating principal root, state may be exhaustively declared in JSON. All client secretes are stripped before signing. (The Go/Rust implementation accomplishes this by using types that preclude secretes.)
Since declarative transactions enumerate the full principal root, they inherently act as checkpoints (see section Checkpoint). As always, the declarative structure is compactified according to Coz.
Example declarative principal:
{
"PT":{ // The actual Principal Tree, at the point of this commit
"AT":{ // Auth Tree
"KT": {
"keys": [{
"tag": "User Key 0",
"alg": "ES256",
"now": 1623132000,
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg"
},{
"tag": "User Key 1",
"alg": "ES256",
"now": 1623132000,
"pub": "iYGklzRf1A1CqEfxXDgrgcKsZca6GZllIJ_WIE4Pve5cJwf0IyZIY79B_AHSTWxNB9sWhYUPToWF-xuIfFgaAQ",
"tmb": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M",
}
],
},
}
}
Embedded into a cyphr/principal/checkpoint/create transaction:
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/checkpoint/create",
"id": "<b64UT>", // The computed AR of the declared state
"PT": {...}
},
"sig": "<b64ut>"
}
8.3 Checkpoints¶
Checkpoints are self-contained snapshots of the authentication-relevant state at a particular point in the chain, and once verified, allows verification from the checkpoint forward without needing to fetch or replay earlier parts of the history.
For a particular commit, each state digest (PR, AR) encapsulates the full state tree (PT, AT). Checkpoints do not rely on prior history to reconstruct AT as all required material is included. Genesis is the foundational checkpoint; services should cache later checkpoints to reduce chain length for verification.
9 Rule Root¶
Level 5 introduces the Rule Root (RR), which denotes weighs and timelocks. Level 6 introduces virtual machine (VM) execution, where rules may be defined in bytecode and executed by a designated virtual machine.
9.1 Weights¶
Weights:
- Every key and every action has a weight score default of
1. - Actions require meeting a threshold weight.
- Enables tiered permissions (e.g., admin keys vs. limited keys)
Unless otherwise defined, each key, action, and transaction weight is implicitly 1.
As a special case, any rule applying to */create also applies to */upsert
for creation, and any rule applying to */update also applies to */upsert for
update.
For example, for "2-out-of-3" for a cyphr/key/create, two cozies need to
be signed by independent keys of weight 1 for the transaction to be valid.
First, rules are defined:
"weights":{
"cyphr/key/create": 2,
}
The rule is added to RR via a rule/create transaction.
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/rule/create",
"rule":{
"weights":{
"cyphr/key/create": 2,
}
}
},
"sig": "<b64ut>"
}
Once the rule is applied, to add a new key, the following two key cozies must be signed for a valid total transaction:
{
"txs": [[
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<signing key tmb>", // First Existing key
"typ": "<authority>/cyphr/key/create",
"id": "<new keys tmb>",
},
"sig": "<b64ut>",
},
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<signing key tmb>", // Second Existing key
"typ": "<authority>/cyphr/key/create",
"id": "<new keys tmb>",
},
"sig": "<b64ut>",
},
]],
"keys": [{
/* new key */
}],
}
9.2 Timelocks¶
A timelock is a delay until a transaction takes effect. This allows principals time to undo or otherwise address actions that may have security implications, such as adding or removing a key. Timelocks are defined the rules where the value is the time in seconds.
{
"timelock": {
"cyphr/key/create": 604800, // 604800 seconds is 7 days.
}
}
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/rule/create",
"rule":{
"timelock": {
"cyphr/key/create": 604800, // 604800 seconds is 7 days.
}
}
},
"sig": "<b64ut>"
}
To cancel a transaction in a timelock, a principal signs a timelock/cancel
where id denotes the czds of the transaction.
9.3 VM¶
Level 6 introduces virtual machine (VM) execution, where rules may be defined in bytecode and executed by a designated virtual machine.
Virtual machine may or may not be Turing complete.
"VM":{
"machine":<digest value>,
"rule":<digest value>
}
10 Embedding¶
An embedding is a digest reference to an external node, such as a principal (PR), key, or key tree. Embedding is the mechanism by which Cyphr achieves hierarchy, delegation, and selective opacity (using nonces and digests).
The default weight of an embedded node is one, regardless of how man children that node contains. Like all nodes, an embedded node may be assigned a different weight by a rule. For example, a principal embedded into KR by default has a weight of one regardless of how nodes are weighed for the embedded principal.
On cyclic imports, embedding stops recursion at the point of cycle, preventing infinite recursion. For example, when principal A embeds principal B, and B embeds A, verifying A includes B's members but does not recursively resolve B's embedding of A.
10.1 Nonce, Embedding, and Opaque Nodes¶
Cyphr permits nonces, embeddings, or otherwise opaque nodes anywhere in the
Principal Tree. Embeddings are indistinguishable from other digest values unless
revealed by the client. One or more nonces may be included at any level of the
state tree. To delete a embedding or nonce, a */nonce/delete is signed.
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr/nonce/create",
"id": "T0T1HFBxNFbhjLC10sJTuzrdSJz060qIme1DKytDML8"
},
"sig": "<b64ut>"
}
10.1.1 Embedding path¶
Nonces, embeddings, or otherwise opaque nodes may be inserted anywhere in the
state tree. typ specifies the path for insertion. A nonce/delete, where
id == nonce removes the nonce.
cyphr/nonce/create // Principal Genesis
cyphr/AT/nonce/create // Nonce is inserted at the root of AT.
cyphr/AT/KT/nonce/create // Nonce is inserted at the root of KT.
10.2 Nonce¶
Although used with a slightly different connotation in the broader industry, in Cyphr a nonce is a unique, high-entropy value. Unless explicitly labeled or revealed, Cyphr is unable to distinguish a nonce from any other node type such as an embedding. Nonces serve a few purposes:
- Obfuscation: Nonces are indistinguishable from key thumbprints and other digest values
- Privacy: Prevents correlation across services
- Reuse: Allows reuse with a distinct identifier
Design notes:
- Nonces, like all other node values, are associated with a hashing algorithm or
a multihash. For example,
SHA256:<nonce_value>. This enables nonces to be opaque as needed while denoting a particular bit strength. - The nonce's bit length must match the declared algorithm's output size (e.g., a nonce declared as SHA-256 must be 256 bits). Bit-checking is the only strength check possible for nonce values.
- Nonce values should be cryptographic randomly generated values must match the target strength of the associated hashing alg.
- Nonces may be implicitly promoted in the Merkle tree just like any other digest or entropic value.
- At any tree level, multiple nonces are permitted.
- For any opaque value, specific structure may need to be revealed for specific
- operations. For example, an opaque public key cannot be used until it is revealed.
- Like other digest values, when calculating a new hashing algorithm value, new values are calculated from a prior value. (See section Conversion.)
Unlike systems that rely on incrementing counters to enforce “used only once” (nonce meaning "number used once") behavior, Cyphr is distributed and cannot guarantee sequential uniqueness across principals. Instead, a sufficiently large random value provides probabilistic uniqueness that is guaranteed in practice. As an aside outside of scope, cryptographic signatures and other identifiers may also act as entropy sources.
10.3 Embedded Principal¶
Authorization is transitively conferred through embedding. An embedded principal is a full Cyphr identity embedded into another principal. An embedded principal has the default weight of a single node and appears in the Merkle tree of another principal as the digest of the external Principal Root (PR).
For PG, PR, and AR exclusively, tip at verification time from the referenced principal is retrieved before any operation. For all other node types no retrieval is performed. However, any node of these types must be revealed first; opaque nodes do not trigger state synchronization.
Typical uses for principal embedding are identity encapsulation, external recovery authorities, social recovery, organizational delegation, and disaster recovery. (See section Recovery.)
An example of embedding multiple external principal's KR's into KR:
Principal Tree (PT0)
│
├── Auth Tree (AT0)
│ │
│ ├── Key Tree (KT0) // Principal 0's tree
│ │
│ ├── Key Root (KR1) from principal 1
│ │
│ ├── Key Root (KR2) from principal 2
Embedded nodes use b64ut digest as the JSON name in tree structure. For example:
{"PT":{
"AT":{
"KT": {
"SHA256:T0T1HFBxNFbhjLC10sJTuzrdSJz060qIme1DKytDML8":"", // External Embedding
}}}}
Principal's Keys labeled as an embedding
```json5
{"PT":{
"AT":{
"KT": {
"SHA256:U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg":{
"tag": "User Key 0",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"alg": "ES256",
"now": 1623132000,
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g",
"prv": "bNstg4_H3m3SlROufwRSEgibLrBuRq9114OvdapcpVA"
},
"SHA256:CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M":{
"tag": "User Key 1",
"tmb": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M",
"alg": "ES256",
"now": 1623132000,
"pub": "iYGklzRf1A1CqEfxXDgrgcKsZca6GZllIJ_WIE4Pve5cJwf0IyZIY79B_AHSTWxNB9sWhYUPToWF-xuIfFgaAQ",
"prv": "dRlV0LjnJOVfK_hNl_6rjVKutZWTHNL-Vs4_dVZ0bls"
},
"SHA256:T0T1HFBxNFbhjLC10sJTuzrdSJz060qIme1DKytDML8":"" // Opaque External Embedding
}}}}
To support embeddings, all major types are objects except CT, which does not support embeddings.
PT: Object AT: Object CT: Object DT: Object KT: Object RT: Object CT: Array
Inner to KT and RT are keys and rules, which are arrays and denote the
principal's own items.
- keys: Array
- rules: Array
10.4 Opacity¶
Example external opaque embedding:
"SHA256:T0T1HFBxNFbhjLC10sJTuzrdSJz060qIme1DKytDML8":"", // Opaque External Embedding. Unknown type.
Example external non-opaque embedding:
"SHA256:T0T1HFBxNFbhjLC10sJTuzrdSJz060qIme1DKytDML8":{"keys":[...]}, // Non-opaque External Embedding.
10.5 Conjunctive Authorization¶
To sign/act as a primary principal, an embedded principal must produce a valid signature according to its own rules (its own AR). Authorization involving an embedded principal is conjunctive:
- Transaction must be valid according to the embedded principal’s own rules (its KR/RR), and
- The act of using that embedded principal must be authorized by the primary principal’s rules.
Example: when a Principal(B) or AR(B) is embedded into a KR(A), embedded principal is treated as one logical key (with a default weight of 1), but the internal authorization depends on Principal(B)
10.6 Meaningful Embeddings¶
All nodes may be embedded into other nodes, but embeddings may not always be meaningful. For example, a Rule Root (RR) embedded into a Key Root carries no meaning. Clients should discourage such practice, but this may not be enforceable due to opaqueness.
10.7 Pinning¶
For PG, PR, and AR exclusively, embedded references trigger tip retrieval on authentication, but synchronization isn't always desired. Pinned identifiers denote static states that prohibit updates, ensuring immutable authorization rules.
A pin prefixes the digest value (PIN:<alg>:<value:):
PIN:ES256:U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg
11 Lifecycle¶
11.1 Flags¶
A principal may be flagged with the following conditions.
| Condition | Definition |
Errored |
Fork detected, invalid chain, or other error |
Deleted |
principal/delete transaction signed |
Frozen |
freeze/create active and freeze/delete not signed |
CanMutateAR |
Has components at the required thresholds to mutate AT |
HasActiveKeys |
At least one active (non-revoked, non-deleted) key exists |
CanDataAction |
Can sign data actions (Level 4+, active key exists) |
Errored is an orthogonal flag and indicates a violation (fork detected, chain
invalid) but does not change the principal base state (see sections States,
Consensus). Any base state can be errored or non-errored. A principal with no
flags is termed "normal".
11.2 States¶
Flags combine into 6 principal base states.
| State | Conditions |
| Active | ¬Deleted, ¬Frozen, CanMutateAR, HasActiveKeys |
| Frozen | Frozen, ¬Deleted, CanMutateAR, HasActiveKeys |
| Deleted | Deleted, ¬Frozen |
| Zombie | ¬CanMutateAR, CanDataAction, ¬Deleted |
| Dead | ¬HasActiveKeys, ¬CanDataAction |
| Nuked | Deleted, all keys revoked or deleted |
- Active: Normal operating state.
- Frozen: Principal has been frozen via
freeze/createand has not yet been unfrozen viafreeze/delete. No mutations until unfrozen. - Deleted: The principal signed
principal/delete. No new transactions or actions (including data actions) are possible. - Zombie: (Level 4+) AR mutation is impossible (
¬CanMutateAR), but data actions are still possible. Example:key/createrequires 2 weight points, but only one key exists with weight 1.comment/createrequires default 1, so comments are still possible but AR mutation is impossible. - Dead: No transactions or actions possible at all. No active keys remain. Dead is a consequence of any condition that leaves the principal with no keys and no data action capability.
- Nuked: (Level 3+) A dead account with all keys revoked, all keys deleted,
and the principal deleted (
principal/delete). The most terminal state.
Deleted and Frozen are mutually exclusive (a principal cannot be frozen and
deleted at the same time). To ensure that no aspect of a deleted principal may
be reused, an account may be "nuked", all keys revoked, then deleted, and then
the principal deleted. This ensures that no new principal may be created reusing
existing principal keys.
11.3 Unrecoverable¶
An unrecoverable principal is where recovery is impossible within the
Cyphr protocol rules. Transactions and recovery are impossible, although
some data actions may still be possible. Unrecoverable is a partially ambiguous
classification: the principal cannot mutate AR (¬CanMutateAR) and is not
deleted, but whether data actions remain possible is not connoted. Once
CanDataAction is evaluated, an unrecoverable principal resolves to either
Zombie (data actions still possible) or Dead (nothing possible).
Note that CanMutateAR is not monotonic in key count at Level 5+. A principal
with active keys may still have ¬CanMutateAR if no key combination meets the
threshold for AT mutation.
11.4 Close¶
Closing a principal is performed via a principal/delete transaction (Principal
Delete, Level 3+). Closed principals are permanently closed and cannot be
recovered; no transactions or actions are possible on a closed account. However,
the protocol does not prevent a user from creating a new principal reusing the
existing keys (unless those keys were revoked).
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<target signing key tmb>",
"typ": "cyphr.me/cyphr/principal/delete",
"id": "<target PR>"
},
"sig": "<b64ut>"
}
11.5 Merge and Fork¶
Out-of-band merging and forking, termed implicit merging and forking, is possible by the protocol as described. An implicit merge is where AT components from two principal are consolidated into a single principal. An implicit fork is where a new principal is created and AT components transferred from an existing principal. If manual merging and forking is allowed by convention, why then is there the need for forking and merging? Protocol level merging and forking explicitly denotes intent, and as a consequence the protocol itself gains awareness for principal history preservation.
Fork / Merge Terminology
- Explicit fork : Protocol-level, history-preserving fork operation
- Explicit merge : Protocol-level, history-preserving merge operation
- Implicit fork : Out-of-band fork via AT component transfer
- Implicit merge : Out-of-band merge via AT component consolidation
- Invalid fork : Forbidden chain divergence that violates protocol append-only & single-tip rules (prohibited)
Related to merging is full delegation is where the source account deletes all keys and adds the PR of the target.
11.5.1¶
Merging where a source adopts the state of another principal, the target, consolidating histories while preserving the targets's original PG (Level 3+).
The source signs a principal/merge transaction where the source is denoted via
merge_from and the target's PR is denoted via merge_to. The target accepts the
merge by signing a principal/merge-ack transaction containing the PR of the
source. Optionally, the source may delete all keys and/or sign a
principal/delete.
Note the target account must explicitly add keys from the source account (if the keys are not already present). AT is not implicitly merged and all component must be explicitly added to the target. Also note that the source principal is considered active unless explicitly deleted; a merge does not set the source to inactive.
Example source principal merge transaction:
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<source signing key tmb>",
"typ": "cyphr.me/cyphr/principal/merge",
"merge_from": "<list of source's PRs>",
"merge_to_pr": "<target Principal Root>"
},
"sig": "<b64ut>"
}
And the merge acknowledgement by the target principal:
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<target signing key tmb>",
"typ": "cyphr.me/cyphr/principal/merge-ack",
"merge_from": "<list of source's PRs>",
"merge_to_pr": "<target Principal Root>"
},
"sig": "<b64ut>"
}
The design of merge-ack, an explicit merge acknowledgement, is important to
prevent a "merge attack", where external accounts could attack an account by
merging in their state.
11.5.2 Fork¶
Forking is where one principal, the source, creates a new principal, the
target, effectively splitting identities while preserving the source's
original PG and a new PG for the target. A fork is created by signing
cyphr/principal/fork/create, cyphr/principal/create (for atomic
orthogonality), and adding at least one key. This transaction bundle is
equivalent to a genesis transaction. For "bad faith" forking (invalid fork), see
section "Consensus".
Example principal fork, consisting of two transactions:
{
"txs": [[
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/key/create",
"id": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
},
"sig": "<b64ut>"
}],[
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/principal/create",
"id": "<b64ut>",
},
"sig": "<b64ut>",
}],[
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<signing tmb>",
"typ": "cyphr.me/cyphr/principal/fork/create",
"fork_pr": "<fresh PG digest, which in this case is just KR>",
"arrow":"<b64ut>"
},
"sig": "<b64ut>"
}],
],
"keys": [
{
"tag": "User Key 0",
"alg": "ES256",
"now": 1623132000,
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg"
}
]
}
12. Multihash¶
12.1 Multihash Identifier¶
A multihash identifier is a set of digests that addresses content. Multihash identifiers are calculated on a per commit basis for each hash algorithm referenced by the principal in KT at the time of commit.
In Cyphr, cryptographic algorithms are pluggable: no single cryptographic primitive is exclusively authoritative or tightly coupled to the architecture. This abstraction enables flexibility in algorithm choice, security upgrades, and rapid removal of broken algorithms. No single algorithm is canonical. All variants in a multihash identifier are considered equivalent by Cyphr and security judgments are out-of-scope.
In summary:
- PG, PR, AR, KR and nodes in the Merkle trees are referenced by multihash identifiers, with one variant per hash algorithm.
- Digests are computed for all hashing algorithms referenced in KT (keys, embeddings).
- When an algorithm primitive is removed, its hash is no longer computed. When a primitive is added, its algorithm's variant begins computation.
- Cyphr makes no relative security judgements. All variants are considered equivalent.
Algorithm Mapping: Each key algorithm implies a hash algorithm, as defined by Coz. See the Coz specification for an exhaustive list of supported hashing algorithms.
| Key Algorithm | Hash Algorithm | Digest Size | Strength Category |
| ES256 | SHA-256 | 32 bytes | 256-bit |
| ES384 | SHA-384 | 48 bytes | 384-bit |
| ES512 | SHA-512 | 64 bytes | 512-bit |
| Ed25519 | SHA-512 | 64 bytes | 512-bit |
12.2 MultiHash Merkle Root (MHMR)¶
The MultiHash Merkle Root (MHMR) algorithm computes digests for all nodes in a state tree. Each MHMR variant is computed with respect to a target hash H. H is determined per commit where H is a hash algorithm associated with a current component in KT. When multiple hash algorithms are referenced, implementations compute a MHMR variant for each H. When an algorithm is removed from reference in KT, its MHMR variant is no longer generated for new commits.
MHMR Computation Given an ordered list of child digests (each child is a binary digest value computed under some hash algorithm):
- Sort the child digests in lexical byte order unless order is otherwise given.
- Implicit promotion:
If there is exactly one child digest, the MHMR_H for any target H is simply the bytes of that child digest (no hashing occurs). Promotion is recursive. - Binary Hashing of Children:
Concatenate the sorted child digest bytes in order.
Compute MHMR_H = H( concatenated bytes ).
MHMR Examples
| Case | Children | Target H | MHMR_H Computation | Result |
| Single child (promotion) | B (32-byte SHA-256) | SHA-384 | — (implicit promotion) | B bytes (32 bytes) |
| Two children, same alg | C, D (both SHA-256) | SHA-256 | sort(C,D) → SHA-256(C ∥ D) | 32-byte digest |
| Two children, different algs | A (48-byte SHA-384), B (32-byte SHA-256) | SHA-384 | sort(A,B) → assume A < B → SHA-384(A ∥ B) | 48-byte digest |
Important Properties
- No re-hashing of children: Inner digests are fed directly into the parent hash function as raw bytes (unless being converted, where the value is hashed first).
- Byte-order determinism: Lexical byte sorting ensures consistent ordering regardless of how children were labeled or enumerated.
- Security bounded by weakest link: The strength of any MHMR_hash variant is limited by the weakest hash algorithm appearing anywhere in the subtree below it.
- Nonce injection: A nonce carrying a desired hash algorithm can be inserted as a child into KT to force computation of that algorithm variant even if no active key natively supports it.
12.3 Conversion¶
To support embedded nodes and upgrades, values from one digest algorithm may be converted as input to another. Conversion happens at the node level and the parent node isn't required to know of the child node's conversion.
For example, in a Merkle tree with a SHA-384 node (A) and SHA-256 node (B), a SHA-384 root is: MR_SHA384(SHA384(A), B). B's value is fed into the hashing algorithm first before being inputted into the MR.
As a consequence of this design, for each algorithm, every node may have an identifier for that hashing algorithm.
SHA-384 Root
┌─────────────┐
│ MR_SHA384 │
└──────┬──────┘
│
SHA-384( A || B )
|
┌───────────┼───────────┐
│ │
│ SHA-384(Node B)
│ │
┌─────────────┐ ┌─────────────┐
│ Node A │ │ Node B │
│ (SHA-384) │ │ (SHA-256) │
└─────────────┘ └─────────────┘
Conversion Example: For a SHA384 tree containing a node that is SHA256 only (for example, for an ES256 key, an opaque embedding, or any node with a different hashing algorithm), the node is converted into a SHA384 node.
The ES256 Key node:
"SHA256:T0T1HFBxNFbhjLC10sJTuzrdSJz060qIme1DKytDML8":{<key data>}
The ES256 key node is converted to SHA384:
"SHA384:NLDDkOyBHNVG4H6yHwSf8AwvI82B-tRhleeuBhYR4LCdvP9Is2-HjXMbllTv0NJk":""
Conversion Security Considerations Conversion is not ideal, but is unavoidable for pluggability, recursion, and embedding. Implementors must be aware that inner nodes may have different security levels than parent nodes. Algorithm diversity aids durability but risks misuse. For a particular node, security is bounded by the weakest link. For uniform security, keys from one strength category may be used.
12.4 Algorithm Incompatibility¶
A multihash component is deemed incompatible if a client cannot support the specific algorithm (alg) used in a principal's message.
However, due to Cyphr’s use of encapsulation, implicit promotion, and other features, a clients do not always require full algorithm support. For example, if a service can process the top-level digests, it may remain compatible even if it cannot verify the underlying primitives of nested components.
Compatibility is strictly required only for operations where the service must verify or interpret the cryptographic material. If such an operation is attempted using an unsupported algorithm, the services are incompatible.
Extended¶
The following sections contain advice, exposition, non-normative, or additions to the core Cyphr protocol.
We are considering the following and it is all brainstorming or work in progress. Some sections like "Authentication" will likely be moved to its own protocol/document/repo.
13 Mutual State Synchronization (MSS)¶
Cyphr enables symmetric, bidirectional state awareness that eliminates the one-sided dependency inherent in traditional password-based or federated authentication models. Cyphr's distributed model does not distinguish between users and services; both are represented by a Cyphr principal, allowing users to track services without depending on legacy systems such as certificate authorities (CA) or email.
In legacy models, only the service tracks user authentication state (first established by passwords and then tracked by sessions), while users have no independent view of the service's view of their account. Recovery is manual, service-specific, and often funneled through email, creating a central point of failure and control. Programmatic key rotation or bulk recovery is typically impossible without service cooperation.
Cyphr inverts and symmetrizes these concerns through Mutual State Synchronization (MSS):
- Services and users are represented as Cyphr principals and maintain independent, cryptographically verifiable views of each other's state.
- Cyphr clients push state mutations to registered services after local application.
- During authentication, services verify these pushes against the principal's current chain, reducing round-trips.
MSS directly addresses concerns about stale distributed state. This practice is similar to double entry accounting, where instead of one entry in a ledger being depended upon as a single source of truth, two entries are cross-checked.
Although Cyphr provides single-sign-on semantics, it differs from historic systems by eliminating passwords, email dependency, and unidirectional state tracking. MSS addresses centralization risks in legacy SSO (passwords + email as de facto recovery root) and bearer-token models (service as sole state oracle). By making state mutual, verifiable, and push-capable, Cyphr enables:
- Low-latency authentication flows.
- Independence from email/CA choke points.
- Recovery without manual per-service intervention.
- Programmable, symmetric trust between users and services.
13.1 Core Properties of MSS¶
| Property | Description | Benefit vs. Legacy Systems |
| Bidirectional tracking | Both user and service verify the other's authentication. | No single source of truth; reduces trust in service logs. |
| Push/Pull-based updates | Client proactively pushes mutations, pull as needed. | Faster auth (pre-synced state); no polling required. |
| Independent verification | Each party resolves the other's state from trust anchor. | Censorship-resistant; survives service outage. |
| Service as principal | Service exposes its own state via API or public chain. | Bypasses CA/email dependency for service identity. |
| Double-entry analogy | Not a single ledger, parties may track each other's state | Stronger auditability and fraud/spoofing/evasion detection. |
13.2 State Resolution and Verification by a Third Party¶
To verify a principal's current state:
- Identify Trust Anchor: the claimed root (PG) or transitive state (PR).
- Obtain transaction history: ordered list of transactions from trust anchor to tip.
- Replay transactions:
- Start with trust anchor
- For each transaction, verify:
- Signature is valid
tmbbelongs to a key in current KR- Principal lifecycle state permits the operation
nowis after previous transaction- Transaction is well-formed for its
typ
- Apply mutation to derive new AR
- Compare: final computed KR/AR/PR should match claimed current state
13.3 Recommended Usage¶
MSS makes authentication quicker by allowing clients to push state to services before authentication. When a client mutates their own state, they may push the mutations to all registered third parties.
- Proactive push on mutation: After transaction (
key/create,key/revoke, etc.), client pushes to all registered services (stored locally through previous registration). - On-demand sync: Before high-value actions, client queries service tip and reconciles if needed.
13.4 MSS API¶
(Non-Normative)
Cyphr's typ system builds a ready to use API. However, there are a few
endpoints not enumerated by typ, such as synchronization. Services should
expose an interface for MSS (like HTTP API). Services may of course limit depth
and have other rate limits. A gossip communication layer may be used to keep
clients in sync. See also section API.
tip
GET /tip?pr=<principal-root>Returns the service's view of the tip (or latest known AR/PR digest) for the principal.
patch - Returns the service's view for the principal.
GET /patch?pr=<principal-root>&from=<ps>&to=<target-ps>- Full formGET /patch?from=<ps>- From PR to currentGET /patch?pr=<principal-root>from PG to currentGET /patch?pr=<principal-root>from=<ps>from PR, for particular PG, to currentGET /patch?from=<ps>&to=<target-ps-or-empty>- Range
to is optional and on omission is tip.
pr is optional since it should be included in patch. May be explicit for debugging.
from is optional if pr is given.
POST /pushAccepts one or more signed transactions (tx_patch). Service verifies chain validity and applies update.
Synchronization check:
- If service-reported tip equals the client's local PR, state is synced.
- On mismatch, client pushes delta or service requests missing patch.
13.5.1 Witness Registration¶
A principal may have many clients. The principal signs a message to inform clients of the registration of external witnesses, which themselves are represented as principals. This message is not included in AR, and is instead may be included in DR as a data action, so that PR may remain unmodified.
cyphr.me/cyphr/witness/register/create: Value of the Principal's PG.
This message is transported to the external witness for registration. The
witness is removed with a delete.
13.5.2 MSS Registration Example¶
Example ledger displaying remote state synchronization. Local Principal X is
at tip State3.
| Principal | Trust Anchor | Synced? |
| A | State0 | N |
| B | State2 | N |
| C | State3 | Y |
After successful syncing:
| Principal | Trust Anchor | Synced? |
| A | State3 | Y |
| B | State3 | Y |
| C | State3 | Y |
13.6 Witness Timestamps¶
Clients should record their own "first_seen" if the oracle has a date after receipt. If external witness timestamps are out of expected range, clients should also record external witness timestamps. This allows MSS to detect conflict, dishonest behavior, and bugs.
13.7 Gossip¶
Unlike existing gossip protocols, like Cassandra, where there is no authority on a particular piece of data, Principals are the authority over their own state.
Clients may periodically query or exchange state details with other clients for
specific principals. This helps detect divergences, ensure consistency, and
propagate changes. For example, a client might check the tip against multiple
sources to verify integrity before accepting updates. Clients may check
registration through GET /tip?pr=<principal-root>, which should return empty
if the principal isn't registered.
Clients may have an principal identity separate from the principal itself in that a principal's primary client may be registered as a witness itself.
13.8 Suggested API¶
See also section "MSS" for tip, patch, and push endpoint definitions.
Good practice for digest identifiers is prepending with Coz algorithm
identifier, e.g. SHA256:<B64-value>.
Since cryptographic digests are suitable, all GETS may simply be looked up by digest.
GET /<diget-value>Alternatively,
efor everything is suggest:GET /e/<diget-value>
14 Recovery¶
14.1 Sideband¶
Simple devices and services using Level 1 should define sideband recovery. A Level 1 principal can self-revoke, but this results in a dead principal where recovery requires sideband intervention. Devices and services should define sideband recovery method where a new key is created and services are updated with a reference to this key.Although out of compliance with this specification, services without a sideband recovery method may ignore revokes to preclude an unrecoverable state. Note that sideband recovery results in a new Principal identity.
Sideband Recovery Example Use SSH access to the device with the unrecoverable principal, delete the old key, replace it with a new key, and update the public key on services that communicate to that device.
14.2 Recovery Mechanisms¶
When a principal loses access to all active keys, or the account is otherwise in an unrecoverable state recovery mechanisms allow regaining control.
There are two main mechanisms:
- Self Recovery, various methods of backup. For user self management.
- External Recovery Where some permissions are delegated to an external account, a Recovery Authority. This may be social recovery or third-party service.
Level 1 doesn't support recovery. Any recovery is accomplished through sideband. Level 2 supports recovery but only atomic swaps. The recovery key can replace the existing key. Level 3+ supports recovery and can add new keys.
14.3 Self-Recovery Mechanisms¶
| Mechanism | Description | Trust Model |
| Backup Key | Backup key stored in a secure location | User custody |
| Paper wallet | Backup key printed/stored offline | User custody |
| Hardware key | Hardware key device (U2F-Zero, solo1) | User custody |
| Airgapped key | Cold storage, never online | User custody |
14.4.1 Recovery Validity¶
Ideally, recovery agents only act when the account is in an unrecoverable state, however, the unrecoverable state may not be definitively known or verifiable by the protocol, such as in the case with lost keys.
Principals should create recovery circumstances carefully. The protocol does not enumerate these conditions here; they are defined by principal rules at the time of the attempted recovery.
14.5 Recovery Transactions¶
14.5.1 — Register Fallback¶
cyphr/recovery/createRegisters a recovery agent (backup key, service, or social contacts).
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<signing key tmb>",
"typ": "<authority>/cyphr/recovery/create",
"recovery": {
"agent": "<recovery agent PG or tmb>",
"threshold": 1
}
},
"sig": "<b64ut>"
}
Fields (within recovery object):
agent: PG of service, tmb of backup key, or array of contact PRsthreshold: For social recovery, M-of-N threshold (default: 1)
14.5.2 — Remove Fallback¶
recovery/deleteRemoves a previously designated recovery agent.
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<signing key tmb>",
"typ": "<authority>/recovery/delete",
"recovery": {
"agent": "<recovery agent PG or tmb>"
}
},
"sig": "<b64ut>"
}
14.6 Recovery Flow¶
When a principal is locked out:
- User generates a new account with a fresh PR
- User contacts recovery agent (out-of-band)
- Agent verifies identity (method varies by agent type)
- Agent signs a Recovery Initialization transaction for the new user key:
This initializes a new Principal Root (PR) that is manually linked to the previous state by the Recovery Authority. The new PR is not cryptographically linked to the previous state, but it is manually linked to the original PG by the Recovery Authority's recovery transaction.
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<recovery agent tmb>",
"typ": "<authority>/key/create",
"id": "<new user key tmb>"
},
"key": {
/* new user key */
},
"sig": "<b64ut>"
}
Because the agent was designated via cyphr/recovery/create, their key/create is valid even though no regular user key signed it.
14.7 External Recovery¶
External recovery is accomplished through embedded principals, where some permissions are delegated to an external account.
External recovery delegates recovery authority to an external principal. The recovery agent verifies identity out-of-band and signs a recovery transaction on behalf of the locked-out user.
| Mechanism | Description | Trust Model |
| Social recovery | M-of-N trusted contacts | Distributed trust |
| Third-party service | Verification service | Service trust |
14.8 Social Recovery¶
For social recovery, multiple contacts signs the same key/create transaction.
When threshold signatures are collected, the transaction is valid. For
example, 3-of-5 social recovery requires 3 contacts to sign the key/create.
14.9 Freeze¶
A freeze is a global protocol state where valid transactions are temporarily
rejected to prevent unauthorized changes during a potential compromise. A freeze
halts all key mutations (key/*) and may restrict other actions depending on
service policy. Freezes are global and apply to all principal actions.
14.9.1 Self-Freeze¶
A user may initiate a freeze if they suspect their keys are compromised but do not yet want to revoke them (e.g., lost device).
- Mechanism: User signs a
cyphr/freeze/createtransaction with an active key. - Effect: Stops all mutations until unfrozen.
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<signing key tmb>",
"typ": "<authority>/cyphr/freeze/create",
"id": "<targeted PR>"
},
"sig": "<b64ut>"
}
A designated Recovery Authority may initiate a freeze based on heuristics (irregular activity) or out-of-band communication (user phone call).
- Mechanism: Recovery agent signs
cyphr/freeze/create. - Effect: Same as self-freeze.
- Trust: The principal explicitly delegates this power to the authority via
cyphr/recovery/create.
14.9.3 Unfreeze (Thaw)¶
To unfreeze an account, a cyphr/freeze/delete is signed:
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<signing key tmb>",
"typ": "<authority>/cyphr/freeze/delete",
"id": "<targeted PR>"
},
"sig": "<b64ut>"
}
- Self-freeze can be unfrozen by active keys.
- External freeze requires the Recovery Authority to thaw (or the principal after a timeout, if configured)
14.10 Security Considerations¶
- Timelocks (Level 5+): Recovery can have a mandatory waiting period.
- Revocation: Backup keys can be revoked if compromised.
- Multiple agents: A principal may designate multiple fallback mechanisms, including M-of-N threshold requirements
- Freeze abuse: External freeze authority requires explicit delegation and trust
14.12 Retroactivity (Reversion, Retrospection)¶
Retroactivity is undoing actions to a given timestamp. This is a complex issue for services.
Unlike Git, and outside of consensus, Cyphr does not use reversion, that is
undoing past actions. Instead, clients should mutate their state using the
appropriate verbs, create and delete, as needed for a targeted state.
For example, even though a key/revoke can be postdated to the time of an
attack, Cyphr should interpret transaction based on current now and not
rvk.
If an attacker gains access to keys and is able to sign actions unauthorized by the agent represented by the Principal, when the Principal regains control, they may want to undo these actions. Instead of retrospectively marking revokes, Principals must explicitly mutate their state forward.
Retrospection Attack: An attacker uses retroactivity to undo legitimate Principal actions.
As a matter of bookkeeping, a client may mark past actions as disowned, expressing the intent of the Principal to mark that action as unintentional. However disowning does not mutate AR.
15 Consensus¶
15.1 Consensus Philosophy¶
Cyphr is a self-sovereign protocol in which the principal is the primary custodian of its own security and state. Consensus rules are deliberately minimal. They detect and respond only to clear, cryptographically undeniable violations such as invalid signatures, contradictions, and conflicting commits that signal compromise, bugs, or attacks.
Consensus rules prioritize simplicity, determinism, and independent verifiability by any witness, without requiring global coordination or a blockchain-like mechanism. Security is largely delegated to the principal itself, with witnesses (clients, services, oracles) enforcing basic invariants to prevent propagation of invalid state. The design assumes principals generally act honestly because repeatedly dishonest clients are dropped from gossip and that principals control their keys.
Consensus is intentionally "shades of grey" rather than strict black-and-white. The design accommodates diverse implementations (including smart-contract clients on external hosted blockchains) and accepts that incompatibility between clients can be an intentional choice. This consensus model also permits logical deduction. Retained errors and timestamps serve as transparent signals of client honesty. It represents a fundamentally different philosophy: consensus emerges from what actually occurred and who published what when, not strictly from coordinated rule enforcement or majority vote, and opens the door for intelligent agents to detect violations through reasoning instead of strict rule matching.
Although outside of the scope of this document, consensus rules attempt to accommodate a wide set of circumstances and implementation, and should assume minimal control of implementation. Consensus rules accommodate as best as possible such situations, but acknowledge that various circumstances may result in client incompatibility, which may be an intentional decision by principals.
For conflict resolution beyond consensus rules, see section "Recovery".
15.2 Proof of Error¶
All messages in Cyphr are signed by the principal, making errors auditable and attributable. Witnesses may retain invalid messages as proof of error, a signed message demonstrating bad behavior (e.g., an invalid signature or conflicting commit). This proof can be shared via gossip, proactive error sharing, presented during recovery/escalation, or retained until resync error resolution. For example, when an invalid fork is received, a witness may retain both conflicting transactions as proof of compromise and broadcast them to other witnesses or the principal for resolution. Proof of error is a hypernym of "fraud proof" and "fault proof". Proof of error is powerful: security may be improved from game theory or novel mechanisms. Exhaustive detailing is outside the scope of this document, but the authors acknowledge its potential in security distributed systems.
15.3 Resync¶
Resync lets a witness advance from a known good trust anchor to the current tip by fetching and verifying the delta (tx_patch) and may be triggered manually or automatically.
- Select Trust Anchor Select trust anchor (trusted PR, AR, or PG).
- Fetch Delta (Patch) Request minimal patch with GET
/patch?from=
&to= or GET /patch?pr= &from= . - Verify Patch Independently verify the patch for valid pre chaining, signing keys that were active with no revocation in path, computed intermediate and final PR that match claims, correct timestamps, thresholds (Level 5+), and no forks.
- Apply and Progress Trust Anchor On success, apply patch, update local state, and promote new PR to trust anchor.
Witnesses should use an exponential backoff cooldown for repeated resync attempts to prevent denial-of-service. Clients should gossip tip to other clients to ensure a uniformed presentation of the principal, especially after long offline periods.
Many transient errors resolve via resync. Persistent failure escalates to errored state or ignore.
15.4 Resync PoP¶
A principal may re-iterate an existing state as authoritative without mutating PR/AR through a resync POP.
- PoP Confirmation: To accelerate resync or confirm intent, the principal
may perform a Proof of Possession (PoP) by signing a challenge message (e.g.,
typ: "cyphr.me/cyphr/resync/create") with an active key. This helps distinguish transient errors from genuine issues.
Resync PoP is also useful for expired timestamps. When a client has been offline, but the principal issue a transaction a while ago, a Resync PoP may confirm current possession without mutating PR. Clients should also gossip these timestamps to other clients that were online to verify integrity.
Local clients track their own last operation timestamp (written to disk) to detect offline periods and decide when a Resync PoP is needed. Local clients should also add their own timestamps to the receipt of principal messages.
For record keeping, a resysnc PoP may be included by a principal into DT, but it must not be used for after-the-fact authentication.
15.5 Principal Consensus States¶
In addition to principal base states (see "Principal Roots" for base states like Active, Errored, etc.) witnesses independently track principal consensus states. Witnesses may escalate ignored/resync errors to error state after repeated failures (e.g., >3 attempts).
- Active: Client is Normal
- Pending: Message requires more context before principal mutation (incomplete transaction) may or may not be gossiped.
- Resync: Attempt automatic recovery before escalating.
- Offline: Failed to communicate with principal. Client should timestamp "Offline Since".
- Error: Mark the principal as errored for severe violations or if resync repeatedly fails. No new transactions or actions are processed until resolved (e.g., via recovery or resync).
- Ignore: Silently discard messages without state change.
- Client Mismatch: When a witness sees an error but the principal’s client insists the message is valid, mark principal as CLIENT_MISMATCH. This usually indicates a bug, version skew, or intentional divergence. Witnesses may reject interaction until resolved.
15.6 Consensus State Transitions¶
| From | To | Trigger |
| Active | Pending | Incomplete transaction received (e.g., missing bundle component) |
| Pending | Active | Transaction completes or witness timeout expires |
| Active | Error | Fork detected, chain invalid, or repeated resync failure |
| Error | Active | Fork resolved (branch selection or resync PoP) |
| Active | Resync | Trust anchor is stale, delta needed |
| Resync | Active | Patch verified and applied |
| Resync | Error | Repeated resync failure (e.g., >3 attempts) |
| Active | Offline | Communication failure with principal |
| Offline | Resync | Reconnect; stale state needs verification |
| Any | Ignore | Principal dropped from gossip |
| Any | Client Mismatch | Witness and client disagree on message validity |
For individual messages:
- Forward: Gossip to local clients.
- Hold Local: Message is incorrect, is held locally, and not forwarded in the gossip.
- Error: Message is incorrect.
- Proof of Error:
Errors:
INVALID_SIGNATURE: Transaction signature fails verification.INVALID_CONSTRUCTION: Malformed coz or transaction (e.g., missing fields).KEY_REVOKED: Signing key hasrvkset.MULTIHASH_MISMATCH: Computed digests do not match claimed values.CHAIN_BROKEN: Cannot calculate tip from prior trust anchor.INVALID_PRIOR:prechain invalid or incomplete.UNKNOWN_KEY:tmbnot in KR.UNKNOWN_ALG: Unsupported algorithm.MESSAGE_TOO_LARGE: Payload exceeds limits.TIMESTAMP_PAST:now< latest known timestamp.TIMESTAMP_FUTURE:now> witness time + tolerance.TIMESTAMP_OUT_OF_RANGE:nowoutside global tolerance (e.g., > 1 year in future).THRESHOLD_NOT_MET(Level 5+)MALFORMED_PAYLOADJUMP_INVALID: State jump fails (e.g., revoked key in path).INVALID_FORK: Conflicting commits (see below).DUPLICATE:*/createfor existing items. Cyphrcreaterejects duplicates.STATE_MISMATCH: Computed PR/AR differs from claimed.
15.7 Consensus and Witnesses¶
Cyphr assumes a single linear chain per principal. An invalid fork occurs when two or more conflicting commits reference the same pre (prior PR), violating this assumption.
- Ignoring the message
- Escalation (e.g., freeze the principal temporarily until resolved) or
- Holding the message as Proof of Error.
Rejection is auditable: Witnesses log the reason (e.g., INVALID_SIGNATURE) and may broadcast it via gossip for other witnesses to confirm.
17.5 Invalid Forks, Fork Detection, and Duplicitous Behavior¶
An invalid fork occurs when two or more commits reference the same pre, violating the single-chain rule. Quick succession (e.g., within timestamp tolerance, ±60s) is strong evidence of compromise (e.g., two devices signing conflicting mutations simultaneously). Witnesses should keep such transaction as proof of error.
Witnesses detect forks via mismatched tips in gossip, inconsistent /patch responses, and conflicting signed proofs.
Response includes broadcast fork proof and rejection of both branches until resolved (for example principal/merge, revocation, or multi-sig confirmation in Level 5+).
15.8 Fork Resolution¶
An invalid fork is resolved when the principal unambiguously selects one branch. Until resolved, witnesses hold both branches and the principal's consensus state is Error.
- 1. New commit - The principal signs a new commit whose
prereferences the tip of the chosen branch. This implicitly abandons the other branch. Witnesses that see this commit treat the selected branch as canonical and discard the abandoned branch (retaining it only as proof of error). - 2. PoP assertion - The principal signs a
resync/createmessage re-asserting the current tip without mutating PR/AR. Witnesses that held both branches resolve to the asserted tip. This is appropriate when the principal wants to confirm which branch is authoritative without advancing the chain.
In both cases, the abandoned branch's transactions become permanently invalid. Keys that were only added in the abandoned branch are not part of KR. Witnesses transition the principal's consensus state from Error back to Active upon observing a valid resolution.
15.9 Timestamp Verification¶
Compromised keys can produce backdated or future-dated messages. Mitigation includes comparing now to witness time on receipt and rejecting outside ±360 s default tolerance, tracking latest known timestamp per principal from the most recent valid message, rejecting any now earlier than latest known timestamp to prevent history rewriting, and rejecting future-dated messages.
Clients maintain their own persistent last-seen timestamp to detect offline periods and trigger Resync PoP when rejoining. Time servers can be compromised. Cyphr remains durable by relying on relative ordering and possession proofs rather than absolute trusted time.
16. Storage¶
16.1 Client/Principal Storage¶
Cyphr distinguishes between several storage contexts. Clients are categorized as thin, fat, or full, based on storage capacity:
Thin clients rely on services for state resolution and only the private key is essential. Fat clients store exhaustive auth history. Full clients store exhaustive auth history and action data for offline verification and maximum sovereignty.
Storing PG, public keys, and Tip is good practice to store locally, but could be retrieved from a service.
Thin Client (browser, IoT):
| Data | Required | Notes |
| Private keys | ✓ | Never transmitted |
| Transactions | Optional | Full audit trail |
| Actions | Optional | Application-specific |
Fat Client (desktop app, trusted device):
| Data | Required | Notes |
| Private keys | ✓ | Never transmitted |
| Transactions | ✓ | Full audit trail |
| Actions | Optional | Application-specific |
Full Client (desktop app, trusted device):
| Data | Required | Notes |
| Private keys | ✓ | Never transmitted |
| Transactions | ✓ | Full audit trail |
| Actions | ✓ | Application-specific |
16.2 Third-Party Service Storage¶
Services that interact with principals store:
| Data | Purpose |
| PG | Principal identity |
| Current PR (Tip) | State verification |
| Active public keys | Signature verification |
| Transaction history | Full audit trail |
| Actions (DR) | Application data |
Service operations:
- Pruning: Services may discard irrelevant user data (old actions, etc.).
- Key recovery: Services may assist in recovery (see "Disaster Recovery").
- State resolution: Services can provide transaction history for principals to verify.
Trust model: Services are optional. Principals can self-host or use multiple services. Full verification is always possible with transaction history.
While full blob storage is out of scope, it is recommended that Blobs are stored as binary files with the file name being the digest value, following Coz semantics. For multihash support, an index file or file pointers may be used.
16.3 Storage API (Non-Normative)¶
This section is informative only. Implementations may use any storage mechanism appropriate to their deployment context.
16.3.1 Action Export Format¶
The recommended export format is newline-delimited JSON (JSONL). Transactions
are saved to one file transactions.jsonl and data actions to
data_actions.jsonl. Entries with typ prefix <authority>/cyphr/* are
authentication transactions; all others are data actions.
{"typ":"cyphr.me/cyphr/key/create","pay":{...},"sig":"...","key":{...}}
{"typ":"cyphr.me/cyphr/key/create","pay":{...},"sig":"...","key":{...}}
{"typ":"cyphr.me/comment/create","pay":{...},"sig":"..."}
Past entries are not modified (unless invalid fork or data action removal) and each line is a complete, signed Coz message.
16.3.2 Blob Store¶
16.3.3 Storage Capabilities¶
Storage backends provide:
- Append: Store signed entries for a principal
- Retrieve: Fetch entries (all or filtered by time range)
- Existence check: Determine if a principal exists
Semantic operations (verification, state computation, key validity) are handled by the Cyphr protocol layer, not storage.
16.4 Append Only Verifiable Logs¶
Cyphr's commit chain forms a verifiable, append-only log of state mutations. Each commit references the prior via pre and is anchored in the Merkle root (MR), enabling efficient verification of history without full chain replay. This supports tamper-evident auditing and is useful for compliance requirements, such as regulatory record-keeping or forensic analysis. This may be implemented as a truly append only data structure or as a mutatable data structure, where events like forks, requiring chain selection, may be deleted (mutatable) or marked as discarded (immutable implementation).
17. Authentication¶
Cyphr replaces password-based authentication with cryptographic Proof of Possession (PoP).
Cyphr recommends AAA (Authenticated Atomic Action) over bearer tokens when possible, but bearer tokens remain useful for access control and for upgrading legacy password systems to Cyphr.
17.1 Proof of Possession (PoP)¶
Every valid signature by an authorized key constitutes a Proof of Possession:
- Genesis PoP: First signature by a key proves possession.
- Transaction PoP: Signing a key mutation proves authorization.
- Action PoP: Signing an action proves the principal performed it.
- Login PoP: Signing a challenge proves identity to a service.
17.2 Login Flow¶
To authenticate to a service:
Option A: Challenge-Response
- Service generates a 256-bit cryptographic challenge (nonce)
- Principal signs the challenge with an authorized key
- Service verifies:
- Signature is valid
tmbbelongs to an active key in principal's KR- Principal lifecycle state is Active (reject Frozen, Deleted, Errored, etc.)
- Challenge matches the one issued (prevents replay)
- Service issues bearer token
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/cyphr/auth/login",
"challenge": "T0T1HFBxNFbhjLC10sJTuzrdSJz060qIme1DKytDML8" // 256 bit nonce from service.
},
"sig": "<b64ut>"
}
Option B: Timestamp-Based
- Principal signs a login request with current
nowtimestamp - Service verifies:
- Signature is valid
tmbbelongs to an active key in principal's KR- Principal lifecycle state is Active (reject Frozen, Deleted, Errored, etc.)
nowis within acceptable window (e.g., ±60 seconds of server time)
- Service issues bearer token
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg,
"typ": "<authority>/<service>/auth/login"
},
"sig": "<b64ut>"
}
17.3 Replay Prevention¶
Two mechanisms prevent signature replay:
| Mechanism | How it works | Trade-off |
| Challenge nonce | Service issues unique 256-bit nonce per login attempt | Requires round-trip |
| Timestamp window | now must be within ±N seconds of server time |
Clock sync required |
Challenge-response is useful for security contexts where time isn't trusted, while timestamp-based authentication works for low-friction flows with reasonably accurate time sources.
17.4 Bearer Tokens¶
After successful PoP, the service issues a bearer token:
- Token is a signed Coz message from the service
typis service-defined (e.g.,<service>/auth/token)- Contains: principal PG, authorized permissions, expiry
- Used for subsequent requests (avoids re-signing each request)
{
"pay": {
"alg": "ES256",
"now": 1623132000,
"tmb": "<service key tmb>",
"typ": "<service>/auth/token",
"pr": "<principal genesis>",
"exp": 1623132000,
"perms": ["read", "write"]
},
"sig": "<b64ut>"
}
Note: The service signs the token with its own key. The principal verifies the token came from the expected service.
17.5 Single Sign-On (SSO)¶
Cyphr provides single sign-on semantics but differs from traditional SSO systems that depend on passwords and email. Traditional SSO creates centralization around identity providers. In Cyphr, the principal's cryptographic keys are the sole authentication factor, verifiable by any party without a central authority.
18. State Jumping¶
For high-volume principals (e.g., those with tens of thousands of transactions) or thin clients with limited bandwidth/storage, full chain replay from a distant trust anchor to the current tip can become prohibitively expensive in time, data transfer, or computation.
State jumping provides an optimization allowing a client to advance directly from a known trust anchor (e.g., an old AR or PR at "block" N) to a much later state (e.g., current tip at "block" M >> N), without fetching or verifying every intermediate transaction.
Multiple jumps may be required to traverse from the trust anchor to the target PR, for example in circumstances where keys have been revoked. In anticipation of a state jump, clients may pre-sign jumps.
Clients may also reject state jumps as state jumps are an optimization that delegates some trust to third party services. By default, the Cyphr reference client rejects state jumps, preserving a conservative security baseline.
When tx_patch becomes very long (hundreds or thousands of transactions),
verification cost can become prohibitive. As an optimization for long chains,
clients may issue checkpoints to preclude the need for transitive transaction.
18.1 Core Mechanism¶
A principal with access to one or more still-active keys from the trust anchor can sign a special state-jump transaction that:
- References the old trust anchor via
pre, - Declares the new target PR in a new field (e.g.,
jump_to_ps), - Is signed by one or more keys authorized at the anchor (and still valid at the tip).
Verification rules for the jump transaction:
- The signing key(s) must be present in KR at the anchor.
- The claimed
jump_to_psmust match the service-reported or independently-obtained current tip. - No intermediate revocations or key mutations are permitted that would
invalidate the signing key(s); this is enforced by requiring the jump
transaction to be accepted only if KR at tip still includes the signing
tmb(s). - Services may require additional proofs (e.g., Merkle inclusion of unchanged KR components) or restrict jumps to trusted checkpoints.
Typical Processing Flow
- Client queries service for current tip (via MSS
/tipendpoint, §10.1). Client notes that chain is larger than optimization threshold (> 1,000) and triggers state jump process. - Client verifies tip KR includes the signing key (fetch minimal tx_patch if needed).
- Client signs and pushes the jump transaction to services.
- Services validate: signature,
prematches known history,jump_to_ps= current tip, no invalidating mutations. - If valid, client updates local trust anchor to
jump_to_ps; future resolutions start from there.
Swim Lane Chart
Local Principal Client Remote Principal
| |
|---1. Query current tip (MSS /tip)--------->|
|<-- Return current PR (tip @100,000)--------|
| |
| (Detect long chain → trigger jump) |
| |
| Verify signing key still in KR @ tip |
| (minimal tx_patch fetch if needed) |
| |
| Sign jump tx: pre=AR@2, jump_to_ps=tip |
| |
|---3. Push signed jump transaction--------->|
| |
| Remote validates: |
| • Signature |
| • pre matches history |
| • jump_to_ps == current |
| |
|<-- Accept (or reject if invalid)-----------|
| |
| Update local trust anchor to new PR |
| Future resolutions start from here |
| |
18.2 Example State Jump Transaction¶
{
pay: {
alg: "ES256",
now: 1623132000,
tmb: "<active key tmb from anchor>",
typ: "cyphr.me/cyphr/principal/state_jump/create",
pre: "<old trust anchor PR>",
jump_to_ps: "<tip PR>",
},
sig: "<b64ut>",
}
18.3 Security Considerations¶
- Jumping must not bypass revocation semantics: if a key was revoked between anchor and tip, the jump fails.
- Clients should verify the jump against multiple services or via full chain replay periodically to detect malicious jumps.
- Services may enforce maximum jump distance or require multi-signature for large jumps to mitigate abuse.
State jumping preserves Cyphr's core properties (verifiable history, no trusted central oracle) while enabling scalable operation for long-lived principals.
18.4 State Jump Examples¶
Example 1: Single large jump (no revokes)
Trust anchor is at block 2, but tip is at block 100,000. As long as keys have not been revoked from block 2, and the principal still has those keys, they can sign a transaction going from 2 → 100,000 which avoids having to download each intermediary checkpoint. (Sometimes called "state warping".)
Example 2: Multi-jump required (revokes in the middle)
Trust anchor is at block 2, but tip is at block 100,000. All keys active at block 2 were revoked sometime between block 2 and block 50,000.
To reach the tip:
- First jump: from block 2 → block 45,000 (using a key still active at 45,000 but revoked later)
- Second jump: from block 45,000 → block 100,000 (using a key that became active after the revoke window and is still valid at the tip)
The client must discover or know an intermediate safe anchor where a valid signing key exists. Services can help by returning recent checkpoints or suggesting viable jump points when the direct jump fails due to revocation.
18.5 Other High Volume Strategies¶
In addition to state jumping, other future related designs include zero-knowledge proofs and trusted third parties. (Ethereum uses Infura; similar infrastructure may be useful for some client situations, although Cyphr is designed for decentralization.)
See also §7.3.3 Checkpoints.
19. Error Conditions¶
This section defines error conditions that implementations must detect. Error responses (HTTP codes, messages, retry behavior) are implementation-defined.
19.1 Transaction Errors¶
| Error | Condition | Level |
INVALID_SIGNATURE |
Signature does not verify against claimed key | All |
UNKNOWN_KEY |
Referenced key (tmb or id) not in current KR |
All |
UNKNOWN_ALG |
Client doesn't know or support the algorithm | All |
TIMESTAMP_PAST |
now < latest known PR timestamp |
All |
TIMESTAMP_FUTURE |
now > server time + tolerance |
All |
MALFORMED_PAYLOAD |
Missing required fields for transaction type | All |
KEY_REVOKED |
Signing key has rvk ≤ now |
All |
INVALID_PRIOR |
pre does not match current AR |
2+ |
DUPLICATE_KEY |
key/create for key already in KR |
3+ |
THRESHOLD_NOT_MET |
Signing keys do not meet required weight | 5+ |
19.2 Recovery Errors¶
| Error | Condition | Level |
UNRECOVERABLE_PRINCIPAL |
No keys capable of transaction and no designated recovery agents | All |
RECOVERY_NOT_DESIGNATED |
Agent not registered via cyphr/recovery/create |
3+ |
19.3 State Errors¶
| Error | Condition | Level |
STATE_MISMATCH |
Computed PR does not match claimed PR | All |
HASH_ALG_MISMATCH |
Multihash variant computed with wrong algorithm | All |
ALG_INCOMPATIBLE |
Referred alg is not supported |
All |
CHAIN_BROKEN |
pre references do not form valid chain to known state |
2+ |
19.4 Action Errors (Level 4+)¶
| Error | Condition | Level |
UNAUTHORIZED_ACTION |
Action typ not permitted |
5+ |
19.5 Error Handling Guidance¶
Implementations must:
- Reject transactions with any error condition
- Not apply partial state changes (atomic)
Implementations should:
- Return meaningful error identifiers to clients
- Log errors for debugging (optional but recommended)
Implementations may:
- Define additional application-specific error conditions
- Implement rate limiting for repeated errors
20. Test Vectors¶
Language-agnostic test vectors are provided in /tests.
20.1 Golden Key "User Key 0" (ES256)¶
{
"tag": "User Key 0",
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"alg": "ES256",
"now": 1623132000,
"pub": "2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g",
"prv": "bNstg4_H3m3SlROufwRSEgibLrBuRq9114OvdapcpVA"
}
20.1.1 Golden Key: "User Key 1" (ES256)¶
{
"tag": "User Key 1",
"tmb": "CP7cFdWJnEyxobbaa6O5z-Bvd9WLOkfX5QkyGFCqP_M",
"alg": "ES256",
"now": 1623132000,
"pub": "iYGklzRf1A1CqEfxXDgrgcKsZca6GZllIJ_WIE4Pve5cJwf0IyZIY79B_AHSTWxNB9sWhYUPToWF-xuIfFgaAQ",
"prv": "dRlV0LjnJOVfK_hNl_6rjVKutZWTHNL-Vs4_dVZ0bls"
}
20.1.2 Golden Key: "Cyphr Server Key A" (ES256)¶
{
"tag": "Cyphr Server Key A",
"tmb": "T0jUB_Bk4pzgvnNWMGfmV0pK4Gu63g_M08pu8HIUGkA",
"alg": "ES256",
"now": 1623132000,
"pub": "yfZ-PY4QdhWKJ0o41yc8-X9qnahpfKoTN6sr0zd68lMFNbAzOwj9LSVdRngno4Bs_CNyDJCQJ6uqq9Q65cjn-A",
"prv": "WG-hEn8De4fJJ3FxWAsOAADDp89XigiRajUCI9MFWSo"
}
20.2 Golden Message¶
The canonical Coz test message with verified signature:
{
"pay": {
"msg": "Coz is a cryptographic JSON messaging specification.",
"alg": "ES256",
"now": 1623132000,
"tmb": "U5XUZots-WmQYcQWmsO751Xk0yeVi9XUKWQ2mGz6Aqg",
"typ": "cyphr.me/msg/create"
},
"sig": "OJ4_timgp-wxpLF3hllrbe55wdjhzGOLgRYsGO1BmIMYbo4VKAdgZHnYyIU907ZTJkVr8B81A2K8U4nQA6ONEg"
}
Computed digests, where
cad= SHA-256(canonical(pay))czd= SHA-256([cad, sig])cad:XzrXMGnY0QFwAKkr43Hh-Ku3yUS8NVE0BdzSlMLSuTUczd:xrYMu87EXes58PnEACcDW1t0jF2ez4FCN-njTF0MHNo
20.3 Golden Nonce¶
"T0T1HFBxNFbhjLC10sJTuzrdSJz060qIme1DKytDML8"
Appendix¶
Cyphr Applications¶
- Cryptographically verifiable, internet-wide, web archive service.
- Unstoppable, internet-wide user comments
- "Bittorrent for social media".
- Enforce/audit life insurance.
- Enforce/audit contracts.
Appendix 1: Coz Field Reference¶
| Field | Description |
alg |
Algorithm identifier |
now |
UTC Unix timestamp |
tmb |
Key thumbprint |
pub |
Public component |
prv |
Private component |
sig |
Signature |
rvk |
Revocation timestamp |
typ |
Action type URI |
msg |
Human readable message |
dig |
External content digest |
cad |
Canonical hash of payload |
czd |
Coz digest (hash of [cad, sig]) |
Other key Coz requirements:
- All signatures must be verified using the key's
alg - ECDSA signatures must be low-S normalized (non-malleable)
tmb's use the hash algorithm associated withalg- State digests use the hash algorithm of the signing key
Algorithm governance is delegated to Coz. Weak algorithm sunsetting is handled by Coz and is inherited by Cyphr. If an algorithm is weakened, Coz will mark it deprecated; principals should discontinue via key removal. Implementations should warn and appropriately and remove support for deprecated algorithms.
Appendix 2: Prior Art¶
- Coz
- Bitcoin
- Ethereum
- PGP
- SSH
- SSL/TLS
- SSHSIG and signify (OpenBSD)
- Secure Quick Reliable Login (SQRL) (https://www.grc.com/sqrl/sqrl.htm)
Appendix 3: See also¶
- Ethereum multihash: https://ethresear.ch/t/multihashing-in-ethereum-2-0/4745
- Merkle-tree-based verifiable logs (A merkle tree where new nodes are added to only one side.)
- Keybase
- Protocol Labs (Multiformats)
Appendix 4: Exposition¶
Action¶
As stated, an action is an authorization to perform an operation; "action" is to
perform a state change. An action's type is denoted by the typ field of the
coz. For example, a cyphr.me/comment/create is colloquially named a "comment
action".
Sharing Keys¶
Nothing in Cyphr stops various principals from sharing keys, as long as genesis does not result in the same PG. Any set of keys that has not been revoked may be used to create a new PG, this includes reusing keys from the source principal. The fork may declare new keys or reuse existing keys.
MALT as a modern transparency log¶
Implementations may choose to expose the commit tree (CT) via tiled static storage for efficiency.
Digest Labeling¶
When referred to alone outside a coz, good practice for digest identifiers is
prepending with the Coz algorithm identifier, e.g. SHA256:<b64ut_value>.
Without explicit algorithm labeling, a system is as strong as the weakest
supported hashing algorithm. Systems may leverage previously identified digests
from being misinterpreted or reused; explicit algorithm prefixes may not always
be strictly required in practice.
HTTP and : Unified Intent + Resource + Verb Descriptor¶
typCyphr's typ is an alternative to HTTP semantics. typ is not just a
naming convention, it's a deliberate design choice that rethinks invoking
actions, addressing resources, and expressing intent in a cryptographically
native, decentralized way. Its full power is realized in Authenticated Atomic
Action (AAA).
Unlike most Internet systems, Cyphr is designed to work in parallel to HTTP,
not necessarily on top of it. Where HTTP has Method
(GET/POST/PUT/DELETE/PATCH...), Path (/users/123/profile/photo), Headers
(Accept, Content-Type, Authorization...) Cyphr collapses almost all of that
expressive into one field: typ.
For example, cyphr.me/cyphr/key/create
This is close to a RESTful path plus method, but:
- It's self-describing and self-authenticating.
- No separate "method" field needed. The verb is included.
- No separate path vs. body distinction for intent. Everything meaningful is inside the pay payload.
- Authority prefix acts like a namespace / origin (replacing domain + scheme in a decentralized setting).
- Cryptographic digests, especially in combination with public key cryptography, naturally provides addressing.
Advantages Over HTTP:
- No trusted third-party sessions (like cookies/tokens). Every request carries its own PoP.
- Replay resistance built-in via
now(timestamp window). - Intermediaries can cryptographically audit actions without trusting the service.
- Decentralized addressing. The Principal root my be outside DNS/CA entirely.
- Append-only mutable state via transactions. Instead of PUT/PATCH fighting over eventual consistency, Cyphr provides a verifiable chain of mutations.
Actions are first-class and atomic. AAA means the "API call" is individually verifiable forever, not just during a session.
Where Cyphr Diverges from Being a Full HTTP Replacement¶
Cyphr's typ + Coz model isn't a wire replacement for HTTP. Instead it offers an alternative interaction model:
| Aspect | HTTP | Cyphr typ + Coz Model |
| Addressing | URL + method | typ string (authority + noun/verb) |
| Authentication | Headers / tokens / cookies | Embedded PoP (signature over the whole intent) |
| State management | Server-side sessions | Client + service mutual sync of auth chain |
| Mutability model | CRUD on resources | Append-only transactions + signed actions |
| Verifiability | Mostly server-trusted | Anyone can verify any action historically |
| Transport | Usually TLS + HTTP | Can be sent any way (TLS, HTTP, IPFS, email, gossip...) |
| Response model | Status + body | Tip, another signed Coz |
Ownership¶
In Cyphr, transferable is cryptographically implementable via key change, but recording such changes in a ledger potentially results in human unreadable transactions. Also, authorities may prohibit key updates to keys outside of the principal, making transfer impossible.
Transfer ambiguity: For example, a comment could be updated to be signed by a new key, but that would be ambiguous: was is a transfer or just as a result of a key update? For that reason, updates with new keys outside of principal should fail and transfer explicitly used for transfer.
Self-Sovereign Philosophy¶
Self-Ownership Philosophy in a Cryptographic System¶
There are three main categories of ownership:
- Possess the private keys (Private Key Possession)
- Possess the data (Data Possession)
- Right to mutate state:
create,delete,update(Right)
These three can be summarized as three points: Keys, Data, Right. The protocol seeks to maximize user ownership across all three dimensions:
Keys → Self-custody, multi-device, revocation, recovery paths. Data → Portable exports, optional self-hosting, minimal service lock-in (via MSS). Rights → AAA replaces bearer tokens; verifiable authorship/actions without centralized session state.
Private Key Possession is important in cryptography. Cryptographic systems are implemented using key possession. (Not your keys, not your crypto.)
Data Possession - For non-encrypted data: Possession generally equates to ownership, as anyone with access can read/copy/use it. For encrypted data: Ownership is tied to possession of decryption keys (which may overlap with Private Key Possession). Encrypted data hosted by third parties (e.g., for availability/security) does not imply loss of ownership if keys remain user-controlled.
Right is relevant for authorship (comments, user history) and where ownership is tracked on a ledger (e.g., bitcoin). Right is proven in a cryptographic system using private keys and PoP.
Cyphr seeks to help users own their keys, data, and rights.
Natural Ownership¶
The originating Principal is the natural owner of its actions. For example,
the principal that creates a comment comment/create is the natural owner of
that comment, and has exclusive rights for future mutations: comment/update,
comment/delete, and comment/upsert. Systems implementing AAA must give
special attention to items with natural ownership properties.
Ownership Right Semantics¶
TODO Perhaps: ownership is proven by the latest valid transfer chain. Ownership = latest valid transfer chain
typs:
cyphr.me/ownership/claim/create
cyphr.me/ownership/transfer
ownership/transfer-ack
TODO multiownership
Perhaps an item itself can be represented as a Principal. Abstract things themselves have a chain.
"smart contracts" are supported by level 5+ Verifiable without full blockchain, Off-chain data friendly No native "minting fee" or gas No miner, validator race, or fee market Revocable/revocable keys Soulbound-like: set transferable=false
QUICK AI Guidance:¶
- DO NOT USE EM DASH OR DASH. Use period, comma, semi-colon, and other sentence construction appropriately.
- DO NOT USE uppercase MAY, SHOULD, or MUST. This isn't an IETF RFC.
- JSON example should be in valid json, not json5 EXCEPT for examples with
JSON comments. ONLY for examples with JSON comments should be markdown JSON5,
all other examples should be markdown JSON. (Use
json5on the markdown so that the comments are valid.)
TODO¶
- Tombstones
- Ownership
- Define Opaque reveal authorization semantics better
- ZAMI finish Login
- In JSON, State is upper case, plural is lower case.
- Consider: Renaming "Implicit promotion" to:
- digest promotion - Preferred
- singleton hash elision
- hash elision
- hash bypass
- Degenerate case (Less preferred by related: Degenerate Identity)
- Singleton Bypass
- Discuss general MR algo for JSON, conform embedding with objects/array to that MR structure, especially declarative.