The core of auto-okf — a multi-writer storage substrate for
OKF
bundles. This package is transport-agnostic: it defines the op wire
contract, the deterministic apply that folds a linearized op stream into
a view, the view keyspace, and an in-memory view double. It never imports
autobee; @auto-okf/store supplies the substrate.
Encode ops, apply them, inspect the view:
import {
createApply,
encodeOp,
MemView,
keyspace as K
} from '@auto-okf/core'
const writer = /* 32-byte writer key */ Buffer.alloc(32, 1)
const W = writer.toString('hex')
const apply = createApply({ genesisKey: writer })
const view = new MemView()
// Every op carries `clock` (the writer's observed vector clock, including
// its own [key, seq] entry) and `at` (informational wall-clock ms).
const ops = [
encodeOp('create-concept', {
path: 'guides/getting-started',
type: 'guide',
clock: [[W, 0]],
at: Date.now()
}),
encodeOp('set-body', {
id: W + '0', // minted id = the creating op's tag (writerKey || seq hex)
base: null,
text: 'Read the [architecture](/guides/architecture) first.',
clock: [[W, 1]],
at: Date.now()
})
]
await apply(
ops.map((value, seq) => ({ key: writer, value, length: seq + 1 })),
view,
null // host: autobee's { addWriter, ackWriter } — null is fine off-substrate
)
const meta = await view.get(K.cMeta(W + '0'))
console.log(JSON.parse(meta.value)) // { type: 'guide', created: '…0' }
console.log(view.dump('wanted/')) // { 'wanted/guides%2Farchitecture/…': true }create-concept, set-field, add-tag, rem-tag, set-body,
set-path, delete-concept, undelete-concept, resolve-conflict,
add-writer, remove-writer, set-writer-policy.
The generated codec lives in spec/ (committed; append-only for the life
of a vault — rebuild with pnpm build:schema only to APPEND new ops).
decodeOp never throws: garbage decodes to { ok: false, reason } and
apply records it under flag/rejected/<writer>/<seq>.
tags: OR-set (SU-set semantics; concurrent add beats remove-of-unobserved; stale citations audit toflag/staletag).- every other frontmatter key: LWW-array register; cross-writer
supersessions audit to
flag/clobber. - bodies: immutable revisions keyed by
revHash; same-base siblings both survive underflag/conflictuntilresolve-conflict; fabricated bases can never displace real ancestry. _idmints from the creating op's tag (squat-proof fallback per §6);_revis derived output —set-fieldontags/_id/_revrejects.
set-path is an LWW register on the concept (renames merge with
concurrent edits). Path collisions — exact or casefolded — are first-wins
by apply ordinal, flagged flag/pathcollision, and self-heal when the
loser is renamed or the winner vacates. index/log terminals and
*.conflict-<hex8+> are reserved.
Genesis key is the first indexer. Governance ops require an indexer
issuer; revocation is causal (clock-based, never positional) with a
seq-at-removal cross-check; unacked writers hold up to 512 ops that fold
when an indexer's add-writer acks them; per-writer token buckets refill
on accepted indexer ops.
| export | what |
|---|---|
createApply(config) |
build the deterministic apply(nodes, view, host); config.genesisKey required |
encodeOp(type, payload) / decodeOp(buf) |
wire codec over spec/ |
makeTag(key, seq) / parseTag(tag) |
opId (writerKeyHex‖seqHex) helpers |
keyspace |
view keyspace codec (c/, path/, link/, wanted/, flag/, …) |
canonical |
NFC + canonical value/array forms |
revHash(base, text) |
body revision hash |
extractLinks(text, dir) |
the §9 normative markdown link extraction |
MemView |
hyperbee2-surface in-memory view for tests |
keyspace.js, canonical.js, revhash.js are copied from
@autovault-ld/core with provenance headers (copy-don't-depend, SPEC §0);
this package has no dependency on @autovault-ld/*.