Skip to main content

Install

Or, with uv:

Bootstrap a signing key

Generates an Ed25519 keypair at ~/.config/mareforma/key (mode 0600). With a key, every claim is signed and the first project you open auto-enrolls you as its root validator (the identity allowed to record a validation). Do this before you start. Without a key, assert_claim still writes, but the claim is unsigned: it is served carrying generator_enrolled=False, and enroll_validator and validate both raise, since each needs a loaded signer. Independence keys on the signing identity (asserter_keyid), which an unsigned claim does not have.

Open a graph

No project setup. No init command. graph.db is created in .mareforma/ under the current directory on first call.

Assert a claim, and record whether the data was there

A claim carries a classification the agent declares (INFERRED, ANALYTICAL, or DERIVED), but a declaration is only as good as the run behind it. So assert the claim from inside observe(): it watches the block, and Mareforma records what the run actually did next to what the agent said it did. Lead with this, not with a bare classification.
obs.verdict is GROUNDED if a read of dataset_alpha was observed inside the block, UNGROUNDED if none was, and OPAQUE if the read happened somewhere Mareforma could not see: a subprocess, a C extension, a warmed connection. The catch worth internalising is what a silent failure lands: when a cited read is attempted and fails, and the failures account for every open of that path, the scope lands UNGROUNDED, not a comfortable OPAQUE. So an agent that declares ANALYTICAL on a pipeline that quietly read nothing leaves the gap on the record instead of hiding it. claim_id is a UUID. The claim is immediately persisted to graph.db and backed up to claims.toml, a human-readable TOML file written after every mutation. If graph.db is ever deleted or corrupted, claims.toml preserves the full record. A claim is a falsifiable assertion with provenance: what was asserted, how the knowledge was derived (classification), from which data source, and building on which upstream claims (supports). Claims are append-only: once recorded, a claim is built upon, contradicted, or retracted, never overwritten. The verdict says a cited read happened, not that it was a read of this claim’s own data. assert_claim has no citation to bind it to, so the stored reason carries [no finding citation to bind]. Use assert_finding with data_source= when you want the verdict cross-checked against the data the claim rests on. See Grounding.

Query what is already known

Always query before asserting. If the graph already carries a well-supported finding on your topic, build on it; don’t duplicate it. Trust itself reads off the derived axes, not the stored label: graph.proposition_status(prop) returns the answer’s status and the question’s question_status, both computed on every read. Nothing stored on a claim ranks it. When the results will be spliced into an LLM prompt, use graph.query_for_llm(...) instead: it sanitizes free-text fields and wraps them in <untrusted_data>...</untrusted_data> delimiters so stored prompt-injection in earlier claim text cannot escape.

Record a prior the rest of the work builds on

Nothing has to be bootstrapped before a claim counts. A finding used to need an already-established upstream before two agreeing lines meant anything, so a project began by minting one; there is no ladder to climb now, and a prior is just a claim like any other.

Let two agents converge

Independence is the signing key, not the generated_by label. Enroll a second validator and sign the second claim with its key, so the two lines carry distinct asserter_keyids.
Two claims under distinct signing keys, sharing an upstream, are two lines. Independence is the signing key (asserter_keyid); generated_by is a display label only. Nothing is written onto either claim: what the pair is worth is computed on read and reported on the trust map’s independence axis.

Record a human signing off

A sign-off needs a witness. No key can validate a claim it signed, so your root key cannot do this step. Enroll a second key that asserted nothing and validate from it.
validate() records a signed human attestation and changes nothing you can filter on. It is identity-gated: the loaded signing key must be enrolled in the project’s validators table, and it must not appear on the claim envelope or on any peer in the converging set. A key that signs off on its own line raises SelfValidationError. The validation event itself is signed and persisted to the claim’s validation_signature column.

Close the graph

Or use a context manager; it closes automatically on exit:

Next

Concepts

What is trust, what is origin, what is the graph.

For agents

Full execution contract: methods, patterns, forbidden patterns.

API reference

Complete method signatures and return types.

Examples

Five runnable examples, from walkthrough to real AI scientists.