Koinii cross-seat dereference: the DISTRIBUTED topology, where independent seats — separate processes, each holding its own copy of the KB — stay in sync by content-addressed commits rather than by sharing one live daemon. A seat asserts a sentence and commits; another seat pulls the same commit and resolves the same sentence from its own KB. The locator travels over a transport; the proof comes from the KB, and neither seat trusts the marker.
A different deployment shape from koinii's default (N agents funnelling writes through
one single-writer daemon — docs/koinii.md, The deployment shape). There the
daemon IS the shared KB and dereference is a plain read; here each seat is its own
reader with its own store, and the shared reference point is a commit, not a socket.
Complements, not rivals: the daemon for live co-writing, this for disconnected or
independently-replicated seats.
The spike's vocabulary, mapped to real primitives:
kb;
every function takes one, exactly as the other koinii modules do.locator), plus the untrusted transport payload that carries it (the marker).dereference finds the sentence in the seat's own store, reads its
provenance, and hands off to why for the proof.Three ideas, each grounded on a primitive that ships:
"sha256:" followed by 64 lowercase hex chars — over a sentex's canonical
identity (its context, polarity, and canonicalized sentence,
docs/canonicalization.md). Three best-practice commitments live in that one string:
dereference
rehashes the resolved form and compares — and SHA-1 has practical chosen-prefix
collisions, so it is the wrong primitive for this job."sha256:" multihash-style tag names the algorithm in
the value itself, so a later migration to another primitive is unambiguous rather
than a silent reinterpretation of 64 anonymous hex chars.pr-str. The digest input is the explicit,
type-tagged, self-delimiting byte encoding vaelii.core/digest hashes — independent of
ambient *print-* vars and injective across the value space a sentence holds, so
a symbol never digests as the like-spelled string and (a b) never as (a (b)).
Two seats holding the same assertion compute the same locator, because v/import!
re-canonicalizes every record through the reading build's own constructor.commit-id is the RFC-6962-style
Merkle root over the seat's sorted per-sentex leaf digests — leaf hashing
domain-separated with a 0x00 byte and internal-node hashing with 0x01, so a leaf
can never be forged as an internal node — prefixed "sha256:". The leaves are the
records the seat believes, not everything it stores: a defeated default is retained
on purpose and is no part of what the seat holds, so two seats that agree on every
belief compute one id however differently their stores were built. Order- and
handle-independent, so two seats that reached the same beliefs by different routes
compute the same commit id (belief and storage are order-independent —
docs/nmtms.md), and a KB exported, pulled and recovered on another seat carries the
id across. A sentence that names a sentex by (sentexHandle n) — every koinii
response act — digests the named record's own identity in place of n
(v/digest), so a reply is located by its target and two seats that built one
conversation in different orders agree on it. The Merkle shape buys pure auditability:
inclusion-proof yields an audit path and verify-inclusion recomputes the root from
just a (locator, proof) pair — no KB — which a flat digest cannot. commit-id fingerprints knowledge (what two seats compare to agree they
hold the same thing); state-root is a second root whose leaves fold each record's
provenance (:creator + :created + identity), a full snapshot identity like a
git commit (covers who/when), so it moves when provenance moves even when content does
not. publish! / pull! move the bytes (via v/export! / v/import!); the roots
are what say two seats hold the same knowledge (and the same snapshot).dereference finds the sentence in the seat's own KB
and rehashes what it found; a stale or tampered marker fails that check and is
rejected, and a marker the seat cannot resolve means the commit was not received —
never that the marker's payload should be believed. Resolution is scoped to BELIEF
for the reason the commit is: a record the seat stores but does not believe is no part
of what it holds, so dereference declines it exactly as inclusion-proof declines to
prove it. A locator alone resolves nothing: the marker's sentence and context find the
record, and the locator checks it. dereference also fails closed on a malformed
payload, as verify-inclusion does on a malformed proof: a marker that is not a
map, whose :locator is not this format, or whose sentence the engine declines to be
asked about is ANSWERED :malformed rather than allowed to throw the engine's
refusal out of the resolve path — a peer must not be able to crash a receiving seat by
sending garbage. Attribution is trustworthy only as far as the identity model makes
it: a distributed KB inherits the same cooperative-vs-proof-tier question.Additive, like the other koinii modules: only the public core API — the record walk
is handles / sentex, and the locator is digest / digests, so a seat digests
what the store itself would. Beside it, koinii types. Nothing under
vaelii.impl, and nothing in core loads it.
Koinii cross-seat dereference: the DISTRIBUTED topology, where
independent *seats* — separate processes, each holding its own copy of the KB — stay
in sync by **content-addressed commits** rather than by sharing one live daemon. A
seat asserts a sentence and commits; another seat pulls the same commit and resolves
the same sentence **from its own KB**. The locator travels over a transport; the
proof comes from the KB, and neither seat trusts the marker.
A different deployment shape from koinii's default (N agents funnelling writes through
one single-writer daemon — `docs/koinii.md`, *The deployment shape*). There the
daemon IS the shared KB and dereference is a plain read; here each seat is its own
reader with its own store, and the shared reference point is a commit, not a socket.
Complements, not rivals: the daemon for live co-writing, this for disconnected or
independently-replicated seats.
The spike's vocabulary, mapped to real primitives:
- **seat** — an independent KB-holding process. In this layer a seat is just a `kb`;
every function takes one, exactly as the other koinii modules do.
- **locator / marker** — a content-addressed reference to a canonical assertion (the
`locator`), plus the untrusted transport payload that carries it (the `marker`).
- **transport** — any byte-faithful channel that carries a marker. A marker is a
plain Clojure map, so any transport that ships EDN ships one.
- **the KB** — the sole authority for *meaning*. A marker is never trusted, only
resolved: `dereference` finds the sentence in the seat's own store, reads its
provenance, and hands off to `why` for the proof.
Three ideas, each grounded on a primitive that ships:
- **The locator is content-addressed, not handle-addressed.** A handle is a number
one store minted and does not travel; a locator is a **self-describing** digest — the
literal `"sha256:"` followed by 64 lowercase hex chars — over a sentex's **canonical
identity** (its context, polarity, and canonicalized sentence,
`docs/canonicalization.md`). Three best-practice commitments live in that one string:
* **SHA-256, not SHA-1.** A locator is a tamper-detection boundary — `dereference`
rehashes the *resolved* form and compares — and SHA-1 has practical chosen-prefix
collisions, so it is the wrong primitive for this job.
* **Self-describing.** The `"sha256:"` multihash-style tag names the algorithm in
the value itself, so a later migration to another primitive is unambiguous rather
than a silent reinterpretation of 64 anonymous hex chars.
* **A spec'd canonical encoding, not `pr-str`.** The digest input is the explicit,
type-tagged, self-delimiting byte encoding `vaelii.core/digest` hashes — independent of
ambient `*print-*` vars and injective across the value space a sentence holds, so
a symbol never digests as the like-spelled string and `(a b)` never as `(a (b))`.
Two seats holding the same assertion compute the **same** locator, because `v/import!`
re-canonicalizes every record through the reading build's own constructor.
- **The commit is a Merkle function of belief.** `commit-id` is the RFC-6962-style
**Merkle root** over the seat's *sorted* per-sentex leaf digests — leaf hashing
domain-separated with a `0x00` byte and internal-node hashing with `0x01`, so a leaf
can never be forged as an internal node — prefixed `"sha256:"`. The leaves are the
records the seat **believes**, not everything it stores: a defeated default is retained
on purpose and is no part of what the seat holds, so two seats that agree on every
belief compute one id however differently their stores were built. Order- and
handle-independent, so two seats that reached the same beliefs by different routes
compute the same commit id (belief and storage are order-independent —
`docs/nmtms.md`), and a KB exported, pulled and recovered on another seat carries the
id across. A sentence that names a sentex by `(sentexHandle n)` — every koinii
response act — digests the named record's own identity in place of `n`
(`v/digest`), so a reply is located by its target and two seats that built one
conversation in different orders agree on it. The Merkle shape buys pure auditability:
`inclusion-proof` yields an audit path and `verify-inclusion` recomputes the root from
just a `(locator, proof)` pair — no KB — which a flat digest cannot. `commit-id` fingerprints **knowledge** (what two seats compare to agree they
hold the same thing); `state-root` is a second root whose leaves fold each record's
provenance (`:creator` + `:created` + identity), a full **snapshot** identity like a
git commit (covers who/when), so it moves when provenance moves even when content does
not. `publish!` / `pull!` move the bytes (via `v/export!` / `v/import!`); the roots
are what say two seats hold the same knowledge (and the same snapshot).
- **The marker is untrusted.** `dereference` finds the sentence in the seat's own KB
and rehashes what it found; a stale or tampered marker fails that check and is
rejected, and a marker the seat cannot resolve means the commit was not received —
never that the marker's payload should be believed. Resolution is scoped to BELIEF
for the reason the commit is: a record the seat stores but does not believe is no part
of what it holds, so `dereference` declines it exactly as `inclusion-proof` declines to
prove it. A locator alone resolves nothing: the marker's sentence and context find the
record, and the locator checks it. `dereference` also **fails closed on a malformed
payload**, as `verify-inclusion` does on a malformed proof: a marker that is not a
map, whose `:locator` is not this format, or whose sentence the engine declines to be
asked about is ANSWERED `:malformed` rather than allowed to throw the engine's
refusal out of the resolve path — a peer must not be able to crash a receiving seat by
sending garbage. Attribution is trustworthy only as far as the identity model makes
it: a distributed KB inherits the same cooperative-vs-proof-tier question.
Additive, like the other koinii modules: only the public core API — the record walk
is `handles` / `sentex`, and the locator is `digest` / `digests`, so a seat digests
what the store itself would. Beside it, koinii `types`. Nothing under
`vaelii.impl`, and nothing in core loads it.(commit-id kb)A content-addressed fingerprint of the seat's believed knowledge: the RFC-6962 Merkle
root over its sorted per-sentex content locators, prefixed "sha256:". Order- and
handle-independent, so two seats believing the same records compute the same id (a
(sentexHandle n) inside a sentence digests the record it names, v/digest), and a KB
exported, pulled and recovered on another seat carries it across.
Scope is every BELIEVED sentex, premises and forward-derived alike, read as CONTENT
(context/polarity/sentence, not provenance). A stored-but-defeated sentex is not a
leaf, so two seats agree exactly when their believed sets match: the pull flow guarantees
that, and independently built seats meet it only if they derived to the same extent (same
rules, same *max-depth*). For attribution-sensitive identity, see state-root.
A content-addressed fingerprint of the seat's **believed knowledge**: the RFC-6962 Merkle root over its sorted per-sentex content locators, prefixed `"sha256:"`. Order- and handle-independent, so two seats believing the same records compute the same id (a `(sentexHandle n)` inside a sentence digests the record it names, `v/digest`), and a KB exported, pulled and recovered on another seat carries it across. Scope is every BELIEVED sentex, premises and forward-derived alike, read as CONTENT (context/polarity/sentence, not provenance). A stored-but-defeated sentex is **not** a leaf, so two seats agree exactly when their believed sets match: the pull flow guarantees that, and independently built seats meet it only if they derived to the same extent (same rules, same `*max-depth*`). For attribution-sensitive identity, see `state-root`.
(dereference kb marker)Resolve marker against the seat's OWN KB, where the marker's untrustedness is enforced.
Returns, on success:
{:resolved? true :handle h :locator <sha256:hex> :sentence S :context C
:polarity t :seat <asserter> :provenance <map>}
and on failure {:resolved? false :reason … :locator <sha256:hex>}:
:malformed: not a marker, with :problem naming the part: :not-a-map, a :locator
that is not "sha256:" + 64 hex, :no-sentence / :no-context, or the engine's
refusal type (:shape, :unsupported-context). Fails closed: a hostile marker is
answered, never allowed to throw out of the resolve path. Distinct from
:not-received: one peer is out of sync, the other sends garbage.:not-received: the sentence is not in this seat's store; the marker's payload is not
trusted instead.:target naming the sender's handle number: a :targets record
this seat does not resolve, or a malformed target marker (local-handle). A :targets
value that is not a map of integers to markers, or a chain deeper than
max-target-depth, is :malformed with :problem :targets.:not-believed: the sentence is stored and not believed (docs/nmtms.md). The record
has no leaf in the commit-id tree, so resolving it would hand back a record the seat
can produce no proof of.:locator-mismatch: stored and believed, but the locator this seat computes differs:
the marker is stale or tampered.Meaning, attribution and (via why-marker) proof all come from this seat's KB.
Resolve `marker` against the seat's OWN KB, where the marker's untrustedness is enforced.
Returns, on success:
{:resolved? true :handle h :locator <sha256:hex> :sentence S :context C
:polarity t :seat <asserter> :provenance <map>}
and on failure `{:resolved? false :reason … :locator <sha256:hex>}`:
- `:malformed`: not a marker, with `:problem` naming the part: `:not-a-map`, a `:locator`
that is not `"sha256:"` + 64 hex, `:no-sentence` / `:no-context`, or the engine's
refusal type (`:shape`, `:unsupported-context`). **Fails closed**: a hostile marker is
answered, never allowed to throw out of the resolve path. Distinct from
`:not-received`: one peer is out of sync, the other sends garbage.
- `:not-received`: the sentence is not in this seat's store; the marker's payload is not
trusted instead.
- a target's reason, with `:target` naming the sender's handle number: a `:targets` record
this seat does not resolve, or a malformed target marker (`local-handle`). A `:targets`
value that is not a map of integers to markers, or a chain deeper than
`max-target-depth`, is `:malformed` with `:problem :targets`.
- `:not-believed`: the sentence is stored and not believed (`docs/nmtms.md`). The record
has no leaf in the `commit-id` tree, so resolving it would hand back a record the seat
can produce no proof of.
- `:locator-mismatch`: stored and believed, but the locator this seat computes differs:
the marker is stale or tampered.
Meaning, attribution and (via `why-marker`) proof all come from this seat's KB.(inclusion-proof kb locator)The audit path proving locator is a leaf of this seat's commit-id tree: a vector of
{:hash <sibling-hex> :side :left|:right} ordered leaf→root, or nil if the seat does not
BELIEVE a record with that locator. A verifier fed this path, the leaf locator and the
published root confirms inclusion via verify-inclusion without the KB. It folds
the same believed leaves as commit-id, so a stored-but-defeated record answers nil.
The audit path proving `locator` is a leaf of this seat's `commit-id` tree: a vector of
`{:hash <sibling-hex> :side :left|:right}` ordered leaf→root, or nil if the seat does not
BELIEVE a record with that locator. A verifier fed this path, the leaf `locator` and the
published root confirms inclusion via `verify-inclusion` **without** the KB. It folds
the same believed leaves as `commit-id`, so a stored-but-defeated record answers nil.(locate kb sentence context)The locator sentence in context would have, computed without requiring it be
stored (v/digest's three-arity): (locate kb S C) equals (locator-of kb h) for the
handle h that S/C resolves to.
The locator `sentence` in `context` **would** have, computed without requiring it be stored (`v/digest`'s three-arity): `(locate kb S C)` equals `(locator-of kb h)` for the handle `h` that `S`/`C` resolves to.
(locator-of kb handle)The locator of the stored sentex at handle (v/digest): independent of the handle,
so reproducible on any seat that holds the same assertion. nil if the handle names no
record.
The locator of the stored sentex at `handle` (`v/digest`): independent of the handle, so reproducible on any seat that holds the same assertion. nil if the handle names no record.
(marker kb handle)The transportable marker for the assertion at handle, which a seat sends over a
transport so another seat can dereference it:
{:locator <sha256:hex> :sentence <asserted form> :context <ctx> :seat <claimed>
:targets {n <marker> …}}
The :locator decides it; :sentence / :context are a lookup payload the receiver does
NOT trust (it resolves against its own KB and rehashes), and :seat is the claimed
asserter, the real one coming off the resolved sentex's provenance. Throws if handle
names no record. :targets carries the marker of every record the sentence names:
a reply's sentence holds (sentexHandle n) with this seat's number, which a seat that
built the same conversation itself numbers differently. dereference resolves each target
on its own seat first and substitutes the handle it finds. The key is absent when the
sentence names no stored record; a handle already on the chain being built is left out
(marker-at).
The transportable marker for the assertion at `handle`, which a seat sends over a
transport so another seat can dereference it:
{:locator <sha256:hex> :sentence <asserted form> :context <ctx> :seat <claimed>
:targets {n <marker> …}}
The `:locator` decides it; `:sentence` / `:context` are a lookup payload the receiver does
NOT trust (it resolves against its own KB and rehashes), and `:seat` is the *claimed*
asserter, the real one coming off the resolved sentex's provenance. Throws if `handle`
names no record. **`:targets` carries the marker of every record the sentence names**:
a reply's sentence holds `(sentexHandle n)` with this seat's number, which a seat that
built the same conversation itself numbers differently. `dereference` resolves each target
on its own seat first and substitutes the handle it finds. The key is absent when the
sentence names no stored record; a handle already on the chain being built is left out
(`marker-at`).(publish! kb dir)(publish! kb dir opts)Write the seat's KB out as a portable export dump in dir, the form a git host carries:
v/export! with :compression :none, so the record streams are a byte-stable function
of the KB (gzip writes a header timestamp). dir must be absent or empty. Returns
export!'s summary; the commit the other seats pull is this directory.
:compression is fixed: anything but :none is refused
(:koinii/compression-fixed); a caller that wants a compressed dump wants v/export!.
Every other export! option passes through.
Write the seat's KB out as a portable export dump in `dir`, the form a git host carries: `v/export!` with `:compression :none`, so the record streams are a byte-stable function of the KB (gzip writes a header timestamp). `dir` must be absent or empty. Returns `export!`'s summary; the commit the other seats pull is this directory. **`:compression` is fixed**: anything but `:none` is refused (`:koinii/compression-fixed`); a caller that wants a compressed dump wants `v/export!`. Every other `export!` option passes through.
(pull! kb dir)(pull! kb dir opts)Open a pulled commit into the seat kb: v/import! the dump at dir, which
re-canonicalizes every record through this build's constructor and recovers belief, so
this seat computes the same locators as the publisher. A seat that is a fork takes the
dump as its base and keeps its own writes and their attestations over it
(identity/rebase!); any other seat must be empty (:not-empty). Every provenance
:principal is dropped (:drop-principal?). A seat registering a reasoner set other
than the publisher's is refused (:reasoner-mismatch); {:reasoners :stored} registers
the publisher's set. Returns import!'s summary.
Open a pulled commit into the seat `kb`: `v/import!` the dump at `dir`, which
re-canonicalizes every record through this build's constructor and recovers belief, so
this seat computes the same locators as the publisher. A seat that is a fork takes the
dump as its base and keeps its own writes and their attestations over it
(`identity/rebase!`); any other seat must be empty (`:not-empty`). Every provenance
`:principal` is dropped (`:drop-principal?`). A seat registering a reasoner set other
than the publisher's is refused (`:reasoner-mismatch`); `{:reasoners :stored}` registers
the publisher's set. Returns `import!`'s summary.(state-root kb)A content-addressed snapshot identity of the seat's believed records: the Merkle
construction of commit-id over the same believed set, with leaves that fold each
record's provenance (:creator + :created) in with its identity. It moves when
provenance moves even if commit-id does not. Two seats compare commit-id to agree on
knowledge and state-root to agree on the snapshot. It is belief-scoped for
commit-id's reason, so the two roots cover one set of records.
A content-addressed **snapshot** identity of the seat's believed records: the Merkle construction of `commit-id` over the same believed set, with leaves that fold each record's provenance (`:creator` + `:created`) in with its identity. It moves when provenance moves even if `commit-id` does not. Two seats compare `commit-id` to agree on *knowledge* and `state-root` to agree on the *snapshot*. It is belief-scoped for `commit-id`'s reason, so the two roots cover one set of records.
(verify-inclusion locator proof root)PURE verification that locator is included under root given proof: recompute the
Merkle root from the leaf digest of locator and the audit path's sibling hashes, and
compare to root. Takes no KB. Domain separation (0x00 leaf / 0x01 node) is
enforced on both sides. Fails closed on a malformed proof: a non-hex sibling, a
missing :hash/:side, or a non-sequential proof returns false, not a throw.
PURE verification that `locator` is included under `root` given `proof`: recompute the Merkle root from the leaf digest of `locator` and the audit path's sibling hashes, and compare to `root`. Takes no KB. Domain separation (`0x00` leaf / `0x01` node) is enforced on both sides. **Fails closed on a malformed proof**: a non-hex sibling, a missing `:hash`/`:side`, or a non-sequential proof returns `false`, not a throw.
(why-marker kb marker)Dereference marker and, when it resolves, attach the proof v/why builds for it —
the extension point a cross-seat proof-identity layer grows into. The marker names WHAT to
prove by content; the proof itself is the seat's own, drawn from its own justification
graph. Returns the dereference map, with :why added on success.
Dereference `marker` and, when it resolves, attach the proof `v/why` builds for it — the extension point a cross-seat proof-identity layer grows into. The marker names WHAT to prove by content; the proof itself is the seat's own, drawn from its own justification graph. Returns the `dereference` map, with `:why` added on success.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |