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 doOperations
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.

Immutable, but not unchangeable

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

Record model only

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:

LensRenders
streamEvery visible contribution, chronologically, by branch sequence.
workbenchTyped 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.

Limit

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:

"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.

Limit

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 classDestroying the key…
portable_publicUnkeyed SHA-256. Nothing to destroy; the digest keeps proving what the bytes were, forever.
scope_erasure_safeKeyed per scope. Destroying the scope key erases verifiability for everything in that scope.
local_erasure_safeKeyed 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.

Limits

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.

Limit

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:

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.

Limit

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:

  1. The attempt's binding is checked as current.
  2. The disclosure — what would be sent, and to which class of recipient — is committed locally first.
  3. A local effect row is committed under a stable execution key, in state prepared. No permit has been consumed and no byte has left.
  4. Kovee calls byom's execution_permit_consume for that exact execution key and stores the one immutable receipt it returns.
  5. The receipt is admitted, attested against the committed consumption row, and authorized into a permit — each step verifying the previous one's keyed tag.
  6. 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.

Whose decision is whose

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.

Limit

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.