ARK Record Protocol
The ARK Record Protocol is the open format for the data inside an ARK. Every piece of a person is a record with a facet, a kind, an authorship, a trust tier, a privacy setting, a time window and its provenance. This is the public edition of version 0: what a record looks like, who may read it or propose one, how its content is hashed, and what ARKZ writes down about every call.
- JSON Schema of the record envelope: arp-envelope.schema.json, the same schema ARKZ serves at /protocol/v0/record.schema.json.
- Test vectors for the content hash and the snapshot root: arp-envelope.json. They are generated by the reference implementation, and a test recomputes them on every verification run.
- Requests and answers, action by action: Reading and Writing in the developer documentation.
The rules
- The owner decides. Anything sent into an ARK is a proposal. Only the owner's acceptance makes it part of their ARK. A proposal that contradicts what the owner has said comes back to them as a question; it never merges on its own.
- Every record says who said it. That is its authorship. Only the person's exact words can ever shape how the ARK sounds.
- Trust is a tier, and ARKZ assigns it. A client never sets it.
- A credential on every call, and a ledger row for every call. Reads and writes alike.
- The Vault never leaves. A record marked
vaultis never served by this protocol, to anyone. - Nothing is spent before the owner says yes. Receiving a proposal runs no model and costs the owner nothing.
- Personal data stays off chain. Only hashes and Merkle roots may ever be anchored.
Facets and kinds
A facet is a region of a person; a kind is a record type inside it. The last column says which kinds the protocol takes as proposals in version 0.
| Facet | Kinds | Takes proposals |
|---|---|---|
identity | name, bio, expertise, mastery, role | None |
belief_value | belief, value, opinion, identity | belief, value, opinion, identity |
knowledge | knowledge, skill, document, passage, raw | knowledge, passage, raw |
framework | process, pattern, definition, correction | process, pattern, definition |
position_decision | decision, position | decision, position |
experience_story | experience, story, journal | experience, story, journal |
preference_boundary | preference, boundary | preference, boundary |
voice | utterance, marker, profile_version | utterance |
likeness | audio_profile, likeness_asset, face_reference, voiceprint, motion_reference, laugh_sample, consent_scope, likeness_model | None |
behavior_execution | habit, task, project, trade | habit, task, project, trade |
relationship | person, interaction | person, interaction |
provenance_consent | certificate, grant, attestation | None |
disposition | baseline, state, expression, trigger, coping | None |
humor | profile, sarcasm_index, laugh, bit, never | None |
mannerism | verbal, physical, ritual, rhythm, appearance | None |
body_of_work | work, collection, credit, style_profile | None |
The envelope
One JSON shape for every facet. Every record carries arp, ark, facet, kind, authorship and content. Every proposal also carries provenance.content_sha256. The fields ARKZ assigns (id, ref, trust_tier and status) are ignored on a proposal.
A record as ARKZ serves it, taken from the test vectors:
{
"arp": "0",
"id": "rec_4f7c2a90-1b3e-4c55-9d1a-2e6b8f0c7d31",
"ref": "ark_insights:9b2d6e14-7a0c-4f3b-8e21-5c4d3a2b1f00",
"ark": "ark:arkz:0c1d2e3f-4a5b-4c6d-8e7f-901a2b3c4d5e",
"facet": "framework",
"kind": "definition",
"authorship": "self_paraphrase",
"trust_tier": "T1",
"privacy": "training",
"subject": {
"ref": "self"
},
"content": {
"text": "Growth is a change in kind; scale is more of the same kind."
},
"domains": [
"operations"
],
"topics": [
"growth vs scale"
],
"time": {
"captured_at": "2026-10-06T14:02:00Z",
"valid_from": null,
"valid_until": null
},
"provenance": {
"source_kind": "knoa_session",
"source_ref": null,
"grant_id": null,
"client_id": null,
"content_sha256": "76ecc893f74b83848db37c1ce4dfd4b1afd010b2f50210d9c564ce5329454d51"
},
"lineage": {
"supersedes": null,
"superseded_by": null
},
"status": "accepted"
}| Field | What it holds |
|---|---|
arp | The protocol version, "0". |
id, ref | The record's names, assigned by ARKZ. ref is <table>:<uuid>, or <table>:<uuid>:<kind> when one stored row gives several records. |
ark | ark:arkz: followed by the ARK's id. |
facet, kind | Where the record sits in the person, from the table above. |
authorship | Who said it and how. |
trust_tier | T0 to T4, assigned by ARKZ. |
privacy | vault, training or licensable. |
subject | Who the record is about: self, person:<id> for someone in the owner's people list, or entity. A proposal may name the entity; a served record carries the kind alone. |
content | text, structured, or both. A proposal carries at least one. |
domains, topics | Lists of plain labels. |
time | captured_at, valid_from and valid_until. |
provenance | source_kind, source_ref, grant_id, client_id, content_sha256 and an optional detached signature. |
lineage | What the record replaces and what replaced it. |
status | proposed, accepted, rejected, expired, superseded, revoked or held. |
Authorship
| Value | Meaning |
|---|---|
self_verbatim | The person's exact words. The only authorship that can ever shape voice. |
self_paraphrase | The person's meaning in other words, such as an insight drawn from a conversation. |
self_structured | The person's own structured entry: a decision, a habit, an attested document. |
about_subject | Someone else talking about the person. The owner may accept it as context; it never shapes voice or outranks the person's own words. |
derived | Produced by a model from other records. It never stands alone as the person's view. |
Trust tiers
| Tier | Meaning |
|---|---|
T0 | The owner's exact words: their corrections and verified voice examples. |
T1 | From the owner: their sessions, journals, decisions, attested documents and connected accounts, and anything sent with their own key. |
T2 | A verified source the owner granted. |
T3 | An unverified external source. |
T4 | Derived by a model. |
A proposal that claims a tier has the claim ignored.
Privacy
Three values, most restrictive first: vault, training, licensable.
vaultis never served. Every read leaves it out.- An owner key reads
trainingandlicensablerecords. An OAuth connection readslicensablerecords. A licence readslicensablerecords within the facets its terms open. - On a proposal, the tighter of the requested privacy and the source grant's default wins.
Credentials
| Credential | Prefix | What it may do |
|---|---|---|
| Owner key | ark_live_ | Read and propose within the scopes the owner gave it. |
| OAuth connection | arka_ | Read licensable records, and propose, within the scopes the owner approved. |
| Licence credential | lic_ | Read within its licence's terms. |
| Source key | ark_src_ | Propose within one source grant's facets, kinds and authorship. It can never read. |
The content hash
provenance.content_sha256 is the lowercase hex SHA-256 of the record's canonical content:
- Text only (no
structured, or an empty one): the UTF-8 bytes ofcontent.textexactly as given. No trimming and no normalisation. - With a non-empty
structured: the UTF-8 bytes of the text, one newline, then the JSON Canonicalization Scheme form (RFC 8785) ofstructured: keys sorted by UTF-16 code unit, no insignificant whitespace, numbers in their shortest round-trip form.
ARKZ recomputes the hash of every proposal and refuses a mismatch. The test vectors give the canonical bytes and the hash for each case.
Snapshots and their Merkle root
An owner can take a snapshot of a set of records, such as the slice a derivative may draw on, and its Merkle root names exactly that version of exactly that slice.
- Leaf: SHA-256 of the byte
0x00followed by the UTF-8 bytes ofrecord_ref|hash|privacy|trust_tier. - hash: the record's
content_sha256; else the SHA-256 (lowercase hex) of the UTF-8 bytes of its structured content's stored text form, which is PostgreSQL's jsonb text (object keys ordered by length and then by bytes, members separated by,, a key from its value by:) and not the RFC 8785 form the content hash uses; else a single-. - Order: records sorted by
record_ref, comparing UTF-8 bytes. - Node: SHA-256 of the byte
0x01followed by the left and right 32-byte digests. An odd last node is paired with itself. - Root: the single leaf when there is one; the SHA-256 of nothing for an empty set; 64 lowercase hex characters with no
0x.
The test vectors give the leaves and the root for an empty set, one record, and an odd number of records given out of order.
What the ledger records
ARKZ writes one ledger row for every protocol call while the protocol is on, before the answer leaves. A protocol answer whose row cannot be written is withheld, and the caller is told to try again. Each row holds:
- the credential's kind and its id;
- the ARK it was asked of;
- the action, and whether it was a read or a write;
- the call's status (one row per message over MCP);
- how many records the answer carried, and its size in bytes;
- the cost: zero for a record read; an ask of the ARK carries its billed cost;
- a request id shared by the rows of one request.
The ledger never holds the content of a record or a question.
What is never written to a chain
No record, no envelope, no text and no personal detail. A record carries a content hash; a snapshot carries a Merkle root over hashes; anchoring a root is optional, and it is the only thing that touches a chain.
Errors
Every protocol action answers an error with a code from a closed set and a plain message. The codes and their HTTP statuses are listed in the developer documentation's errors reference.
Status
The protocol is in early access: it answers only for an ARK whose owner has switched it on. The format is open, and anyone may build to it. ARKZ keeps the ark:arkz: namespace, the trust-tier assignment, source verification and the hosted write path.