🧠KNOA🏠Life🌐Market

Developers

Writing into an ARK Early access

Your platform or your AI proposes records. The owner reviews them at login and decides what becomes part of the ARK.

The record envelopeEarly access

Every field of record.schema.json. Fields marked read only are assigned by ARKZ and ignored on a proposal.

NameTypeRequiredLimitsDescription
arp"0"yesProtocol version.
idstringnopattern ^rec_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$Record id, assigned by ARKZ. Absent on a proposal. (read only)
refstringnopattern ^[a-z0-9_]+:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(:[a-z_]+)?$Record reference, assigned by ARKZ: <table>:<uuid>, or <table>:<uuid>:<kind> when one row gives several kinds. Either id or ref names a record. (read only)
arkstringyespattern ^ark:arkz:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$The ARK this record belongs to.
facet"identity" | "belief_value" | "knowledge" | "framework" | "position_decision" | "experience_story" | "preference_boundary" | "voice" | "likeness" | "behavior_execution" | "relationship" | "provenance_consent" | "disposition" | "humor" | "mannerism" | "body_of_work"yesA region of the person.
kind"name" | "bio" | "expertise" | "mastery" | "role" | "belief" | "value" | "opinion" | "identity" | "knowledge" | "skill" | "document" | "passage" | "raw" | "process" | "pattern" | "definition" | "correction" | "decision" | "position" | "experience" | "story" | "journal" | "preference" | "boundary" | "utterance" | "marker" | "profile_version" | "audio_profile" | "likeness_asset" | "face_reference" | "voiceprint" | "motion_reference" | "laugh_sample" | "consent_scope" | "likeness_model" | "habit" | "task" | "project" | "trade" | "person" | "interaction" | "certificate" | "grant" | "attestation" | "baseline" | "state" | "expression" | "trigger" | "coping" | "profile" | "sarcasm_index" | "laugh" | "bit" | "never" | "verbal" | "physical" | "ritual" | "rhythm" | "appearance" | "work" | "collection" | "credit" | "style_profile"yesA record type inside its facet. The pairing of facet and kind is checked below.
authorship"self_verbatim" | "self_paraphrase" | "self_structured" | "about_subject" | "derived"yesWho said it and how. Only self_verbatim can ever feed voice; derived never stands as canon alone.
trust_tier"T0" | "T1" | "T2" | "T3" | "T4"noAssigned by ARKZ from the credential, the grant and the authorship. Never set by a client; a proposal that sends it has it ignored. (read only)
privacy"vault" | "training" | "licensable"noMost restrictive first. A vault record is never served by the protocol. On a proposal the tighter of the grant default and this value wins.
subjectobjectnoWho the record is about.
subject.refstringyespattern ^(self|entity|person:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$
subject.namestringno
contentobjectyesText for insight kinds; structured for decisions, habits, tasks, projects, people and trades. A proposal carries at least one; a derived or metadata-only record read back may carry neither.
content.textstringno
content.structuredobjectno
domainsstring[]no
topicsstring[]no
timeobjectno
time.captured_atstring | nullnoformat date-time
time.valid_fromstring | nullnoformat date-time
time.valid_untilstring | nullnoformat date-time
provenanceobjectyesWhere the record came from.
provenance.source_kindstring | nullnoFree text, lower snake case.
provenance.source_refstring | nullnoWhere it came from, for example a URL.
provenance.grant_idstring | nullnoThe source grant that wrote it; null when an owner key wrote it.
provenance.client_idstring | nullnoThe OAuth client, owner key id or source key id.
provenance.content_sha256string | nullyespattern ^[0-9a-f]{64}$Lower-case hex SHA-256 of the canonical content. Required on every proposal; ARKZ recomputes it and refuses a mismatch.
provenance.signaturestring | nullnoOptional detached signature by the source's registered key.
lineageobjectno
lineage.supersedesstring | nullno
lineage.superseded_bystring | nullno
lineage.derived_fromstring[]no
status"proposed" | "accepted" | "rejected" | "expired" | "superseded" | "revoked" | "held"noAssigned by ARKZ.

Facets and kindsEarly access

A facet is a region of the person; a kind is a record type inside it. Only the proposable kinds can be sent; the rest are made by ARKZ.

FacetKindsProposable kinds
identityname, bio, expertise, mastery, rolenone
belief_valuebelief, value, opinion, identitybelief, value, opinion, identity
knowledgeknowledge, skill, document, passage, rawknowledge, passage, raw
frameworkprocess, pattern, definition, correctionprocess, pattern, definition
position_decisiondecision, positiondecision, position
experience_storyexperience, story, journalexperience, story, journal
preference_boundarypreference, boundarypreference, boundary
voiceutterance, marker, profile_versionutterance
likenessaudio_profile, likeness_asset, face_reference, voiceprint, motion_reference, laugh_sample, consent_scope, likeness_modelnone
behavior_executionhabit, task, project, tradehabit, task, project, trade
relationshipperson, interactionperson, interaction
provenance_consentcertificate, grant, attestationnone
dispositionbaseline, state, expression, trigger, copingnone
humorprofile, sarcasm_index, laugh, bit, nevernone
mannerismverbal, physical, ritual, rhythm, appearancenone
body_of_workwork, collection, credit, style_profilenone

AuthorshipEarly access

Who said it and how. Label it honestly; the label decides what the record may feed.

NameMeaning
self_verbatimThe owner's own words, exactly as said or written. The only authorship that can ever feed voice.
self_paraphraseThe owner's meaning in other words.
self_structuredStructured facts the owner gave, such as a decision or a task.
about_subjectSomeone else talking about the owner: a testimonial, a review, an interview question. Context only, never canon and never voice.
derivedInferred by software. Never stands as canon alone, and cannot be proposed in v0: an envelope sent with it answers grant_scope.

Trust tiersEarly access

A trust tier is assigned by ARKZ from the credential, the grant and the authorship. A caller never sets it; a proposal that sends trust_tier has it ignored.

T0 T1 T2 T3 T4

PrivacyEarly access

Most restrictive first. A vault record is never served by the protocol. On a proposal, the tighter of the grant default and the value sent wins.

vault training licensable

The content hashEarly access

Every proposal carries provenance.content_sha256: lower-case hex SHA-256 of the canonical content, UTF-8 encoded. ARKZ recomputes it and refuses a mismatch. The canonical content is the text as given; when content.structured is a non-empty object it is the text, a newline, then the structured object in RFC 8785 canonical JSON (keys sorted, no spaces).

These snippets canonicalize ASCII keys, strings and integers correctly. Floats and non-ASCII keys need a real RFC 8785 library.

Hash fixture: text only (the hash is over the text as given)

content
{
  "text": "Ship the smallest thing that proves the idea."
}
canonical
Ship the smallest thing that proves the idea.
sha256
14fdf7de13a45ffd735d60ef9a3aa2b4da5612a106e9b6f0faf9722f06aec75c
JavaScript (Node 18 or later)
const { createHash } = require('node:crypto')

// RFC 8785 for ASCII keys, strings and integers: keys sorted, no spaces.
const jcs = (v) => Array.isArray(v) ? `[${v.map(jcs).join(',')}]`
  : v !== null && typeof v === 'object' ? `{${Object.keys(v).sort().map((k) => `${JSON.stringify(k)}:${jcs(v[k])}`).join(',')}}`
  : JSON.stringify(v)
const canonical = (c) => (c.text ?? '') +
  (c.structured && Object.keys(c.structured).length ? '\n' + jcs(c.structured) : '')

const content = {"text":"Ship the smallest thing that proves the idea."}
console.log(canonical(content) === "Ship the smallest thing that proves the idea.")
console.log(createHash('sha256').update(canonical(content), 'utf8').digest('hex'))
// 14fdf7de13a45ffd735d60ef9a3aa2b4da5612a106e9b6f0faf9722f06aec75c
Python 3
import hashlib, json

content = json.loads("{\"text\":\"Ship the smallest thing that proves the idea.\"}")
s = content.get('structured')
canon = content.get('text', '') + ('\n' + json.dumps(s, sort_keys=True, separators=(',',':'), ensure_ascii=False) if s else '')
print(canon == "Ship the smallest thing that proves the idea.")
print(hashlib.sha256(canon.encode('utf-8')).hexdigest())
# 14fdf7de13a45ffd735d60ef9a3aa2b4da5612a106e9b6f0faf9722f06aec75c
Shell (on Linux, sha256sum in place of shasum -a 256)
printf '%s' 'Ship the smallest thing that proves the idea.' | shasum -a 256
# 14fdf7de13a45ffd735d60ef9a3aa2b4da5612a106e9b6f0faf9722f06aec75c  -

Hash fixture: text plus structured (text, a newline, then the structured value as JCS)

content
{
  "text": "Three steps to a first draft.",
  "structured": {
    "steps": 3,
    "name": "first-draft",
    "order": [
      "outline",
      "draft",
      "cut"
    ]
  }
}
canonical
Three steps to a first draft.
{"name":"first-draft","order":["outline","draft","cut"],"steps":3}
sha256
8c449eb93e00db519c830a86ecedd96384944a6bb1755426654c17eb59bb9f76
JavaScript (Node 18 or later)
const { createHash } = require('node:crypto')

// RFC 8785 for ASCII keys, strings and integers: keys sorted, no spaces.
const jcs = (v) => Array.isArray(v) ? `[${v.map(jcs).join(',')}]`
  : v !== null && typeof v === 'object' ? `{${Object.keys(v).sort().map((k) => `${JSON.stringify(k)}:${jcs(v[k])}`).join(',')}}`
  : JSON.stringify(v)
const canonical = (c) => (c.text ?? '') +
  (c.structured && Object.keys(c.structured).length ? '\n' + jcs(c.structured) : '')

const content = {"text":"Three steps to a first draft.","structured":{"steps":3,"name":"first-draft","order":["outline","draft","cut"]}}
console.log(canonical(content) === "Three steps to a first draft.\n{\"name\":\"first-draft\",\"order\":[\"outline\",\"draft\",\"cut\"],\"steps\":3}")
console.log(createHash('sha256').update(canonical(content), 'utf8').digest('hex'))
// 8c449eb93e00db519c830a86ecedd96384944a6bb1755426654c17eb59bb9f76
Python 3
import hashlib, json

content = json.loads("{\"text\":\"Three steps to a first draft.\",\"structured\":{\"steps\":3,\"name\":\"first-draft\",\"order\":[\"outline\",\"draft\",\"cut\"]}}")
s = content.get('structured')
canon = content.get('text', '') + ('\n' + json.dumps(s, sort_keys=True, separators=(',',':'), ensure_ascii=False) if s else '')
print(canon == "Three steps to a first draft.\n{\"name\":\"first-draft\",\"order\":[\"outline\",\"draft\",\"cut\"],\"steps\":3}")
print(hashlib.sha256(canon.encode('utf-8')).hexdigest())
# 8c449eb93e00db519c830a86ecedd96384944a6bb1755426654c17eb59bb9f76
Shell (on Linux, sha256sum in place of shasum -a 256)
printf '%s' 'Three steps to a first draft.
{"name":"first-draft","order":["outline","draft","cut"],"steps":3}' | shasum -a 256
# 8c449eb93e00db519c830a86ecedd96384944a6bb1755426654c17eb59bb9f76  -

LanesEarly access

  • Typed: a proposable facet and kind with its content. This is the normal lane.
  • Raw: material such as a transcript, sent as knowledge/raw. It is held as the owner received it until the owner runs Extract on it in the proposals panel.
  • Voice: a voice/utterance proposal is checked at intake (it must be self_verbatim) and stored, but acceptance always refuses it in v0. Do not send voice.

Batches, dedupe and idempotencyEarly access

propose_ark_records takes a batch and answers one result per envelope, in order; one refused item never stops the rest. A record your source already proposed answers status duplicate with duplicate_of naming the earlier proposal, so a retry after a timeout is safe: resend the same envelopes.

QuotasEarly access

Proposals count against quotas that are separate from call limits: a source grant has its own daily proposal quota, and an owner key or OAuth credential has its own. The values:

LimitValueUnitDefined in
grant_proposal_quota200proposals per UTC calendar date per grant (a quota, not a call limit; the owner may set 1 to 5000)supabase/migrations/20261215110000_arp_p1_keys_and_ledger.sql:41
credential_proposal_quota2,000proposals per UTC calendar date per owner key or OAuth connection without a grant (a quota, not a call limit)supabase/migrations/20261216100000_arp_p2_proposals.sql:251

The subject and about_subjectEarly access

  • subject.ref says who a record is about: self, an entity, or person:<uuid>.
  • The subject-reserved rule: only an owner key may propose a boundary, an identity record or a relationship record. From an OAuth credential or a source key these answer subject_reserved.
  • about_subject marks material written by someone other than the owner about the owner. It is kept as context for answers. It never becomes canon and never feeds voice. Whether a source grant may send it is set on the grant.
  • The verbatim rule: self_verbatim means the exact words. Never invent, tidy or translate words and label them verbatim.

Worked examplesEarly access

A definition the owner wrote, in their own words

{
  "arp": "0",
  "ark": "ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d",
  "facet": "framework",
  "kind": "definition",
  "authorship": "self_paraphrase",
  "privacy": "training",
  "content": {
    "text": "A keystone habit is one routine that pulls the rest of a plan along with it. Name it, guard it, and let the other habits hang from it."
  },
  "topics": [
    "habits",
    "planning"
  ],
  "provenance": {
    "content_sha256": "767df8399817b451238ab1075a2bc55a8eb9cc144aaad4a969814e528308dd5a",
    "source_kind": "notes_app"
  }
}

A story the owner told, retold faithfully

{
  "arp": "0",
  "ark": "ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d",
  "facet": "experience_story",
  "kind": "story",
  "authorship": "self_paraphrase",
  "content": {
    "text": "I priced my first workshop by asking three past clients what it had changed for them. Their answers set the price, not my nerves."
  },
  "domains": [
    "business"
  ],
  "provenance": {
    "content_sha256": "a4e3655a5c16aee2aa907c5cea50a559205255ed1c54a3428cc20a258b5eeed0",
    "source_kind": "interview_notes"
  }
}

A raw transcript, word for word (the raw lane)

{
  "arp": "0",
  "ark": "ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d",
  "facet": "knowledge",
  "kind": "raw",
  "authorship": "self_verbatim",
  "content": {
    "text": "Q: What do you check first in a new plan?\nA: Who owns each step. If nobody owns it, it is not a step yet."
  },
  "provenance": {
    "content_sha256": "6f9e3498285055a9734c9634e4229be548989eb2e6877a7e41bda6f4c27ca69e",
    "source_kind": "transcript",
    "source_ref": "https://example.com/episodes/12"
  }
}

A testimonial someone else wrote about the owner (about_subject)

{
  "arp": "0",
  "ark": "ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d",
  "facet": "experience_story",
  "kind": "experience",
  "authorship": "about_subject",
  "subject": {
    "ref": "self"
  },
  "content": {
    "text": "Working with Dana, our team stopped arguing about priorities. She made us write the one decision we were avoiding at the top of every agenda."
  },
  "provenance": {
    "content_sha256": "25e7a8d61eaa7b8f052d022f97c59fac982c515ce8a5560a64202111dfc2fdce",
    "source_kind": "client_testimonial"
  }
}
propose_ark_record
curl -s https://www.arkz.ai/api/human \
  -H "Authorization: Bearer $ARKZ_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"action":"propose_ark_record","envelope":{"arp":"0","ark":"ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d","facet":"framework","kind":"definition","authorship":"self_paraphrase","privacy":"training","content":{"text":"A keystone habit is one routine that pulls the rest of a plan along with it. Name it, guard it, and let the other habits hang from it."},"topics":["habits","planning"],"provenance":{"content_sha256":"767df8399817b451238ab1075a2bc55a8eb9cc144aaad4a969814e528308dd5a","source_kind":"notes_app"}}}'
A batch: propose_ark_records with every worked example
curl -s https://www.arkz.ai/api/human \
  -H "Authorization: Bearer $ARKZ_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"action":"propose_ark_records","envelopes":[{"arp":"0","ark":"ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d","facet":"framework","kind":"definition","authorship":"self_paraphrase","privacy":"training","content":{"text":"A keystone habit is one routine that pulls the rest of a plan along with it. Name it, guard it, and let the other habits hang from it."},"topics":["habits","planning"],"provenance":{"content_sha256":"767df8399817b451238ab1075a2bc55a8eb9cc144aaad4a969814e528308dd5a","source_kind":"notes_app"}},{"arp":"0","ark":"ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d","facet":"experience_story","kind":"story","authorship":"self_paraphrase","content":{"text":"I priced my first workshop by asking three past clients what it had changed for them. Their answers set the price, not my nerves."},"domains":["business"],"provenance":{"content_sha256":"a4e3655a5c16aee2aa907c5cea50a559205255ed1c54a3428cc20a258b5eeed0","source_kind":"interview_notes"}},{"arp":"0","ark":"ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d","facet":"knowledge","kind":"raw","authorship":"self_verbatim","content":{"text":"Q: What do you check first in a new plan?\nA: Who owns each step. If nobody owns it, it is not a step yet."},"provenance":{"content_sha256":"6f9e3498285055a9734c9634e4229be548989eb2e6877a7e41bda6f4c27ca69e","source_kind":"transcript","source_ref":"https://example.com/episodes/12"}},{"arp":"0","ark":"ark:arkz:3f6c1a52-8d4e-4b7a-9c2f-5e1d0a7b8c9d","facet":"experience_story","kind":"experience","authorship":"about_subject","subject":{"ref":"self"},"content":{"text":"Working with Dana, our team stopped arguing about priorities. She made us write the one decision we were avoiding at the top of every agenda."},"provenance":{"content_sha256":"25e7a8d61eaa7b8f052d022f97c59fac982c515ce8a5560a64202111dfc2fdce","source_kind":"client_testimonial"}}]}'

What happens nextEarly access

  • The proposal waits. Nothing joins the ARK on its own.
  • The owner reviews it at login in the proposals panel and accepts, rejects or holds it.
  • Nothing is spent until the owner accepts.
  • An accepted record counts as one ingested insight on the owner's plan, and the AI that files it is charged in actions at the owner's rate.
  • A source never pays to write.

Checking what happenedEarly access

get_ark_proposal_status answers the status of proposals your credential wrote (a source key sees every proposal of its grant). Send proposal_ids to check specific ones, in order, or omit them to page through your proposals newest first with since, limit and after. It answers status only: no content, no owner note, no reason.

StatusMeaning
proposedWaiting for the owner. decided_at is null.
heldThe owner set it aside to decide later. decided_at is null.
acceptedPart of the ARK. An owner key or an OAuth credential that also holds read:records gets record_ref; a source key never does.
rejectedThe owner declined it.
expiredIt waited past its expiry without a decision.
not_foundNo such proposal, or not yours. The two answer the same.

Rejected proposals are removed 30 days after the decision and expired ones 30 days after their scheduled expiry; after that their ids answer not_found. The job that expires and removes proposals is not yet scheduled: until it runs, an undecided proposal stays proposed and older ids keep answering.

Polling with backoff, batched by proposal_ids
const API = 'https://www.arkz.ai/api/human'
const KEY = process.env.ARKZ_KEY
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
const call = (action, params) => fetch(API, {
  method: 'POST',
  headers: { Authorization: 'Bearer ' + KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ action, ...params }),
}).then((r) => (r.status === 429 || r.status >= 500 ? Promise.reject(new Error('retry ' + r.status)) : r.json()))
// Retry with backoff; safe for proposals because a resend answers duplicate.
const withRetry = (fn, wait = 2000, tries = 6) =>
  fn().catch((e) => (tries <= 1 ? Promise.reject(e) : sleep(wait).then(() => withRetry(fn, wait * 2, tries - 1))))

// Ask about the open ids in batches of 100, backing off between rounds.
async function waitForDecisions(ids, rounds = 12) {
  const decided = new Map()
  let wait = 30_000
  for (let round = 0; round < rounds && decided.size < ids.length; round++) {
    const open = ids.filter((id) => !decided.has(id))
    for (let i = 0; i < open.length; i += 100) {
      const answer = await withRetry(() => call('get_ark_proposal_status', { proposal_ids: open.slice(i, i + 100) }))
      for (const p of answer.proposals) if (p.status !== 'proposed' && p.status !== 'held') decided.set(p.proposal_id, p.status)
    }
    if (decided.size < ids.length) { await sleep(wait); wait = Math.min(wait * 2, 6 * 3600 * 1000) }
  }
  console.log([...decided].filter(([, s]) => s === 'accepted').length, 'accepted of', ids.length)
}

await waitForDecisions(['<proposal id>', '<proposal id>'])

Licensed for use, not for copying: you may not use this ARK's output to train, build or populate another ARK, model or dataset.

Your wallet on this device

Your wallet is not currently authenticated on this new device. Authenticate now or later?

Your wallet, your choice

Create a wallet in ARKZ or use one you already own. Connecting proves ownership and moves no funds.

Sign in to continue