Skip to content

Governed commits

The governed API on port 7001 records an authorized RDF change together with its decision evidence and side-effect intents. Use it when a mutation needs a signed policy decision, object version checks, and a receipt for recovery. Ordinary /update authorization does not provide this contract.

This reference describes the implementation through source commit a3622025 of 16 October 2026. Confirm that every node runs a build containing these routes before enabling clients. A source commit does not identify the image currently deployed in your environment.

Write endpoints

Method and path Contract
POST /governed/validate Validate a proposed commit without applying its changes.
POST /governed/commit Apply facts, version changes, evidence, and outbox intents atomically.
POST /governed/revert/prepare Prepare inverse facts. A caller must authorize and submit the resulting change separately.

A version 1.0 commit includes mutation_id, tenant_id, ontology, intent, policy, facts, and decision_trace. It can also include preconditions, expected_object_versions, expected_snapshot_index, branch, and outbox. mutation_id is a producer-stable UUID. Keep it when retrying the same mutation.

The server verifies the authenticated actor, tenant, signed policy proof, ontology binding, graph access, and applicable validation rules. The proof binds the canonical fact patch to the decision. An outbox entry records an intent; external delivery is a separate operation.

Condition Result
Same mutation_id and request hash as a committed request Stored receipt, including after the original proof expires.
Same mutation_id, different request hash 409 MUTATION_ID_CONFLICT.
An expected object version differs 409 STALE_OBJECT, with current_object_versions.
Too many touched objects 413 ACTION_TOO_LARGE; IG_GOVERNED_MAX_OBJECTS defaults to 10,000 across the process.

Every touched object receives a new igov:objectVersion, even when the request does not supply expected versions. Every RID in expected_object_versions also advances, including a RID whose facts remain unchanged. The version map is a concurrency precondition; it is not part of the signed patch hash.

Prepare a versioned change

The following reads accept GET query parameters or POST JSON and return Cache-Control: private, no-store. They establish a linearizable read point in cluster mode. The caller needs Read permission and a matching verified tenant; administrative and standalone development exceptions are separate from this normal tenant-bound path.

Path Request Response
/governed/snapshot tenant_id tenant_id, snapshot_index.
/governed/objects/versions tenant_id, objects: [{rid, graph}], 1 to 1,000 entries tenant_id, snapshot_index, object_versions.
/governed/receipt tenant_id and mutation_id, request_hash, or both Stored receipt, patch_hash, and committed_at.

For a versioned edit, read object versions before reading the facts, then submit the change with expected_object_versions. A new object can use expected version 0. A hidden object and a nonexistent object are both absent from a version lookup. Version 0 means a readable object has no governed version yet.

curl http://localhost:7001/governed/objects/versions \
    -H "Authorization: Bearer $TOKEN" \
    -H 'Content-Type: application/json' \
    -d '{
        "tenant_id": "acme",
        "objects": [{
            "rid": "https://id.indentia.ai/document/item/acme/example/42",
            "graph": "https://id.indentia.ai/sources/acme/example"
        }]
    }'

snapshot_index is store-wide, so writes by another tenant can advance it. Use object versions for independent edits that must not conflict with unrelated ingestion. Adding expected_snapshot_index introduces that broader conflict.

Recover a lost response

Look up the original mutation_id at /governed/receipt. A receipt is visible only to the committing actor or an Admin.

  1. On 200, compare patch_hash with the canonical patch hash you submitted.
  2. On 404, retry with the same mutation_id. The original request may still complete after the lookup.
  3. If a retry with a new proof returns 409 MUTATION_ID_CONFLICT, look up the receipt again and compare patch_hash.

The lookup returns the stored object_versions, not later object versions. Its snapshot_index is the current index, affected_triples is 0, and idempotent_replay is true. It does not reproduce the original commit index. If both lookup keys are supplied, a mismatched request hash returns 409.

RDF 1.2 triple terms

Fact objects and fact preconditions accept a recursive triple_term value:

{
    "kind": "triple_term",
    "subject": "https://id.indentia.ai/document/item/acme/example/42",
    "predicate": "http://purl.org/dc/terms/title",
    "object": {"kind": "literal", "value": "Quarterly report"}
}

A triple term describes a triple without asserting it. Subjects and predicates must be IRIs; blank nodes are not accepted. Nesting is limited to eight levels. The server includes the RDF 1.2 <<( s p o )>> form in canonical patch hashing.

Configure proof verification

Configure IG_ACTION_PROOF_JWKS_URL or IG_POLICY_PROOF_JWKS_FILE for proof verification. URL-based JWKS is loaded at startup, so key rotation requires a restart. Production requires HTTPS. With IG_GOVERNED_COMMIT_REQUIRED=true, a production server refuses to start without a proof verifier.

Governed branch writes are confined to the selected tenant ontology branch. Release-manifest activation has a separate guarded path. A successful governed commit is not evidence that an ontology release has been activated.