cruxible-ai/cruxible: Hard, verifiable state layer for AI agents. Contribute to cruxible-ai/cruxible development by creati

github.com · 2026-07-14 · by Long Nguyen ·github
Hard, verifiable state layer for AI agents. Contribute to cruxible-ai/cruxible development by creating an account on GitHub.

cruxible-ai/cruxible

Hard, verifiable state layer for AI agents. Contribute to cruxible-ai/cruxible development by creating an account on GitHub.

7 stars · 0 forks · Python Topics: python, state-management, sqlite, mcp, ontology, provenance, knowledge-graph, agents, governance, multi-agent-systems, ai-agents, llm, ai-governance, llm-tools, model-context-protocol, mcp-server, agent-memory, hard-state

README

Cruxible

cruxible.ai · quickstart · docs · kits · skills

Cruxible is a governed state engine for AI agents — an ontology-backed alternative to general-purpose agent memory for claims that must be settled, enforced, and auditable. All humans and agents share one typed model of a real domain: every claim is validated against declared rules, judgment calls route through review, and every computed answer carries its receipt. We call it hard state. In practice it's a Python daemon with a CLI and an MCP server, storing a typed graph in SQLite. You declare your domain's ontology in a Terraform-like YAML config (entity types, relationships, write rules, queries); humans and agents build the graph through governed writes, and named queries traverse it: blast radius, downstream impact, the multi-hop questions similarity retrieval can't follow.

pip install cruxible — the Quickstart goes install to first query; Get Started below runs a seeded demo world in ~3 minutes, no tokens.

How You Use It

Model the domain. Start from a kit, or hand the authoring skills to your agent with your data: it drafts the YAML ontology, you review what it proposes. Pin the sources. The exports, tables, and documents your truth comes from register as content-hashed artifacts; claims cite into them. Ingest the hard facts. Deterministic workflows match source rows into typed entities and edges, previewed before they commit. Propose the judgment calls. Claim types you declared governed can't be written directly, not even by a workflow; they're proposed into review groups carrying the evidence that matched them. Review and mint, only where you opted in. Governed claims land in review: a human, or an agent under trust rules you declared, approves or rejects. Everything else is live the moment it's written, and approving or correcting it is one verb, straight from a query result. Ask, and act on the answer. Agents work through MCP or the CLI at the permission tier you give them; queries return answers with receipts; guards refuse writes that break the rules, and gates hold outside actions (a merge, a deploy) until state agrees.

Once the config is declared, every mutation and computed query is a receipted CLI or MCP verb: the walkthrough below runs this loop as the actual commands.

There is no LLM inside the engine. Judgment enters only where you saw it (modeling, proposals, review); everything the engine itself does — validate, refuse, record, answer — is deterministic, works with any agent or harness, and lands in a SQLite file you own. Work compounds into a durable record of what you've determined to be true. When the expensive question arrives — which assets are exposed? what breaks downstream? is this authority still good law? — the answer is computed over that record, not re-derived each session from a pile of raw context. Get Started pip install cruxible Model your own domain: hand your agent the authoring skills in skills/ (prepare-data → create-state → review-state) with your exports (wiki-to-state converts an existing team wiki or Obsidian vault), or start from Modeling State and the config template. Or run the demo — a seeded supply-chain world, ~3 minutes, no tokens (sandbox writes attribute to a built-in operator identity):

shell 1 — local sandbox daemon

CRUXIBLE_SERVER_STATE_DIR="$HOME/.cruxible/sandbox" cruxible server start

shell 2 — kit bundles are fetched from the release and digest-verified

(agent-operation is the optional agent-ops layer; domain-only works too)

cruxible --server-url http://127.0.0.1:8100 init --kit agent-operation --kit supply-chain-blast-radius cruxible context connect --server-url http://127.0.0.1:8100 --instance-id <instance-id>

deterministic ingest: preview, then commit

cruxible run --workflow build_seed_state && cruxible apply --workflow build_seed_state --from-last-preview cruxible run --workflow ingest_incidents && cruxible apply --workflow ingest_incidents --from-last-preview

the incident feed can only PROPOSE impact edges; the judgment is yours, on the record

cruxible propose --workflow propose_incident_impacts_supplier cruxible group list --status pending_review cruxible group resolve --group <GRP-id> --action approve \ --rationale "Confirmed against supplier geography" --expected-pending-version 1

receipted answers through the edges you just admitted

cruxible query run open_incident_impacts --json cruxible query run incident_impacted_suppliers --param incident_id=INC-TW-RAIL-2026-07 --json When agents join, identity turns on: restart with CRUXIBLE_SERVER_AUTH=true, claim the bootstrap credential, and mint each agent its own token — every write is attributed. Details, permission tiers, and hardening: Quickstart · Runtime Auth And Agent Roles. The Rules Run An LLM can interpret a policy. Cruxible enforces it. Interpretation is sampling: an LLM's compliance is a probability, drawn fresh on every call, however good the model — and storing the policy in memory doesn't change that; remembered text is still interpreted, not enforced. A rule declared in config is an invariant, run at a chokepoint no writer can skip.

Prompted Enforced

"The agent knows an exposure can't be closed while unremediated" The write chokepoint refuses the transition until the remediation claim, with its evidence, is linked

"The model says these sources support the claim" The write is refused unless every reference dereferences to a content-hash-verified source chunk

"The agent was told not to accept claims it proposed itself" The guard compares the acting actor against the creation receipt's recorded actor and refuses, including create-as-accepted

"The agent remembers the ingest procedure" The workflow is declared, previewed, and locked to pinned providers; every run leaves a receipt

Two enforcement directions, one purpose — keeping the shared state a truth layer the rest of your stack can trust:

Guards face inward. They protect the write boundary of accepted state: a claim enters or changes it only when the declared conditions hold — the actor is authorized, the transition is legal, the evidence dereferences, frozen fields stay frozen. Gates face outward. An action outside Cruxible checks state before it proceeds: a deploy holds while critical exposures are open, a filing step waits for the citation check, a merge waits for its approved review. The check wires in wherever the action lives (a CI job, an internal protocol, a git hook).

gates: merge-review: kind: git-pre-push entity_type: ReviewRequest match_property: change_head condition: {status: approved} adapter: {branch_pattern: refs/heads/main} The first shipped gate kind holds a git repository to its state: one line in .git/hooks/pre-push, and a push is refused unless every branch merged into main is pinned by an approved review — fail closed, and mandatory once the same check is wired where pushes can't skip it (CI, protected infrastructure). This repository runs on it: the gate refused our own 0.2.2 release push until the review record was corrected in state. We fixed the state, not the hook. Judgment stays with the model; the rules run without it. What A Governed Domain Looks Like Where this section ends up: one reviewed judgment — this incident impacts this supplier — and from it, a query walks a recursive bill of materials to name every exposed shipment, five typed hops downstream, with a receipt. Here is how that truth gets built. A minimal slice of a supply-chain ontology, as authored in a kit config: entity_types: Supplier: properties: supplier_id: { type: string, primary_key: true } name: { type: string, indexed: true } primary_geography: { type: string, optional: true } Component: properties: component_id: { type: string, primary_key: true } name: { type: string, indexed: true } criticality: { type: string, optional: true, enum_ref: criticality } Incident: properties: incident_id: { type: string, primary_key: true } title: { type: string, indexed: true } severity: { type: string, optional: true, enum_ref: incident_severity }

relationships: - name: supplier_supplies_component from: Supplier to: Component # Governed judgment: an incident materially impacts a supplier. - name: incident_impacts_supplier from: Incident to: Supplier

named_queries: # Blast radius: from an incident, traverse impacted suppliers to the # components they supply. components_exposed_by_incident: mode: traversal entry_point: Incident returns: Component traversal: - relationship: incident_impacts_supplier direction: outgoing - relationship: supplier_supplies_component direction: outgoing The ontology is only part of the config: the same file declares the enum vocabularies, guards, proposal routing, workflows, and providers, so a domain's model, rules, and procedures ship together as one versioned, composable kit. Nobody types this state in by hand: it enters through the pathways the config declares, and different state earns different treatment. Hard facts are deterministic ingest. A BOM workflow pins the export as an artifact and matches its rows into suppliers, components, and supply edges, previewed before it commits: cruxible run --workflow ingest_bom --input-file ./exports/bom-2026-07.csv # preview cruxible apply --workflow ingest_bom --from-last-preview # commit incident_impacts_supplier is a judgment call, so it is governed: every live direct write is refused — CLI, MCP, batch, at any permission tier (a direct write can at most stage the edge for review). It enters only through the governed verbs the config declares, and in this domain that is proposal and review. The incident feed's workflow records the incidents themselves as hard facts, but the impact edges it can only propose. Those candidates land in a review group, each carrying the signals and evidence that matched it: cruxible propose --workflow propose_incident_impacts_supplier --input-file ./exports/incidents.json The judgment itself stays with a human, or with an agent when the trust rules you declared allow it. Approval is what mints the edges into accepted state: attributed, rationale on record. cruxible group list --status pending_review cruxible group resolve --group GRP-7f3a --action approve \ --rationale "Confirmed: fab flooding halts board shipments" \ --expected-pending-version 1 # pins the decision to the state the reviewer saw Notice where the judgment went: exactly one edge — does this incident materially impact this supplier? — passed through review. Everything downstream is computation over settled truth: which components those suppliers supply is ingested fact, so the blast radius is a traversal across the two, not another judgment call. An agent (or app) asks the expensive question without re-deciding anything: cruxible query run components_exposed_by_incident \ --param incident_id=INC-42 \ --json Results come back carrying a receipt id — the receipt is the deterministic path from query parameters to traversed edges to returned rows: { "items": [ { "entity_type": "Component", "entity_id": "component-main-board" } ], "receipt_id": "RCP-2f61a90c84d3" } cruxible explain --receipt RCP-2f61a90c84d3 --format json { "query_name": "components_exposed_by_incident", "parameters": { "incident_id": "INC-42" }, "nodes": [ { "node_type": "query", "detail": { "entry_point": "Incident" } }, { "node_type": "edge_traversal", "relationship": "incident_impacts_supplier" }, { "node_type": "edge_traversal", "relationship": "supplier_supplies_component" }, { "node_type": "result", "entity_type": "Component", "entity_id": "component-main-board" } ] } Receipts are not logs — they are typed evidence graphs. Mutation receipts record exactly what a write changed, and governed edges carry a reference back to the receipt of the operation that created them. A receipt doesn't prove a claim is true — it records what state, evidence, and rules produced the result; the same query against the same state reproduces it. And the cascade keeps going, with a sharp line through it: what gets judged, and what flows. In the full kit, impact judgments stop at components and assemblies; product and shipment exposure are never judged at all. incident_exposed_shipments derives them by traversal — walking the accepted impacts up a recursive bill of materials (assemblies nest eight levels deep), into the finished products, out to the shipments carrying them. Downstream truth is computed from upstream judgment, never asserted alongside it. Overturn one impact edge in review and every product and shipment answer downstream moves with it, on the next query, for free. To run this end to end on the seeded world — the staged cascade, the review seats, the receipted blast radius — follow the supply chain guide. This is what a pending review group looks like in the inspection UI: the signal matrix, each proposed edge with the evidence that matched it, and the provenance rail tying the proposal back to its workflow, receipts, and provider traces.

Governance Cruxible separates writing state from accepting it. State enters one of two ways:

Write mode Use it for What happens

Direct write Asserting hard state: imports, deterministic relationships, source evidence Live and queryable at once, with evidence when supplied, but unreviewed until a governed process approves it

Governed proposal Judgment calls: uncertain or interpretive relationships Pending candidates land in a review group under one thesis, each carrying its matching evidence; the group resolves through declared policy, human approval, or an agent with the permission to judge — acceptance mints attributed state, rejection records why

Guards are declared in config and enforced at a single write chokepoint. A relationship type can refuse direct writes entirely; a work item can be blocked from closing until an approved review is linked; a write can be required to co-create a linked entity in the same unit of work; a claim can be required to carry source evidence. Evidence requirements are enforced, not decorative: the write is refused unless every reference dereferences to a registered source chunk whose content hash matches. The agent-operation kit ships these live: a work item cannot close without an approved review linked, and a review verdict must co-write its rationale note in the same unit of work, so the work itself is typed state whose guards enforce review. Each kit README renders its declared guards as a generated table (agent-operation's). One Truth, Many Writers Cruxible is built for many writers out of the box. Every writer, human or agent, acts under its own minted credential at one of four cumulative permission tiers (read_only ⊂ governed_write ⊂ graph_write ⊂ admin); give each agent the least tier that does its job. Every write is attributed to the actor that made it, and roles can be separated by receipt: a guard can require that the actor who created a record is never the one to approve it, anchored on the creation receipt rather than anything a writer can forge. Humans and agents sit in the same loops under the same rules. An agent with the permission to judge resolves review groups exactly as you would, and its approvals are attributed exactly like yours — while every reader, whatever the agent, model, or session, computes the same answer from the same accepted state. Wiring and hardening: Agent Setup. Why Not Markdown, RAG, Or Vector Memory? We spent fifty years keeping the facts that matter in systems with schemas, constraints, and transactions — then handed agents piles of markdown, because prose was the only interface they spoke. Cruxible models the domain instead of engineering the context: the durable slice of what's true becomes typed, governed state — accepted at a point in time, changed only through explicit lifecycle, read back with a receipt. What changes:

Markdown · RAG · vector memory Cruxible

A claim is prose; nothing refuses one without a source Evidence-gated writes refuse references that don't verify against content-hashed sources

Edits are reviewed as diffs, not claims Writes pass typed validation, guards, and review

Links live in prose, re-inferred on every read Typed edges, traversed multi-hop, visibility rules at every hop

A rollup is a one-off summary Counts and joins are deterministic, receipted, re-runnable

No record of which answer was settled on One accepted state; the same query returns the same answer for every agent

No record of when a source was captured Sources are dated and hashed; staleness is queryable

A correction is just more text Feedback attaches to the exact claim; claims mature from proposed to accepted

A better model reads the pile better It can't read what was never written; the record and its derivations don't move when you swap models

The guarantee is a property of the whole write surface: one chokepoint every write passes through, provenance on every claim, evidence that must dereference wherever you require it, answers that re-run the same way from the same state. Assembled from parts — a schema here, a review queue there, an audit column bolted on — every seam between the parts is a bypass. This is not a second wiki to tend — the state accumulates through the same loops that use it. And if the wiki already exists (a team wiki, an Obsidian vault), the wiki-to-state skill converts it: pages become pinned evidence, an agent proposes the typed claims, you review what gets minted. Workflows And Pinned Providers Workflows orchestrate reads, providers, shaping, and writes as one declared, reproducible procedure. Providers — deterministic transforms and data loaders in Python, over HTTP, or as commands — are pinned, not trusted: the kit lockfile (cruxible.lock.yaml) records each one's version, content digest, and declared side effects, and every call leaves an execution trace. Canonical workflows are preview-first: cruxible run --workflow build_local_state # executes against a clone, returns an apply digest cruxible apply --workflow build_local_state --from-last-preview run never touches live state. apply re-verifies the preview's digest against the current config, lockfile, and head snapshot before committing. If anything shifted underneath, it refuses. Workflows that produce governed proposals run through cruxible propose and land in review instead of in live state. Declare → preview → apply, with a receipt at every step. Domain State And Operating State Cruxible models two kinds of state, strongest together. Domain state is the durable model of the world an agent reasons about — assets, vulnerabilities, suppliers, cases, controls, risks — answering what is true, proposed, reviewed, or constrained:

Agent operating state is the coordination layer for the work itself — work items, review requests, decisions, risks, open questions, actors — tracking what's active or blocked, why, who reviewed it, and what changed. A domain kit models the thing being worked on; an operating-state kit tracks the work around it; typed edges compose them into one queryable graph. The type map of the composed supply-chain instance above:

State That Compounds The one real cost is the config — the types, rules, and queries that model your domain. You don't write it from scratch: point an agent at your data, or an existing wiki, and it drafts the model; you review what it proposes instead of authoring it. The rules are few, static, and reviewed once; the writes they govern are many and continuous — that asymmetry is the point. And the cost keeps paying: knowledge no longer gets wiped out by a context refresh, a model swap, or a handoff, and three loops keep the state current through the same governed work that uses it:

Feedback and outcomes. Correction

source

github.com · 8 images · 187 links

Comments (0)

No comments yet