Skip to content

Epistemic OS Hardening — current capability catalog

Purpose of this page. This is the code-verified, line-anchored catalog of every capability the "Epistemic OS Hardening" program (Phase 0 → Phase 3 + the "exceed" tracks) shipped into epistemic-graph. It exists so a static audit can use CHANGELOG.md for release history and docs/capabilities.md for the operation-by-operation parity matrix) can read what is actually true of the shipped code without re-deriving it from source. Where the program's original intent and the shipped reality diverge, this page documents the shipped reality — every claim below is kept aligned with current crates/, src/, and Cargo.toml, not inferred from a design document or historical CHANGELOG prose.

How this page relates to the other capability docs (read this before citing any of them):

  • docs/capabilities.generated.md is the machine-checked ledger — one row per wire Method, generated by cargo run -p eg-capabilities --features canonical-ledger --bin gen_ledger from the eleven domain-owned ROWS declarations and their sole deterministic registry iterator in crates/eg-capabilities/src/domains/mod.rs. It is authoritative for mutates / durability-domain / authz-action / idempotent / audited / emits-CDC / txn-participation facts, per method.
  • docs/capabilities.md is the hand-maintained, per-surface parity matrix (SQL / SPARQL / Cypher / GraphQL / vector / …) — coarser-grained, answers "which surface supports which operation", not per-method security semantics.
  • This page is the per-capability, program-narrative catalog: for each thing the hardening program built, what it is, where it lives, whether it is default-on or opt-in (and the exact feature name/env var), how to exercise it, and its honest limitations. It goes deeper than the CHANGELOG's release-note prose and cross-links into the other two rather than duplicating their tables.
  • Release-certification evidence is tracked separately from source completeness: hardware, multi-host soak, and live external-service runs prove a particular build in a particular environment; they are not described here as missing source features.

Verification discipline for this page: every code anchor below (file path, item name, env var, feature name, exact count) was grep/read-confirmed in this worktree. Counts that are mechanically derived are intentionally not copied into prose. Use the generated ledger and exhaustive tests so a new protocol method cannot make this page's hard-coded count silently stale. Environment-dependent evidence is called out in Release-certification evidence.


Phase 0 — Trustworthy core

Phase 0 (shipped in 2.19.0, with one closeout item — the mutation-gateway rollout — completed in 2.20.0) makes the engine's own mutation/durability/security machinery internally self-consistent and machine-checked, rather than relying on hand-maintained classifiers agreeing with each other by convention.

The generated capability ledger (eg-capabilities)

  • What it is: a complete, mechanically enforced MethodPolicy for every variant of the wire-protocol Method enum (crates/eg-types/src/protocol.rs). The crate crates/eg-capabilities (workspace member, crates/eg-capabilities/src/lib.rs) declares:
    pub struct MethodPolicy {
        pub mutates: bool,
        pub durability_domain: DurabilityDomain,   // durable state domains | VolatileControl | None
        pub authz_action: &'static str,             // "<domain>:<verb>" scope string
        pub idempotent: bool,
        pub audited: bool,
        pub emits_cdc: bool,
        pub txn_participation: TxnParticipation,    // Atomic | Saga | Snapshot | None
    }
    pub fn method_policy_entries()
        -> impl Iterator<Item = (&'static str, MethodPolicy, &'static str)>;
    pub fn policy(m: &Method) -> MethodPolicy;
    
    The domain-owned registry is joined against the complete protocol-method inventory; a missing or duplicate Method declaration fails the static and Rust consistency gates (CONCEPT:EG-P0-1).
  • Inventory invariant: crates/eg-capabilities/tests/consistency.rs checks the feature-selected canonical registry iterator. The generated ledger is the current count and policy inventory; this narrative does not duplicate that changing number.
  • Default-on or opt-in: the crate is a workspace member but is not linked independently — it is pulled in only via the facade's server feature (server = ["dep:tokio", "dep:clap", "dep:tracing-subscriber", "dep:async-trait", "dep:eg-capabilities"], Cargo.toml), because the one consumer (the mutation gateway, below) is server-only.
  • How to exercise it: cargo run -p eg-capabilities --features canonical-ledger --bin gen_ledger regenerates docs/capabilities.generated.md from the live domain registry (crates/eg-capabilities/src/bin/gen_ledger.rs).
  • Honest limitation: authz_action, idempotent, and txn_participation are judgment calls — the crate's own doc comment on MethodPolicy says so plainly: there is no pre-existing classifier in the codebase to cross-check them against, unlike mutates/durability_domain (cross-checked against access.rs and the canonical commit gateway) and audited/emits_cdc (cross-checked against audit.rs/cdc.rs).

The MutationPlan commit gateway (src/server/mutation.rs)

  • What it is: the single place a routed mutation's authz + durable write + CDC emission happen together, driven by eg_capabilities::policy() rather than a re-hardcoded classifier. MutationPlan is populated FROM the ledger; commit_mutation (and, for the runtime-conditional family, commit_conditional_mutation / commit_conditional_mutation_async) is the one call site.
  • Exhaustive rollout state: every mutating method in the live ledger is assigned exactly once to the canonical gateway or to a named domain-owned transaction protocol. OPEN_NOT_JUSTIFIED is machine-checked empty by mutation::tests::gateway_routed_set_matches_mutating_policy_surface; adding an uncovered mutating method is a test failure. The test, not a prose count, is the current inventory.
  • RunRules was found, during this rollout, to be a policy correction, not a route: handle_run_rules reasons over an off-lock snapshot and returns inferred triples with no writeback (unlike its sibling RunDatalogReasoning, which materializes in-place) — the ledger's mutates: true guess for it was wrong and was corrected rather than the method being routed.
  • What the gateway does NOT reimplement: the tamper-evident hash-chain audit log (src/audit.rs + redb_store::append_audit_entry) stays inside the durable-commit path (PersistenceBackend::record/record_durable) — the gateway delegates to it rather than risking a second, diverging chain.
  • Coalescer interaction (L18, closed): step 4 of commit_mutation routes a coalescable routed mutation (AddNode/RemoveNode/AddEdge/RemoveEdge) through the single WriteCoalescerRegistry::writer_for path, so the hot-path structural writes still batch — this was a real scale regression (found by the full --lib suite, not a filtered subset) that was fixed by re-entering the coalescer from inside the gateway rather than bypassing it.
  • Default-on or opt-in: not feature-gated separately — it is part of the server build (every server binary links it); there is no way to disable the gateway short of building without server.
  • Exercise it: cargo test -p epistemic-graph --lib mutation:: (in-crate); the cross-check test builds a real RedbBackend and reads the audit chain back.

Authoritative durability closure

  • Every method policy declares its state domain exhaustively. Any mutating method assigned DurabilityDomain::None fails the capability consistency gate. Explicit process/session-only transitions use VolatileControl, which never acknowledges a user-data commit or claims crash durability.
  • Graph mutations share one canonical mutation applier and authoritative redb commit path. Domain-specific stores provide equivalent transactional gateways.
  • Commit-before-ack is unconditional in served mode. There is no file-WAL, snapshot-backend, checkpoint, or write-behind branch.
  • Runtime-conditional mining and query methods resolve their write status before authorization and commit, so writeback=true cannot bypass durability.

Canonical time-series store (EG-P0-4)

  • What it is: unifies two stores (the authoritative graph shard and series.redb) that cannot share a WriteTransaction (exclusive-lock conflict). The fix is not a single-file merge — it is a documented two-hop write with a reconciliation pass closing the gap:
  • Graph + vector + blob-ref + measurement (+ lowered axiom/CONSTRUCT/plan-writeback) land in one authoritative-shard WriteTransaction (redb_store.rs::commit_crossmodal) — that set is the true cross-modal ACID boundary.
  • The measurement is then replayed into the served series.redb (state.tsdb_store) immediately after step 1 commits, so TsRange/TsAsofJoin/ TsWindow/TsGapFill/UQL Op::TsScan see it post-commit and post-restart.
  • This replay is a separate, non-atomic redb write on a different file: a crash strictly between the two commits leaves the measurement durable in the shard but not yet reflected in series.redb. L16 closes this window: RedbBackend:: reconcile_time_series runs once at boot, scans every authoritative shard's SERIES tables, and replays into series.redb any series whose point count hasn't converged — an exact multiset diff, so it is idempotent and never duplicates a point.
  • Process note (found during the ledger-closeout wave): the Phase-0 integration pass initially missed merging this workstream's branch to main (additive, so build gates passed regardless of the miss) — it was caught later by the L16 agent and restored. Worth remembering when auditing "is X really on main": additive changes can pass every gate while silently absent.
  • Default-on: part of the redb-backed durable server; not a separate feature flag.

Fail-secure verified request context (src/server/auth.rs)

  • Only posture: a served engine accepts only the eg2. envelope. It binds principal, tenant, audience, effective agent, roles, scopes, policy version, delegation, request/method/body, timestamp, nonce, and idempotency. Startup requires the security feature, a non-empty secret, configured audience/tenant/policy revision, durable replay state, and a non-empty trusted signer-key registry.
  • Fresh-store bootstrap: an empty durable identity/RBAC store permits only a signer-backed eg2. self-registration in __commons__ as System, with no teams, roles, or delegation and the single exact security:bootstrap scope. Once the first rule is durable, all requests use ordinary graph/admin policy.
  • Replay: EPISTEMIC_GRAPH_ENVELOPE_SKEW_SECS bounds timestamp skew; eg2. nonce acceptance uses the durable replay ledger and survives process restart.
  • Native federation: RemoteEngineSource now signs the live UQL/Cypher request with the same context. Empty secret, missing claims, invalid delegation, or a routable target without TLS fails before dialing.

Default-deny RLS (crates/eg-core/src/isolation.rs)

IsolationLayer filters every served GraphView before query execution. An unowned, undecodable, or untagged row is denied unless it is explicitly _visibility: "public" or its _owner/grant policy authorizes the verified agent. The served engine always uses this posture; there is no environment switch or builder path that weakens it. The result-cache key includes the full RLS context so a filtered result cannot cross an authority boundary.

Exhaustive audit + admin scopes (EG-P0-6)

  • 8 broker/stream mutating ops previously classified as read in access.rs (StreamDeclare/Publish/Trim/CommitOffset, PublishConfirmed, BrokerAck/ NackTag, PublishIdempotent) were a real security gap (L10) — a read-only caller could invoke them — and are now correctly classified as writes.
  • Admin scoping is driven off the ledger's authz_action field, checked once in dispatch_inner — covering every existing admin method and, by construction, every future one the ledger declares.
  • A 64-method exhaustive audit surface backs audit.rs's coverage.

Phase 1 — Universal modality & work contracts

eg-modality runtime registry + the 12-point TCK

  • The trait (crates/eg-modality/src/contract.rs): ModalityContract has 4 core methods every implementer must answer (storage_kind, to_rowset, txn_stage, cdc_topic), 4 default-empty methods overridden only where meaningful (provenance, evidence, policy_labels, analytics_ops), and — added by EG-P1-1 — 4 more default-"unsupported" hooks that make the TCK genuinely 12-for-12 (ingest_report, storage_stats, backup_selfcheck, recovery_selfcheck), plus one honesty escape hatch, tck_not_applicable(point) -> Option<&'static str>, for a point that genuinely does not apply to a given modality's nature (distinct from "not yet implemented").
  • The 12 TCK points (crates/eg-modality/src/tck.rs, TckPoint::ALL): SchemaAndIds, IngestStreaming, CodecUnsupportedFormat, StorageIndexStats, TypedQueryOperators, TxnOrSagaOutbox, CdcDeleteRetentionGc, TenantRowRegionPolicy, ProvenanceEvidenceLineage, BackupRestoreMigrateRecover, SingleNodeFailure, InteropWorkloadSmoke. Each point resolves to one of three honest TckStatus values: Pass, NotApplicable(reason) (counts as first-class — not a gap), or NotImplemented(reason) — there is deliberately no silent-skip / default-pass status. Production serving is stricter: TckReport::is_production_ready() accepts only 12 Pass results plus a passing native codec/normalization/index/query/resource probe; N/A or a missing probe is never a production exemption.
  • The registry: crate::register_modality/registered_modalities() (crates/eg-modality/src/registry.rs) is a OnceLock<Mutex<Vec<ModalityDescriptor>>> — a deliberate choice over linkme/inventory (neither is a workspace dependency anywhere; both would be the first proc-macro dependency eg-modality pulls in, which is meant to stay the thinnest possible seam). Important nuance, verified in the registry module's own doc comment: register_modality() is called from inside the #[cfg(test)] conformance-test module the modality_conformance_tests! macro generates — so registered_modalities() only populates when that crate's own test binary actually runs its conformance suite. This is a per-process, test-time registry, not the server's routing table ("Cross-process caveat" in the crate's own README). Production serving does not depend on this test registry: the closed ServedModalityKind enum dispatches document/image/audio/video to concrete decoders, and its capability operation rejects any runtime whose TCK is not 12/12 PASS with a passing native production probe.
  • The macro: modality_conformance_tests!($T) (exported from eg-modality) generates the full battery per implementer: round-trip losslessness, rollback symmetry, provenance-family non-panic, cdc_topic well-formedness, a malformed-payload decode-as-Err check, analytics_ops() well-formedness, and the TCK-report-generation
  • registration test. Invoked inside each crate's own #[cfg(feature = "contract")] module. Document/image/audio/video additionally expose the common serving runtime and are enabled by the main build's modality-serving feature.
  • Adoption is source-enforced: each ModalityContract implementation invokes modality_conformance_tests! behind its crate's contract feature. Fleet validation discovers the live invocations rather than trusting a copied crate count.

Arrow-backed KnowledgeBatch (eg-plan/knowledge_batch.rs)

  • What it is: a third RowSet projection (alongside the row-oriented RowSet and the graph-native KnowledgeSet) laying results out as a real Arrow RecordBatch (crates/eg-plan/src/knowledge_batch.rs). Columns (arrow_schema()/to_record_batch()/from_record_batch(), round-trip tested): id (Utf8), kind (Utf8), one score_<name> (Float32) per named score, confidence (Float64), evidence_kind (Utf8 — a filterable summary spanning the governed EvidenceLocus address kinds, see X-1 below), bitemporal valid_from/valid_until/tx_from/tx_to (Int64), plus list-typed provenance/policy/evidence-ref/contradiction/proof/transformation/alternative-id columns and a lazy blob_handle (Utf8) + has_payload (Boolean).
  • Reserved epistemic columns — populated, not stub (L22 closure): contradiction_ids (symmetric, via AuxEdgeIndex), proof_ids (via explain_belief), and transformation_ids (via GENERATED_BY:Activity edges) are populated by the mining and job write-back paths. ALTERNATIVE_TO is read-wired for explicitly modeled alternatives; calibration is populated by calibrated eg-jobs results and remains honestly null for producers that compute no calibration signal.
  • Native served currency: the facade knowledge-batch feature is folded into full. result_stream.rs adapts graph, SQL, RDF, vector, time-series, job, and cross-modal producers to bounded, snapshot-bound KnowledgeBatchEnvelopes. Every row receives verified tenant/policy/snapshot/query/derivation/evidence references, and write_arrow_ipc holds only one bounded batch at a time.
  • RowSet remains the internal operator algebra; served results cross the public query/job boundary as governed KnowledgeBatch, rather than as a family-specific terminal encoding.

eg-document / eg-image / eg-audio / eg-video + eg-alignment (EG-P1-3)

  • What it is: five leaf crates — eg-document (bounded UTF-8 page/layout/table/ private-lexeme extraction), eg-image (strict PNG pixel reconstruction and pHash), eg-audio (strict PCM/WAV waveform/spectral feature extraction), eg-video (strict ISOBMFF sample tables, timing, keyframes, and 24-bit raw-RGB samples), and eg-alignment (the shared EvidenceResolver trait). Each media crate exposes its normalized data model, header parser, governed contract, and native runtime; alignment exposes its resolver and graph. Each of the four data-model crates implements ModalityContract and GovernedModality and is registered in the EG-P1-1 TCK via its own contract feature.
  • Served status: all four expose ServedModalityRuntime<T> aliases and are enabled in the main build. The runtime provides atomic batch/stream ingest, OCC update, idempotent replay, governed delete/legal hold, policy-filtered paging, monotonic CDC, cold/restore lifecycle, snapshot recovery, rebuilt lexical/spatial/temporal/ multi-probe-signature postings, and exact typed native queries. Their fleet TCK reports are exactly 12 PASS and zero N/A with passing native probes.
  • Universal identity: eg-modality::ArtifactBundle ties Artifact, Occurrence, Rendition, Segment, Feature, and EvidenceLocus to policy, privacy attestation, and derivation. Its validated OpaqueRef cannot encode source paths, endpoints, email addresses, or display names. See governed modality serving.
  • The one facade-reachable piece: CasEvidenceResolver (src/server/blob/cas_resolver.rs:52, implements eg_alignment::EvidenceResolver). Gated by the alignment feature (alignment = ["blob", "dep:eg-alignment", "dep:eg-modality"], Cargo.toml, included in full). It resolves a governed text/table EvidenceLocus to a real UTF-8 excerpt read from the engine's own blob CAS (ChunkStore/stream_blob_get, a blob_ref property looked up in a GraphView snapshot); every other locus kind resolves to a real CAS-digest reference, never a fabricated excerpt. This resolver remains a provenance lookup boundary; native image/audio/video decoding and extraction stay in the served runtimes rather than being duplicated here.
  • The blob-CAS and epistemic resolvers consume only the single ArtifactBundle/EvidenceLocus protocol.

Persistent index pushdown into the served planner (EG-P1-4)

  • What it is: the served planner now binds directly to the maintained persistent BM25 text index and the live SemanticStore (vector index) via a ServedTextIndex downcast seam over IndexManager, instead of rebuilding/cloning a snapshot copy of the whole index on every UnifiedQuery/NlQuery request.
  • Adaptive re-optimization: cardinality-based cost re-optimization now runs automatically mid-execution, gated by the same EPISTEMIC_GRAPH_COST_OPT kill-switch (0 → identity/off) that already governed plan-time optimization.
  • Streaming cursor: KnowledgeBatchStream is the sole governed producer. Its authority- and snapshot-bound KnowledgeStreamCursor resumes bounded iterator-backed batches without a materialized compatibility cursor.
  • Current closeout: vector and spatial indexes publish completeness manifests; SQL cancellation uses CancelRequest and the bounded request-timeout contract; and both serial and par-runtime drivers run the same adaptive re-optimization loop. Snapshot-derived text and diagnostic materializations remain bounded fallbacks when a complete persistent index is unavailable.

Phase 2 — Distributed planes

PlacementCatalog (src/raft/placement.rs)

  • What it is: an epoch'd catalog (PlacementCatalog struct, src/raft/ placement.rs:217) tracking which (group, epoch) a graph — or a split partition-key range (split_tenant_key, PartitionState enum) — currently belongs to. Online split/merge/move runs a prepare-then-fenced-cutover sequence: MultiRaft::placement_fence_cutover (src/raft/multi.rs:724) bumps the epoch so any caller still presenting a pre-cutover epoch is redirected, never served stale data. The catalog takes priority over the hash-ring router for any graph with an explicit placement entry (an unpinned graph still falls back to the ring).
  • Crash-safe moves: every move stores an immutable graph inventory, original route/epoch, target, completed graph set, and stage in a replicated PartitionMoveJournal. The placement leader reconciles non-terminal journals at startup and re-verifies authoritative rows before cutover. Journal decoding and stage/placement mismatches fail startup closed; transitions and completed-graph evidence are monotonic. Exactly the placement leader drives a move, with an opaque per-partition local guard preventing competing local workflows. abort_move is permitted only before the epoch fence; if abort intent races a committed fence, recovery records the cutover and rolls forward rather than stranding an ambiguous Aborting state.
  • Wire surface (DIST-P2-4 read / DIST-P2-5 admin): Method::PlacementRoute is always in the enum (pure serde, present in every build per docs/capabilities.generated.md); the real answer needs the raft/cluster feature plus a live MultiRaft cluster, otherwise it returns a well-formed authoritative-unplaced route — not an error. Method::PlacementAdmin { op } (DIST-P2-5, op one of Assign/Move/AbortMove — one Method variant with a nested op enum, mirroring ServedModality { op }) is the admin mutation that closes the gap PlacementRoute alone left: before it existed, the assign/split/merge/online-move machinery below was reachable ONLY from in-process Rust (tests/harnesses) — there was no way for an external caller, on a real multi-node cluster, to trigger a placement decision or drive an online move. It is a thin RPC entry point over the SAME already-proven MultiRaft/TenantManager API (src/server/handlers/placement.rs), admin-scoped ("admin:cluster", the same tier as Reshard/CatalogAssign), returning a typed "not available" error on a non-raft build, and classified ClusterMutationRoute::VolatileControl (not ConsensusNative) since it replicates via its own internal commit_placement round-trip rather than the generic native-command proposal wrapper. Proven against a REAL three-node cluster (three independent tokio-spawned nodes, real openraft consensus — not the one-node/two-group simplification below) by src/raft/tests.rs::placement_admin_wire_rpc:: placement_admin_wire_rpcs_move_data_across_a_real_three_node_cluster: assign the decision, write data, move it to the other group, and read it back — from a DIFFERENT physical node at every step — asserting the data is actually placed and readable post-reshard, not merely that the RPC returned Ok.
  • Default-on or opt-in: gated raft/cluster (opt-in layer stacked on full, per docs/architecture/tiers.md). The gate stays default-OFF by design (a single-node homelab deployment has no second group to place anything on); what changed is that the path BEHIND the gate is now wire-reachable and exercised end-to-end, not merely present.

Multi-group production startup + cross-shard read fan-out (DIST-P2-2)

  • Env var: EPISTEMIC_GRAPH_RAFT_GROUPS (src/raft/config.rs:90, parse_groups at :126), default 1 — unset/empty/"0" all collapse to the single-group path, byte-for-byte unchanged from before this workstream.
  • Cross-shard reads: CrossShardReader::read (src/raft/xread.rs) first takes a linearizable placement-catalog barrier, then fans bounded keyset pages to each owning group's current leader through the authenticated shared Raft peer channel. Requests cap graph count, fan-out, rows, an aggregate response-byte budget, and the end-to-end deadline (including route discovery); the byte budget is divided across active legs rather than multiplied by them. Completion is either require-complete or an explicitly typed partial result. Placement epoch changes are re-resolved once, and stale or bound-violating peer replies fail closed.
  • Consistency contract: each leg reports its own ReadIndex and durable graph version. Continuations reject a changed version. These are per-group linearizable pages, not a fabricated global snapshot; a true cross-group snapshot requires a separately replicated global read fence.
  • Deterministic pagination: the coordinator performs a keyset K-way merge ordered by (node_id, input-leg-order) and consumes duplicate ids from every leg before a page boundary, preventing duplicate reappearance on continuation.
  • Proven by live harnesses (not just unit tests): src/raft/xread_harness.rs, src/raft/xshard_harness.rs, src/raft/placement_harness.rs (a one-node/two-group live setup).

Lazy graph lifecycle + bounded hot-context cache (DIST-P2-3)

  • Served mode performs a catalog-only scan. A graph's durable identity is known at boot, but its rows hydrate on first access through BackendGraphMaterializer. There is no eager served profile.
  • EPISTEMIC_GRAPH_MAX_RESIDENT_GRAPHS caps simultaneously resident graphs and evicts the coldest eligible graph through the durability-gated cold-offload path. __commons__ is never evicted. The default is 1024 and zero is rejected.
  • L38 "paged adjacency" — ✅ CLOSED (surpass-6mo WS-3, 2026-07-13). Was: "lazy" meant when a graph's data loads (deferred past boot to first access), not partial/paged loading — BackendGraphMaterializer::materialize called read_graph_material_blocking, which returned the whole graph's material in one shot, so first access to a lazily-opened graph still fully rehydrated it into memory. Now: eg_core::registry::GraphMaterializer::materialize_page (a seam that already existed, unit-tested in isolation, but was never called by the facade/ server) is wired end to end:
  • EPISTEMIC_GRAPH_LAZY_OPEN_PAGE_SIZE (src/server/persistence/cold_offload.rs::lazy_open_page_size) — a positive value switches cold_offload::lazy_open from a full rehydrate to a source-bounded paged load. The unset production default is 4096 records; development retains 0. Incomplete graphs are intentionally not queryable: graph operations receive typed PARTIAL_MATERIALIZATION metadata and can retry once the background continuation completes.
  • RedbBackend::read_graph_material_page_blocking (src/server/persistence/redb_backend.rs) overrides the trait's default (full-fetch-then-slice) with a genuinely SOURCE-bounded scan — redb_store::read_graph_dump_page skips/takes a bounded window of the per-graph nodes/edges table range directly, never collecting the whole graph's rows into memory first. BackendGraphMaterializer::materialize_page (src/server/persistence/read_through.rs) routes to it.
  • Lifecycle correctness: every catalog entry has an immutable incarnation id and cancellation token. Create/delete/evict/open serialize on the graph's lifecycle lock. Durable pages carry the source incarnation and version captured in one read transaction; publication is rejected after delete/recreate, cancellation, or source-version drift. Durable fetches run outside the global registry lock, and a failed mixed partial image is evicted so the next access restarts at page zero.
  • Maintained-index correctness: text, spatial, temporal, and semantic indexes publish a manifest containing source snapshot, build version, completeness cursor, and validity. Lazy recovery rebuilds every maintained index before the graph becomes available. Served indexes reject a stale/incomplete manifest; Health and ListGraphs expose graph and index completeness/freshness.
  • Adversarial evidence: registry tests cover stale-page rejection after delete/recreate and source-version drift; served completeness tests cover index publication only after final-page rebuild; scripts/check_lazy_lifecycle_architecture.py is the static architecture gate for fences, cancellation, bounded I/O, manifests, explicit partial responses, and durable identity.
  • Default policy: served mode is always lazy and bounded. Positive explicit resident/page limits override 1024/4096; zero and invalid values fail startup.

Durable analytics-job plane (eg-jobs, INT-P2-1)

  • What it is: Method::AnalyticsJob { op } (async submit/status/cancel/resume, src/server/handlers/jobs.rs) over a redb-backed AnalyticsJob state machine (crates/eg-jobs/src/model.rs):
    pub struct AnalyticsJob {
        pub job_id: JobId,
        pub input_snapshot: InputSnapshotHandle,   // pinned OCC version — re-readable via AS OF, never a row copy
        pub policy: JobPolicy,
        pub algo: AlgoVersion,                      // full algorithm/params/code-version lineage
        pub retry: RetryPolicy,
        pub state: JobState,
        pub cancel_requested: bool,
        pub created_at_ms: i64,
        pub updated_at_ms: i64,
    }
    
    result_ref() is a deterministic function of (input_snapshot, algo), not job_id — two jobs (an original run and a retry under a fresh job_id) that ran the same algorithm+params over the same snapshot converge on the same result_ref, the basis for idempotent result-commit. The result-commit path writes the same :Claim/:Evidence convention eg-epistemic reads (see Phase 3).
  • Default-on or opt-in: feature
    jobs = ["server", "mining", "epistemic", "dep:eg-jobs", "eg-types/jobs", "eg-capabilities/jobs"]
    
    (Cargo.toml) — included in full. P2 adds renewable leases/epoch fencing, placement and tenant quotas, durable retry/checkpoint/cancellation state, typed KnowledgeBatch results, and a non-terminal Publishing phase so success is impossible before evidence-bearing result claims commit.

Governed external compute stream (INT-P2-2)

  • Method::KnowledgeStream is the single signed handoff protocol for query and native AnalyticsJob results. Its KnowledgeStreamCursor is authority-, snapshot-, and placement-bound; producers page bounded Arrow IPC and consumers resume without a row-by-row protocol projection. External workers submit work through the durable analytics-job state machine and retrieve evidence-bearing committed results through the same stream, avoiding a second listener, registry, or result-publication protocol.
  • The engine's outer async driver and every Tokio worker use the same explicit 4 MiB stack contract. server::spawn_engine_driver owns the driver-thread boundary and normalizes spawn/join failures without reflecting panic payloads or environment data; RUST_MIN_STACK is not a deployment requirement or fallback.

Change-ledger → lake materialization + Iceberg-REST catalog + OpenLineage (INT-P2-3, lake)

  • What it is: LakeManager materialize/compact/delete runs emit a real OpenLineage RunEvent (src/server/lake/lineage.rs) — job/run/input-dataset/output-dataset with schema/datasource/output-statistics facets plus an engine-specific LSN/Iceberg-snapshot custom facet (schema URL https://openlineage.io/spec/1-0-5/OpenLineage.json#/$defs/RunEvent).
  • Optional push: env var EPISTEMIC_GRAPH_OPENLINEAGE_URL (OPENLINEAGE_URL_ENV constant, src/server/lake/lineage.rs:36) — unset means a silent no-op; lineage export never blocks or fails a materialization run.
  • Default-on or opt-in: lake = ["server", "blob", "tsdb", "dep:eg-lake", "eg-lake/lake", "dep:ureq"] — as of W4.8, folded into full (the maintained Polars native-Parquet codec + pure-Rust apache-avro, NOT the heavier upstream arrow/parquet crates, measured within the Pi-4 release-binary budget). The materialization tier and the Iceberg-REST listener (lake-rest, also in full) remain opt-in at runtime (GRAPH_SERVICE_PERSIST_DIR + EPISTEMIC_GRAPH_LAKE_MATERIALIZE_INTERVAL_SECS/ --iceberg-addr). The Iceberg v2 Avro manifest/manifest-list writer (crates/eg-lake/src/iceberg_avro.rs, pure-Rust apache-avro) is real (per-column stats for predicate pushdown), not a stub.

Phase 3 — Epistemic differentiation (eg-epistemic)

eg-epistemic (crates/eg-epistemic/, ~5,760 lines across model.rs, propagate.rs, tms.rs, recompute.rs, causal.rs, ranking.rs, redact.rs, query.rs, evidence.rs, adapter.rs, contract.rs) is the epistemic layer. Claims/Evidence/ Sources are ordinary type-tagged graph nodes — no new persistence — and Support/Contradict/Attack are ordinary edges; the base epistemic feature (epistemic = ["query", "dep:eg-epistemic", "eg-types/epistemic", "dep:eg-plan", "eg-plan/epistemic", "dep:eg-modality"], Cargo.toml) is folded into full (verified: full = [… , "epistemic"] is the last entry in the full feature list) — so a standard build always links the epistemic substrate and its TMS, redaction, evidence and causal layers. Expensive argumentation searches remain request-bounded; steady-state TMS/conflict/causal/materialization maintenance consumes committed MutationBatch outbox records incrementally and advances a durable projection cursor.

Claim/Evidence/Source/BeliefState + confidence propagation

  • Base epistemic feature (shipped 2.16.0, restated here as the foundation Phase 3 builds on). eg_epistemic::propagate_confidence (crates/eg-epistemic/src/ propagate.rs) does cycle-guarded Bayesian-conjugate confidence propagation over support/contradiction/attack edges. Method::ExplainBelief returns the full justification tree.

Paraconsistent TMS + Dung argumentation (tms.rs)

  • What it is: crates/eg-epistemic/src/tms.rs (687 lines) implements Dung abstract-argumentation semantics over a BeliefGraph: grounded_extension, preferred_extensions, stable_extensions, is_skeptically_accepted/ is_credulously_accepted, plus retract (RetractionResult) for dependency-directed retraction. Paraconsistent means a contradiction is contained to the arguments actually in tension, never an explosion into "everything is now unbelieved."
  • Default-on or opt-in: gated for composition as epistemic-tms, and included in the one main full build.

Dependency-driven recompute + durable projection

  • The recompute engine (crates/eg-epistemic/src/recompute.rs): TruthMaintenanceregister(id, depends_on, …), on_change(&ChangeEvent) -> BTreeSet<String> (which ids just went Stale), status_of, stale(), dependents_of, recompute (re-derives a stale id to Fresh/Retracted), and register_from_provenance(tm, view, derived_id) — the intended real-world registration path, reading a node's :DerivedFrom/:GeneratedBy edges (EPI-P3-1's lineage) to auto-populate dependencies rather than requiring a caller to hand-list them (L45 closure). Dependency and generator reverse indexes make ordinary invalidation and model/ontology retirement proportional to the affected closure rather than the complete materialization registry.
  • The served authority (crates/eg-epistemic/src/incremental.rs and src/server/reasoning_projection.rs): one compact, per-graph projection consumes authoritative MutationBatch outbox records in graph-version order. It indexes epistemic/causal edges, provenance dependencies, generators, stale/retracted state, and recompute fence epochs. State-backed batches carry a digest-bound, privacy-safe projection wake-up containing only domain-separated identity hashes and closed relationship/invalidation tags; source properties and labels are not duplicated.
  • Failure and restart semantics: the projection image is fsync'd before its outbox lease is acknowledged. Exact-position replay is idempotent. Missing or corrupt images fail served status/recompute closed; a corrupt image is never silently replaced with an empty index. A missing image is bootstrapped once from the recovered authoritative graph and then maintained incrementally.
  • Invalidation coverage: committed node/edge/CAS mutations, provenance edge changes, and explicit policy_changed, model_retired, and ontology_evolved mutation events invalidate the affected transitive closure. Model and ontology invalidations use the generator reverse index instead of scanning all rows.
  • Fenced recompute: RecomputeMaterialization requires both the authoritative graph version and durable projection watermark to equal the caller's expected source version. A monotonically increasing per-materialization epoch prevents a late writeback from clearing a newer invalidation. Provenance is resolved from the graph post-image; callers cannot submit a dependency set.
  • Default-on or opt-in: gated for composition by epistemic-tms and included in the one main full build.

Calibrated causal reasoning (causal.rs) — engine-native, partially wire-exposed

  • What it is: crates/eg-epistemic/src/causal.rs (632 lines) — a genuine linear-Gaussian structural causal model (CausalGraph, StructuralEquation) with real Pearl do-calculus:
  • intervene(...) (causal.rs:232) — graph surgery: cuts incoming edges to the intervened variable, does not just condition on it. This is the operationally meaningful distinction between do(X=x) and observe(X=x).
  • observe(...) (:262) — conditional, backward-inference-aware.
  • counterfactual(...) (:342) — abduction/action/prediction (Pearl's three-step counterfactual recipe).
  • Every query returns a CausalEstimate (:73) with a calibrated credible interval, not a point estimate.
  • Wire exposure: CausalEstimate and CausalCounterfactual expose intervention and counterfactual inference; observation remains a crate-level primitive used by the same causal engine.
  • Feature gating (two compositional layers, included in full):
  • eg-epistemic's own epistemic-causal feature gates the crate-internal implementation.
  • The facade feature epistemic-causal = ["epistemic", "eg-epistemic/epistemic-causal"] turns on the wire Method::CausalEstimate/Method::RankByProvenance handler arms (#[cfg(feature = "epistemic-causal")] in src/server/handlers/query.rs).

Provenance-aware retrieval ranking (ranking.rs)

  • What it is: crates/eg-epistemic/src/ranking.rsrank(candidates: &[RetrievalCandidate], weights: RankWeights) -> Vec<RankedResult>, scoring by evidence_quality (reliability, corroboration, calibration precision, freshness) in addition to similarity, not similarity alone.
  • Wire exposure: Method::RankByProvenance (handler rank_by_provenance_wire, src/server/handlers/query.rs), a pure function over request-carried candidates — no graph snapshot needed.
  • Default-on or opt-in: same composable facade epistemic-causal feature as CausalEstimate; included in full.

Policy-aware proof redaction (redact.rs, Method::ExplainBelief disclosure_level)

  • What it is: crates/eg-epistemic/src/redact.rs (614 lines) defines DisclosureLevelFull < Skeleton < ExistenceOnly (a total ordering by how much is hidden) — and ExistenceSignal (Supported/Contradicted/Uncertain, the coarse "is this believed at all" signal surfaced even at ExistenceOnly, deliberately never a raw float confidence). explain_belief_redacted/ explain_belief_redacted_capped (:238/:299) reuse the exact same RowVisibility/can_see_row check (crates/eg-core/src/isolation.rs) every other RLS-aware read path enforces — masking (never silently dropping) an evidence node the caller's RLS context cannot see.
  • The dual-arm handler (src/server/handlers/query.rs, both verified by direct read):
  • #[cfg(feature = "epistemic-redaction")] arm: disclosure_level: None takes the byte-for-byte classic explain_belief path; Some(cap) routes through explain_belief_redacted_wire under the caller's own RLS actor.
  • #[cfg(all(feature = "epistemic", not(feature = "epistemic-redaction")))] arm: disclosure_level: Some(_) returns an explicit error ("requires the epistemic-redaction feature, not enabled in this build") rather than silently ignoring the parameter and returning an unredacted tree — the one behavior that would actually be dangerous.
  • Default-on or opt-in: epistemic-redaction = ["epistemic", "security", "eg-epistemic/epistemic-redaction"] — requires security too (for the shared IsolationLayer); in full since WS-1b (2026-07-12; both security and epistemic are already in full, so this was a zero-new-dependency fold-in).

The bitemporal epistemic_status capstone (query.rs, EPI-P3-5)

  • What it is: crates/eg-epistemic/src/query.rs (740 lines) implements the why/why-not/what-changed/what-would-invalidate acceptance query family over the bitemporal AsOf axis, layered on the paraconsistent TMS. Two wire methods:
  • Method::EpistemicStatus { node_id } — the acceptance capstone: "is this still believed, as of when, and why" (handler epistemic_status_wire).
  • Method::WhatChanged { tx_from, tx_to } — a whole-graph bitemporal diff between two transaction times; the one facet EpistemicStatus does not subsume (handler what_changed_wire).
  • Default-on or opt-in: both gated by the composable epistemic-tms feature, included in full.

Exceed tracks

X-1: multimodal evidence graph spine

  • EvidenceLocus is the sole located-evidence identity. Its governed EvidenceAddress covers text spans, table cells, image/page regions, audio/video intervals, metric windows, versioned rows, code symbols, and trace spans while policy, derivation, occurrence, rendition, and content references remain attached to one validated artifact bundle.
  • The citation resolver: evidence_citations/resolve_locus/ justification_citations in crates/eg-epistemic/src/evidence.rs (387 lines) — walks the same support/contradiction/attack BeliefGraph topology ExplainBelief walks and returns every transitively-cited node's EvidenceLocus plus its AssetOccurrence/Blob identity chain.
  • Wire exposure: Method::ExplainEvidence { node_id } (handler explain_evidence_wire, src/server/handlers/query.rs).
  • Feature gating and resolution:
  • The wire Method::ExplainEvidence variant itself is gated only by the base epistemic feature (already in full).
  • The handler arm that actually answers it is gated #[cfg(feature = "evidence-graph")] (evidence-graph = ["epistemic", "eg-epistemic/evidence-graph"]in full since WS-1b, 2026-07-12); a build that explicitly disables it (--no-default-features without re-adding it) falls through to the not-built catch-all.
  • alignment's CasEvidenceResolver implements content resolution for this same EvidenceLocus contract; it is not a second identity model.

X-6: reversible intelligence, via the TMS recompute engine

  • Realized entirely by the truth-maintenance + live CDC hook covered under Phase 3 above: a retraction propagates Stale along real dependency edges (never across an unrelated contradiction — the paraconsistency property is preserved), and TruthMaintenance::recompute re-derives a stale id to Fresh or Retracted — this is genuinely reversible, dependency-directed derived-knowledge maintenance, not just forward-append. Provenance-bearing commits auto-register materializations, and the durable outbox/cursor projection persists and resumes incremental recomputation.

Feature-gating map

The default build is full (default = ["graph", "algorithms", "metrics", "full"], Cargo.toml). The table below covers only the features this program introduced or wired; consult the build feature map for the complete composition.

Feature In full? What it turns on Depends on / implies
epistemic yes Claim/Evidence/Source/BeliefState, confidence propagation, base ExplainBelief query, dep:eg-epistemic, dep:eg-modality
epistemic-tms yes Paraconsistent TMS + Dung argumentation, durable incremental projection worker, EpistemicStatus/WhatChanged epistemic, eg-epistemic/epistemic-tms
epistemic-redaction yes (WS-1b) ExplainBelief's disclosure_level (policy-aware proof redaction) epistemic, security, eg-epistemic/epistemic-redaction
epistemic-causal yes CausalEstimate, counterfactuals, RankByProvenance, incremental causal dependency projection epistemic, eg-epistemic/epistemic-causal
evidence-graph yes (WS-1b) ExplainEvidence citation resolver (crate-internal, eg-epistemic-side) epistemic, eg-epistemic/evidence-graph
alignment yes CasEvidenceResolver (blob-CAS-backed, facade-reachable) blob, dep:eg-alignment, dep:eg-modality
knowledge-batch yes Native governed streaming query/job result (eg-plan) query, dep:arrow
modality-serving yes Universal governed document/image/audio/video state machine and concrete dependency-light runtimes server, redb, security, streaming, media runtime crates, eg-modality
contract (per-crate) no ModalityContract conformance-test battery, selected directly for each implementation dep:eg-modality (+ crate-specific extras, e.g. eg-rdf also needs owl/sparql)
jobs yes Method::AnalyticsJob distributed durable analytics-job plane (eg-jobs) server, mining, epistemic, dep:eg-jobs
lake yes (W4.8) Parquet/Delta/Iceberg-REST materialization + OpenLineage server, blob, tsdb, dep:eg-lake
lake-rest yes (W4.8) Iceberg-REST catalog endpoint on top of lake lake
raft / cluster no (opt-in layer) PlacementCatalog, multi-group Raft, lazy lifecycle's cross-shard leg server, redb, dep:openraft

Small-footprint invariant: the native knowledge-batch and dependency-light media serving features are intentionally part of full; external codec/model toolchains remain outside it. KnowledgeBatch shares the Arrow dependency already used by the main query stack, while the media runtimes add no native library. A cargo tree on the default build links no new heavy dependency class from this program beyond what the pre-existing full build already carried (DataFusion, Tantivy, redb) — the workspace [features] block's comments assert the dependency boundary at each opt-in feature's definition site.


Release-certification evidence

The source contracts above are implemented. The following environment-dependent runs belong to exact-release certification; absence of a result for a new release does not mean the source feature is deferred:

  • cargo tree --features full and cargo tree --features cluster dependency-policy evidence for the exact source revision.
  • The 24–72 hour multi-host soak/chaos campaign at the target resident-graph count.
  • Live GPU/robotics parity on the target hardware and driver/toolchain versions.
  • OpenLineage delivery against the deployment's configured collector and trust profile.

The generated capability ledger supplies the current method count. This narrative never freezes that count or substitutes release-history estimates for the executable gates.