ranke

package module
v0.25.1 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2026 License: Apache-2.0 Imports: 26 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/flocko-motion/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/flocko-motion/ranke-go.

Install

go get github.com/flocko-motion/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/flocko-motion/ranke-go"
	"github.com/flocko-motion/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 / 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 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 / history type: logic job: the History contract — persists the archive head-id timeline k₀…kₙ (append + read-back) — plus SpliceHistory, the graft a tagging pass uses to update it limits: interface only; concrete timeline stores live in adapters (-> adapter/history)

package: ranke / history type: logic job: HistoryItem — one immutable entry in the head-id timeline (head id, height, append time) plus its constructor and getters limits: a value type; the timeline it belongs to is History (-> history)

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 / 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 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 limits: needs no Universe read and no reference resolution, so it judges a record that arrived as bytes or through AssembleClaim 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")

	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}")
	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")
	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")

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

	ErrCreatedAtNotMonotone = errors.New("ranke.verify: claim is dated before a claim it references")

	// --- 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`)")
)

EdgeClasses lists every edge class, for validation and enumeration.

Functions

func AdmitCreatedAt added in v0.12.0

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 added in v0.3.0

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 CheckDeletable added in v0.13.0

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 added in v0.11.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.5.0

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 added in v0.14.0

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 added in v0.4.0

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 GetClaimHeight added in v0.3.0

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

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

func GetTag added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

func Hops(n int) *int

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

func InClosure added in v0.3.0

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 IsTextEncoding added in v0.3.0

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) (ed25519.PrivateKey, error)

LoadEd25519PrivateKeyPEM loads an Ed25519 private key from a PKCS#8 PEM file (`openssl genpkey -algorithm ed25519`).

func LoadEd25519PublicKeyPEM

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

LoadEd25519PublicKeyPEM loads an Ed25519 public key from a SubjectPublicKeyInfo PEM file (`openssl pkey -pubout`).

func MarshalCBOR added in v0.15.5

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 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 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 added in v0.3.0

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 added in v0.3.0

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 SyncedNow added in v0.3.0

func SyncedNow(id Id) <-chan SyncResult

SyncedNow returns a closed channel with an immediate success.

func TemporalMidpointMs added in v0.25.0

func TemporalMidpointMs(s string) (int64, bool)

TemporalMidpointMs is edtfMidpointMs, exported for a storage layer to project at write time — a native `compare: temporal` ORDER BY sorts on the projection rather than parsing EDTF itself (`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 ValidateQuery added in v0.14.0

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 added in v0.3.0

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 added in v0.3.0

func WithDetail(sentinel error, detail string) error

WithDetail returns sentinel with detail appended lazily.

func WithReport added in v0.3.0

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 added in v0.3.0

func Wrap(sentinel, cause error) error

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

func WrapDetail added in v0.3.0

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 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 added in v0.2.0

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
	// 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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 is the claim's generation number (§4.1): 0 on an initial claim, else
	// 1 + max(reference heights), which a referencing claim must declare.
	Height uint64

	// 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 added in v0.22.0

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 added in v0.3.0

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

WithAutoHeight makes Sign read each referenced claim's committed height from u and set 1 + max (0 with no references), exclusive with WithHeight.

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 added in v0.25.0

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 added in v0.25.1

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

func (b ClaimBuilder) WithHeight(h uint64) ClaimBuilder

WithHeight sets the generation number (§4.1) — 1 + max over the referenced heights, or 0 for an initial claim. The verifier re-derives and enforces it.

func (ClaimBuilder) WithInlineContent added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.10.0

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 added in v0.10.0

func NewConstraints(opts ...ContributionOption) Constraints

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

func (Constraints) AdmitBranch added in v0.12.0

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 added in v0.11.0

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 added in v0.10.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.10.0

type ContributionOption func(*Constraints)

ContributionOption sets one constraint.

func WithCreatableBranches added in v0.12.0

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 added in v0.10.0

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 added in v0.11.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

func (f EdgeFilterFieldValue) IsEdgeFilter() bool

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

func (EdgeFilterFieldValue) MatchEdge added in v0.3.0

func (f EdgeFilterFieldValue) MatchEdge(e Edge) bool

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

func (EdgeFilterFieldValue) MatchNode added in v0.3.0

func (f EdgeFilterFieldValue) MatchNode(Node) bool

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

type EdgeFilterType added in v0.3.0

type EdgeFilterType struct {
	Type string
}

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

func (EdgeFilterType) IsEdgeFilter added in v0.3.0

func (f EdgeFilterType) IsEdgeFilter() bool

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

func (EdgeFilterType) MatchEdge added in v0.3.0

func (f EdgeFilterType) MatchEdge(e Edge) bool

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

func (EdgeFilterType) MatchNode added in v0.3.0

func (f EdgeFilterType) MatchNode(Node) bool

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

type EdgeFilterTypes added in v0.3.0

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 added in v0.3.0

func (f EdgeFilterTypes) IsEdgeFilter() bool

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

func (EdgeFilterTypes) MatchEdge added in v0.3.0

func (f EdgeFilterTypes) MatchEdge(e Edge) bool

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

func (EdgeFilterTypes) MatchNode added in v0.3.0

func (f EdgeFilterTypes) MatchNode(Node) bool

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

type EdgeParts added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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.

type Field added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

type GetOption func(*getConfig)

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

func WithNotDiffMaterialized added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

func NewHeightCache() *HeightCache

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

func (*HeightCache) Get added in v0.3.0

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

Get returns the cached height for id, if present.

func (*HeightCache) GetClaimHeights added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 History added in v0.3.0

type History interface {
	// Append records id as the new head, returning the stamped item.
	Append(ctx context.Context, id Id, height int, revision int) (HistoryItem, error)
	// Latest returns kₙ, or the zero item when the timeline is empty.
	Latest(ctx context.Context) (HistoryItem, error)
	// At returns kᵢ; an out-of-range i is an error.
	GetAtRevision(ctx context.Context, revision int) (HistoryItem, error)
	GetBulk(ctx context.Context, fromRevision, toExcludingRevision int) ([]HistoryItem, error)
	// Len returns the number of entries (n+1).
	Len(ctx context.Context) (int, error)
	Close() error
}

History persists the head-id timeline k₀…kₙ. Append records a new head, assigning its height and stamping the time; the rest read the timeline back. Oldest first: At(0) is k₀, Latest is kₙ, List runs k₀…kₙ.

type HistoryItem added in v0.3.0

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

HistoryItem is one entry in the head-id timeline: the head id, its height (position in the sequence, i=0 the oldest), and the time it was appended. Fields are unexported; construct one with NewHistoryItem and read via the getters.

func NewHistoryItem added in v0.3.0

func NewHistoryItem(id Id, revision int, height int, timestamp time.Time) HistoryItem

NewHistoryItem builds a HistoryItem. It lets adapters outside package ranke reconstruct an item from persisted storage (id + its recorded height and append time).

func SpliceHistory added in v0.3.0

func SpliceHistory(existing, tagged []HistoryItem) []HistoryItem

SpliceHistory grafts tagged onto existing at the revision tagged begins at. TagArchive returns only the revisions it just (re)tagged — its first item's GetRevision is the splice point — so this keeps existing's entries below that revision and replaces everything from there with tagged. That makes it correct both for a plain forward advance (tagged starts one past the end, so nothing is dropped) and for a re-tag from an earlier revision (the superseded tail is discarded). An empty tagged leaves existing unchanged.

Entries are matched by GetRevision, not slice position, so a non-contiguous or unordered existing timeline still splices at the right boundary.

func TagArchive added in v0.3.0

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

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

func (HistoryItem) GetHeight added in v0.3.0

func (h HistoryItem) GetHeight() int

GetHeight returns the entry's height

func (HistoryItem) GetId added in v0.3.0

func (h HistoryItem) GetId() Id

GetId returns the head id this entry records.

func (HistoryItem) GetRevision added in v0.3.0

func (h HistoryItem) GetRevision() int

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

func (HistoryItem) GetTimestamp added in v0.3.0

func (h HistoryItem) GetTimestamp() time.Time

GetTimestamp returns the time the head was appended.

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 ParseId

func ParseId(s string) (Id, error)

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

func StrandedByDeletion added in v0.13.0

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 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) (Keypair, error)

LoadPrivateKey loads an Ed25519 PKCS#8 PEM private key from path and pre-computes its multikey-encoded public key.

type Limit added in v0.3.0

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

Limit bounds a read.

type MergableContribution added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.14.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.14.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.19.1

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 added in v0.3.0

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 added in v0.19.1

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.4.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

type Sequencer interface {
	// 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
	// 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, advancing the head (step 6),
	// and returns a Receipt.
	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 added in v0.3.0

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 added in v0.3.0

type SortDir string

SortDir is a sort direction.

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

type StorageTier added in v0.3.0

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 added in v0.20.0

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 added in v0.20.0

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 added in v0.3.0

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 added in v0.3.0

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

	// 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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

type VerifyOption func(*verifyConfig)

VerifyOption configures a verification run.

func WithCreatedAfter added in v0.3.0

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 added in v0.3.0

func WithExternalContent() VerifyOption

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

func WithMaxClaims added in v0.3.0

func WithMaxClaims(n int) VerifyOption

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

func WithMaxDepth added in v0.3.0

func WithMaxDepth(n int) VerifyOption

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

func WithOnError added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

func WithStopAfter(n int) VerifyOption

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

func WithTrusted added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

func VerifyRuleSet() []VerifyRule

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

type Where added in v0.3.0

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 added in v0.11.0

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 added in v0.11.0

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 added in v0.11.0

func (w WireConstraints) Options() []ContributionOption

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

type WireKind added in v0.5.0

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 added in v0.5.0

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

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

func NewWireReader added in v0.5.0

func NewWireReader(r io.Reader) *WireReader

NewWireReader reads a contribution stream from r.

func (*WireReader) Constraints added in v0.11.0

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 added in v0.5.0

func (wr *WireReader) Err() error

Err returns the first error that stopped the stream.

func (*WireReader) Next added in v0.5.0

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 added in v0.5.0

func (wr *WireReader) Record() WireRecord

Record returns the record Next decoded.

type WireRecord added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

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

WriteContent appends externalized content under its hash.

Directories

Path Synopsis
adapter
history/dev
package: adapter/history/dev / testkit type: adapter job: an in-memory History for tests and development — the head-id timeline k₀…kₙ held in a slice, stamped from an injected deterministic Clock limits: not durable and not concurrent (single-threaded, no locks); stores ids only, like adapter/history/mem but with a controllable clock so a generated archive's history is reproducible
package: adapter/history/dev / testkit type: adapter job: an in-memory History for tests and development — the head-id timeline k₀…kₙ held in a slice, stamped from an injected deterministic Clock limits: not durable and not concurrent (single-threaded, no locks); stores ids only, like adapter/history/mem but with a controllable clock so a generated archive's history is reproducible
history/file
package: file / coordination type: adapter job: file-backed History storing the head-id timeline as a text file, one "id timestamp" line per entry, atomic-rename writes limits: single-node only (-> a db timeline for distributed); stores ids only (-> ranke)
package: file / coordination type: adapter job: file-backed History storing the head-id timeline as a text file, one "id timestamp" line per entry, atomic-rename writes limits: single-node only (-> a db timeline for distributed); stores ids only (-> ranke)
history/mem
package: mem / coordination type: adapter job: in-memory History — the head-id timeline held in a slice, lost on process exit limits: not durable (-> adapter/history/file); stores ids only (-> ranke)
package: mem / coordination type: adapter job: in-memory History — the head-id timeline held in a slice, lost on process exit limits: not durable (-> adapter/history/file); stores ids only (-> ranke)
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 six steps with 2–5 run in parallel off the sequencing thread, and step 6 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 six steps with 2–5 run in parallel off the sequencing thread, and step 6 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 / 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 / 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 and content blobs 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 and content blobs 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 / persistence type: adapter job: shard one Universe across N backends — each key routed to a shard by hash(id) mod N (paper §Composing Universes) limits: no storage of its own (-> the shard Universes); N is fixed (consistent-hashing / resharding not modelled)
package: partition / persistence type: adapter job: shard one Universe across N backends — each key routed to a shard by hash(id) mod N (paper §Composing Universes) limits: no storage of its own (-> the shard Universes); N is fixed (consistent-hashing / resharding not modelled)
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 / persistence type: logic job: the background filler — a bounded, deduplicating, lossy batch queue for opportunistic cache writes that must never slow or fail a read limits: puts claims into layers the stack chose; deciding WHICH layers and WHY is the router's (-> stack.go, query.go)
package: stack / persistence type: logic job: the background filler — a bounded, deduplicating, lossy batch queue for opportunistic cache writes that must never slow or fail a read limits: puts claims into layers the stack chose; deciding WHICH layers and WHY is the router's (-> stack.go, query.go)
package: client / transport type: adapter job: the HTTP client for a running ranke-db — one credential, one base URL, and the request plumbing every endpoint call shares, including the server's machine-readable error codes limits: transport only; what the endpoints mean is the OpenAPI contract's and what travels over them is the library's (-> codec_wire, query_codec)
package: client / transport type: adapter job: the HTTP client for a running ranke-db — one credential, one base URL, and the request plumbing every endpoint call shares, including the server's machine-readable error codes limits: transport only; what the endpoints mean is the OpenAPI contract's and what travels over them is the library's (-> codec_wire, query_codec)
cmd
ranke command
package: main / 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: main / 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: main / 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: main / 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: main / 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: main / 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: main / vectors_broken type: cmd job: the records every implementation must reject, each isolating one failure — a wrong id, a record stored bare where an envelope belongs, an unresolvable contributor, a declared height that is not the derived one, content that misses its hash limits: derives from the toy graph (-> graph.go); each case breaks one thing, so a rejection names a cause
package: main / vectors_broken type: cmd job: the records every implementation must reject, each isolating one failure — a wrong id, a record stored bare where an envelope belongs, an unresolvable contributor, a declared height that is not the derived one, content that misses its hash limits: derives from the toy graph (-> graph.go); each case breaks one thing, so a rejection names a cause
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: main / 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: main / 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: main / 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: main / 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: main / 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: main / 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
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