ranke

package module
v0.33.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

README

ranke-go

Go reference implementation of the Ranke-Graph ADT (spec §4) — a content-addressed, provenance-carrying graph of attributed claims.

The project home, paper, and cross-language conformance suite live at github.com/rankegraph/ranke-graph. This repository is the Go module: the canonical library importable from downstream projects, and the reference other implementations are checked against.

Full API docs: pkg.go.dev/github.com/rankegraph/ranke-go.

Install

go get github.com/rankegraph/ranke-go

Quickstart

Build a small attributed graph and commit it to a branch over an in-memory store:

package main

import (
	"context"
	"fmt"

	"github.com/rankegraph/ranke-go"
	"github.com/rankegraph/ranke-go/adapter/mem"
)

func main() {
	ctx := context.Background()

	// A contributor is the root of attribution (identity-signed here;
	// pass a key via ClaimBuilder.Pubkey + .Sign(key) for real signing).
	aliceClaim, err := ranke.ClaimBuilder{
		Type:    ranke.NodeContributor,
		Content: []byte("alice@example.com"),
	}.Sign()
	if err != nil {
		panic(err)
	}
	alice, err := aliceClaim.AsContributor()
	if err != nil {
		panic(err)
	}

	// A source claim attributed to Alice.
	email, err := ranke.ClaimBuilder{
		Type:        ranke.TypeSource("email"),
		Encoding:    ranke.EncodingMessage("rfc822"),
		Content:     []byte("From: a\r\nTo: b\r\n\r\nhi\r\n"),
		Contributor: alice,
	}.Sign()
	if err != nil {
		panic(err)
	}

	g := ranke.NewGraph(alice)
	if err := g.Add(email); err != nil {
		panic(err)
	}

	// Compose an Archive (𝒰, B_h) and commit the graph to branch "main".
	arc, err := ranke.NewArchive(ctx, mem.New(), ranke.NewMemBranchTableHead())
	if err != nil {
		panic(err)
	}
	if err := arc.AddGraph(ctx, "main", g, alice); err != nil {
		panic(err)
	}

	// Read the head back and verify its provenance (§5.10).
	if err := arc.VerifyBranch(ctx, "main"); err != nil {
		panic(err)
	}
	br, _ := arc.GetBranch(ctx, "main")
	fmt.Println("main head:", br.Latest().Head())
}

Swap the backend by composing different parts — nothing else changes:

u, _ := fs.New("data/universe")                         // adapter/fs
bth, _ := ranke.NewFsBranchTableHead("data/B_h")
arc, _ := ranke.NewArchive(ctx, u, bth)

Architecture

The pieces compose explicitly — no per-backend factories, so the shape of a deployment is visible at the call site:

Piece Spec What it is
Claim §4.1–4.3 A node plus its edges, atomically created and immutable. Built with ClaimBuilder; its id is the signature over its canonical encoding.
Graph §4.4 A set of claims with provenance walking, validation, and consolidation.
Universe §4.5 Content-addressed store of claims and content bytes; no branches.
BranchTableHead §4.7 The single mutable id of the current branches claim.
Archive §4.8 The (𝒰, B_h) tuple — NewArchive(u, bth). Owns neither dependency, so Archives can share a Universe.

Persistence is adapter-shaped. The domain types are core; a Universe binding to a backing store is an adapter implemented strictly against the public API. Each lives in its own package:

  • adapter/mem — ephemeral, map-backed (mem.New()).
  • adapter/fs — flat-directory filesystem (fs.New(dir)).
  • downstream S3 / Neo4j / SQL satisfy the same interface.

Records are content-addressed with IPFS multihash (SHA-256) over CBOR Deterministic encoding (RFC 8949 §4.2). Serialization (Claim.Encode / DecodeClaim) and content integrity (VerifyContent) are storage-agnostic, so adapters move opaque bytes and never touch the internal representation.

Writing your own adapter? Implement ranke.Universe and delegate the cross-universe copy to adapter.DefaultCopyClaims / DefaultCopyContents.

Build & test

make            # run the tests
make build      # verify the library compiles
make test-verbose
make docs       # re-fetch the spec and papers into docs/papers/ (gitignored)
make verify     # build, gofmt, lint, citations, scenarios

make verify checks the rule ids comments cite — a backticked V-… or R-… — against the spec's own declarations, so it reads docs/papers/. That directory is fetched rather than committed, and verify brings it up to the ranke-graph ref first: one git ls-remote against the commit stamped in docs/papers/.ranke-graph-sha, cloning only when the ref has moved. A gate that cannot see the spec fails rather than passing, and so does one that cannot establish the copy's age — an expiring cache reads green against whatever it happens to hold. Working without the network is a deliberate ask: RANKE_DOCS_OFFLINE=1 keeps the copy on disk, and RANKE_SPEC points the citation gates at one of your own.

It also reproduces each conformance scenario and diffs the result against the committed bundle, which is what holds the claim ids in conformance/scenarios/*/data_reference/ to a value. That regenerates conformance/scenarios/*/data/ in your working tree — generated output that .gitignore covers and make clean removes. After an intentional change to a scenario or to anything that moves an id, promote the new bundle with make update-references, then read the diff and confirm each scenario still reports every claim valid.

The shared black-box suite in adapter/adaptertest runs against any Universe; adapter/fs and adapter/mem each wrap it, and fs adds medium-specific tests (corrupted/truncated files). End-to-end scenarios drawn from the paper live under conformance/scenarios/.

License

Apache 2.0 — see LICENSE.

Documentation

Overview

package: ranke / archive type: logic job: an immutable Ranke-Archive snapshot RA_k = (𝒰, k); reads claims and branches through the head claim, delegating closure ops to the Universe limits: advances nothing — writes go through the Sequencer (-> sequencer); closure traversal belongs to the Universe (-> universe)

package: ranke / bookmark type: crypto job: the bookmark record (`V-BMENV`) — a COSE_Sign1 over S([i, s, k]) that 𝒰_hist holds under id_seq(i, s) — plus id_seq itself, the minted seed, and the checks a fetched record is held to (`V-BMSIG`, `V-BMSLOT`, `V-BMREF`) limits: stores nothing; the store is bookmark_store.go's and the list its searches walk is bookmarks.go's (-> bookmarks)

package: ranke / bookmark_locator type: logic job: BookmarkLocator — which bookmark list to open, given as a seed or as the id of one entry, so the seed is settled before any cursor over the list exists limits: names a list and resolves it; the cursor and its searches are bookmarks.go's (-> bookmarks, bookmark_store)

package: ranke / bookmark_store type: io job: the 𝒰_hist interface — bookmark records by id_seq(i, s) — with the in-process reference implementation and the refusal a backend holding no 𝒰_hist hands out limits: opaque bytes by key and nothing else; the record's shape and its rules are bookmark.go's, the list over a store bookmarks.go's (-> bookmark, bookmarks)

package: ranke / bookmarks type: logic job: Bookmarks — one bookmark list over 𝒰_hist: the writer's Append at the next free index and the O(log n) searches that find a list's range from any one of its entries limits: holds no signing key (Append takes the contributor per call) and no store of its own; the record and its rules are bookmark.go's (-> bookmark, bookmark_store)

package: ranke / claim type: logic job: the Claim type and the concrete claim with its methods limits: the Contributor extension lives in claim_type_contributor.go; construction/signing in claim_builder.go; helpers in claim_helpers.go; codec in codec.go

package: ranke / claim type: logic job: AssembleClaim — rebuild a Claim from its parsed components + id, without CBOR and without signing (the field-oriented sibling of DecodeClaim, for non-CBOR cache backends) limits: reconstructs a cache view, not a self-verifiable claim — it has no canonical bytes, so it cannot be structurally verified; a cache is checked by comparison to the authoritative layer (-> verify.go)

package: ranke / claim type: logic job: ClaimBuilder — assembles, validates, attributes, and signs a claim into an immutable Claim limits: pure construction helpers live in claim_helpers.go; signing primitives in sign.go

package: ranke / claim type: logic job: pure helpers for claim construction — type parsing, pubkey resolution, edge checks limits: no I/O or signing; the builder that calls them is in claim_builder.go

package: ranke / claim_type_branch type: logic job: the Branch view over a contribution/branch edge — a named pointer into an archive's branch table — and the form a branch name takes limits: the branch-table logic (materialising the diff chain) lives in the Archive (-> archive); a Branch only navigates the subgraph its edge references

package: ranke / claim_type_contributor type: logic job: the Contributor extension of Claim — a contribution/contributor claim, plus a session-scoped signing key limits: the base Claim + concrete claim live in claim.go; construction/signing in claim_builder.go

package: ranke / codec type: io job: canonical CBOR (de)serialization of the claim record — S(v) with its edges inlined, which is what an envelope carries as payload, and what DecodeClaim reads back out of one limits: persists nothing (-> universe, adapter); the envelope around the record, and the signature over it, are codec_envelope.go's; content integrity lives in content.go

package: ranke / codec_envelope type: crypto job: the claim envelope (`V-ENV`) — a COSE_Sign1 over S(v), which the Universe stores under id(v) = H(S(env(v))) limits: the record it wraps is codec.go's, and this says nothing about its shape; resolves no contributor key (-> verify)

package: ranke / codec_wire type: io job: the contribution codec — a CBOR sequence (RFC 8742) opening with the branches it touches, then claims under their id and externalized content under its hash limits: the record level only; the claim and node records it carries are codec.go's, and admitting them into an archive is the Sequencer's (-> sequencer, contribution)

package: ranke / contribution type: logic job: the contribution contract — the staged → verified → mergable advance of a Ranke-Archive (RA_k → RA_k') limits: the contract plus the shared wire drain; opening, verifying and merging a contribution are a Sequencer's (-> adapter/sequencer/dev, adapter/sequencer/concurrent)

package: ranke / deletion type: logic job: the planned-deletion sweep — remove the bytes of every claim whose delete_by has fallen due, leaving the gap the citing edges already explain (`R-DPLANNED`) limits: the planned form only; a requested deletion is the Sequencer's to carry out (`R-DREQUEST`). Removes bytes through the Universe port and rewrites no id

package: ranke / edge type: data job: the Edge directed-reference type, its filters, and the closed edge type vocabulary limits: does not build claims (-> claim) or serialize edges (-> codec)

package: ranke / taxonomy type: logic job: the edge-type vocabulary (§4.8) — the closed class set, the subtypes the ADT defines, and their compact aliases — with enumeration/validation helpers limits: vocabulary only; edge construction and matching live elsewhere (-> edge, filter)

package: ranke / taxonomy type: logic job: EDTF Level 1 parsing for `dated` (`V-DATED`), including its own date-and-time form, and the millisecond span/midpoint `R-QTEMPORAL`'s `compare: temporal` reads off it — an instant shares the same axis as a zero-width span limits: Level 1 only: no sets, no individual-component qualification, no exponential years

package: ranke / taxonomy type: logic job: the encoding (MIME media-type) vocabulary — the closed top-level class set with compact aliases, well-known subtype aliases with two-way resolution, the class constructors, and named constants for the popular media types limits: vocabulary + alias resolution only; the codec applies the aliases into the canonical bytes and the node holds the value (-> codec, node)

package: ranke / errors type: data job: centralized package error sentinels — one static error per fixed condition limits: no fmt.Errorf anywhere in the package — dynamic errors compose via wrap/withDetail/wrapDetail over these sentinels

package: ranke / taxonomy type: logic job: field-name/value size caps and the well-known field-name constants, enforced at claim/edge construction limits: reference-impl caps (the paper leaves them open); the codec/verifier never reject an already-stored record (-> claim_builder)

package: ranke / filter type: logic job: the Filter contract and the built-in edge filters (by field value, by type) that Claim.Edges applies, AND-combined limits: selection only; edge and claim construction live elsewhere (-> edge, claim)

package: ranke / graph type: logic job: a Ranke-Graph handle RG ⊆ 𝒰 — stages claims into a Universe under the atomic-creation rule, tracks open heads, consolidates limits: claims live in the Universe (-> universe); does not bind graphs to branches (-> archive); verification lives in verify.go

package: ranke / id type: crypto job: the content-addressed Id type — a multihash with parsing and equality limits: does not sign or verify ids (-> sign); does not verify content bytes (-> content)

package: ranke / node type: data job: the Node structural component of a claim, plus the closed node/encoding type vocabularies limits: does not build nodes (-> claim) or serialize them (-> serialize)

package: ranke / taxonomy type: logic job: the node-type vocabulary (§4.8) — the closed class set, the subtypes the ADT defines, and their compact aliases — with enumeration/validation helpers limits: vocabulary only; node construction and content live elsewhere (-> node)

package: ranke / query type: data job: the declarative read AST (RQL — the paper's §Filtered Reads) and its result/stream shapes limits: types only; the reference executor is DefaultQuery (-> query_default.go)

package: ranke / query_codec type: io job: the RQL wire codec — a Query to and from the canonical JSON that ranke-graph's rql.schema.json fixes, plus the shape checks a query must pass before an engine sees it limits: shape only; which claims a read returns is the executor's (-> query_default)

package: ranke / query type: logic job: `R-QCONTENT` — how many bytes of inline content an encoded claim carries, read off Output.Content and spent by the codec across the claim's content sequence limits: inline content only, per §Content the bytes a record holds; external content stays in the Universe, which is what lets content in full be S(v) (`R-QCANON`)

package: ranke / query type: logic job: DefaultQuery — the reference RQL executor a byte-store Universe delegates to: a forward-closure walk (reverse via closure inversion) with filter/order/limit/shape limits: performance-ignorant; a graph-native backend overrides with a native lowering (-> adapter/storage/neo4j)

package: ranke / query type: logic job: fill QueryResult's Encoded fields per Output.Detail and Encoding — the serialized claim as CBOR or JSON, or the stored envelope copied out under its own tag limits: serialisation only; which claims a read returns is the executor's (-> query_default, adapter/storage/neo4j)

package: ranke / query_report type: logic job: the RQL execution report — a structured event log a query collects across the call chain when Execution.Report is set limits: a passive collector carried in ctx; a nil collector (report off) makes every log a no-op

package: ranke / query walk type: logic job: the reference RQL traversal — walk a Select's Path from its root (forward, and reverse via a built closure-inversion index), returning the reached set and each claim's canonical route limits: reachability only; filter/order/limit/shape live in query_default.go. Route ties break on the (created_at, id) total order so a path shape is byte-identical to a Cypher lowering

package: ranke / sequencer type: logic job: the Sequencer contract — the sole writer of a Ranke-Archive: hands out read snapshots and advances the head by merging contributions limits: interfaces only; the naive concrete implementation is a write-path mechanism (-> adapter/sequencer_mechanism)

package: ranke / sign type: crypto job: multikey pubkey framing (`V-SIGN`) plus Ed25519 key encoding and PEM loading limits: signs and verifies nothing — a claim's signature lives in its envelope (-> envelope); supports only Ed25519 today

package: ranke / spine_item type: data job: one revision of the branch-table spine for timeline tracking limits: a value type over U, unrelated to the bookmark list that locates a moving head (-> bookmarks); the walk that builds it is universe_default_tagger.go's

package: ranke / taxonomy type: logic job: `V-TIME` for the optional timestamp fields — delete_by, pubkey_valid_from, pubkey_expires_after — refused wherever a claim arrives, by decode and by assemble; created_at is a record slot decodeNode already parses, and FormatTimestamp writes the form limits: no verifyRules entry, since the closure verifier decodes every claim it walks and would reach a rule that can never fire; an ABSENT field is no violation, only a present unparsable one

package: ranke / universe type: io job: the 𝒰 interface — a content-addressed bag of claims and content with bulk get/put/has, copy, and single-item wrappers limits: defines the contract only; concrete backends live under adapter/; does no validation (-> graph, content)

package: ranke / universe_default type: logic job: the reference Default* implementations a Universe delegates to — diff materialisation, closure membership/lookup, and copy walkers — all in terms of the public Universe API limits: called by Universe implementations (byte-store adapters delegate here; a graph-native backend may override with native queries); does no storage of its own

package: ranke / tagger type: logic job: TagArchive — descend an archive's branch-table spine and stamp every branch's closure with each member claim's .node.Height() limits: optional (gated by Capabilities.Tags); tags are read off the claims (GetClaims injects them) and written through the Universe's bulk SetClaimsTags — this file holds no backend I/O of its own

package: ranke / universe_memory type: io job: NewMemoryUniverse — a naive in-process Universe storing canonical CBOR in maps, decoded on read: the Graph's nil-Universe fallback and the reference Universe for tests limits: ephemeral; correctness over speed (decodes per read, no caching); closure/copy via the Default* helpers; persistent/graph-native backends live under adapter/

package: ranke / taxonomy type: logic job: list of field names and special values (taxonomy) in context of Universe (storage layer) limits: names only; the tagger logic lives in universe_default_tagger.go,

package: ranke / verify type: logic job: the configurable closure verifier — §5.10 per-claim integrity + authenticity over a graph, archive, or branch closure, as a live progress run limits: does not fetch content bytes unless asked (WithExternalContent); does not persist or advance anything (-> universe, sequencer)

package: ranke / content type: crypto job: storage-agnostic content integrity (§5.10) — verify bytes against a hash+size, whole or streamed limits: does not store or fetch content (-> universe); does not hash claim records (-> hash)

package: ranke / verify type: logic job: `R-DEXPIRY`'s second sentence — the contribution/expiry edge that moves the end of a contributor's key window, resolved once per contributor per run limits: finds the edge, never applies the window (-> verify for the comparison)

package: ranke / verify type: logic job: the per-claim rules re-derived from a claim's direct references — `V-HEIGHT` height and `V-MONO` created_at, which the closure walk makes transitive limits: judges one claim against its references only (-> verify for the walk and the registry)

package: ranke / verify type: logic job: the live handle on a verification — a Failure list and a progress count safe to read while the closure walk runs, plus the Wait that blocks until it is done limits: holds no rules and walks nothing; the walk and the rule registry are verify.go's

package: ranke / verify type: logic job: the per-claim rules read off a claim's own shape — `V-TYPE` type classes, `V-REL` relation_direction, `V-EORDER` the inlined edge order, `R-DREQUEST` a delete mark's target, `V-ARCHIVEHEIGHT` the initial branch table's height limits: needs no Universe read and no reference resolution, so a record that arrived as bytes is judged as readily as one this library built

Index

Constants

Closed contribution/* edge type strings, for EdgeConfig.Type — each combined from its class and subtype constants. Branch and Prune are edge-only — no claim counterpart.

View Source
const (
	EncodingJSON        = "application/json"
	EncodingJSONLD      = "application/ld+json"
	EncodingXML         = "application/xml"
	EncodingXHTML       = "application/xhtml+xml"
	EncodingPDF         = "application/pdf"
	EncodingOctetStream = "application/octet-stream"
	EncodingWebManifest = "application/manifest+json"
	EncodingRTF         = "application/rtf"

	// Archives / compression.
	EncodingZIP   = "application/zip"
	EncodingGZIP  = "application/gzip"
	EncodingTAR   = "application/x-tar"
	EncodingBzip2 = "application/x-bzip2"
	Encoding7Z    = "application/x-7z-compressed"
	EncodingRAR   = "application/vnd.rar"
	EncodingJAR   = "application/java-archive"
	EncodingEPUB  = "application/epub+zip"

	// Office / OpenDocument.
	EncodingMSWord       = "application/msword"
	EncodingMSExcel      = "application/vnd.ms-excel"
	EncodingMSPowerPoint = "application/vnd.ms-powerpoint"
	EncodingDOCX         = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
	EncodingXLSX         = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
	EncodingPPTX         = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
	EncodingODT          = "application/vnd.oasis.opendocument.text"
	EncodingODS          = "application/vnd.oasis.opendocument.spreadsheet"
	EncodingODP          = "application/vnd.oasis.opendocument.presentation"
)

Application media types (application/*): structured data, documents, archives.

View Source
const (
	EncodingPlain      = "text/plain"
	EncodingHTML       = "text/html"
	EncodingCSS        = "text/css"
	EncodingJavaScript = "text/javascript"
	EncodingCSV        = "text/csv"
	EncodingMarkdown   = "text/markdown"
	EncodingCalendar   = "text/calendar"
)

Text media types (text/*).

View Source
const (
	EncodingPNG  = "image/png"
	EncodingAPNG = "image/apng"
	EncodingJPEG = "image/jpeg"
	EncodingGIF  = "image/gif"
	EncodingWebP = "image/webp"
	EncodingAVIF = "image/avif"
	EncodingSVG  = "image/svg+xml"
	EncodingBMP  = "image/bmp"
	EncodingTIFF = "image/tiff"
	EncodingICO  = "image/vnd.microsoft.icon"
)

Image media types (image/*).

View Source
const (
	EncodingMP3       = "audio/mpeg"
	EncodingAAC       = "audio/aac"
	EncodingWAV       = "audio/wav"
	EncodingOggAudio  = "audio/ogg"
	EncodingWebmAudio = "audio/webm"
	EncodingMIDI      = "audio/midi"
)

Audio media types (audio/*).

View Source
const (
	EncodingMP4       = "video/mp4"
	EncodingMPEG      = "video/mpeg"
	EncodingWebmVideo = "video/webm"
	EncodingOggVideo  = "video/ogg"
	EncodingAVI       = "video/x-msvideo"
	EncodingMP2T      = "video/mp2t"
	Encoding3GP       = "video/3gpp"
)

Video media types (video/*).

View Source
const (
	EncodingWOFF  = "font/woff"
	EncodingWOFF2 = "font/woff2"
	EncodingTTF   = "font/ttf"
	EncodingOTF   = "font/otf"
)

Font media types (font/*).

View Source
const (
	FieldName             = "name"
	FieldNameAlias        = "n"
	FieldEdges            = "edges"
	FieldEdgesAlias       = "e"
	FieldContent          = "content"
	FieldContentAlias     = "c"
	FieldContentSize      = "content_size"
	FieldContentSizeAlias = "s"
	FieldContentHash      = "content_hash"
	FieldContentHashAlias = "h"
	FieldHeight           = "height"
	FieldHeightAlias      = "H"
	// FieldEdgesDiffOmit lists, on a diff claim, names of inherited edges to drop when
	// materialising, one per line — only named edges inherit. Overwrite/add is
	// re-stating the edge.
	FieldEdgesDiffOmit      = "edges_diff_omit"
	FieldEdgesDiffOmitAlias = "E"
	// FieldFieldsDiffOmit is the node-field analogue: newline-separated names.
	FieldFieldsDiffOmit      = "fields_diff_omit"
	FieldFieldsDiffOmitAlias = "F"
	// FieldPubkeyValidFrom and FieldPubkeyExpiresAfter bound a contributor key's
	// validity (RFC 3339, paper 2 §Contributor Keys): a claim it signed is dated
	// within the closed window they describe. Either may stand alone.
	FieldPubkeyValidFrom         = "pubkey_valid_from"
	FieldPubkeyValidFromAlias    = "v"
	FieldPubkeyExpiresAfter      = "pubkey_expires_after"
	FieldPubkeyExpiresAfterAlias = "x"
	// FieldDeleteBy schedules a claim's bytes for removal (RFC 3339, paper 2
	// §Deletion). Every edge referencing such a claim carries the date too, so the
	// schedule travels with the reference and explains the gap the deletion leaves.
	FieldDeleteBy      = "delete_by"
	FieldDeleteByAlias = "d"
)

Aliases are bare; the codec adds the reserved "." prefix on the wire.

View Source
const (
	NodeContributor = string(NodeClassContribution) + "/contributor"
	NodeHead        = string(NodeClassContribution) + "/head"
	NodeBranches    = string(NodeClassContribution) + "/branches"
	// NodeDelete and NodeExpiry are limiting claims: each names a target claim it
	// restricts (paper 2 §Deletion, §Key rotation).
	NodeDelete = string(NodeClassContribution) + "/delete"
	NodeExpiry = string(NodeClassContribution) + "/expiry"
)

Closed contribution/* node type strings, for ClaimBuilder.Type.

Closed contribution/* node type strings, for ClaimBuilder.Type.

View Source
const (
	// BranchUniverse is unconfined, privileged access rooted at Select.Claim,
	// which it therefore requires.
	BranchUniverse = "$universe"
	// BranchArchive confines to the whole Ranke-Archive: the closure of the
	// branch-table header. Select.Claim defaults to the current archive head.
	BranchArchive = "$archive"
	// TargetBranches names the branch table itself (§Access), the surface a grant over
	// branch creation is held against. It scopes no read, so Select.Branch rejects it.
	TargetBranches = "$branches"
)

Reserved virtual branch scopes for Select.Branch (the $-target family, spec §Access); a real branch name confines to that branch's closure.

View Source
const (
	// SpineRevKey records the spine revision a branch-table claim sits at — the
	// commit marker written last per revision.
	SpineRevKey = "_br"
	// ArchiveTagKey marks a claim as a member of the archive's closure, which covers
	// the branches and the spine — where the operator signing the tables sits.
	ArchiveTagKey = "_ba"
)
View Source
const ReservedPrefix = "_"

ReservedPrefix marks the library-owned property namespace: any node/edge property key beginning with "_" is reserved (a tag or a codec flag), never a user field. A backend that surfaces properties (e.g. neo4j) uses it to separate reserved keys from claim fields.

View Source
const WireMediaType = "application/cbor-seq"

WireMediaType is the Content-Type a contribution stream is served under: a CBOR sequence is self-framing, so records concatenate with no envelope.

Variables

View Source
var (
	// --- Universe / storage (exported API) ---
	ErrNotFound  = errors.New("ranke: not found")
	ErrIntegrity = errors.New("ranke: integrity check failed")
	ErrClosed    = errors.New("ranke: closed")
	// ErrUnsupported: the backend does not support this operation (e.g. an
	// opaque byte store asked to tag). Callers gate on the relevant Capability.
	ErrUnsupported = errors.New("ranke: operation not supported by this backend")
	// ErrContentCapped: a layer holds a claim/reference but its stored content
	// is shorter than the expected content_size — capped or truncated (e.g. a
	// cache filled under a smaller cap in a previous run). A stack treats it
	// like a miss and descends to a layer that holds the full bytes.
	ErrContentCapped = errors.New("ranke: content capped (stored shorter than content_size)")
	// ErrBranchNotFound: the archive holds no branch of that name. Also matches
	// ErrNotFound, so a caller may read it either way.
	ErrBranchNotFound = errors.New("ranke.Archive.GetBranch: branch not found")
	// ErrSequencerGenesis: the bookmark list holds no entry, so this archive does
	// not exist yet and every operation on it waits for Found. A caller branches on
	// Sequencer.InGenesis rather than on this error.
	ErrSequencerGenesis = errors.New("ranke.Sequencer: the archive awaits its first contributor (Found)")

	ErrQueryNoHead    = errors.New("ranke.Query: Select.Head is required under $universe (it has no natural head to scope by)")
	ErrQueryNoScope   = errors.New("ranke.Query: Select.Branch is required (scope is mandatory — use BranchUniverse for an unconfined read)")
	ErrQueryScanShape = errors.New("ranke.Query: a scan (no Select.Path) reaches claims by no stated route, so Output.Shape must be single")
	ErrQueryEncoding  = errors.New("ranke.Query: unknown Output.Encoding (native | json | cbor)")

	ErrQueryWhereForm      = errors.New("ranke.Query: a where node is exactly one of and | or | not | {field, test}")
	ErrQueryTimeOperand    = errors.New("ranke.Query: a comparison on a time field takes a `V-TIME` timestamp or an EDTF Level 1 value (`R-QTIMEOP`)")
	ErrQueryComparisonForm = errors.New("ranke.Query: a comparison applies exactly one operator (eq | ne | lt | le | gt | ge | in | glob)")
	ErrQueryHops           = errors.New("ranke.Query: a PathStep's hop bounds admit no count")
	ErrQueryEnum           = errors.New("ranke.Query: value outside the set the schema fixes for its field")
	ErrQueryBounds         = errors.New("ranke.Query: value below the minimum the schema fixes for its field")
	ErrQueryEnvelopeAxis   = errors.New("ranke.Query: detail envelope returns the stored bytes, which this axis would have to change (`R-QDETAIL`)")
	ErrQueryOrderField     = errors.New("ranke.Query: a sort key names the field it orders on")
	ErrQueryLayerName      = errors.New("ranke.Query: execution.layer names a layer, so a stated one may not be empty")
	ErrReservedType        = errors.New("ranke.Contribution: node type is the Sequencer's alone (lift it to add one)")
	ErrFutureDated         = errors.New("ranke.Contribution: claim is dated after the base the contribution opened against")
	ErrBranchNotCreatable  = errors.New("ranke.Contribution: branch is absent from the base, and creating one is a right of its own")
	ErrBranchName          = errors.New("ranke: a branch name is at most 128 bytes over [a-z0-9_] with no leading _")
	ErrUnreadableReference = errors.New("ranke.Contribution: reference reaches a claim outside the branches this contribution may read")

	ErrWire               = errors.New("ranke.Wire")
	ErrWireKind           = errors.New("ranke.Wire: unknown record kind (0 claim | 1 content | 2 branches | 3 referencable | 4 lifted)")
	ErrWireNoBranch       = errors.New("ranke.Wire: a claim record must name the branch it joins")
	ErrWireNoHeader       = errors.New("ranke.Wire: a stream opens with the branches it writes to and the branches it may reference from")
	ErrWireLateConstraint = errors.New("ranke.Wire: every constraint record precedes the payload")
	ErrWireUndeclared     = errors.New("ranke.Wire: claim names a branch the header does not declare")

	ErrKeyNotYetValid        = errors.New("ranke.verify: claim is dated before its contributor key's pubkey_valid_from")
	ErrKeyExpired            = errors.New("ranke.verify: claim is dated after its contributor key's pubkey_expires_after")
	ErrKeyWindowField        = errors.New("ranke.verify: contributor key validity bound is not RFC 3339")
	ErrContributorUnresolved = errors.New("ranke.verify: contributor claim unresolved")

	ErrDeleteByNotCopied     = errors.New("ranke.verify: an edge must carry exactly the delete_by its referenced claim declares")
	ErrStructureNotDeletable = errors.New("ranke: a contribution/* claim carries the graph's structure and its own identity, so it takes no delete_by")
	ErrHeightMismatch        = errors.New("ranke.verify: claim height ≠ 1 + max(reference heights)")
	ErrCreatedAtNotMonotone  = errors.New("ranke.verify: claim is dated before a claim it references")

	ErrRefsBranchTable = errors.New("ranke.verify: claim references a branch table")

	ErrKeyEncrypted = errors.New("ranke: the key is encrypted, and decrypting one is the caller's (openssl pkcs8 -topk8 -nocrypt)")
	ErrKeyFormat    = errors.New("ranke: the key is not a PKCS#8 PEM (openssl pkcs8 -topk8 converts one)")

	// --- ADT shape, checked wherever a claim arrives rather than at the builder
	// alone: a record decoded or assembled meets these too.
	ErrContentBothSlots  = errors.New("ranke: a record carries both content and content_hash, which are mutually exclusive")
	ErrIDMismatch        = errors.New("ranke.verify: the claim's id is not the hash of the envelope it is stored as")
	ErrEnvelopeHeaders   = errors.New("ranke: an envelope carries the alg parameter alone, protected, and an empty unprotected header (`V-ENV`)")
	ErrEdgeOrder         = errors.New("ranke.verify: a claim's edges are inlined ascending by id(e) (`V-EORDER`)")
	ErrUnknownTypeClass  = errors.New("ranke.verify: type class is not one of the fixed set")
	ErrRelationDirection = errors.New("ranke.verify: a relation/* edge carries relation_direction 1 or -1, an edge of any other class 0")
	// ErrDeleteMarkNoTarget: a mark that names nothing explains no gap (`R-DGAP`).
	ErrDeleteMarkNoTarget = errors.New("ranke.verify: a contribution/delete claim must carry a contribution/delete edge naming its target")
	ErrTimestampForm      = errors.New("ranke: a timestamp must be RFC 3339, UTC, at nanosecond precision (2026-01-05T12:00:00.000000000Z)")
	ErrDatedForm          = errors.New("ranke: dated must be an RFC 3339 timestamp or a valid EDTF Level 1 value (`V-DATED`)")
	// ErrArchiveFirstTableHeight: the archive's initial branch table stands on its
	// contributor edge alone, so height 1 is the only value that re-derives.
	ErrArchiveFirstTableHeight = errors.New("ranke.verify: an archive's first branch-table claim must have height 1 (`V-ARCHIVEHEIGHT`)")

	// ErrUnexplainedGap: a claim's bytes are missing and nothing explains the gap —
	// no copied delete_by on the edge reaching it, no contribution/delete mark against
	// it. Indistinguishable from data loss, which is why it fails.
	ErrUnexplainedGap = errors.New("ranke.verify: a missing claim with no explained gap (no copied delete_by, no contribution/delete mark)")
)

EdgeClasses lists every edge class, for validation and enumeration.

Functions

func AdmitCreatedAt

func AdmitCreatedAt(c Claim, base time.Time) error

AdmitCreatedAt applies step 2's timestamp rule: a claim is dated at or before the base time t (§Timestamping). Both Sequencers call it, so the rule reads one way.

func BranchTagKey

func BranchTagKey(branch string) string

BranchTagKey marks a claim as a member of branch b's closure, valued with the branch table's height at the revision it entered.

func CheckBookmarkHead added in v0.27.0

func CheckBookmarkHead(ctx context.Context, u Universe, bm Bookmark) error

CheckBookmarkHead holds a bookmark's k to `V-BMREF`: it resolves to a contribution/branches claim. One 𝒰 read, which is why a fetch leaves it to the explicit verification a list offers (-> Bookmarks.Verify).

func CheckDeletable

func CheckDeletable(class NodeClass, sub string, fields map[string]string) error

CheckDeletable reports whether a claim of this type may schedule its own removal (`R-DSTRUCT`). Four subtypes may not, each being what another rule reads: a contributor's pubkey (`V-SIG`), the chain to the initial table (`V-ARCHIVE`), a gap's explanation (`R-DGAP`), a key's window (`R-DEXPIRY`). Subtypes beyond them are open vocabulary (`V-TYPE`), so any other claim MAY.

func DecodePublicKey

func DecodePublicKey(b []byte) (multicodec.Code, crypto.PublicKey, error)

DecodePublicKey parses a multikey into its scheme code and typed Go key.

func DefaultClaimsInBranches

func DefaultClaimsInBranches(ctx context.Context, u Universe, branches map[string]Id, ids []Id) ([]bool, error)

DefaultClaimsInBranches walks each closure once, marking what it reaches — slow, and correct wherever a layer indexes nothing of its own.

func DefaultCopyClaims

func DefaultCopyClaims(ctx context.Context, dst, src Universe, ids []Id, opts ...CopyOption) error

DefaultCopyClaims is the reference CopyClaims, walking claim-by-claim through the single-item helpers. DiscoveryComplete flips true once the walk drains.

func DefaultCopyContents

func DefaultCopyContents(ctx context.Context, dst, src Universe, refs []ContentRef, opts ...CopyOption) error

DefaultCopyContents is the reference CopyContents, copying blob-by-blob and skipping what the receiver holds. Honours WithProgress alone.

func DefaultGetClaimHeights

func DefaultGetClaimHeights(ctx context.Context, u Universe, ids []Id) ([]uint64, error)

DefaultGetClaimHeights reads each committed height positionally, from claims loaded in delta form — height is a claim's own field (§4.1).

func DefaultSync

func DefaultSync(ctx context.Context, dst, src Universe, id Id) <-chan SyncResult

DefaultSync fills dst for id's closure from src, claims and content, fetching only the gap. A nil src leaves dst as it stands.

func DefaultTag

func DefaultTag(ctx context.Context, u Universe, head Id) error

DefaultTag is the reference implementation of Tag: walk the branch-table spine and stamp membership claim by claim.

func DrainWire

func DrainWire(ctx context.Context, c Contribution, u Universe, wr *WireReader) error

DrainWire fills c as records arrive: content lands in u under its hash, each claim stages under its branch, and an undeclared branch stops the fill.

func EncodePublicKey

func EncodePublicKey(pub crypto.PublicKey) ([]byte, error)

EncodePublicKey wraps a Go public key as a multikey, the framing `V-SIGN` fixes: <multicodec varint naming the scheme><raw key bytes>.

func EncodeQuery

func EncodeQuery(q Query) ([]byte, error)

EncodeQuery renders a query as canonical JSON, omitting every zero-valued field, so DecodeQuery returns the query it started as.

func EncodeResults

func EncodeResults(results []QueryResult, out Output) error

EncodeResults fills each result's ClaimEncoded/PathEncoded per out.Encoding, in out.Form, inlining the content out.Content allows (`R-QCONTENT`). Native asks for the Go objects, which the executor already set and which keep their content whole — an in-process caller holds the claim itself, so a cap would only cost it the bytes.

func EncodingApplication

func EncodingApplication(sub string) string

EncodingApplication returns the "application/<sub>" media type.

func EncodingAudio

func EncodingAudio(sub string) string

EncodingAudio returns the "audio/<sub>" media type.

func EncodingExample

func EncodingExample(sub string) string

EncodingExample returns the "example/<sub>" media type.

func EncodingFont

func EncodingFont(sub string) string

EncodingFont returns the "font/<sub>" media type.

func EncodingImage

func EncodingImage(sub string) string

EncodingImage returns the "image/<sub>" media type.

func EncodingMessage

func EncodingMessage(sub string) string

EncodingMessage returns the "message/<sub>" media type.

func EncodingModel

func EncodingModel(sub string) string

EncodingModel returns the "model/<sub>" media type.

func EncodingMultipart

func EncodingMultipart(sub string) string

EncodingMultipart returns the "multipart/<sub>" media type.

func EncodingText

func EncodingText(sub string) string

EncodingText returns the "text/<sub>" media type.

func EncodingVideo

func EncodingVideo(sub string) string

EncodingVideo returns the "video/<sub>" media type.

func FormatTimestamp added in v0.28.0

func FormatTimestamp(t time.Time) string

FormatTimestamp renders t in `V-TIME` form: RFC 3339, UTC, fixed-width nanoseconds — the one spelling a time comparison takes (`R-QTIMEOP`).

func GetClaimHeight

func GetClaimHeight(ctx context.Context, u Universe, id Id) (uint64, error)

GetClaimHeight is the single-item form of Universe.GetClaimHeights.

func GetTag

func GetTag(ctx context.Context, u Universe, id Id, tag string) (found bool, value string, err error)

GetTag reports whether tag is set on the claim at id, and its value.

func HasClaim

func HasClaim(ctx context.Context, u Universe, id Id) (bool, error)

HasClaim is the single-item form of Universe.HasClaims.

func HasContent

func HasContent(ctx context.Context, u Universe, id Id) (bool, error)

HasContent is the single-item form of Universe.HasContents.

func HeightOf

func HeightOf(refs ...Claim) uint64

HeightOf returns the generation number a new claim referencing refs must carry: 1 + max(refs' heights), or 0 for an initial claim. Pass every claim the new one references — contributor and predecessor edges count too.

func Hops

func Hops(n int) *int

Hops is a PathStep.Min value; Hops(0) admits the step's start itself.

func InClosure

func InClosure(ctx context.Context, u Universe, branch string, heads []Id, id Id) (bool, error)

InClosure reports whether id is reachable within scope branch from any of heads — GetFromClosure without materialising the claim.

func IsEncryptedKey added in v0.32.0

func IsEncryptedKey(pemBytes []byte) bool

IsEncryptedKey reports whether the PEM holds an encrypted private key, which is what lets a caller ask for a passphrase only when one is wanted.

func IsTextEncoding

func IsTextEncoding(encoding string) bool

IsTextEncoding reports whether a media type carries human-legible text — text/*, message/* (e.g. rfc822), and structured-text application types (application/json, application/xml, and any +json / +xml suffix). Binary types (image/audio/video/font, application/octet-stream, …) return false. An empty encoding is treated as non-text. Used to decide whether content is worth inlining as readable text vs left to a byte store.

func LoadEd25519PrivateKeyPEM

func LoadEd25519PrivateKeyPEM(path string, opts ...KeyOption) (ed25519.PrivateKey, error)

LoadEd25519PrivateKeyPEM is ParseEd25519PrivateKeyPEM over the file at path.

func LoadEd25519PublicKeyPEM

func LoadEd25519PublicKeyPEM(path string) (ed25519.PublicKey, error)

LoadEd25519PublicKeyPEM is ParseEd25519PublicKeyPEM over the file at path.

func MarshalCBOR

func MarshalCBOR(v any) ([]byte, error)

MarshalCBOR returns v in CBOR Deterministic Encoding (RFC 8949 §4.2), for a payload other than a claim: a result id, a route of ids, an execution report. A claim's own bytes come from Claim.EncodeCBOR. One encoder serves the whole system, since a second would be a second answer about byte order.

func MintSeed added in v0.28.0

func MintSeed() ([]byte, error)

MintSeed returns a fresh bookmark seed: seedBytes of crypto/rand, distinct from every other list's. Whoever founds a list mints its seed and keeps it — the value arrives at a Bookmarks already made (-> Seed), so nothing inside mints.

func NewVerifyingReader

func NewVerifyingReader(src io.ReadCloser, hash Id, size uint64) (io.ReadCloser, error)

NewVerifyingReader wraps src so reading to EOF also verifies the stream (§5.10): the final Read answers an integrity error rather than a clean io.EOF.

func ParseEd25519PrivateKeyPEM added in v0.31.0

func ParseEd25519PrivateKeyPEM(pemBytes []byte, opts ...KeyOption) (ed25519.PrivateKey, error)

ParseEd25519PrivateKeyPEM reads an Ed25519 private key from a PKCS#8 PEM block (`openssl genpkey -algorithm ed25519`).

func ParseEd25519PublicKeyPEM added in v0.31.0

func ParseEd25519PublicKeyPEM(pemBytes []byte) (ed25519.PublicKey, error)

ParseEd25519PublicKeyPEM reads an Ed25519 public key from a SubjectPublicKeyInfo PEM block (`openssl pkey -pubout`).

func PutClaim

func PutClaim(ctx context.Context, u Universe, c Claim) error

PutClaim is the single-item form of Universe.PutClaims.

func PutContent

func PutContent(ctx context.Context, u Universe, id Id, content []byte) error

PutContent is the single-item form of Universe.PutContents.

func ReportEnabled

func ReportEnabled(ctx context.Context, level ReportLevel) bool

ReportEnabled reports whether ctx is collecting events at level — the guard a backend uses before building expensive report detail.

func ReportEvent

func ReportEvent(ctx context.Context, engine, op string, level ReportLevel, detail string, attrs map[string]any)

ReportEvent logs one event into the report on ctx (no-op if off) — the hook a router or engine uses to record its part of a query.

func SignBookmark added in v0.27.0

func SignBookmark(self Contributor, index uint64, seed []byte, head Id) ([]byte, error)

SignBookmark returns the record 𝒰_hist holds at id_seq(index, seed): S([i, s, k]) signed under self, whose id the protected header carries as kid (`V-BMENV`, `V-BMSIG`).

func SyncedNow

func SyncedNow(id Id) <-chan SyncResult

SyncedNow returns a closed channel with an immediate success.

func TemporalMidpointMs

func TemporalMidpointMs(s string) (int64, bool)

TemporalMidpointMs is edtfMidpointMs, exported so a storage layer can project `dated` at write time and sort on the projection natively (`R-QTEMPORAL`).

func TypeDerivation

func TypeDerivation(sub string) string

TypeDerivation returns the "derivation/<sub>" node type.

func TypeEntity

func TypeEntity(sub string) string

TypeEntity returns the "entity/<sub>" node type.

func TypeRelation

func TypeRelation(sub string) string

TypeRelation returns the "relation/<sub>" node type.

func TypeSource

func TypeSource(sub string) string

TypeSource returns the "source/<sub>" node type.

func ValidateBranchName added in v0.30.0

func ValidateBranchName(name string) error

ValidateBranchName holds a branch label to the form `R-FIELDS` gives a name — `[a-z0-9_]`, no leading underscore, at most 128 bytes. The charset admits no `$`, so a branch can never take a reserved target's name (BranchArchive, BranchUniverse, TargetBranches) and be shadowed by it at read time.

func ValidateQuery

func ValidateQuery(q Query) error

ValidateQuery holds a query to the schema's rules plus the one it cannot state, a step's Min against its Max. Wire and Go callers share it, so both reach one verdict.

func VerifyContent

func VerifyContent(hash Id, size uint64, data []byte) error

VerifyContent checks data against the content hash addresses and the expected size, answering ErrIntegrity on either mismatch (`V-CONTENT`, `V-HASH`).

func WantsDelta

func WantsDelta(opts ...GetOption) bool

WantsDelta reports whether opts ask for stored delta form, which a layer keeping none must fail rather than answer materialised.

func WithDetail

func WithDetail(sentinel error, detail string) error

WithDetail returns sentinel with detail appended lazily.

func WithReport

func WithReport(ctx context.Context, level ReportLevel) (context.Context, func() []QueryEvent)

WithReport returns a ctx collecting events at or above level, plus a snapshot func. It makes the log readable for calls no ResultStream covers — which layer of a stack served a GetClaims, what a copy walk visited. An outer collector, if ctx already has one, is reused and its events included.

func Wrap

func Wrap(sentinel, cause error) error

Wrap returns sentinel wrapping cause; both are matchable via errors.Is.

func WrapDetail

func WrapDetail(sentinel error, detail string, cause error) error

WrapDetail returns sentinel with detail and a wrapped cause.

Types

type Archive

type Archive interface {
	// Head is k, the half of the tuple that fixes which snapshot this is.
	Head() Id

	HasClaim(ctx context.Context, id Id) (bool, error)
	GetClaim(ctx context.Context, id Id) (Claim, error)
	GetClaimContent(ctx context.Context, id Id) (io.Reader, error)

	HasBranch(ctx context.Context, name string) (bool, error)
	GetBranch(ctx context.Context, name string) (Branch, error)
	GetBranches(ctx context.Context) ([]Branch, error)
	// MissingBranches are those of names this archive does not carry — the branches a
	// contribution would create, so a caller knows whether it needs the right to.
	MissingBranches(ctx context.Context, names []string) ([]string, error)

	// Query answers an RQL read: resolve q.Select.Branch to a Scope, delegate to 𝒰.
	Query(ctx context.Context, q Query) (ResultStream, error)

	Verify(ctx context.Context, opts ...VerifyOption) (VerificationRun, error)
}

Archive is an immutable read snapshot RA_k = (𝒰, k): the Universe plus the head claim 𝒰(k), which plays the paper's branch-table role (spec §Branches).

func NewArchive

func NewArchive(ctx context.Context, u Universe, k Id) (Archive, error)

NewArchive opens the snapshot RA_k = (𝒰, k) by loading the head claim at k.

type Bookmark added in v0.27.0

type Bookmark struct {
	// contains filtered or unexported fields
}

Bookmark is one entry of a bookmark list: the index i it sits at, the seed s its list is keyed on, and the head id k it records (`V-BMENV`). Signer is the contribution/contributor claim its envelope names as kid, whose key signed it.

func DecodeBookmark added in v0.27.0

func DecodeBookmark(raw []byte) (Bookmark, error)

DecodeBookmark reads a stored record as a bookmark and holds it to `V-BMENV`: a tagged COSE_Sign1 whose protected header carries alg and kid alone, over a three-element S([i, s, k]). The signature is another rule's (`V-BMSIG`), since checking it needs the kid's contributor claim.

func NewBookmark added in v0.27.0

func NewBookmark(index uint64, seed []byte, head, signer Id) Bookmark

NewBookmark builds a bookmark value, for a reader reconstructing one it holds the parts of. The record a store keeps is SignBookmark's.

func VerifyBookmark added in v0.27.0

func VerifyBookmark(ctx context.Context, u Universe, slot Id, raw []byte) (Bookmark, error)

VerifyBookmark reads the record offered at slot and holds it to the three rules a fetch can afford: its shape (`V-BMENV`), its signature against the pubkey of the contributor its kid names (`V-BMSIG`), and id_seq(i, s) recomputed from its own payload reproducing the slot it came from (`V-BMSLOT`).

func (Bookmark) Head added in v0.27.0

func (b Bookmark) Head() Id

Head returns k, the archive head this bookmark records.

func (Bookmark) Index added in v0.27.0

func (b Bookmark) Index() uint64

Index returns i, the bookmark's position in its list.

func (Bookmark) Seed added in v0.27.0

func (b Bookmark) Seed() []byte

Seed returns s, fixed once per list and carried by every entry, so any one of them opens the list.

func (Bookmark) Signer added in v0.27.0

func (b Bookmark) Signer() Id

Signer returns the contributor claim whose key signed this bookmark.

func (Bookmark) Slot added in v0.27.0

func (b Bookmark) Slot() (Id, error)

Slot returns id_seq(i, s), the key 𝒰_hist holds this bookmark under.

type BookmarkLocator added in v0.28.0

type BookmarkLocator struct {
	// contains filtered or unexported fields
}

BookmarkLocator names one bookmark list. Its two arms carry different contracts, and both deliver the seed before the cursor exists — which is what keeps a list's key material immutable for the life of a Bookmarks.

func At added in v0.28.0

func At(id Id) BookmarkLocator

At locates a PRUNED list from the id of any surviving entry, whose verified record yields the seed every bookmark carries — so index 0 need not exist (foundation paper §Backup). Pruning is out-of-band, BookmarkStore having no Delete.

func Seed added in v0.28.0

func Seed(s []byte) BookmarkLocator

Seed locates the list keyed on s, which starts at index 0 and is never pruned, so its genesis is detectable by probing id_seq(0, s). Any non-empty s serves: it keeps lists apart and is no security value, though a minted one carries 128 bits (`V-BMENV`).

func (BookmarkLocator) Open added in v0.28.0

Open resolves the locator against the Universe holding the list's 𝒰_hist.

type BookmarkStore added in v0.27.0

type BookmarkStore interface {
	// Get returns the record at key, ErrNotFound where the slot holds nothing.
	Get(ctx context.Context, key Id) ([]byte, error)
	// Put stores record at key, replacing whatever the slot held.
	Put(ctx context.Context, key Id, record []byte) error
}

BookmarkStore is 𝒰_hist, the bookmark store: records under id_seq(i, s), a keyspace defined apart from 𝒰 and freely co-located with it physically (foundation paper §Bookmarks). Its guarantees are deliberately weaker than 𝒰's — a bookmark is only a locator, so an entry may be overwritten or purged.

func UnsupportedBookmarks added in v0.28.0

func UnsupportedBookmarks() BookmarkStore

UnsupportedBookmarks is the store a Universe reporting Capabilities.Bookmarks false hands out: every operation is ErrUnsupported, the shape tags already take where a backend holds no side-data. One type serves every such backend, so a refusal is never reimplemented per adapter.

type Bookmarks added in v0.27.0

type Bookmarks struct {
	// contains filtered or unexported fields
}

Bookmarks is one bookmark list: entries under id_seq(i, seed) in hist, each recording an archive head in 𝒰. The first three fields are written at construction and never again, so Seed() and BookmarkId() need no lock and cannot answer nil. The rest is the bounds cache.

func NewBookmarks added in v0.27.0

func NewBookmarks(u Universe, seed []byte) (*Bookmarks, error)

NewBookmarks is a cursor over the list keyed on seed, which starts at index 0 (-> Seed). Nothing is minted here: a seed that arrives already made is what lets the field be final, where a constructor minting one would have to report failure.

func OpenBookmarks added in v0.27.0

func OpenBookmarks(ctx context.Context, u Universe, id Id) (*Bookmarks, error)

OpenBookmarks recovers a list from the id of ANY of its entries (foundation paper §Backup): the verified record there yields the seed every entry carries, and its index seeds the search. No index is privileged — 0 may have been purged.

func (*Bookmarks) Append added in v0.27.0

func (b *Bookmarks) Append(ctx context.Context, self Contributor, head Id) (Bookmark, error)

Append records head as the next bookmark, signed under self. The index is one past the settled top rather than the caller's, so a write can neither skip a slot nor clobber one — which is how the list stays gapless (`R-C7BOOKMARK`, `V-BMGAPLESS`).

func (*Bookmarks) BookmarkId added in v0.27.0

func (b *Bookmarks) BookmarkId() Id

BookmarkId returns the id of one entry of this list, which is the single value a bundle keeps to reopen the archive later (foundation paper §Backup). Fixed at construction, so it answers the same value from any goroutine at any time.

func (*Bookmarks) GetAtIndex added in v0.27.0

func (b *Bookmarks) GetAtIndex(ctx context.Context, i int) (Bookmark, error)

GetAtIndex returns the entry at index i; a slot outside the list's range is an error.

func (*Bookmarks) GetBulk added in v0.27.0

func (b *Bookmarks) GetBulk(ctx context.Context, from, toExcluding int) ([]Bookmark, error)

GetBulk returns the half-open index range [from, toExcluding).

func (*Bookmarks) Latest added in v0.27.0

func (b *Bookmarks) Latest(ctx context.Context) (Bookmark, error)

Latest returns the top entry, or the zero Bookmark when the list holds none. The writer's own last Append answers it, so a commit pays for no read.

func (*Bookmarks) Len added in v0.27.0

func (b *Bookmarks) Len(ctx context.Context) (int, error)

Len returns how many entries the list holds, counting from its lowest present index.

func (*Bookmarks) Seed added in v0.27.0

func (b *Bookmarks) Seed() []byte

Seed returns s, the value id_seq(i, s) keys this list on — a copy, the seed itself being fixed before this object existed.

func (*Bookmarks) Verify added in v0.27.0

func (b *Bookmarks) Verify(ctx context.Context) error

Verify holds the list to the two rules a fetch cannot afford: contiguity (`V-BMGAPLESS`) and every entry's k (`V-BMREF`). Two reads per entry, so it is an explicit entry point.

type Branch

type Branch interface {
	Edge

	// Name is the branch name — the edge's "name" field.
	Name() string
	// Head is the root of the branch's subgraph — the edge's Reference().
	Head() Id

	// Prev is the previous revision of this branch: the branch pointing at the
	// head's contribution/diff predecessor, nil at the first revision.
	Prev(ctx context.Context) (Branch, error)

	// Subgraph access — membership and lookup within the branch's closure,
	// resolved against the Universe the branch was read from.
	HasClaim(ctx context.Context, id Id) (bool, error)
	GetClaim(ctx context.Context, id Id) (Claim, error)
	GetClaimContent(ctx context.Context, id Id) (io.Reader, error)

	// Verify runs a (possibly long-running) verification over the branch's
	// closure. See verify.go for options.
	Verify(ctx context.Context, opts ...VerifyOption) (VerificationRun, error)
}

Branch is one entry of an archive's branch table — the named contribution/branch edge itself, plus navigation into its subgraph.

type Capabilities

type Capabilities struct {
	// Overwrite: Put replaces an existing key's bytes, so a read-through repair
	// restores a corrupted entry in place. WORM buckets report false.
	Overwrite bool
	// Delete: a stored key can be removed, for lawful deletion (R6) and eviction.
	Delete bool
	// Enumerate: stored keys can be listed, so a Universe can be recovered or
	// audited without a known head.
	Enumerate bool
	// Persistent: stored data survives a process restart.
	Persistent bool
	// ReverseWalk: the backend follows edges backward natively and cheaply (neo4j
	// indexes both directions). A stack ORs it over layers; a partition ANDs.
	ReverseWalk bool
	// RawClaims: the backend stores claims as CBOR
	RawClaims bool
	// ExternalContent: the backend holds externalized content of ANY size —
	ExternalContent bool
	// ContentCap is the largest .content value in bytes this backend holds; 0 = no limit
	ContentCap uint64
	// Tags: holds mutable per-claim tags (branch membership) and implements Tagger.
	Tags bool
	// Bookmarks: holds 𝒰_hist, so this backend can carry an archive's locator. A
	// rebuildable projection reports false — the head would be lost with the reindex.
	Bookmarks bool
	// Tier is the write-durability role this layer is configured in; the stack
	// composes its write path from each layer's reported tier.
	Tier StorageTier
}

Capabilities describes what a Universe's backend can do beyond the base get/put/has contract, so composites and config can reason about a deployment. The zero value is the most restrictive.

func (Capabilities) AllowsTier

func (c Capabilities) AllowsTier(t StorageTier) bool

AllowsTier reports whether a layer with these capabilities may be configured in tier t: authoritative demands verbatim, unbounded storage (RawClaims && ExternalContent), while every other tier is a routing role any layer can fill.

type Claim

type Claim interface {
	Node() Node
	// Edges returns the edges in canonical order, keeping those every filter matches (AND).
	Edges(filters ...Filter) []Edge

	Tags() map[string]string
	// Tag returns value of tag best effort, with "" as fallback
	Tag(key string) string
	HasTag(key string) bool
	SetTag(ctx context.Context, u Universe) error

	// Contributor returns this claim's contribution/contributor claim, which for
	// the root (no-edge) contributor is itself.
	Contributor() Contributor
	IsContributor() bool
	// AsContributor returns a contribution/contributor claim as a Contributor,
	// caching its pubkey so later signing needs no Universe.
	AsContributor(ctx context.Context, u Universe, signingKey ...crypto.Signer) (Contributor, error)
	ID() Id

	// GetContent reads the claim's content unscoped: inline from the claim,
	// external streamed from u, as is a dropped inline body.
	GetContent(ctx context.Context, u Universe) (io.Reader, error)

	// EncodeJSON renders every record slot as text, content base64. It reports a
	// claim; the id is verified against the CBOR form's bytes.
	EncodeJSON(form Form) ([]byte, error)
	// EncodeCBOR returns the serialized claim as canonical CBOR: FormOriginal as
	// written, FormMaterialized with its overlay resolved (`R-QDETAIL`).
	EncodeCBOR(form Form) ([]byte, error)
	// Envelope returns the stored record — the serialized claim paired with the
	// signature over it, the bytes id(v) hashes (`V-ENV`). This is what
	// persistence writes and the wire carries. A claim rebuilt from parts holds
	// no signature, so it has no envelope to give.
	Envelope() ([]byte, error)
	// contains filtered or unexported methods
}

Claim is a node together with the edges in its edges set. Atomically created (spec §4.3); immutable after.

func AssembleClaim

func AssembleClaim(parts ClaimParts) (Claim, error)

AssembleClaim rebuilds a Claim from parts, taking every id as given and ordering edges canonically, so faithful parts re-encode to identical bytes. The closure verifier over the authoritative Universe is what checks the result.

func DecodeClaim

func DecodeClaim(id Id, b []byte) (Claim, error)

DecodeClaim decodes a claim's canonical CBOR into a Claim with its id set. An error tells callers the bytes are content rather than a claim.

func DefaultMaterialize

func DefaultMaterialize(ctx context.Context, u Universe, claims []Claim, opts ...GetOption) ([]Claim, error)

DefaultMaterialize resolves each claim's contribution/diff overlay in place per the read opts. Idempotent; predecessors load through GetClaim, so recursively.

func GetClaim

func GetClaim(ctx context.Context, u Universe, id Id, opts ...GetOption) (Claim, error)

GetClaim is the single-item form of Universe.GetClaims; materialisation is the Universe's job, so this delegates.

func GetFromClosure

func GetFromClosure(ctx context.Context, u Universe, branch string, heads []Id, id Id) (Claim, error)

GetFromClosure returns the claim at id when a reference-edge walk from heads reaches it within scope branch, else ErrNotFound. heads are the scope roots, unioned.

type ClaimBuilder

type ClaimBuilder struct {
	Type          string
	TypeClass     NodeClass
	TypeSub       string
	Encoding      string
	EncodingClass EncodingClass
	EncodingSub   string
	InlineContent []byte
	ContentHash   Id
	ContentSize   uint64
	CreatedAt     time.Time
	// Dated is the time the claim's subject is assumed to stem from, an EDTF Level 1
	// value (`V-DATED`); "" leaves it absent.
	Dated       string
	Contributor Contributor
	// DiffOf makes this claim a diff over the referenced predecessor; the loader
	// materialises the chain.
	DiffOf Id
	Edges  []Edge
	Fields map[string]string
	// Height answers the claim's generation number (§4.1) from the references the
	// assembled claim carries: FixedHeight for a value already known, HeightsIn or
	// HeightsFrom to derive it. A referencing claim must set one.
	Height HeightResolver

	// SigningKey signs this claim's envelope. A contributor claim's pubkey is its
	// InlineContent, multikey-encoded.
	SigningKey crypto.Signer
	// contains filtered or unexported fields
}

ClaimBuilder is the data-only input to ClaimBuilder{...}.Sign(): Type is required, and a Contributor except on the root contributor claim (§4.3).

func NewClaim

func NewClaim(typ string, contributor Contributor) ClaimBuilder

NewClaim seeds a ClaimBuilder with the required type and attributing contributor. Chain With* setters for the optionals, then call .Sign().

func (ClaimBuilder) AllowInvalid

func (b ClaimBuilder) AllowInvalid() ClaimBuilder

AllowInvalid builds a claim the rules refuse: the type vocabulary, field limits, edge cardinality, the content slots and the key/pubkey pairing all go unjudged. What still happens is the sealing — canonical bytes, envelope, id — so the result is a real record breaking exactly what its caller aimed at, which is what a conformance case needs. A Sequencer verifies on admission, so such a claim reaches no archive through the front door.

func (ClaimBuilder) Sign

func (b ClaimBuilder) Sign(signingKey ...crypto.Signer) (Claim, error)

Sign finalizes a ClaimBuilder into an immutable Claim. A variadic key overrides SigningKey, which otherwise comes from the Contributor (§5.7).

func (ClaimBuilder) WithAutoHeight

func (b ClaimBuilder) WithAutoHeight(ctx context.Context, u Universe) ClaimBuilder

WithAutoHeight resolves the height against u.

func (ClaimBuilder) WithContributor

func (b ClaimBuilder) WithContributor(c Contributor) ClaimBuilder

WithContributor sets the attributing contributor.

func (ClaimBuilder) WithCreatedAt

func (b ClaimBuilder) WithCreatedAt(t time.Time) ClaimBuilder

WithCreatedAt sets the creation timestamp, which defaults to now in UTC.

func (ClaimBuilder) WithDated

func (b ClaimBuilder) WithDated(t time.Time) ClaimBuilder

WithDated sets `dated` to t's calendar day, UTC (`V-DATED`) — distinct from CreatedAt, when the archive witnessed it. For anything EDTF alone can say — an interval, a decade, a season, an uncertain or approximate value — use WithDatedEDTF directly.

func (ClaimBuilder) WithDatedEDTF

func (b ClaimBuilder) WithDatedEDTF(d string) ClaimBuilder

WithDatedEDTF sets `dated` to a raw EDTF Level 1 value (`V-DATED`), for a value WithDated's single calendar day can't express.

func (ClaimBuilder) WithDiff

func (b ClaimBuilder) WithDiff(id Id) ClaimBuilder

WithDiff makes this claim a diff over the predecessor at id, restating only what differs, and adds the contribution/diff edge.

func (ClaimBuilder) WithEdges

func (b ClaimBuilder) WithEdges(edges ...Edge) ClaimBuilder

WithEdges appends the given edges to the builder's Edges slice.

func (ClaimBuilder) WithEncoding

func (b ClaimBuilder) WithEncoding(e string) ClaimBuilder

WithEncoding sets the content media type ("class/sub").

func (ClaimBuilder) WithExternalContent

func (b ClaimBuilder) WithExternalContent(hash Id, size uint64) ClaimBuilder

WithExternalContent references content stored elsewhere by hash and byte size, exclusive with WithInlineContent.

func (ClaimBuilder) WithField

func (b ClaimBuilder) WithField(key, value string) ClaimBuilder

WithField sets one implementation-defined node field (§4.1) in a copied map.

func (ClaimBuilder) WithHeight

func (b ClaimBuilder) WithHeight(h uint64) ClaimBuilder

WithHeight sets the generation number (§4.1) as a value already known — 1 + max over the referenced heights, or 0 for an initial claim. HeightsIn and HeightsFrom derive it instead. The verifier re-derives and enforces it either way.

func (ClaimBuilder) WithHeightResolver added in v0.33.0

func (b ClaimBuilder) WithHeightResolver(ctx context.Context, resolve HeightResolver) ClaimBuilder

WithHeightResolver makes Sign ask resolve for the height, against every reference the closed claim carries. For a caller whose heights live somewhere other than a Universe — a database, or claims in memory.

func (ClaimBuilder) WithInlineContent

func (b ClaimBuilder) WithInlineContent(c []byte) ClaimBuilder

WithInlineContent sets the content bytes the claim itself carries.

func (ClaimBuilder) WithSigningKey

func (b ClaimBuilder) WithSigningKey(k crypto.Signer) ClaimBuilder

WithSigningKey sets the key used to sign the claim id.

func (ClaimBuilder) WithType

func (b ClaimBuilder) WithType(t string) ClaimBuilder

WithType sets the claim type ("class/sub").

type ClaimParts

type ClaimParts struct {
	ID            Id                // the node/claim id (a signature — taken as given, not recomputed)
	Type          string            // "class/sub"
	Encoding      string            // "class/sub", or "" when there is no content
	CreatedAt     time.Time         // must retain nanosecond precision to re-encode identically
	Height        uint64            // §4.1 generation number (0 for an initial claim); part of the id-preimage
	Dated         string            // EDTF Level 1 (`V-DATED`); "" when absent
	ContentHash   Id                // nil when the claim carries no content
	ContentSize   uint64            //
	InlineContent []byte            // present for inline content; omitted for external
	Fields        map[string]string //
	Edges         []EdgeParts       //
	Tags          map[string]string // mutable runtime tags (branch membership, revision) — not part of the id
}

ClaimParts rebuilds a stored claim without its canonical CBOR, which a graph-native cache holds as node and edge properties. External content needs no blob — the id commits to hash and size — while inline content needs the bytes.

type Collation

type Collation string

Collation is how ordered values compare.

const (
	CompareLexical  Collation = "lexical"  // string comparison (default)
	CompareNumeric  Collation = "numeric"  // numeric comparison
	CompareTemporal Collation = "temporal" // by span midpoint, ms — a timestamp and an EDTF `dated` on one axis (`R-QTEMPORAL`)
)

type Comparison

type Comparison struct {
	Eq   any
	Ne   any
	Lt   any
	Le   any
	Gt   any
	Ge   any
	In   []any  // set membership
	Glob string // shell-style wildcard (path.Match)
}

Comparison tests one field with exactly one operator.

type Constraints

type Constraints struct {
	// contains filtered or unexported fields
}

Constraints are what a contribution may do: the caller declares them and the Sequencer guarantees them, so an access system is built by composing them.

func NewConstraints

func NewConstraints(opts ...ContributionOption) Constraints

NewConstraints applies opts. The zero value is the strictest reading: every reserved type refused.

func (Constraints) AdmitBranch

func (c Constraints) AdmitBranch(ctx context.Context, base Archive, branch string) error

AdmitBranch reports whether the contribution may write to branch, which the base carrying it settles: creating one is a right of its own (§Access, C over $branches).

func (Constraints) AdmitReferences

func (c Constraints) AdmitReferences(ctx context.Context, u Universe, base Archive, branch string, own []Claim) error

AdmitReferences enforces step 3's read requirement: every claim the contribution's own reference, other than each other, must lie in a scope it may read.

func (Constraints) AdmitType

func (c Constraints) AdmitType(nodeType string) error

AdmitType reports whether a claim of nodeType may be added under these constraints.

type ContentBlob

type ContentBlob struct {
	Hash    Id
	Content []byte
}

ContentBlob is one content blob for a write: its hash and the bytes.

type ContentKind

type ContentKind int

ContentKind states whether and where a claim's content lives — the node's own definitive answer, which Claim.GetContent routes on.

const (
	ContentNone     ContentKind = iota // no content
	ContentInline                      // inline in the claim record
	ContentExternal                    // a separate Universe blob
)

type ContentRef

type ContentRef struct {
	Hash        Id
	ContentSize uint64
}

ContentRef addresses one content blob for a read: its hash plus the expected size, which backends use to allocate, range-fetch or verify.

type Contribution

type Contribution interface {
	// Base is the (k, t) the contribution was opened against.
	Base() (head Id, t time.Time)
	// AddClaims fills the contribution (step 2), naming the branch the claims join.
	// Several may be named, and an empty one is an error. Passing the claims is what
	// lets step 2's rules reach every one of them.
	AddClaims(branch string, claims []Claim) error
	// AddWire fills from a WireMediaType stream, which declares its own branches.
	// It takes the reader, so a caller that checked Branches hands on the same one.
	AddWire(ctx context.Context, wr *WireReader) error
	// CompleteAndVerify closes the contribution over its base and verifies it
	// (steps 3–4), yielding a sealed VerifiedContribution.
	CompleteAndVerify(ctx context.Context) (VerifiedContribution, error)
}

Contribution is an in-progress advance of a Ranke-Archive: open it against a base RA_k, fill it, then CompleteAndVerify seals and verifies it.

type ContributionOption

type ContributionOption func(*Constraints)

ContributionOption sets one constraint.

func WithCreatableBranches

func WithCreatableBranches(branches ...string) ContributionOption

WithCreatableBranches names the branches a contribution may bring into being, a branch the base does not carry being created by the merge that names it. TargetBranches admits any, the surface §Access holds a creation grant against.

func WithLiftedTypes

func WithLiftedTypes(types ...string) ContributionOption

WithLiftedTypes admits node types otherwise reserved to the Sequencer, so a caller holding it can create what it would mint.

func WithReferencableBranches

func WithReferencableBranches(branches ...string) ContributionOption

WithReferencableBranches names the branches a contribution may reference claims from (step 3). BranchArchive admits every branch, BranchUniverse the whole 𝒰.

type Contributor

type Contributor interface {
	Claim
	// SigningKey is the private key matching this contributor's pubkey (§5.7), nil
	// on one loaded for reading, which verifies claims rather than making them.
	SigningKey() crypto.Signer
	// Pubkey is the multikey public key (§5.7), which AsContributor resolves once
	// and caches, so signing can check a key without a Universe.
	Pubkey() []byte
}

Contributor is a typed view over a "contribution/contributor" claim, from Claim.AsContributor or Claim.Contributor.

func WithSigningKey

func WithSigningKey(c Contributor, key crypto.Signer) Contributor

WithSigningKey returns a Contributor carrying the key matching c's pubkey, so later calls sign on its behalf. A nil key leaves it able to read, not to build.

type CopyConfig

type CopyConfig struct {
	Closure  bool
	Content  bool
	Progress func(CopyProgress)
}

CopyConfig is the resolved set of copy options, which Universe implementations obtain via NewCopyConfig.

func NewCopyConfig

func NewCopyConfig(opts ...CopyOption) CopyConfig

NewCopyConfig applies opts in order and returns the resolved config.

type CopyOption

type CopyOption func(*CopyConfig)

CopyOption tunes CopyClaims / CopyContents — see each method for what it honors.

func WithClosure

func WithClosure() CopyOption

WithClosure copies each id's full provenance closure (CopyClaims only).

func WithContent

func WithContent() CopyOption

WithContent also copies the content bytes the copied claims reference.

func WithProgress

func WithProgress(fn func(CopyProgress)) CopyOption

WithProgress registers a best-effort progress callback, called on an unspecified goroutine at an implementation-chosen frequency. Keep it cheap: it runs on the copy's goroutine.

type CopyProgress

type CopyProgress struct {
	ClaimsCopied uint64 // claim records written so far
	BytesCopied  uint64 // content bytes written so far (only under WithContent)

	// Outstanding work known so far: LOWER BOUNDS that may rise as the closure is
	// walked until DiscoveryComplete, after which they only fall to 0.
	ClaimsRemaining uint64
	BytesRemaining  uint64

	// DiscoveryComplete reports the full work set enumerated: render an
	// indeterminate indicator until it flips, then a real percentage.
	// BytesRemaining counts what the receiver intends to fetch, at its discretion.
	DiscoveryComplete bool
}

CopyProgress is a best-effort snapshot of an in-flight copy.

type Detail

type Detail string

Detail is how much each element carries (`R-QDETAIL`).

const (
	DetailID     Detail = "id"     // identities only
	DetailClaims Detail = "claims" // the serialized claim, as Form and Content shape it (default; empty == claims)
	// DetailEnvelope is the stored record copied out: the bytes whose hash is the id,
	// carrying the signature over them (`R-QCANON`). Form and Content do not apply,
	// and it is CBOR alone — the bytes are what they are.
	DetailEnvelope Detail = "envelope"
)

type Direction

type Direction string

Direction is which way a step follows an edge. Provenance (outgoing) is the cheap default; Uses/Connections walk backward, native on ReverseWalk backends.

const (
	DirProvenance  Direction = "provenance"  // outgoing (default) — follow each edge to its reference
	DirUses        Direction = "uses"        // incoming — the claims that reference this one
	DirConnections Direction = "connections" // either direction
)

type Edge

type Edge interface {
	Reference() Id
	Type() string
	TypeClass() EdgeClass
	TypeSub() string

	// Encoding is the content's MIME media type ("class/sub"), "" without content;
	// a content-bearing edge declares one (§Nodes) — reasoning, a proof, a meaning.
	Encoding() string
	EncodingClass() EncodingClass
	EncodingSub() string

	// GetContentHash is the address of EXTERNAL content, H(content); nil for
	// inline content (bytes in the edge, §Content) and for no content.
	GetContentHash() Id
	// GetContentSize is the content's full byte length (0 when no content), whatever
	// this record holds of it.
	GetContentSize() uint64
	// ContentKind reports whether and where the edge's content lives — Inline,
	// External, or None.
	ContentKind() ContentKind
	// ContentComplete reports whether this record holds every byte it declares; a read
	// under a content cap serves a prefix (`R-QCONTENT`).
	ContentComplete() bool
	// GetInlineContent returns the inline content bytes (nil when none), which may be a
	// PREFIX of what the edge declares — check ContentComplete. It errors when the
	// content is external.
	GetInlineContent() ([]byte, error)
	// GetContent returns a reader over the content, transparently
	// streaming external content from u; u may be nil for inline content.
	GetContent(ctx context.Context, u Universe) (io.Reader, error)

	// RelationDirection is RelationFrom (+1) or RelationTo (-1) on
	// relation/* edges, 0 elsewhere (§4.7).
	RelationDirection() RelationDirection
	HasField(name string) bool
	GetField(name string) (string, error)
	Fields() []string
	ID() Id
	// contains filtered or unexported methods
}

Edge is a directed reference from the owning claim back to the older claim it cites (spec §4.2). Part of exactly one claim; may carry inline or external content.

func NewEdge

func NewEdge(cfg EdgeConfig) (Edge, error)

NewEdge constructs an Edge from cfg (§4.2) — the public wrapper over the internal newEdge, which yields the concrete type.

type EdgeClass

type EdgeClass string

EdgeClass is the closed top-level vocabulary for edge types.

const (
	EdgeClassContribution      EdgeClass = "contribution"
	EdgeClassContributionAlias EdgeClass = "c"
	EdgeClassDerivation        EdgeClass = "derivation"
	EdgeClassDerivationAlias   EdgeClass = "d"
	EdgeClassRelation          EdgeClass = "relation"
	EdgeClassRelationAlias     EdgeClass = "r"
)

type EdgeConfig

type EdgeConfig struct {
	Reference Id
	// Referenced is the claim Reference names, for the fields an edge derives from its
	// target: today the delete_by every edge must carry (`R-DPLANNED`). Supply it wherever
	// the target is in hand, since an edge cannot learn this after it is built.
	Referenced        Claim
	Type              string
	TypeClass         EdgeClass
	TypeSub           string
	Encoding          string // content media type ("class/sub"); required with content, forbidden without
	InlineContent     []byte // exclusive with ContentHash
	ContentHash       Id     // external content, with ContentSize
	ContentSize       uint64
	RelationDirection RelationDirection // RelationFrom or RelationTo on relation/* edges, zero elsewhere
	Fields            map[string]string
}

EdgeConfig is the data-only input to NewEdge. Reference and a type (Type, or TypeClass+TypeSub) are required; NewEdge enforces the per-field rules below.

type EdgeFilterFieldValue

type EdgeFilterFieldValue struct {
	Field string
	Value string
}

EdgeFilterFieldValue matches an edge carrying a field named Field whose value equals Value exactly. An edge lacking the field never matches.

func (EdgeFilterFieldValue) IsEdgeFilter

func (f EdgeFilterFieldValue) IsEdgeFilter() bool

IsEdgeFilter reports that this filter selects edges (not nodes).

func (EdgeFilterFieldValue) MatchEdge

func (f EdgeFilterFieldValue) MatchEdge(e Edge) bool

MatchEdge reports whether e carries the named field with the exact value.

func (EdgeFilterFieldValue) MatchNode

func (f EdgeFilterFieldValue) MatchNode(Node) bool

MatchNode always passes — this is an edge-only filter.

type EdgeFilterType

type EdgeFilterType struct {
	Type string
}

EdgeFilterType matches an edge whose type ("class/sub") equals Type exactly.

func (EdgeFilterType) IsEdgeFilter

func (f EdgeFilterType) IsEdgeFilter() bool

IsEdgeFilter reports that this filter selects edges (not nodes).

func (EdgeFilterType) MatchEdge

func (f EdgeFilterType) MatchEdge(e Edge) bool

MatchEdge reports whether e's type ("class/sub") equals the target exactly.

func (EdgeFilterType) MatchNode

func (f EdgeFilterType) MatchNode(Node) bool

MatchNode always passes — this is an edge-only filter.

type EdgeFilterTypes

type EdgeFilterTypes struct {
	Types []string
}

EdgeFilterTypes matches an edge whose type ("class/sub") is any of Types — EdgeFilterType widened to a set (OR within this one filter).

func (EdgeFilterTypes) IsEdgeFilter

func (f EdgeFilterTypes) IsEdgeFilter() bool

IsEdgeFilter reports that this filter selects edges (not nodes).

func (EdgeFilterTypes) MatchEdge

func (f EdgeFilterTypes) MatchEdge(e Edge) bool

MatchEdge reports whether e's type equals any of the target types.

func (EdgeFilterTypes) MatchNode

func (f EdgeFilterTypes) MatchNode(Node) bool

MatchNode always passes — this is an edge-only filter.

type EdgeParts

type EdgeParts struct {
	ID                Id // required — the cached edge id
	Reference         Id
	Type              string // "class/sub"
	Encoding          string // "class/sub", or "" when the edge has no content
	RelationDirection RelationDirection
	ContentHash       Id
	ContentSize       uint64
	InlineContent     []byte
	Fields            map[string]string
}

EdgeParts is the parsed structure of one edge. ID (the derived edge id) is required: a field-oriented cache must store it, since recomputing it means re-serializing S(e) from the claim CBOR the cache does not hold.

type EdgeSubtype

type EdgeSubtype string

EdgeSubtype is a contribution/* edge subtype. Every subtype vocabulary is open (paper 1 §Type Vocabulary); these are the ones the ADT gives meaning to, so a verifier acts on them and passes any other through as an ordinary reference.

const (
	EdgeSubtypeContributor      EdgeSubtype = "contributor"
	EdgeSubtypeContributorAlias EdgeSubtype = "c"
	EdgeSubtypeHead             EdgeSubtype = "head"
	EdgeSubtypeHeadAlias        EdgeSubtype = "h"
	EdgeSubtypeBranches         EdgeSubtype = "branches"
	EdgeSubtypeBranchesAlias    EdgeSubtype = "B"
	EdgeSubtypeBranch           EdgeSubtype = "branch"
	EdgeSubtypeBranchAlias      EdgeSubtype = "b"
	EdgeSubtypePrune            EdgeSubtype = "prune"
	EdgeSubtypePruneAlias       EdgeSubtype = "p"
	EdgeSubtypeDiff             EdgeSubtype = "diff"
	EdgeSubtypeDiffAlias        EdgeSubtype = "d"
	// A limiting claim points at its target through an edge of its own class
	// (paper 1 §Type Vocabulary): delete documents a gap where bytes were, expiry
	// names the last time a contributor's key is valid.
	EdgeSubtypeDelete      EdgeSubtype = "delete"
	EdgeSubtypeDeleteAlias EdgeSubtype = "x"
	EdgeSubtypeExpiry      EdgeSubtype = "expiry"
	EdgeSubtypeExpiryAlias EdgeSubtype = "e"
)

type EncodingClass

type EncodingClass string

EncodingClass is the closed top-level MIME vocabulary (RFC 6838 media types); the subtype is open.

type EncodingSubtype

type EncodingSubtype string

EncodingSubtype is the open second-level media type. Every well-known subtype carries a compact alias resolved two ways for the canonical encoding.

type Execution

type Execution struct {
	Layer string // pin to one named storage layer; empty = the backend chooses
	// Report is the execution-report verbosity threshold (see ReportLevel); when
	// set, the stream carries a QueryReport (ResultStream.Report).
	Report ReportLevel
}

Execution selects where the query runs and how deeply it reports on itself.

type Failure

type Failure struct {
	ID    Id
	Depth int
	Err   error
}

Failure is one verification failure: the claim that failed, its depth in the walk, and why. It is an error itself, so it travels as the cause of one: errors.Is reaches the rule through Unwrap, errors.As recovers the claim and depth.

func (Failure) Error added in v0.33.1

func (f Failure) Error() string

Error names the claim and the rule it broke.

func (Failure) Unwrap added in v0.33.1

func (f Failure) Unwrap() error

Unwrap yields the rule that failed, which is what errors.Is matches.

type Field

type Field string

Field is a node/edge field name: open user vocabulary over [a-z0-9_] with no leading "_". Charsets outside that are reserved system namespaces, e.g. ".".

type Filter

type Filter interface {
	MatchEdge(e Edge) bool
	MatchNode(n Node) bool
	IsEdgeFilter() bool
}

Filter selects a subset of edges matching some criterion. Passed to Claim.Edges as a variadic list — every filter must match (AND). Callers can implement their own; NewTypeFilter and NewEncodingFilter cover the common cases.

type Form

type Form string

Form is whether a claim is returned as written (the id-defining bytes) or resolved via its diff chain.

const (
	FormMaterialized Form = "materialized" // resolved via the diff chain (default; empty == materialized)
	FormOriginal     Form = "original"     // as written (the stored/canonical claim)
)

type GetOption

type GetOption func(*getConfig)

GetOption configures a claim read (Universe.GetClaims and the package GetClaim helper).

func WithNotDiffMaterialized

func WithNotDiffMaterialized() GetOption

WithNotDiffMaterialized returns claims in stored delta form, the contribution/diff overlay left unresolved.

type Graph

type Graph interface {
	// AddClaims stores claims in the Universe and advances the open-head
	// frontier: each claim becomes a head, the ids it references drop out.
	AddClaims(ctx context.Context, claims ...Claim) error
	// ContainsClaim reports whether id is in the closure of an open head.
	ContainsClaim(ctx context.Context, id Id) (bool, error)
	// GetClaim loads the claim at id if it is reachable from an open head.
	GetClaim(ctx context.Context, id Id) (Claim, error)
	// Heads returns the open heads — the frontier claims (§4.5). A single
	// head makes the graph a closure RG_h.
	Heads() []Id
	// IsConsolidated reports whether one claim already reaches everything the graph
	// needs reached — the open frontier and whatever Cite named.
	IsConsolidated() bool
	// Cite makes ids roots the next Consolidate wraps alongside the open heads, for a
	// claim that must be reachable from the head in its own right — one sitting behind
	// a claim scheduled for deletion (§Deletion).
	Cite(ids ...Id)
	// Consolidate wraps every open head in one head claim, adds it, returns it.
	// createdAt defaults to now and must satisfy monotonicity (§4.3).
	Consolidate(ctx context.Context, contributor Contributor, createdAt ...time.Time) (Claim, error)
	// Verify walks the closure from every open head and verifies each claim
	// (§5.10 integrity + authenticity), returning a live run.
	Verify(opts ...VerifyOption) VerificationRun
}

Graph is a Ranke-Graph handle: a subset RG ⊆ 𝒰 (spec §4.5). It holds the open-head frontier; membership is closure(heads, 𝒰), resolved on demand.

func NewGraph

func NewGraph(ctx context.Context, u Universe) (Graph, error)

NewGraph opens an empty graph over u; nil u gets an ephemeral memory Universe.

func NewGraphFromClosure

func NewGraphFromClosure(ctx context.Context, head Id, universe Universe) (Graph, error)

NewGraphFromClosure opens a graph at an existing head in universe; per §Closures the id alone recovers RG_head = closure(head, 𝒰) on demand.

type HeightCache

type HeightCache struct {
	// contains filtered or unexported fields
}

HeightCache memoises claim heights. A height is bound into the id (§4.1), so an entry holds forever. Construct with NewHeightCache; a nil cache simply misses.

func NewHeightCache

func NewHeightCache() *HeightCache

NewHeightCache returns an empty, ready-to-use cache.

func (*HeightCache) Get

func (c *HeightCache) Get(id Id) (uint64, bool)

Get returns the cached height for id, if present.

func (*HeightCache) GetClaimHeights

func (c *HeightCache) GetClaimHeights(ctx context.Context, u Universe, ids []Id) ([]uint64, error)

GetClaimHeights answers ids from the cache, loading the misses from u in one bulk DefaultGetClaimHeights and caching them.

func (*HeightCache) Note

func (c *HeightCache) Note(id Id, height uint64)

Note records id→height when absent, checking under the read lock first so the hot path never serialises writers.

func (*HeightCache) NoteClaims

func (c *HeightCache) NoteClaims(cs ...Claim)

NoteClaims records the height of every non-nil claim, so a get/put path warms the cache with a batch it already holds.

type HeightResolver added in v0.33.0

type HeightResolver func(ctx context.Context, refs []Id) (uint64, error)

HeightResolver answers the generation number (§4.1) a claim carries, given the references its assembled edge set holds — the contributor edge included, since the edge set is the input. `V-HEIGHT` fixes the answer at 1 + max over those references' heights, so a resolver that cannot reach one errors rather than treating it as height 0, which would build a claim verification refuses.

func FixedHeight added in v0.33.0

func FixedHeight(h uint64) HeightResolver

FixedHeight answers h whatever the references are — the resolver for a caller that already holds the value, and what WithHeight sets.

func HeightsFrom added in v0.33.0

func HeightsFrom(claims ...Claim) HeightResolver

HeightsFrom derives the height from claims already in hand, for a caller building against claims in memory rather than a Universe. A reference none of claims carries is reported absent, which is what catches a claim citing one the caller left out.

func HeightsIn added in v0.33.0

func HeightsIn(u Universe) HeightResolver

HeightsIn derives the height from u: 1 + max over the references' committed heights, and 0 for a claim referencing nothing.

type Id

type Id interface {
	String() string
	Equal(other Id) bool
	// Algorithm names the hash that built this id, e.g. "sha2-256".
	Algorithm() string
	// contains filtered or unexported methods
}

Id is a content-addressed identifier, and always a multihash (`V-HASH`): id(v) = H(S(env(v))) for a claim (`V-ID`), id(e) = H(S(e)) for an edge, and H(c) for external content. The envelope carries the signature that once framed a claim's id (`V-ENV`), so one framing now serves all three.

func HashContent

func HashContent(content []byte) (Id, error)

HashContent returns the content-address (SHA2-256 multihash) of bytes.

func IdSeq added in v0.27.0

func IdSeq(i uint64, s []byte) (Id, error)

IdSeq computes id_seq(i, s) := H(S([i, s])) (`V-IDSEQ`) — a two-element array of an unsigned integer and a byte string, which CBOR's major type alone separates from any claim's map encoding (`V-SER`).

func ParseId

func ParseId(s string) (Id, error)

ParseId parses a multibase-encoded id string into its multihash.

func StrandedByDeletion

func StrandedByDeletion(ctx context.Context, u Universe, heads []Id, own []Claim) ([]Id, error)

StrandedByDeletion reports which of own a walk from heads reaches only through a claim carrying delete_by (`R-DPLANNED`): deleting one removes the record its edges lived in, so a walk stops at the gap while what lay behind stays present.

The search stays inside own — a claim is referenced only by claims made after it, so nothing already in the archive leads to one this contribution brings.

type KeyOption added in v0.32.0

type KeyOption func(*keyConfig)

KeyOption supplies what reading a key may need beyond its bytes.

func WithPassphrase added in v0.32.0

func WithPassphrase(pass []byte) KeyOption

WithPassphrase decrypts an encrypted key with pass.

func WithPassphraseFrom added in v0.32.0

func WithPassphraseFrom(fn func() ([]byte, error)) KeyOption

WithPassphraseFrom fetches the passphrase only if the key proves encrypted, so an unencrypted one never prompts, never reads an environment and never blocks.

type Keypair

type Keypair struct {
	Private crypto.Signer
	Pubkey  []byte // multikey-encoded (see EncodePublicKey)
}

Keypair pairs a private signing key with its multikey-encoded public key, the key material a contributor claim needs.

func LoadPrivateKey

func LoadPrivateKey(path string, opts ...KeyOption) (Keypair, error)

LoadPrivateKey is ParseKeypair over the file at path.

func ParseKeypair added in v0.31.0

func ParseKeypair(pemBytes []byte, opts ...KeyOption) (Keypair, error)

ParseKeypair reads an Ed25519 PKCS#8 PEM private key and pre-computes its multikey-encoded public key. Bytes rather than a path, a key arriving as readily from an environment variable, a pipe or a paste (-> keysource).

type Limit

type Limit struct {
	Results int           // max claims; 0 = unbounded
	Time    time.Duration // execution budget; 0 = none
}

Limit bounds a read.

type MergableContribution

type MergableContribution interface {
	// Heads are the open head ids per branch, which the new branch-table claim
	// references under those names.
	Heads() map[string][]Id
}

MergableContribution is ready for step 6: its claims are durably in 𝒰 and its head claim(s) are known.

type Node

type Node interface {
	Type() string
	TypeClass() NodeClass
	TypeSub() string
	// GetContentHash is the address of EXTERNAL content, H(content); nil for
	// inline content (bytes in the node, §Content) and for no content.
	GetContentHash() Id
	// GetContentSize is the content's full byte length (0 when no content), whatever
	// this record holds of it: paired with the hash it defends against truncation, and
	// against GetInlineContent it says how much arrived.
	GetContentSize() uint64
	// ContentKind reports where the node's content lives — Inline, External, or
	// None — reading through a diff overlay via the effective content source.
	ContentKind() ContentKind
	// ContentComplete reports whether this record holds every byte it declares. A read
	// under a content cap serves a prefix (`R-QCONTENT`), so a caller needing the whole
	// content asks here rather than assuming what GetInlineContent returned is all of it.
	ContentComplete() bool
	// GetInlineContent returns the inline content bytes, which may be a PREFIX of the
	// content the node declares — check ContentComplete. It errors when the content is
	// external; check ContentKind first, or use GetContent with a Universe.
	GetInlineContent() ([]byte, error)
	// GetContent returns a reader over the content, transparently
	// streaming external content from u; u may be nil for inline content.
	GetContent(ctx context.Context, u Universe) (io.Reader, error)
	Encoding() string
	EncodingClass() EncodingClass
	EncodingSub() string
	CreatedAt() time.Time
	// Dated is the time the claim's subject is assumed to stem from, an EDTF Level 1
	// value (`V-DATED`); "" when absent. Unlike CreatedAt it denotes an interval, not
	// an instant, and is neither `V-TIME`- nor `V-MONO`-constrained.
	Dated() string
	// Height is the claim's generation number: 0 for an initial claim, else 1 + max
	// over referenced heights (§4.1). In the node hash, so the id commits to it.
	Height() uint64
	// Edges returns the ids of edges created with this claim, in canonical order.
	Edges() []Id
	HasField(name string) bool
	GetField(name string) (string, error)
	Fields() []string
	ID() Id
}

Node is the structural component of a claim; its id is the claim id. Identical content under different provenance yields different ids (spec §4.1).

type NodeClass

type NodeClass string

NodeClass is the closed top-level vocabulary for node types.

const (
	NodeClassContribution      NodeClass = "contribution"
	NodeClassContributionAlias NodeClass = "c"
	NodeClassSource            NodeClass = "source"
	NodeClassSourceAlias       NodeClass = "s"
	NodeClassDerivation        NodeClass = "derivation"
	NodeClassDerivationAlias   NodeClass = "d"
	NodeClassEntity            NodeClass = "entity"
	NodeClassEntityAlias       NodeClass = "e"
	NodeClassRelation          NodeClass = "relation"
	NodeClassRelationAlias     NodeClass = "r"
)

type NodeSubtype

type NodeSubtype string

NodeSubtype is the second-level node-type vocabulary (the "/sub" part).

const (
	NodeSubtypeBranches         NodeSubtype = "branches"
	NodeSubtypeBranchesAlias    NodeSubtype = "B"
	NodeSubtypeContributor      NodeSubtype = "contributor"
	NodeSubtypeContributorAlias NodeSubtype = "c"
	NodeSubtypeHead             NodeSubtype = "head"
	NodeSubtypeHeadAlias        NodeSubtype = "h"
	// The limiting claims (paper 1 §Type Vocabulary). Each takes the letter its
	// edge subtype takes, as contributor, head and diff already do.
	NodeSubtypeDelete      NodeSubtype = "delete"
	NodeSubtypeDeleteAlias NodeSubtype = "x"
	NodeSubtypeExpiry      NodeSubtype = "expiry"
	NodeSubtypeExpiryAlias NodeSubtype = "e"
)

"branch" and "diff" are absent: both are edge subtypes alone. A branch is named by a contribution/branch edge on the table, and diff-ness lives in the contribution/diff edge, so no node carries either. @tbl:aliases still assigns them b and d — one table shared by nodes and edges — which the edge side holds.

type OrderKey

type OrderKey struct {
	Field   string
	Compare Collation // how values compare; empty = lexical
	Dir     SortDir   // asc | desc; empty = asc
}

OrderKey is one sort key; keys apply in priority order, ties by (created_at, id).

type Output

type Output struct {
	Shape    Shape          // single | path
	Detail   Detail         // id | claims
	Form     Form           // original | materialized
	Content  *OutputContent // content inlined per claim; nil inlines none
	Encoding ResultEncoding // serialized form of each claim (json | cbor)
}

Output shapes each result along orthogonal axes: Shape, Detail, Form, Content, Encoding.

type OutputContent

type OutputContent struct {
	Max      int      // cap in bytes for the whole claim; 0 inlines in full
	Overflow Overflow // where the prefix ends at the cap; empty is OverflowOmit
}

OutputContent caps the content inlined per claim (`R-QCONTENT`). Max bounds the claim rather than each record, spent along the claim's content sequence — its edges' content in S(v)'s order, then the node's — and what arrives is a prefix of it. Max 0 inlines in full, as a zero bound elsewhere in a query means unbounded.

type Overflow

type Overflow string

Overflow is where a claim's inlined content ends once Max is reached. A claim keeps every field it carries either way, so no value stands in for content left out.

const (
	OverflowCutoff Overflow = "cutoff" // inline the bytes up to the cap, ending inside a value
	OverflowOmit   Overflow = "omit"   // inline whole values only (default; empty == omit)
)

type PathStep

type PathStep struct {
	Edges []string
	Dir   Direction // default DirProvenance
	Min   *int      // min hops; nil means 1. Hops(0) includes the step's start.
	Max   int       // max hops; 0 = unbounded for this step
	Nodes []string
}

PathStep follows typed edges over a hop range, optionally constraining endpoint node types. Entries are globs over "class/sub"; a leading "-" excludes.

func (PathStep) MinHops

func (s PathStep) MinHops() int

MinHops is Min with its default of one hop applied. ValidateQuery refuses a negative Min and every read passes it, so this states the value it is given.

type Query

type Query struct {
	Select    Select
	Where     *Where // nil = no filter
	Output    Output
	Order     []OrderKey // sort keys in priority order; empty = natural (created_at, id)
	Limit     Limit
	Execution Execution
}

Query is a declarative read (RQL): generate a set of claims (Select), filter it (Where), shape each result (Output), then order and bound the read. Its meaning is fixed here, whichever layer answers it via Universe.Query.

func DecodeQuery

func DecodeQuery(data []byte) (Query, error)

DecodeQuery reads a query from its canonical JSON. An absent field keeps its zero value; what a caller's silence becomes is the binding's to decide.

type QueryEvent

type QueryEvent struct {
	At       time.Duration  `json:"at_ns"`                 // offset from QueryReport.StartedAt
	Engine   string         `json:"engine"`                // who emitted it: "native", "cypher", "stack", "partition", …
	Op       string         `json:"op"`                    // what it did: "load-root", "step", "filter", "sort", "route", "translate-cypher", …
	Level    ReportLevel    `json:"level"`                 // info | warn | error
	Duration time.Duration  `json:"duration_ns,omitempty"` // elapsed for a timed step; 0 for a point event
	Detail   string         `json:"detail,omitempty"`      // human message, or the translated query text (e.g. Cypher)
	Attrs    map[string]any `json:"attrs,omitempty"`       // structured extras: layer/shard name, depth, edge/result counts, …
}

QueryEvent is one logged step or point during execution.

type QueryReport

type QueryReport struct {
	StartedAt time.Time     `json:"started_at"` // wall clock at query start
	Elapsed   time.Duration `json:"elapsed_ns"` // total execution time
	Results   int           `json:"results"`    // items emitted
	Truncated bool          `json:"truncated"`  // whether Limit cut the read short
	Events    []QueryEvent  `json:"events,omitempty"`
}

QueryReport is a query's execution log (Execution.Report set). It travels as the stream's final element under KindReport (`R-QSTREAM`), which ResultStream.Report reads back. Engine/layer identity is per-event, since one query can span several engines. A report travels a result sequence as its last record, so its field names are wire names: lower-case like every other, and a duration says its unit, since one serialises as a bare integer of nanoseconds.

func ReportOf

func ReportOf(results []QueryResult) *QueryReport

ReportOf returns the report the sequence's final element carries, nil when that element is a result. It is the one place a stream's Report reads from, so the element and the accessor cannot disagree.

type QueryResult

type QueryResult struct {
	Kind         ResultKind
	ClaimId      Id
	PathId       []Id
	ClaimNative  Claim
	PathNative   []Claim
	ClaimEncoded []byte
	PathEncoded  [][]byte
	Report       *QueryReport // KindReport alone: the run's execution log
}

QueryResult is one element of a result stream: a reached claim shaped per Output, or the report a reported run ends with. Kind names the one field carrying the payload, so a caller switches once and streams that field, and a reader learns what an element holds without inspecting it (`R-QSTREAM`).

func AppendReport

func AppendReport(results []QueryResult, rep *QueryReport) []QueryResult

AppendReport ends results with rep as a tagged element, when there is a report to end with: `R-QREPORT` puts one in the stream when, and only when, the query asked for it, so a nil report leaves the sequence as it was.

type Receipt

type Receipt interface {
	Head() Id
}

Receipt is the outcome of a committed merge — the new head the archive advanced to.

type RelationDirection

type RelationDirection int8

RelationDirection tags an entity's role on a relation/* edge (§4.7): zero = not a relation edge, RelationFrom (+1) / RelationTo (-1) otherwise. All-from or all-to expresses a symmetric relation.

const (
	RelationFrom RelationDirection = 1
	RelationTo   RelationDirection = -1
)

type ReportLevel

type ReportLevel string

ReportLevel classifies a QueryEvent.

const (
	ReportError ReportLevel = "error" // failures — always logged when a report is requested
	ReportWarn  ReportLevel = "warn"  // fallbacks, caps hit, recoverable issues
	ReportInfo  ReportLevel = "info"  // high-level stages: select, filter, sort, results
	ReportDebug ReportLevel = "debug" // routing decisions, per-layer hits, engine lowering (e.g. Cypher)
	ReportTrace ReportLevel = "trace" // per-claim / per-edge steps — exhaustive
)

Report levels are one ordered scale (Error < Warn < Info < Debug < Trace); Execution.Report sets the threshold and every event at or above it is kept. Empty means no report.

type ResultEncoding

type ResultEncoding string

ResultEncoding is the form a result is returned in, carrying the same information whichever it is.

const (
	ResultNative ResultEncoding = "native" // Go objects in ClaimNative/PathNative (default; empty == native)
	ResultJSON   ResultEncoding = "json"   // Encoded holds text, content base64
	ResultCBOR   ResultEncoding = "cbor"   // Encoded holds the stored canonical CBOR, verbatim
)

type ResultKind

type ResultKind string

ResultKind names the QueryResult field an element's payload is in: for a result, the product of Output.Shape (single | path) and Output.Detail/Encoding (id | native | encoded); for the report a reported run ends with, KindReport.

const (
	KindClaimId      ResultKind = "claim_id"
	KindPathId       ResultKind = "path_id"
	KindClaimNative  ResultKind = "claim_native"
	KindPathNative   ResultKind = "path_native"
	KindClaimEncoded ResultKind = "claim_encoded"
	KindPathEncoded  ResultKind = "path_encoded"
	// KindClaimEnvelope / KindPathEnvelope tag stored bytes copied out under
	// DetailEnvelope, apart from an encoded serialized claim, since a reader parses
	// the one and hands the other on as bytes (`R-QSTREAM`).
	KindClaimEnvelope ResultKind = "claim_envelope"
	KindPathEnvelope  ResultKind = "path_envelope"
	// KindReport tags the final element of a run that asked for a report
	// (`R-QREPORT`). The value is the one ranke-ts discriminates its framed report
	// record under, the wire having carried the report in band all along.
	KindReport ResultKind = "report"
)

type ResultStream

type ResultStream interface {
	// Next advances to the next element, returning false at end of stream or error.
	Next() bool
	// Result returns the current element (valid after Next returned true). A reported
	// run ends with a KindReport element, which is the report itself.
	Result() QueryResult
	// Report returns the report the stream's last element carries, once Next has
	// returned false — a convenience over that element rather than a channel beside
	// it, so the two cannot disagree. nil when Execution.Report asked for none.
	Report() *QueryReport
	// Err returns the first error that stopped the stream, if any.
	Err() error
	// Close releases the stream's resources.
	Close() error
}

ResultStream streams a query's elements one at a time, in the query's order: its results, then the report where one was asked for (`R-QSTREAM`).

func DefaultQuery

func DefaultQuery(ctx context.Context, u Universe, q Query, scope Scope) (ResultStream, error)

DefaultQuery is the reference implementation of Universe.Query, reading only through the public Universe API: walk the closure, materialise, filter, order, limit, shape.

type Scope

type Scope struct {
	Head     Id     // closure anchor; nil = unconfined ($universe)
	Branch   string // resolved branch name — a native prune key
	Height   uint64 // branch-head height
	Revision int    // archive spine revision (_br)
}

Scope is the branch context a query runs in — the Archive's resolution of q.Select.Branch, Head confining to its closure and the rest pruning natively.

type Select

type Select struct {
	Branch string // scope: BranchUniverse, BranchArchive, or a branch name
	Head   Id     // narrows the scope to this claim's closure
	Claim  Id     // anchors the frontier; nil leaves the pattern unanchored
	Path   []PathStep
}

Select is a generator: Branch is the scope, Head narrows it to one claim's closure (`R-QHEAD`), Claim anchors the frontier (`R-QANCHOR`) and Path is the traversal; no Path reads the frontier's outward closure (`R-QSTEPS`).

type Sequencer

type Sequencer interface {
	// InGenesis reports no archive here yet, leaving Found the one operation.
	InGenesis() bool
	// Found creates the archive, once: the Sequencer's initial claim, the first
	// contributor carrying pubkey, and the branch binding it, so the list alone
	// reaches it. Returns its claim, which `V-SIG` lets only the Sequencer sign.
	Found(ctx context.Context, pubkey []byte, branch string) (Claim, error)
	// GetArchive returns the current immutable snapshot RA_k.
	GetArchive(ctx context.Context) (Archive, error)
	// GetContributor returns the contributor the Sequencer attests branch
	// advances with.
	GetContributor() Contributor
	// BookmarkId returns the id of a bookmark in this archive's list — the one
	// written at bootstrap. Any single bookmark id recovers the latest recorded
	// head (foundation paper §Backup), so this is what a bundle keeps to be
	// reopened later via OpenBookmarks.
	BookmarkId() Id
	// NewContribution opens a contribution against the current archive, under the
	// constraints opts declare (step 1).
	NewContribution(ctx context.Context, opts ...ContributionOption) (Contribution, error)
	// Merge commits a persisted contribution: it mints the branch table (step 6)
	// and takes the advance into effect against a new bookmark (step 7).
	Merge(ctx context.Context, c MergableContribution) (Receipt, error)
}

Sequencer is the single writer of a Ranke-Archive (RankeDB paper §Sequencer): it hands out immutable read snapshots and advances the head, k → k', by merging contributions. The concrete implementation (naive here, concurrent in a server) lives in an adapter.

An implementation is safe to drive from several goroutines. Which one a caller holds decides how fast that goes, never whether it is allowed.

type Shape

type Shape string

Shape is whether each result is a single item or a path (the chain to it).

const (
	ShapeSingle Shape = "single" // individual claims (default; empty == single)
	ShapePath   Shape = "path"   // the route to each claim
)

type SortDir

type SortDir string

SortDir is a sort direction.

const (
	SortAsc  SortDir = "asc"  // ascending (default)
	SortDesc SortDir = "desc" // descending
)

type SpineItem added in v0.28.0

type SpineItem struct {
	// contains filtered or unexported fields
}

SpineItem is one revision of the branch-table spine: head id, height, and creation time.

func NewSpineItem added in v0.28.0

func NewSpineItem(id Id, revision int, height int, timestamp time.Time) SpineItem

NewSpineItem builds a SpineItem. It lets adapters outside package ranke reconstruct an item from persisted storage.

func SpliceSpine added in v0.28.0

func SpliceSpine(existing, tagged []SpineItem) []SpineItem

SpliceSpine grafts tagged onto existing at tagged's first revision, dropping existing's entries from there on. An empty tagged leaves existing as is.

func TagArchive

func TagArchive(ctx context.Context, a Archive, opts ...TagArchiveOptions) ([]SpineItem, error)

TagArchive descends a's branch-table spine and tags each revision's branch closures oldest→newest

func (SpineItem) GetHeight added in v0.28.0

func (h SpineItem) GetHeight() int

GetHeight returns the entry's height.

func (SpineItem) GetId added in v0.28.0

func (h SpineItem) GetId() Id

GetId returns the head id this entry records.

func (SpineItem) GetRevision added in v0.28.0

func (h SpineItem) GetRevision() int

GetRevision returns the entry's position in the timeline (0 is the oldest).

func (SpineItem) GetTimestamp added in v0.28.0

func (h SpineItem) GetTimestamp() time.Time

GetTimestamp returns the time the head was appended.

type StorageTier

type StorageTier string

StorageTier is how a stack writes to a layer: the adapter constrains which tiers it allows, the deployment picks one, the layer reports the choice.

const (
	// StorageTierAuthoritative: the source of truth, holding the archive verbatim —
	// a write MUST succeed here. Requires RawClaims && ExternalContent.
	StorageTierAuthoritative StorageTier = "authoritative"
	// StorageTierEager: written synchronously alongside the authoritative tier,
	// best-effort — the layer re-syncs from the authoritative copy (neo4j).
	StorageTierEager StorageTier = "eager"
	// StorageTierBackground: written in a background goroutine, best-effort.
	StorageTierBackground StorageTier = "background"
	// StorageTierLazy: populated on a read miss served from below (redis).
	StorageTierLazy StorageTier = "lazy"
)

type SweepResult

type SweepResult struct {
	Claims   []Id
	Contents []Id
}

SweepResult is one sweep's outcome: the claims whose bytes were removed, and the content blobs that went with them.

func DeletePlanned

func DeletePlanned(ctx context.Context, u Universe, heads []Id, now time.Time) (SweepResult, error)

DeletePlanned removes the bytes of every claim in the closure of heads whose delete_by has fallen due at now (`R-DPLANNED`), and is idempotent.

It never deletes the four `R-DSTRUCT` subtypes, whatever a field says; CheckDeletable is that set. The gap it leaves is explained by the date the citing edges already copied (`R-DGAP`), and external content goes with the claim.

type SyncResult

type SyncResult struct {
	SyncedTo Id
	Err      error
}

SyncResult is a Sync outcome: SyncedTo is the branch-table claim now fully readable, nil on error, and Err the failure.

type TagArchiveOptions

type TagArchiveOptions struct {
	// RetagAll ignores the resume bound and descends to the spine root,
	// re-tagging every revision from 0.
	RetagAll bool
}

TagArchiveOptions tunes TagArchive.

type Universe

type Universe interface {
	// GetClaims returns the claims for ids, positionally; a missing claim fails
	// the whole call with ErrNotFound, so callers tolerating gaps HasClaims first.
	// Claims are diff-materialised unless WithNotDiffMaterialized asks for the delta.
	GetClaims(ctx context.Context, ids []Id, opts ...GetOption) ([]Claim, error)
	// PutClaims stores cs; idempotent, as claims are content-addressed.
	PutClaims(ctx context.Context, cs []Claim) error
	// HasClaims reports, positionally per id, whether each is present.
	HasClaims(ctx context.Context, ids []Id) ([]bool, error)
	// GetClaimsRaw returns each claim's stored canonical CBOR verbatim,
	// positionally — verification hashes these bytes, replication copies them. A
	// structure-only cache (RawClaims false) misses, so a stack routes below it.
	GetClaimsRaw(ctx context.Context, ids []Id) ([][]byte, error)
	// DeleteClaims removes the stored bytes of each id: a lawful deletion the graph
	// still explains (`R-DGAP`), or a cache eviction. Idempotent — an id already
	// absent is no error, so a sweep may run twice. ErrUnsupported where
	// Capabilities.Delete is false, rather than a silent success.
	//
	// Removing bytes rewrites no id: every referencing claim still commits to the
	// one it named, which is what leaves a gap rather than a hole. Whether a
	// deletion is LAWFUL is the caller's to establish (-> DeletePlanned); this
	// removes what it is told to.
	DeleteClaims(ctx context.Context, ids []Id) error

	// GetContents returns the bytes for each ref, positionally; a missing blob
	// fails the whole call with ErrNotFound.
	GetContents(ctx context.Context, refs []ContentRef) ([][]byte, error)
	// PutContents stores blobs. Idempotent.
	PutContents(ctx context.Context, blobs []ContentBlob) error
	// HasContents reports, positionally per hash, whether each is present.
	HasContents(ctx context.Context, hashes []Id) ([]bool, error)
	// StreamContent returns a reader for one blob — the lazy, singular GetContents.
	StreamContent(ctx context.Context, hash Id, size uint64) (io.ReadCloser, error)
	// DeleteContents removes the blobs at hashes, on DeleteClaims' terms: idempotent,
	// and ErrUnsupported without Capabilities.Delete.
	DeleteContents(ctx context.Context, hashes []Id) error

	// GetClaimHeights returns the committed heights (§4.1) of the claims at ids,
	// positionally. Height is fixed at creation and read back cheaply here, since
	// recomputing it over a large closure is expensive.
	GetClaimHeights(ctx context.Context, ids []Id) ([]uint64, error)

	// ClaimsInBranches reports, per id, whether any of branches holds it. Names map to
	// heads, so a walker resolves by head and a graph-native backend by the name it
	// indexes; BranchArchive names the archive entire.
	ClaimsInBranches(ctx context.Context, branches map[string]Id, ids []Id) ([]bool, error)

	// Query answers a declarative RQL read (the paper's §Filtered Reads) with
	// scope as the resolved branch context, so head/height/revision are known. A
	// byte store delegates to DefaultQuery, a graph-native backend lowers to its
	// own query language; a query means the same whichever layer answers.
	Query(ctx context.Context, q Query, scope Scope) (ResultStream, error)

	// CopyClaims copies the claim records at ids from src into the receiver —
	// roots under WithClosure, the exact set otherwise. WithClosure brings each
	// id's full provenance, the "copy === merge" semantics yielding a mergeable
	// state; bare, the copy is PARTIAL, for HasClaims-driven frontier sync where
	// the caller guarantees the rest. WithContent adds the referenced content
	// bytes. An interrupted copy leaves a partial result.
	CopyClaims(ctx context.Context, src Universe, ids []Id, opts ...CopyOption) error
	// CopyContents copies the content blobs at refs from src into the receiver,
	// the content-only half of sync. Only WithProgress is honored.
	CopyContents(ctx context.Context, src Universe, refs []ContentRef, opts ...CopyOption) error

	// SetClaimsTags applies tags per claim, keyed by claim-id string: it clears
	// every existing tag matching a clearTags glob, then applies that claim's
	// pairs. A backend that cannot hold tags returns ErrUnsupported.
	SetClaimsTags(ctx context.Context, clearTags []string, tags map[string]map[string]string) error
	// GetClaimTags returns each claim's tags positionally, nil when it has none.
	GetClaimTags(ctx context.Context, claims []Id) ([]map[string]string, error)

	// Tag signals that the archive at head has advanced. Purely an accelerator: it
	// changes how fast a read answers, and what a layer indexes is its business.
	Tag(ctx context.Context, head Id) error

	// Sync fills the receiver for id's closure by copying what it lacks from src
	// (claims + content); a stack calls it on its eager layer with the layers below
	// as src. A nil src leaves the receiver synced.
	Sync(ctx context.Context, src Universe, id Id) <-chan SyncResult

	// Bookmarks returns this backend's 𝒰_hist, the second address scheme keyed on
	// id_seq(i, s) rather than on a record's bytes (foundation paper §Bookmarks).
	// The Universe owning it is what lets the bookmark list inherit the layering,
	// replication and backup of the store beneath it (RankeDB paper §Sequencer);
	// a separate port cannot, which is why there is no way to obtain one.
	// Capabilities.Bookmarks false returns a store answering ErrUnsupported.
	Bookmarks() BookmarkStore

	// Capabilities reports optional backend abilities; composites derive theirs.
	Capabilities() Capabilities

	Close() error
}

Universe is 𝒰 from spec §4.5 — a content-addressed bag of claims and the content bytes they reference, shareable by many Archives. Operations are bulk so backends use their native batch path (S3 batch, Neo4j UNWIND, SQL bulk insert).

func NewMemoryUniverse

func NewMemoryUniverse() Universe

NewMemoryUniverse returns an ephemeral in-process Universe: canonical claim CBOR keyed by id, content keyed by hash. It stores the exact bytes like any byte store and decodes on read, so it is stable and drift-free — the right reference behaviour, with no live-claim aliasing.

type VerificationRun

type VerificationRun interface {
	// Verified is the number of claims that passed so far.
	Verified() int
	// Failures is a snapshot of the failures found so far.
	Failures() []Failure
	// Done reports whether the walk has finished (completed or stopped).
	Done() bool
	// Err is a terminal error that aborted the walk (a load failure,
	// ctx cancellation) — distinct from per-claim Failures. Nil otherwise.
	Err() error
	// Wait blocks until the walk is Done.
	Wait()
}

VerificationRun is a live handle on a verification, safe to read while the walk runs — poll for progress, or Wait for completion.

type VerifiedContribution

type VerifiedContribution interface {
	// Ids are the claim ids the contribution adds.
	Ids() []Id
	// Persist writes the sealed closure to 𝒰 (step 5), yielding a
	// MergableContribution the Sequencer can merge.
	Persist(ctx context.Context) (MergableContribution, error)
}

VerifiedContribution is a sealed, verified contribution: its contents are fixed, so by immutability whatever verified stays valid.

type VerifyOption

type VerifyOption func(*verifyConfig)

VerifyOption configures a verification run.

func WithCreatedAfter

func WithCreatedAfter(t time.Time) VerifyOption

WithCreatedAfter prunes any claim created before t. The closure walks toward older references, so this bounds verification to a recent window.

func WithExternalContent

func WithExternalContent() VerifyOption

WithExternalContent also fetches and verifies externalized content (default: inline content only — external blobs can be gigabytes).

func WithMaxClaims

func WithMaxClaims(n int) VerifyOption

WithMaxClaims caps the walk at n claims processed (0 = unlimited).

func WithMaxDepth

func WithMaxDepth(n int) VerifyOption

WithMaxDepth bounds the closure walk to depth n (0 = full closure).

func WithOnError

func WithOnError(fn func(Failure)) VerifyOption

WithOnError registers a callback fired as each failure is found. It runs on the run's goroutine, so it must be cheap and concurrency-safe.

func WithSkipRules

func WithSkipRules(names ...string) VerifyOption

WithSkipRules omits the verification rules named by VerifyRule.Name (see VerifyRuleSet), so a scan can drop expensive rules. Unknown names are ignored.

func WithStopAfter

func WithStopAfter(n int) VerifyOption

WithStopAfter stops the walk once n failures are found (1 = fail fast, 0 = verify everything).

func WithTrusted

func WithTrusted(fn func(Id) bool) VerifyOption

WithTrusted prunes the walk at any claim for which fn returns true, backed by whatever the caller has: a DB, a bloom filter, the Sequencer's committed set.

type VerifyRule

type VerifyRule struct{ Name, Rule string }

VerifyRule describes a registered verification rule: Name is the stable identifier (WithSkipRules), Rule the statement printed on violation.

func VerifyRuleSet

func VerifyRuleSet() []VerifyRule

VerifyRuleSet lists the verification rules, in application order — the menu a caller picks from when deciding what to skip.

type Where

type Where struct {
	And   []Where
	Or    []Where
	Not   *Where
	Field string      // leaf: the field tested
	Test  *Comparison // leaf: the comparison on Field
}

Where is a boolean tree: exactly one of And, Or, Not or a leaf (Field + Test).

type WireConstraints

type WireConstraints struct {
	Branches     []string // record 2, required
	Referencable []string // record 3, required
	Lifted       []string // record 4, empty asks for nothing
	Creatable    []string // record 5, empty asks for nothing
}

WireConstraints are the constraints a stream declares, one record each, all of them ahead of the payload so a receiver reads them before taking anything.

Branches and Referencable narrow: declaring them can only reduce what the contribution does. Lifted widens, so a receiver relaying a stream from a party it does not trust checks that record against what that party may ask for.

func (WireConstraints) Narrow

Narrow returns the constraints both w and o permit. Declaring restricts, so two declarations compose to their overlap and a relay adds its own limits by merging rather than by appending a second record.

func (WireConstraints) Options

func (w WireConstraints) Options() []ContributionOption

Options renders the declarations as the options the contribution opens under.

type WireKind

type WireKind uint64

WireKind tags what a record carries, so a reader switches once per record.

const (
	// WireClaim is [0, id, canonical claim CBOR, branch] — a claim and the branch
	// it joins, since a contribution may name several.
	WireClaim WireKind = 0
	// WireContent is [1, hash, blob bytes]. Content is addressed by its hash and
	// lives in the Universe unbranched, so it names no branch.
	WireContent WireKind = 1
	// WireBranches is [2, [branch, ...]] — the branches this contribution writes to.
	WireBranches WireKind = 2
	// WireReferencable is [3, [branch, ...]] — the branches, $archive or $universe it
	// may reference claims from.
	WireReferencable WireKind = 3
	// WireLifted is [4, [type, ...]] — the reserved node types it asks to create.
	WireLifted WireKind = 4
	// WireCreatable is [5, [branch, ...]] — the branches it may bring into being, a
	// branch the base does not carry being created by the merge that names it.
	WireCreatable WireKind = 5
)

type WireReader

type WireReader struct {
	// contains filtered or unexported fields
}

WireReader streams a contribution, decoding one record at a time.

func NewWireReader

func NewWireReader(r io.Reader) *WireReader

NewWireReader reads a contribution stream from r.

func (*WireReader) Constraints

func (wr *WireReader) Constraints() (WireConstraints, error)

Constraints are what the stream declares, read from its leading records alone — so a caller inspects them, and refuses what it will not grant, before taking the payload.

func (*WireReader) Err

func (wr *WireReader) Err() error

Err returns the first error that stopped the stream.

func (*WireReader) Next

func (wr *WireReader) Next() bool

Next decodes the next record, returning false at end of stream or on error. The kinds differ in arity, so the elements are taken raw and read per kind.

func (*WireReader) Record

func (wr *WireReader) Record() WireRecord

Record returns the record Next decoded.

type WireRecord

type WireRecord struct {
	Kind   WireKind
	Claim  Claim       // WireClaim: decoded under the id the record names
	Branch string      // WireClaim: the branch this claim joins
	Blob   ContentBlob // WireContent: the bytes, checked against the hash
}

WireRecord is one decoded record: Kind names which fields carry the payload.

type WireWriter

type WireWriter struct {
	// contains filtered or unexported fields
}

WireWriter writes a contribution stream. Records concatenate, so a contribution of any size streams without being buffered.

func NewWireWriter

func NewWireWriter(w io.Writer, cons WireConstraints) *WireWriter

NewWireWriter writes a contribution under cons, which heads the stream so a reader checks it before taking the rest.

func (*WireWriter) WriteClaim

func (ww *WireWriter) WriteClaim(branch string, c Claim) error

WriteClaim appends a claim joining branch, as the canonical record under its id — the bytes the id was signed over, so the receiver can verify against it.

func (*WireWriter) WriteContent

func (ww *WireWriter) WriteContent(b ContentBlob) error

WriteContent appends externalized content under its hash.

Directories

Path Synopsis
adapter
sequencer
package: adapter/sequencer / adapter type: adapter job: package root for Sequencer adapters — concrete implementations of the ranke.Sequencer write path limits: no implementation here; concrete Sequencers live in subpackages — the blocking single-threaded reference (-> adapter/sequencer/dev) and the concurrent write path (-> adapter/sequencer/concurrent)
package: adapter/sequencer / adapter type: adapter job: package root for Sequencer adapters — concrete implementations of the ranke.Sequencer write path limits: no implementation here; concrete Sequencers live in subpackages — the blocking single-threaded reference (-> adapter/sequencer/dev) and the concurrent write path (-> adapter/sequencer/concurrent)
sequencer/concurrent
package: adapter/sequencer/concurrent / adapter type: adapter job: the concurrent Sequencer — the paper's seven steps with 2–5 run in parallel off the sequencing thread, and steps 6–7 a serialised group commit folding a whole batch of contributions into ONE branch-table advance limits: single-process (the sequencing thread is a mutex, not consensus); its committed-id set grows with the archive; step 6 costs one closure test per contributed head, so a slow backend serialises there; no cross-branch merge, no limiting/expiry claims
package: adapter/sequencer/concurrent / adapter type: adapter job: the concurrent Sequencer — the paper's seven steps with 2–5 run in parallel off the sequencing thread, and steps 6–7 a serialised group commit folding a whole batch of contributions into ONE branch-table advance limits: single-process (the sequencing thread is a mutex, not consensus); its committed-id set grows with the archive; step 6 costs one closure test per contributed head, so a slow backend serialises there; no cross-branch merge, no limiting/expiry claims
sequencer/dev
package: adapter/sequencer/dev / testkit job: the dev Sequencer's contribution — steps 2–5 (filling, completing, verifying, persisting) behind the ranke.Contribution contract type: adapter limits: one contribution at a time, filled from one goroutine; merging is step 6 and belongs to the Sequencer (-> dev.go)
package: adapter/sequencer/dev / testkit job: the dev Sequencer's contribution — steps 2–5 (filling, completing, verifying, persisting) behind the ranke.Contribution contract type: adapter limits: one contribution at a time, filled from one goroutine; merging is step 6 and belongs to the Sequencer (-> dev.go)
storage
package: adapter/storage / blobstore type: logic job: the BlobStore seam — three byte primitives (Get/Put/Has) become a full ranke.Universe limits: no storage of its own; concrete blob backends live in sub-packages (-> adapter/fs, adapter/mem)
package: adapter/storage / blobstore type: logic job: the BlobStore seam — three byte primitives (Get/Put/Has) become a full ranke.Universe limits: no storage of its own; concrete blob backends live in sub-packages (-> adapter/fs, adapter/mem)
storage/adaptertest
package: adaptertest / conformance type: test job: black-box conformance suite exercising any ranke.Universe via the public API limits: no medium-specific scenarios; each adapter adds those alongside (-> adapter/storage/fs, adapter/storage/mem)
package: adaptertest / conformance type: test job: black-box conformance suite exercising any ranke.Universe via the public API limits: no medium-specific scenarios; each adapter adds those alongside (-> adapter/storage/fs, adapter/storage/mem)
storage/fs
package: fs / persistence type: adapter job: stores claims, content blobs and bookmark records as files in a single flat directory limits: no indexing or codec logic; a BlobStore behind storage.NewBlobUniverse (-> adapter)
package: fs / persistence type: adapter job: stores claims, content blobs and bookmark records as files in a single flat directory limits: no indexing or codec logic; a BlobStore behind storage.NewBlobUniverse (-> adapter)
storage/mem
package: mem / persistence type: adapter job: ephemeral in-memory Universe for tests and short-lived sessions — a thin alias to the core reference implementation limits: nothing persists across process restarts (-> adapter/fs)
package: mem / persistence type: adapter job: ephemeral in-memory Universe for tests and short-lived sessions — a thin alias to the core reference implementation limits: nothing persists across process restarts (-> adapter/fs)
storage/minimal
package: minimal / persistence type: adapter job: the smallest possible Universe — a map[string][]byte behind the BlobStore seam limits: ephemeral, unsynchronized illustration of the minimum an adapter must implement (-> adapter)
package: minimal / persistence type: adapter job: the smallest possible Universe — a map[string][]byte behind the BlobStore seam limits: ephemeral, unsynchronized illustration of the minimum an adapter must implement (-> adapter)
storage/neo4j
package: neo4j / persistence-cache type: logic job: map ranke claims to/from neo4j node + relationship property maps (the cache's on-graph shape) limits: pure mapping, no I/O; the Cypher and driver calls live in neo4j.go.
package: neo4j / persistence-cache type: logic job: map ranke claims to/from neo4j node + relationship property maps (the cache's on-graph shape) limits: pure mapping, no I/O; the Cypher and driver calls live in neo4j.go.
storage/partition
package: partition / bookmarks type: logic job: the partition's 𝒰_hist — every shard holds the whole bookmark list, the one place this adapter broadcasts where it would otherwise route by key limits: carries no policy beyond that choice; the record and its rules are the library's (-> partition.go, bookmark_store in the library)
package: partition / bookmarks type: logic job: the partition's 𝒰_hist — every shard holds the whole bookmark list, the one place this adapter broadcasts where it would otherwise route by key limits: carries no policy beyond that choice; the record and its rules are the library's (-> partition.go, bookmark_store in the library)
storage/redis
package: redis / persistence-cache type: adapter job: stores claims and content blobs as keys in redis — a fast, shared, in-memory byte cache tier limits: a storage.BlobStore behind storage.NewBlobUniverse (-> adapter); not authoritative — a cache under a query layer and above a durable store (paper §Composing Universes)
package: redis / persistence-cache type: adapter job: stores claims and content blobs as keys in redis — a fast, shared, in-memory byte cache tier limits: a storage.BlobStore behind storage.NewBlobUniverse (-> adapter); not authoritative — a cache under a query layer and above a durable store (paper §Composing Universes)
storage/rest
package: rest / persistence type: adapter job: stores claims and content blobs over a tiny HTTP blob API (GET/PUT/HEAD) limits: no auth, retries, or pagination; just the BlobStore primitives over HTTP (-> adapter)
package: rest / persistence type: adapter job: stores claims and content blobs over a tiny HTTP blob API (GET/PUT/HEAD) limits: no auth, retries, or pagination; just the BlobStore primitives over HTTP (-> adapter)
storage/s3
package: s3 / persistence type: adapter job: stores claims and content blobs as objects in an S3 bucket limits: no indexing or codec logic; a BlobStore behind storage.NewBlobUniverse (-> adapter)
package: s3 / persistence type: adapter job: stores claims and content blobs as objects in an S3 bucket limits: no indexing or codec logic; a BlobStore behind storage.NewBlobUniverse (-> adapter)
storage/sqlite
package: sqlite / persistence type: adapter job: stores claims and content blobs as rows in a single SQLite table limits: no indexing or codec logic; a BlobStore behind storage.NewBlobUniverse (-> adapter)
package: sqlite / persistence type: adapter job: stores claims and content blobs as rows in a single SQLite table limits: no indexing or codec logic; a BlobStore behind storage.NewBlobUniverse (-> adapter)
storage/stack
package: stack / bookmarks type: logic job: the stack's 𝒰_hist — a delegating BookmarkStore that puts and gets through the same tier rules, fan-out and fall-through the claim path uses limits: carries no policy of its own; every decision is stack.go's, called not copied (-> stack.go, bookmark_store in the library)
package: stack / bookmarks type: logic job: the stack's 𝒰_hist — a delegating BookmarkStore that puts and gets through the same tier rules, fan-out and fall-through the claim path uses limits: carries no policy of its own; every decision is stack.go's, called not copied (-> stack.go, bookmark_store in the library)
cmd
ranke command
package: cmd/ranke / cli type: cmd job: read-only CLI to inspect filesystem-backed Ranke-Graph archives limits: no mutation commands; building claims lives in tests + downstream apps (-> tests)
package: cmd/ranke / cli type: cmd job: read-only CLI to inspect filesystem-backed Ranke-Graph archives limits: no mutation commands; building claims lives in tests + downstream apps (-> tests)
scenariodoc command
package: cmd/scenariodoc / cli type: cmd job: regenerate conformance/scenarios/*/scenario.md from each scenario's main.go comments limits: doesn't run scenarios; only extracts their doc comments (-> conformance/scenarios)
package: cmd/scenariodoc / cli type: cmd job: regenerate conformance/scenarios/*/scenario.md from each scenario's main.go comments limits: doesn't run scenarios; only extracts their doc comments (-> conformance/scenarios)
test command
package: cmd/test / cli type: cmd job: `test` — a cobra CLI for running the project's customizable test tooling; today one subcommand, `performance`, drives the backend matrix limits: a thin flag→Config→RunMatrix adapter; the matrix logic lives in tests/performance (shared with `go test`)
package: cmd/test / cli type: cmd job: `test` — a cobra CLI for running the project's customizable test tooling; today one subcommand, `performance`, drives the backend matrix limits: a thin flag→Config→RunMatrix adapter; the matrix logic lives in tests/performance (shared with `go test`)
vectors command
package: cmd/vectors / bookmarks type: cmd job: the 𝒰_hist cases — one valid bookmark list every implementation must open, plus a record per rule a bookmark can break: its envelope, its signature, its slot, its k, and its list's contiguity limits: builds records, not lists on disk; the head each one records comes from the conformance graph (-> graph.go)
package: cmd/vectors / bookmarks type: cmd job: the 𝒰_hist cases — one valid bookmark list every implementation must open, plus a record per rule a bookmark can break: its envelope, its signature, its slot, its k, and its list's contiguity limits: builds records, not lists on disk; the head each one records comes from the conformance graph (-> graph.go)
conformance
helpers
package: helpers / conformance type: test job: shared boilerplate for ../scenarios/<n>/main.go (bundle setup, reload+verify, id collection) limits: builds no claims itself; scenarios do that inline (-> conformance/scenarios)
package: helpers / conformance type: test job: shared boilerplate for ../scenarios/<n>/main.go (bundle setup, reload+verify, id collection) limits: builds no claims itself; scenarios do that inline (-> conformance/scenarios)
scenarios/01_personal_graph command
package: conformance/scenarios/01_personal_graph / scenario type: cmd job: build & persist the scenario-01 personal-graph data bundle limits: doesn't verify variant reproductions; that's the run.sh harness (-> conformance/helpers)
package: conformance/scenarios/01_personal_graph / scenario type: cmd job: build & persist the scenario-01 personal-graph data bundle limits: doesn't verify variant reproductions; that's the run.sh harness (-> conformance/helpers)
scenarios/02_agent_analyses command
package: conformance/scenarios/02_agent_analyses / scenario type: cmd job: build & persist the scenario-02 agent-analyses data bundle limits: doesn't verify variant reproductions; that's the run.sh harness (-> conformance/helpers)
package: conformance/scenarios/02_agent_analyses / scenario type: cmd job: build & persist the scenario-02 agent-analyses data bundle limits: doesn't verify variant reproductions; that's the run.sh harness (-> conformance/helpers)
scenarios/03_agent_corrects_agent command
package: conformance/scenarios/03_agent_corrects_agent / scenario type: cmd job: build & persist the scenario-03 agent-corrects-agent data bundle limits: doesn't verify variant reproductions; that's the run.sh harness (-> conformance/helpers)
package: conformance/scenarios/03_agent_corrects_agent / scenario type: cmd job: build & persist the scenario-03 agent-corrects-agent data bundle limits: doesn't verify variant reproductions; that's the run.sh harness (-> conformance/helpers)
internal
exclusive
package: internal/exclusive / lock type: tool job: a cross-process lock, so tests sharing one live service serialise without `go test -p 1` limits: test support; it orders access and wipes nothing itself
package: internal/exclusive / lock type: tool job: a cross-process lock, so tests sharing one live service serialise without `go test -p 1` limits: test support; it orders access and wipes nothing itself
vectors
package: internal/vectors / check type: logic job: runs an artifact set's cases through the library, so the generator's self-check and the conformance gate ask the same question of the same code limits: reports what the library did; deciding whether that is conformant is the caller's
package: internal/vectors / check type: logic job: runs an artifact set's cases through the library, so the generator's self-check and the conformance gate ask the same question of the same code limits: reports what the library did; deciding whether that is conformant is the caller's
package: keysource / logic type: io job: the one grammar an app resolves a key argument through — a file, an environment variable, stdin or a prompt — with the rules that keep the material off disk and off the command line limits: yields bytes and reads nothing into a key; what a PEM means is the library's (-> ranke.ParseKeypair).
package: keysource / logic type: io job: the one grammar an app resolves a key argument through — a file, an environment variable, stdin or a prompt — with the rules that keep the material off disk and off the command line limits: yields bytes and reads nothing into a key; what a PEM means is the library's (-> ranke.ParseKeypair).
package: queries / contributors type: logic job: the reads a caller needs before it can write anything — a branch's contributors, and the one carrying a given public key limits: reads an Archive snapshot and nothing else; every helper is ordinary RQL, so this is a worked example as much as a library (-> query for the language itself)
package: queries / contributors type: logic job: the reads a caller needs before it can write anything — a branch's contributors, and the one carrying a given public key limits: reads an Archive snapshot and nothing else; every helper is ordinary RQL, so this is a worked example as much as a library (-> query for the language itself)
scripts
rqlgate command
package: scripts/rqlgate / tool type: tool job: compare rql.schema.json against the Go query implementation — every constraint the schema states is probed against DecodeQuery, and an unknown keyword fails the gate limits: reads the schema and the ranke wire decoder; asserts nothing about the spec prose (-> scripts/rule-citations.sh for rule ids)
package: scripts/rqlgate / tool type: tool job: compare rql.schema.json against the Go query implementation — every constraint the schema states is probed against DecodeQuery, and an unknown keyword fails the gate limits: reads the schema and the ranke wire decoder; asserts nothing about the spec prose (-> scripts/rule-citations.sh for rule ids)
package: tests / conformance type: test job: small error-checking helper shared across the integration suite limits: no test logic itself (-> tests/scenarios.go, tests/integration.go)
package: tests / conformance type: test job: small error-checking helper shared across the integration suite limits: no test logic itself (-> tests/scenarios.go, tests/integration.go)
backends
package: tests/backends / integration type: tool job: the shared backend matrix — one named opener per storage configuration, each spinning up a FRESH, EMPTY local instance (podman pods for s3/redis/neo4j, or host-native services) limits: wiring only — no test logic and no assertions; the rows are consumed by the conformance matrix (-> tests/matrix) and the timing harness (-> tests/performance)
package: tests/backends / integration type: tool job: the shared backend matrix — one named opener per storage configuration, each spinning up a FRESH, EMPTY local instance (podman pods for s3/redis/neo4j, or host-native services) limits: wiring only — no test logic and no assertions; the rows are consumed by the conformance matrix (-> tests/matrix) and the timing harness (-> tests/performance)
generator
package: tests/generator / testkit type: tool job: Generate — build a deterministic kitchen-sink Ranke-Archive from a Spec, driving the dev Sequencer's write path; return a Manifest naming every corner limits: builds via the public ranke API + the dev testkit adapters; the caller supplies the Universe (-> adapter); asserts nothing (-> tests).
package: tests/generator / testkit type: tool job: Generate — build a deterministic kitchen-sink Ranke-Archive from a Spec, driving the dev Sequencer's write path; return a Manifest naming every corner limits: builds via the public ranke API + the dev testkit adapters; the caller supplies the Universe (-> adapter); asserts nothing (-> tests).
helpers
package: tests/helpers / testkit type: tool job: shared helpers for driving a ranke.Sequencer from tests and test tooling limits: the contract only — no fixtures, no assertions, no backend knowledge (-> tests, tests/backends)
package: tests/helpers / testkit type: tool job: shared helpers for driving a ranke.Sequencer from tests and test tooling limits: the contract only — no fixtures, no assertions, no backend knowledge (-> tests, tests/backends)
matrix
package: tests/matrix / conformance type: test job: the cross-backend agreement matrix — build one deterministic archive into every available backend, run the shared RQL corpus against each, and assert every backend's answer matches the reference's limits: correctness only, never timing (-> tests/performance); it asks whether an answer is right, never how a backend produced it — routing, lowering, and cache tiers are the backend's business
package: tests/matrix / conformance type: test job: the cross-backend agreement matrix — build one deterministic archive into every available backend, run the shared RQL corpus against each, and assert every backend's answer matches the reference's limits: correctness only, never timing (-> tests/performance); it asks whether an answer is right, never how a backend produced it — routing, lowering, and cache tiers are the backend's business
performance
package: tests/performance / integration type: tool job: the reusable performance-matrix harness — generate a deterministic size-N archive into each backend and time the chapters (write / verify / random access), reporting per-step latency distributions limits: decoupled from the testing package so both the _test.go entrypoint and cmd/test can drive it; timing only — the backend rows come from tests/backends and correctness belongs to tests/matrix
package: tests/performance / integration type: tool job: the reusable performance-matrix harness — generate a deterministic size-N archive into each backend and time the chapters (write / verify / random access), reporting per-step latency distributions limits: decoupled from the testing package so both the _test.go entrypoint and cmd/test can drive it; timing only — the backend rows come from tests/backends and correctness belongs to tests/matrix
rql
package: tests/rql / integration type: tool job: the shared RQL corpus — a broad set covering each axis of the read language (traversal, filter, shape, order, bound) for the conformance matrix, plus the small subset the timing harness measures limits: queries and their names only; executing them and comparing answers live alongside (-> run.go), and the backend rows come from tests/backends
package: tests/rql / integration type: tool job: the shared RQL corpus — a broad set covering each axis of the read language (traversal, filter, shape, order, bound) for the conformance matrix, plus the small subset the timing harness measures limits: queries and their names only; executing them and comparing answers live alongside (-> run.go), and the backend rows come from tests/backends

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL