Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

OWL Ontology Layer

Quipu supports OWL 2 RL reasoning through a built-in ontology engine. OWL ontologies define class hierarchies, property characteristics, and constraints that Quipu uses to materialize inferred facts, and — when owl.validate_on_write is enabled — enforces at write time.

Loading an Ontology

Ontologies are OWL axioms expressed in Turtle format. Load one via the CLI or MCP tool:

quipu ontology load aegis-ontology ontology.ttl --db quipu.db

Or via the MCP quipu_load_ontology tool:

{
  "action": "load",
  "name": "aegis-ontology",
  "turtle": "@prefix owl: <http://www.w3.org/2002/07/owl#> ...",
  "timestamp": "2026-04-13T00:00:00Z"
}

On load, Quipu:

  1. Parses the Turtle and extracts OWL/RDFS axioms
  2. Persists the ontology in SQLite (like SHACL shapes)
  3. Materializes entailments into the fact log

Supported Axioms

AxiomEffect
rdfs:subClassOfTransitive closure: instances of a subclass are also instances of all superclasses
owl:disjointWithWrite-time validation (opt-in): rejects an entity typed with two disjoint classes
rdfs:subPropertyOfMaterialization: a fact under a subproperty is restated under every superproperty (transitive)
owl:inverseOfMaterialization: (a P b) produces (b Q a)
owl:FunctionalPropertyWrite-time validation (opt-in): rejects a second value on a functional property
owl:SymmetricPropertyMaterialization: (a P b) produces (b P a)
owl:equivalentClassMaterialization: instances of A become instances of B and vice versa
owl:TransitivePropertyMaterialization: full closure — (a P b), (b P c) produce (a P c), chained to fixpoint
owl:equivalentPropertyMaterialization: facts under either property are restated under the other
rdfs:domain / rdfs:rangeMaterialization: infers type from property usage
owl:sameAsMaterialization: identity closure (symmetric + transitive), and every fact about one individual is restated about its co-referents. Subjects and objects only — predicates are not rewritten. See below

owl:TransitiveProperty and owl:equivalentProperty were parsed and counted but not materialized before 2026-08-27 — the same silently-dropped shape rdfs:subPropertyOf had before aegis-qfncf. Loading one reported success and derived nothing.

owl:sameAs: identity comes from your DATA, not from the ontology

Every other axiom in the table is read from the ontology document you load. owl:sameAs is not — it is read from the graph itself, because identity between individuals is asserted as ordinary data (the quipu align verbs and /knot both write it). You do not declare owl:sameAs in an ontology; you assert it about two things, and materialization picks it up:

ex:dolt owl:sameAs ex:doltLan .
ex:dolt ex:hosts   ex:beads .

After materialization ex:doltLan ex:hosts ex:beads is entailed, and the identity itself is closed both ways and through chains: with a sameAs b and b sameAs c, facts about a reach c.

⚠️ Predicates are not rewritten. If you assert owl:sameAs between two PROPERTIES, the identity itself is closed, but facts are not restated under the co-referent property — ex:box ex:hosts ex:svc with ex:hosts owl:sameAs ex:runs does not entail ex:box ex:runs ex:svc. This is OWL 2 RL’s eq-rep-p, and it is not implemented: the rule language (reasoner/ast.rs) can only put variables in argument position, so a rule quantifying over the predicate is not expressible. Use owl:equivalentProperty instead, which IS materialized and is the right axiom for saying two properties mean the same thing. Tracked as the named gap on aegis-yro9m.

Materialization

Materialized facts are written with source = "owl:materialize" into ROOT’s companion inferred graph (urn:quipu:graph:root#inferred, quipu-0b6) — quarantined by placement, composed back in with FROM <urn:quipu:graph:root> FROM <urn:quipu:graph:root#inferred>. When an ontology changes, derived facts can be re-materialized.

Materialization runs to fixpoint across axiom families: a type introduced by rdfs:range feeds the subclass closure of the next pass, and passes repeat until one derives nothing new. Each pass derives only facts not already present, so re-running materialization at fixpoint is a no-op and the report counts stay honest.

Materialization can also stay live: with [quipu.owl] reactive_materialize = true (requires the owl and reactive-reasoner features — release full builds have both), the server re-runs materialization whenever a committed write touches vocabulary the loaded ontologies mention, so the closure extends as members arrive instead of going stale after load. This setting defaults to true; set it to false to disable the per-write materialization cost.

ex:fido a ex:Dog .
ex:Dog rdfs:subClassOf ex:Mammal .
ex:Mammal rdfs:subClassOf ex:Animal .

After materialization, ASK FROM <urn:quipu:graph:root> FROM <urn:quipu:graph:root#inferred> { ex:fido a ex:Animal } returns true — a plain ASK does not, because the entailment lives in the companion, not beside its premises.

Write-Time Validation

Two OWL constraints are enforced at write time by default when built with the owl feature. Set owl.validate_on_write = false only for an explicitly informal deployment. Before adopting new axioms, measure the existing graph: turning an incompatible declaration into live policy can reject future writes that touch historical drift.

Disjoint classes: If ex:Person owl:disjointWith ex:Robot, then an entity cannot be typed as both. Attempting to assert ex:alice a ex:Robot when ex:alice a ex:Person already exists returns a structured error and the write is rolled back.

Functional properties: If ex:ssn a owl:FunctionalProperty, an entity can have at most one current value. A later value supersedes the earlier one while preserving history; two competing values in one batch are rejected because the write provides no ordering.

Both reject the whole transaction: the constraint runs inside the write’s savepoint, so a violating batch commits nothing. Violations are reported together rather than one per round-trip.

Constraints are evaluated against the union of all loaded ontologies, and a load or remove through POST /ontology takes effect on the next write.

Feature Flag

OWL support is behind the owl feature flag:

cargo build --features owl
cargo test --features owl

The shacl feature continues to work independently.