Concepts
The records kovee keeps, and what each one is for.
Everything named on this page exists in the daemon today — each section names the operations that reach it, and every operation named here is checked against the registry when this site is built. Where a concept is only half built, the limit is in the section, not in a footnote.
Spaces
A space is the unit of shared work and of visibility. It lives
in a project, in a realm; the personal profile has exactly one realm,
realm-personal. A space has a title, a status, a default
classification, a policy set, and — from the moment it is created — a main
branch.
| What you do | Operations |
|---|---|
| Create, read, list, retitle | space_create,
space_show,
space_list,
space_update_metadata |
| Change its lifecycle | space_freeze,
space_archive,
space_reopen |
| Narrow who can see it | space_restrict,
space_policy_narrow |
| Widen who can see it | space_access_widen_prepare,
…_confirm,
…_cancel,
…_show,
…_list |
| Manage participants and grants | space_participant_add,
…_activate,
…_update,
…_remove,
space_access_grant_create,
…_revoke |
The asymmetry between narrowing and widening is deliberate. Restricting a space is one operation. Widening it is a prepared, two-step path: prepare states exactly what would become visible to whom, and a separate confirm applies it — so nobody widens visibility as a side effect of editing a field.
Contributions
A contribution is one immutable, typed entry appended to a branch. It carries body parts (media type and text), an author, a classification, a retention policy, its position in the space and on the branch, and a content digest that later records bind to.
The kind is a closed enum of 12
values — utterance, goal, question,
claim, observation, evidence,
proposal, critique, synthesis,
result, decision_reference,
system_notice. An unknown kind is refused, not stored as free text.
A contribution may also carry an epistemic posture —
asserted, tentative, observed,
reported or contested — because "I think" and "I
measured" are different claims about the same sentence.
Nothing edits a contribution in place. Three operations move the record
forward instead:
contribution_supersede points at
a replacement, contribution_withdraw
marks it withdrawn, and
contribution_redact destroys the key
material that made its content verifiable — see
erasure. All three leave the original position and
provenance in place.
Appending is a compare-and-swap. The caller presents the branch and the head digest it expects, and a writer working from a stale view is refused rather than interleaved. The CLI computes that expected head client-side, by folding the authorized event ledger — a client, not a privileged reader.
Relations
A relation connects two exact revisions and is attributed to
whoever asserted it:
relation_assert and
relation_retract. The kind is closed too,
at 11 values:
addresses, supports, challenges,
refines, qualifies, supersedes,
depends_on, derived_from, produced_by,
quotes, evaluates. A synthesis addresses a
question; evidence supports a claim. Because both endpoints are exact
revisions, a relation does not quietly follow an edit.
A relation is a claim about the work, not a decision about it, and it is recorded as one — an assistant's relation is stored with its class and its author, not merged into the record as fact.
Branches
Every space has a main branch, every contribution carries an origin branch and
a branch sequence, and the branch head is a fold over the contributions on it.
But there is no branch_* operation
in the registry: nothing forks a branch and nothing merges one. The
forking and digest-bound adoption in kovee's design are not built. What the
branch model buys today is the compare-and-swap on append, and an honest place
to put the sequence.
Frontiers
A frontier pins an exact point in a space so that a later read
means what it meant then:
frontier_pin and
frontier_show. Every
context assembly pins its own frontier in the same
transaction that creates it, which is what makes "exactly what the assistant was
shown" a checkable statement rather than a hopeful one.
Reads that are not pinned are still positioned:
events_read pages by an authenticated cursor,
events_wait long-polls from one, and list
reads hand back a boundary cursor for the position their answer was true at.
Lenses
A lens changes presentation, never visibility or authority. Two are seeded into every new space with deterministic identifiers, so they are there the moment the space exists:
| Lens | Renders |
|---|---|
stream | Every visible contribution, chronologically, by branch sequence. |
workbench | Typed cards — every non-utterance contribution — each with its asserted relations in and out attached. |
Six operations reach them:
lens_read,
lens_create,
lens_show,
lens_list,
lens_update,
lens_revoke. Items a lens returns are
projections: they confer no visibility, no authority, and no invocation.
lens_create accepts 7
kinds — stream, workbench, pulse,
branch_compare, ensemble, provenance,
custom — but only workbench renders differently
today. The query grammar that would make the other five distinct is not
pinned yet, so they all serve the chronological projection. Creating a
pulse lens gets you a stream with a different name.
ContextAssembly
When an assistant is invoked, what it was given is recorded as an immutable
ContextAssembly:
context_assembly_create and
context_assembly_show. It is
evidence of selection — never a bearer capability, and never a thing you can
hand someone to gain access.
The record names, for one exact frontier:
- every item: object reference, revision, digest, size, classification, role, order, and the reason it was included;
- the relations among those items, by reference and digest;
- transformations applied, each with a source and result digest;
- omissions — visible candidates that were left out, and why;
- the audience, the purpose, the triggers, the selection policy and its digest, the authority digest, and totals in items, bytes and estimated tokens.
"Exact" is enforced at both ends. Creating an assembly pins its frontier in the same transaction; a required reference that cannot be resolved, or that belongs to another space or branch, fails the assembly rather than being quietly dropped or substituted. Reading one re-resolves every item and compares digests: if an item is gone or has changed, the read returns unavailable rather than serving a different selection under the same identifier.
One selection policy is served: explicit_refs_v1, which includes
exactly the references the caller named. Under it omissions and
transformations are always empty and every item's inclusion
reason is explicit_ref — the record has room for a richer
selection story than anything shipped can put in it. Recipe-driven assembly is
refused as a later capability.
Artifacts and erasure-safe digests
Bytes attach through a staged upload — begin, credential, finalize, with abort and show — and become an artifact only when the finalizing transaction commits. The interesting part is how they are addressed.
An artifact's content digest is not a plain SHA-256 of the bytes. It is an HMAC under a 32-byte secret minted for that one object, stored wrapped under the realm key, never kept raw beside the object and never derived from a root key. That single design choice is what "erasure-safe" means:
| Digest class | Destroying the key… |
|---|---|
portable_public | Unkeyed SHA-256. Nothing to destroy; the digest keeps proving what the bytes were, forever. |
scope_erasure_safe | Keyed per scope. Destroying the scope key erases verifiability for everything in that scope. |
local_erasure_safe | Keyed per object. Destroying that one secret erases verifiability for exactly that object and nothing else. This is what artifacts use. |
So erasing an artifact is not a flag. It removes the sealed blob and the staging copies, then null-columns the secret, the content digest, the storage reference, the size and the media type, leaving identity and provenance behind. Redacting a contribution does the same to its key material: the digest value stays as the object's address, and is now unverifiable — which is precisely what erasure means. There is no retained plaintext hash anywhere to undo it, and the tests for this grep the database file, its WAL and the artifact store for the plaintext rather than trusting the code path.
Uploads are capped at 64 MiB and expire after an hour. Verification checks size, the declared raw digest, the media type and the typed content digest — no malware or secret scanning is performed or claimed; the scanner set is pinned empty and says so in the record.
Assistants
An assistant is registered, given revisions, deployed, aliased, and then
invoked — assistant_create,
assistant_revision_register,
deployment_create,
deployment_activate,
assistant_alias_bind,
invocation_create,
invocation_show,
invocation_cancel.
The running assistant does not use the client socket. It claims an attempt on
the worker socket, receives an attempt id and a fence epoch, and
every mutation it makes carries them — so a stale attempt, or a second run of a
completed one, is refused rather than handed the old receipts. The Python SDK
wraps this: subclass Assistant, implement run, and give
every mutating call an operation_key that becomes its durable
idempotency key.
Invocation is explicit — some client decides to run the assistant and names
what it should look at. Nothing wakes an assistant on its own: there is
no attention_* operation
and crates/kovee-attention is a two-line stub.
The MCP server
kovee-mcp exposes 14 tools
over stdio JSON-RPC. It is not a hand-written adapter: each tool's input schema
is derived from the operation's own request schema, minus the fields the
channel already fixes — realm, project, actor, attempt, fence, and the envelope.
A check re-derives all fourteen from the registry and the schemas on every run,
and refuses to start the server if the embedded bundle disagrees with the build.
The tool table lists every one of them.
Three rules the server enforces rather than documents:
- No worker or operator operation is ever a tool. That is re-derived from the registry's per-surface rows, so it cannot rot into being false.
- Gating follows the schema, not an opinion. An operation is
a mutation exactly when its request schema requires
meta; mutations are gated, reads are marked safe to allow. The one recorded exception iskovee_artifact_upload_credential— a read that stays gated because its result carries a live storage credential. - Idempotency keys are derived, not invented. The key is an HMAC over the canonical tool call under a per-process session salt, so the same harness retrying the same call is a replay, not a second record.
Governance is byom's, over BPP
Kovee never decides governance. When a personal installation is bound to
governance, the owner is byom, reached over the Byom
Participation Protocol through the kovee-byom adapter. The owner
enum has exactly two arms — byom and none — and the
code rejects anything else.
Enablement is a greenfield saga, not a cutover:
governance_enable creates the realm
binding and the society mapping inert, then swings the owner binding from
none to byom by compare-and-swap at an expected
revision. Kovee is never the genesis governance actor — that rule is enforced by
reading byomd's own projection, not by trusting the request.
governance_show reads the binding, and
governance_disable takes it back.
Promoting work is the other half:
endeavor_promotion_prepare,
…_start,
…_show,
…_cancel,
…_reconcile, with
byom_episode_binding_show
reading the resulting binding. A local row is committed before the peer is
called, so a process that dies in between restarts pending and reconciles
against what the peer really committed.
These nine operations are the built part of
governed_work_binding_v1, and the bundle has fourteen. Because a
bundle is atomic, hello advertises
3 bundles, not
4 — the nine dispatch, but a client
should not discover or depend on them yet. There is no CLI verb for any of
them; they are reachable over the socket only.
The model broker and its one-shot permit
An assistant that wants a model call does not hold a provider key and does not
open a socket. It calls model_complete on
the worker surface with a prompt, a model profile, a purpose and a
classification. No provider, no host, no URL, no header and no credential
reference appear in those arguments — that is the point of the
operation's shape. The daemon supplies all of them.
What then happens, in order:
- The attempt's binding is checked as current.
- The disclosure — what would be sent, and to which class of recipient — is committed locally first.
- A local effect row is committed under a stable execution key, in state
prepared. No permit has been consumed and no byte has left. - Kovee calls byom's
execution_permit_consumefor that exact execution key and stores the one immutable receipt it returns. - The receipt is admitted, attested against the committed consumption row, and authorized into a permit — each step verifying the previous one's keyed tag.
- The single use is claimed in kovee's durable ledger before the socket opens, and only then does the request go out.
The permit is one-shot in a specific, mechanical sense: byom's receipt must
declare exactly one use, the permit type cannot be cloned or deserialized and is
passed to dispatch by value, and the durable claim is a conditional
UPDATE that moves a row only a real byom consumption created. A
second attempt at the same effect finds the row already spent and sends nothing.
The act chain that authorizes work is byom's, and kovee
neither authors nor widens it. Kovee calls exactly one byom operation here —
execution_permit_consume — and everything else it passes is a
notice byomd re-checks against its own committed values. A caller naming
another act's references gains nothing.
This constrains the broker's calls. It does not stop a different process on the same machine from calling a provider itself, and inside one process it is the durable ledger — not cryptography — that bounds a forgery. Read provider bypass before describing any of this as prevention. Ambiguity is also reported honestly: once bytes may have left, a transport failure terminalizes as ambiguous, never as failed.
Akson, and what does not cross
Work that crosses to an installation you do not control is akson's concern, carried as byom delegation. It is never a kovee commitment and never a shared broker subject. Nothing in this repository opens a network listener: kovee's two sockets are local Unix sockets, and the only outbound path is the brokered provider call above.