Expand description
Deontic Logic extension for the Qualia Bytecode VM.
Implements a defeasible deontic contract evaluator over a &[NQuin] slice.
Conforms to the 42 MB Prolog Sentinel memory ceiling, the 48-byte Super-Quin
invariant, and the zero-heap-allocation mandate (no Vec, String, or Box).
§Opcodes
Three raw u8 constants define the deontic modality, packed into bits 0–7 of
the predicate field of every norm Quin:
| Constant | Value | SDL formula | Meaning |
|---|---|---|---|
OP_OBLIGATE | 0x10 | O(φ) | Party must perform action φ |
OP_PERMIT | 0x11 | P(φ) | Party may perform action φ |
OP_FORBID | 0x12 | F(φ)=O(¬φ) | Party must not perform action φ |
§48-byte Norm Quin Layout
┌──────────┬──────────────────────────────────────────────────────────────────┐
│ Field │ Bit layout │
├──────────┼──────────────────────────────────────────────────────────────────┤
│ subject │ [63]=0 (rsvd) │ [0..62] = FNV-1a hash of the bound party DID │
│ predicate│ [63]=DEFEATER │ [8..62] = property-path hash (action/norm URI) │
│ │ │ [0..7] = deontic opcode (OP_OBLIGATE etc.) │
│ object │ [63]=0 (rsvd) │ [0..62] = FNV-1a hash of the action object │
│ context │ [56..63] = sensitivity class (from NQuin::SENSITIVITY_*) │
│ │ [0..55] = q_hash of the contract/graph DID │
│ metadata │ [61..62] = PermissiveRoutingLane bits │
│ │ [32..60] = Lamport logical clock │
│ │ [0..31] = expiry as truncated Unix-32 timestamp │
│ parity │ XOR fold of subject ⊕ predicate ⊕ object ⊕ context (ECC check) │
└──────────┴──────────────────────────────────────────────────────────────────┘§Defeater Nodes — q42:unless
A Quin with bit 63 of predicate set (DEFEATER_BIT) is not a primary norm;
it is a q42:unless exception node that defeats any norm sharing the same
(subject, context, property-path) fingerprint. This supports non-monotonic,
defeasible reasoning:
Alice is forbidden from disclosing project data — unless she is speaking to a certified auditor.
The evaluator performs a two-phase linear scan:
- Defeater harvest — collect up to
MAX_DEFEATER_SLOTSfingerprints into a fixed[u64; 64]stack buffer (512 bytes, one cache-line group on Cortex-A78). - Norm evaluation — for each non-defeater Quin: check expiry, probe the
defeater buffer, emit a
DeonticVerdictinto the caller-suppliedoutslice.
§Legal SHACL Blueprint
§Non-Disclosure Agreement (NDA)
NDA between did:web:alice.example and did:web:bob.example, covering
confidential project-X data, valid until 2028-01-01 (Unix epoch 1 830 297 600).
Three Quins fully encode the agreement and its auditor exception:
// Quin 1 — Alice's confidentiality prohibition
subject = q_hash("did:web:alice.example")
predicate = OP_FORBID as u64
| (q_hash("q42:disclose") << 8) // property-path in [8..62]
object = q_hash("q42:data:project-x:confidential")
context = q_hash("did:web:nda:contract-001") // contract graph
metadata = 1_830_297_600_u64 // expiry in bits [0..31]
parity = subject ^ predicate ^ object ^ context // ECC fold
// Quin 2 — Bob's symmetric prohibition (identical structure, different subject)
subject = q_hash("did:web:bob.example")
predicate = OP_FORBID as u64 | (q_hash("q42:disclose") << 8)
object = q_hash("q42:data:project-x:confidential")
context = q_hash("did:web:nda:contract-001")
metadata = 1_830_297_600_u64
parity = subject ^ predicate ^ object ^ context
// Quin 3 — Defeater: Alice MAY disclose to a certified auditor (q42:unless)
subject = q_hash("did:web:alice.example")
predicate = DEFEATER_BIT // bit 63 marks q42:unless
| OP_PERMIT as u64
| (q_hash("q42:disclose") << 8) // same property-path as Quin 1
object = q_hash("q42:role:certified-auditor") // excepted entity class
context = q_hash("did:web:nda:contract-001") // same contract graph
metadata = 1_830_297_600_u64
parity = subject ^ predicate ^ object ^ contextQuin 3 shares (subject, context, property-path) with Quin 1, so the evaluator
marks Quin 1 as DeonticStatus::Defeated when an auditor invokes the exception.
§Guardianship Contract
Ward: did:web:ward.example, Guardian: did:web:guardian.example.
Guardianship expires at majority (2030-01-01, epoch 1 893 456 000):
// Quin 1 — Guardian obligated to act in the ward's best interest
subject = q_hash("did:web:guardian.example")
predicate = OP_OBLIGATE as u64
| (q_hash("q42:actInBestInterest") << 8)
object = q_hash("did:web:ward.example")
context = q_hash("did:web:guardianship:contract-002")
metadata = 1_893_456_000_u64 // contract expires when ward reaches majority
parity = subject ^ predicate ^ object ^ context
// Quin 2 — Temporal defeater: ward may self-determine after majority age
subject = q_hash("did:web:ward.example")
predicate = DEFEATER_BIT
| OP_PERMIT as u64
| (q_hash("q42:actInBestInterest") << 8) // defeats the same obligation path
object = q_hash("did:web:ward.example")
context = q_hash("did:web:guardianship:contract-002")
metadata = 1_893_456_000_u64 // carries same timestamp; expiry semantics applied
parity = subject ^ predicate ^ object ^ contextAfter majority, now_unix > 1_893_456_000 causes Quin 1 to emit
DeonticStatus::Expired, and the defeater Quin 2 itself becomes moot —
demonstrating how temporal bounds compose naturally with defeasibility in a
single linear scan without branching stacks.
§Edge-native 3-core CPU triad
The two-phase design maps to the triad naturally:
- Core 0 — defeater harvest (Phase 1, read-only, highly prefetchable).
- Core 1 — norm evaluation (Phase 2, linear probe of the 512-byte buffer).
- Core 2 — verdict dispatch / downstream enforcement routing.
Cache-line pressure is bounded: the [u64; MAX_DEFEATER_SLOTS] buffer fits in
8 × 64-byte cache lines; each DeonticVerdict is 64 bytes (one cache line).
Re-exports§
pub use crate::frame_layout::DEFEATER_BIT;
Structs§
- Deontic
Verdict - A verdict emitted for one norm Quin. Exactly 64 bytes — one cache line.
Enums§
- Defeat
Kind - How a norm came to be
DeonticStatus::Defeated— Hart/Pollock’s rebutting vs undercutting distinction. A rebutting defeater asserts the contrary conclusion (DEFEATER_BIT+ an O/P/F opcode); an undercutting defeater (OP_UNDERCUT) severs the rule’s support without asserting the contrary. - Deontic
Error - Deontic
Status - The result of evaluating a single norm Quin against temporal bounds and defeaters.
- Norm
Resolution - The outcome of resolving a norm conflict.
Constants§
- MAX_
DEFEATER_ SLOTS - Stack capacity for defeater fingerprints per evaluation call. 64 slots × 8 bytes = 512 bytes — fits within a single L1 cache-line group.
- OP_
CONDITIONAL - O(q | p) — the head of a dyadic / conditional obligation: q is obligatory
given condition p. Evaluation is fact-driven (see
evaluate_conditional_obligation); contrary-to-duty is the special case p = “primary breached”. - OP_
FORBID - F(φ) = O(¬φ) — the subject party must not perform the action.
- OP_
GRATUITOUS - G(φ) — gratuitousness:
¬O(φ). The agent is free to omit φ (it may still be permitted or forbidden). May be asserted or derived (seeis_gratuitous). - OP_
OBLIGATE - O(φ) — the subject party must perform the action.
- OP_
OPTIONAL - U(φ) — optionality / indifference:
¬O(φ) ∧ ¬F(φ). The system is indifferent; neither doing nor omitting φ is a violation. May be asserted or derived (seeis_optional). - OP_
PERMIT - P(φ) — the subject party may perform the action.
- OP_STIT
- Reserved for Phase 3 (STIT agency):
O[α stit φ]. Declared here to fence the opcode so nothing else claims it before agency lands. - OP_
UNDERCUT - An undercutting defeater: combined with
DEFEATER_BITit invalidates the inference linkp ⇒ Oqwithout asserting¬Oq(vs a rebutting defeater — aDEFEATER_BITnode with an O/P/F opcode — which asserts the contrary). The fingerprint match is identical (the opcode byte is masked out); only the classification inDefeatKinddiffers.
Functions§
- compile_
n3_ rule_ to_ norm - Compile an N3 [
Rule] into a norm Quin (or defeater Quin for^>rules). - compile_
norm_ quin - Build a norm Quin from its logical components.
- compile_
permission_ constraint - Compile a permission into a non-fungible cryptographic constraint bound to a specific
target nquin: the constraint carries, in
context, a BLAKE3 binding totarget, so the permission cannot be detached and reused for a different nquin — it travels persistently with that exact one. The identity layer SIGNS this envelope (the engine never holds keys — seemeta_deontic::endorsement_credential); this constructs the bound, verifiable constraint. - defeater_
fingerprint - Produces the defeater-matching fingerprint for any Quin (norm or defeater).
- evaluate_
conditional_ obligation - General dyadic / conditional obligation
O(obligation | condition): the obligation is binding only given the condition holds. Returnstrueiff the conditional is satisfied — either the condition does not hold (vacuously satisfied), or it holds AND the obligation has been fulfilled. - evaluate_
contrary_ to_ duty - Contrary-to-duty obligation
O(reparation / breach): a secondary obligation that arises precisely because a primary obligation was breached (the remedy/reparation logic — Geneva/ICCPR remedy instruments). Returnstrueiff the CTD is satisfied: either the primary was NOT breached by the party (the CTD is not triggered), or it was breached AND the reparation has been fulfilled. - evaluate_
deontic_ contract - Evaluate a deontic contract encoded as a
&[NQuin]slice. - extract_
deontic_ opcode - Extracts the deontic opcode from bits [0..7] of a
predicateword. - extract_
expiry_ unix32 - Extracts the 32-bit expiry from bits [0..31] of a
metadataword. A zero value means “no expiry set” and is always treated as valid. - harvest_
defeater_ fingerprints - Harvest
q42:unlessdefeater fingerprints from a contract slice (Phase 1 only). - is_
gratuitous - True iff action φ is gratuitous (non-obligatory) for
party:¬O(φ)— no active obligation over(party, action)(it may still be permitted or forbidden). - is_
optional - True iff action φ is optional / indifferent for
party:¬O(φ) ∧ ¬F(φ)— no active (non-defeater) obligation and no prohibition over(party, action)in the norm slice. (An explicitOP_OPTIONALassertion also counts.) - norm_
has_ active_ defeater - Returns
trueif the defeater buffer contains a fingerprint that matchesnorm. - norm_
lifecycle_ status - Compute the full lifecycle status of a single norm against an effectivity window, the current time, the harvested defeaters, and a fact slice.
- norms_
conflict - Do two norms CONFLICT — same party (
subject) over the same action (object), with deontically opposed opcodes? Active (non-defeater) norms only. - nquin_
binding_ hash - A collision-resistant (BLAKE3) fingerprint over a Quin’s six fields — the cryptographic binding anchor. Changing any field changes the fingerprint.
- opcodes_
conflict - Do two deontic OPCODES conflict — an obligation/permission to do φ vs a prohibition of φ?
- permission_
binds_ to - Verify that a permission
constraintis bound totarget(the non-fungibility check): itscontextbinding must matchtarget’s current fingerprint. Tampering withtarget, or moving the constraint to a different nquin, breaks the binding. - resolve_
norm_ conflict - Resolve a norm conflict by, in strict order:
- term_
uri_ hash