Admissibility compiler: Γ ⊢ z : p until ε
Project description
Noethers Turnstile
Noethers Turnstile is an admissibility compiler for approximate consequential systems. Given a proof context Γ — a set of gap records, profiles, proof tokens, and authority constraints — it produces the strongest permission p the evidence supports and a binding expiry ε. The answer is a judgment in a typed form that the Rust borrow checker prevents from being read after it expires.
This library is for teams building systems where autonomous or consequential actions should be gated on structured evidence: calibration certificates, negative-control results, role assertions, scope restrictions. Noethers Turnstile handles the algebra; your domain supplies the certifiers.
Judgment form: Γ ⊢ z : p until ε
Documentation
| Document | Description |
|---|---|
docs/guide/introduction.md |
Gentle introduction to admissibility compilers for approximate consequential systems — concepts, vocabulary, worked examples, and when this design does and doesn't fit |
docs/papers/admissibility_compilers_for_approximate_consequential_systems.md |
Core compiler paper: judgment form, permission algebra, gap/profile/token machinery, 19 structural theorems, PGM benchmark results |
docs/papers/admissible_compilability_representation_theorem.md |
Representation theorem: characterizes exactly when a domain admits a bounded sharp monotone compiler; WQO and semialgebraic corollaries |
Permission Chain
Total order. OOC is the bottom (weakest). AAA is the top (strongest). Meet = min.
OOC < EXP < REF < UNS < ETA < ESC < ROL < DIA < REV < AEX < ALR < AAA
Quick Start — Rust
use noethers_turnstile_core::{
compile,
context::{Membership, ProofContext, Scope},
expiry::{Expiry, LiveJudgment, RuntimeContext},
gap::{GapRecord, GapRequirement, Profile, RequiredStatus},
permission::Permission,
token::{compute_provenance_hash, ProofToken, TokenStatus},
};
use chrono::Utc;
// 1. Build a context with one gap that needs closing.
let claim_id = "my-claim";
let candidate_id = "z-001";
let context_id = "ctx-001";
let allowed_use = "diagnostics";
let hash = compute_provenance_hash(claim_id, candidate_id, context_id, allowed_use);
let ctx = ProofContext {
claim_id: claim_id.into(),
candidate_id: candidate_id.into(),
context_id: context_id.into(),
context_fingerprint: "fp-001".into(),
allowed_use: allowed_use.into(),
disallowed_uses: vec![],
scope: Scope::default(),
gaps: vec![GapRecord::closed("calibration-gap", "calibration_gap")],
profiles: vec![Profile {
permission: Permission::DIA,
required_gaps: vec![GapRequirement {
gap_id: "calibration-gap".into(),
minimum_status: RequiredStatus::ClosedRequired,
}],
}],
tokens: vec![ProofToken {
token_id: "tok-1".into(),
token_type: "CALIBRATION_CERT".into(),
schema_version: "0.1".into(),
status: TokenStatus::Valid,
closes_gaps: vec!["calibration-gap".into()],
bounds_gaps: vec![],
provenance_hash: hash,
issued_at: Utc::now(),
expires_at: None,
issuer: "domain-certifier".into(),
details: serde_json::Value::Null,
is_negative_control: false,
}],
expiry: Expiry::never(),
authority_ceiling: Permission::AAA,
permission_ceiling: Permission::AAA,
membership: Membership::InClass,
};
// 2. Compile.
let judgment = compile(ctx).unwrap();
assert_eq!(judgment.permission, Permission::DIA);
// 3. Read through LiveJudgment (expiry check enforced by Rust borrow checker).
let rt = RuntimeContext::new(Utc::now(), "fp-001");
let live = noethers_turnstile_core::expiry::LiveJudgment::new(judgment, &rt);
assert_eq!(live.permission(), Permission::DIA);
Composition
use noethers_turnstile_core::compose;
let composed = compose(ctx1, ctx2)?;
// Composition is non-promoting:
// compile(composed).permission <= min(compile(ctx1).permission, compile(ctx2).permission)
Quick Start — Python
import time
import noethers_turnstile as ts
# Compute provenance hash.
h = ts.compute_provenance_hash("my-claim", "z-001", "ctx-001", "diagnostics")
# Build a ProofToken.
token = ts.ProofToken(
token_id="tok-1",
token_type="CALIBRATION_CERT",
schema_version="0.1",
status="valid",
closes_gaps=["calibration-gap"],
bounds_gaps=[],
provenance_hash=h,
issued_at=time.time(),
issuer="domain-certifier",
)
# Build the context.
ctx = ts.ProofContext(
claim_id="my-claim",
candidate_id="z-001",
context_id="ctx-001",
allowed_use="diagnostics",
membership=ts.Membership.InClass,
authority_ceiling=ts.Permission.AAA,
expiry=ts.Expiry.never(),
gaps=[ts.GapRecord("calibration-gap", "calibration_gap", status="closed")],
profiles=[ts.Profile(
ts.Permission.DIA,
[ts.GapRequirement("calibration-gap", "closed")],
)],
tokens=[token],
)
# Compile.
live = ts.compile(ctx)
rt = ts.RuntimeContext(now_unix=time.time(), context_fingerprint="ctx-001")
print(live.permission_str(rt)) # "DIA"
Composition in Python
composed = ts.compose(ctx1, ctx2)
live = ts.compile(composed)
# Non-promotion guarantee holds automatically.
The 4 Structural Guarantees
| Property | Description |
|---|---|
| Non-promotion under composition | compile(Γ₁ ⊗ Γ₂).permission ≤ min(compile(Γ₁), compile(Γ₂)) — composition cannot launder permission |
| Provenance enforcement | Tokens are accepted only when their SHA-256 provenance hash exactly matches (claim_id, candidate_id, context_id, allowed_use); no fuzzy matching |
| Expiry fires at boundary | LiveJudgment::permission() returns EXP for all now ≥ deadline; Rust borrow checker prevents stale reads |
| Evidence monotonicity | Adding a closed proof token to a context never lowers the emitted permission |
All four properties are checked by proptest property-based tests on every run:
cargo test -p noethers-turnstile-tests
1195 tests total — 998 Rust (85 files) + 100 Python (8 files) + 97 PGM example tests (6 files). Every test passes on every commit (ubuntu + macos CI matrix).
The PGM example (examples/pgm/bridge/certifier.py) ships a reference certifier implementation: PGMExactCertifier self-computes all fingerprints from inputs and runs inference internally before issuing a token; PGMModelSpecificationCertifier is a documented stub that raises NotImplementedError with an explanation of why domain-expert attestation cannot be automated. See examples/pgm/README.md for the full certifier boundary discussion.
Implementing a Certifier
The Certifier trait is the primary extension point. A certifier is the domain component that issues and validates proof tokens. Turnstile calls validate() at compile time; your domain layer calls issue().
use noethers_turnstile_core::certifier::{Certifier, Evidence, IssueError, ValidationResult};
use noethers_turnstile_core::context::ProofContext;
use noethers_turnstile_core::token::{compute_provenance_hash, ProofToken, TokenStatus};
use chrono::Utc;
struct CalibrationCertifier;
impl Certifier for CalibrationCertifier {
fn name(&self) -> &str { "calibration" }
fn issue(&self, evidence: Evidence) -> Result<ProofToken, IssueError> {
// Inspect evidence.payload, run domain checks, then emit a token.
let ctx_tuple = serde_json::from_value::<(String,String,String,String)>(
evidence.payload.clone()
).map_err(|e| IssueError::Internal(e.to_string()))?;
Ok(ProofToken {
token_id: uuid::Uuid::new_v4().to_string(),
token_type: "CALIBRATION_CERT".into(),
schema_version: "0.1".into(),
status: TokenStatus::Valid,
closes_gaps: vec!["calibration-gap".into()],
bounds_gaps: vec![],
provenance_hash: compute_provenance_hash(
&ctx_tuple.0, &ctx_tuple.1, &ctx_tuple.2, &ctx_tuple.3,
),
issued_at: Utc::now(),
expires_at: None,
issuer: "calibration-certifier".into(),
details: evidence.payload,
is_negative_control: false,
})
}
fn validate(&self, token: &ProofToken, _ctx: &ProofContext) -> ValidationResult {
if token.token_type == "CALIBRATION_CERT" {
ValidationResult::ok()
} else {
ValidationResult::fail("wrong token type")
}
}
}
Turnstile's compiler does not call certifiers directly — it only inspects token provenance hashes and gap membership. Certifiers are called by your domain layer before tokens are placed in a ProofContext.
Caller Responsibilities
Two checks that the compiler does not enforce internally — callers must invoke them:
1. Scope candidate admission
The compiler does not verify that candidate_id ∈ scope.allowed_candidates. After compiling, check before acting on the judgment:
// Returns true when allowed_candidates is empty (unconstrained) or contains candidate_id.
fn candidate_in_scope(scope: &Scope, candidate_id: &str) -> bool {
scope.allowed_candidates.is_empty()
|| scope.allowed_candidates.iter().any(|c| c == candidate_id)
}
if !candidate_in_scope(&ctx.scope, &ctx.candidate_id) {
// reject — candidate is outside declared scope
}
2. Profile monotonicity (Law G01)
The compiler does not validate that profiles are monotone (stronger permissions require at least as strong evidence as weaker ones). Validate on profile construction:
// Returns an error message if profiles[i].permission > profiles[j].permission but
// profiles[i] declares a weaker gap requirement than profiles[j] for the same gap_id.
fn check_profile_monotonicity(profiles: &[Profile]) -> Option<String> {
for i in 0..profiles.len() {
for j in 0..profiles.len() {
if profiles[i].permission <= profiles[j].permission { continue; }
for req_j in &profiles[j].required_gaps {
if let Some(req_i) = profiles[i].required_gaps.iter()
.find(|r| r.gap_id == req_j.gap_id)
{
let rank = |r: RequiredStatus| match r {
RequiredStatus::OpenAllowed => 0u8,
RequiredStatus::BoundedRequired => 1u8,
RequiredStatus::ClosedRequired => 2u8,
};
if rank(req_i.minimum_status) < rank(req_j.minimum_status) {
return Some(format!(
"gap '{}': {} requires {:?} but {} requires {:?}",
req_j.gap_id,
profiles[i].permission, req_i.minimum_status,
profiles[j].permission, req_j.minimum_status,
));
}
}
}
}
}
None
}
Building
Rust
# Build everything.
cargo build
# Run all tests (unit + structural + property).
cargo test
cargo test -p noethers-turnstile-tests
# Run benchmarks.
cargo bench -p noethers-turnstile-core
Python (via maturin)
# Install maturin if needed.
pip install maturin
# Build and install in development mode.
python3 -m venv .venv && .venv/bin/pip install maturin pytest
.venv/bin/maturin develop
# Run the Python integration test suite.
.venv/bin/pytest
# Build a release wheel.
.venv/bin/maturin build --release
After maturin develop, the noethers_turnstile package is importable in the active environment.
Architecture
docs/guide/ Conceptual guides
introduction.md Admissibility compilers for approximate consequential systems — concepts and worked examples
docs/papers/ Research papers
admissibility_compilers_for_approximate_consequential_systems.md
Compiler paper: judgment form, 19 theorems, PGM benchmark
admissible_compilability_representation_theorem.md
Representation theorem: when a domain admits a sharp compiler
examples/pgm/ PGM inference integration example (97 Python tests)
bridge/ domain adapter — token types, fingerprinting, gap profiles
certifier.py PGMExactCertifier + PGMModelSpecificationCertifier (stub)
demo/ self-contained diabetes BIF memory-budget sweep demo
inference/ certified inference compiler (copied + stripped from hilbert-flow)
bif_loader.py BIF parser + ModelInstance factory
tokens.py InferenceResult → noethers-turnstile ProofToken translation layer
run_demo.py main script: 3-row OOC/DIA/AEX budget table
tests/ 97 tests: bridge (10), demo (4), stress (32), BIF (32), gaps (20), tokens (9)
results/ captured test and demo outputs (dated)
conftest.py auto-inserts workspace python/ ahead of any installed wheel
noethers-turnstile-core/ Pure Rust library (no PyO3 dependency)
permission.rs Permission enum + total order + algebra
gap.rs GapStatus, GapRecord, Profile, GapRequirement
token.rs ProofToken, provenance hashing
context.rs ProofContext (Γ), Scope, Membership
compiler.rs compile() — descending search + structural meets
composition.rs compose() — lax monoidal composition
expiry.rs Expiry, RuntimeContext, LiveJudgment<'ctx>
error.rs TurnstileError hierarchy
registry.rs Append-only schema registry
audit.rs AuditEntry, Derivation, AuditStore trait
certifier.rs Certifier trait (main extension point)
noethers-turnstile-py/ PyO3 bindings (thin wrapper over turnstile-core)
src/lib.rs #[pymodule] + all #[pyclass] wrappers
noethers-turnstile-tests/ Structural and property-based tests (998 Rust tests)
ec003*/ EC-003 theorem suite (composition algebra,
provenance, expiry, token status, OOC variants, …)
ec004_*/ EC-004 profile well-formedness
ec005_*/ EC-005 domain admission
ec006_* Law G01 profile monotonicity validator
ec007_* Derivation chain soundness (non-increasing steps)
ec008_* Concurrent AuditStore integrity
ec009_* Permission::from_str exhaustive coverage
ec010_* Scope candidate admission (rule [ADMISSIBLE])
ec011_* GapStatus min_status algebra invariants
ec012_* Priority tier dominance (T8, T10)
ec013_* Composition fail-closed on all conflict types
ec014_* SchemaRegistry append-only invariants R1–R7
ec015_* Disallowed-use accumulation (T13)
ec016_* Compile determinism (sequential + concurrent)
ec017_* Error type coverage (all variants reachable)
ec018_* Large-context stress (100–500 gaps, 200 tokens)
ec019_* T11 diagnostic/action separation (exhaustive)
ec020_* Token and context expiry edge cases
ec021_* MalformedContext validation (all 4 conditions)
ec022_* LiveJudgment<'ctx> runtime T15 contract
ec023_* descending() order stability (pinned sequence)
ec024_* Token expiry masking in composition
ec025_* BoundKind variant coverage (Numeric/SetValued/Infinity)
ec026_* Dead-token expiry semantics (only Valid triggers EXP)
ec027_* Compose claim_id/candidate_id semantics
ec028_* Provenance hash unicode and large inputs
ec029_* Poisoned-mutex recovery (SchemaRegistry + AuditStore)
ec030_* compile()/compose() never panic (adversarial inputs)
ec031_* Adversarial families A1–A10 (EC-001 §34)
ec032_* Positive families P1–P10 (EC-001 §34, in-class domains)
ec033_* Negative families N1–N10 (EC-001 §34, OOC exact)
ec034_* Permission tier semantics (T8, exhaustive 144-pair meet)
ec035_* Multi-profile descending search (determinism, S7–S12)
ec036_* Token liveness and freshness (L1–L15, EXP floor)
ec037_* Serde round-trip and wire-format stability (W1–W12)
ec038_* Scope intersection semantics (SI1–SI10, T14)
ec039_* Derivation and audit trail integrity (D1–D12, T18)
ec040_* Composition identity laws (CI1–CI8, T6 end-to-end)
ec041_* Allowed-use soundness (AU1–AU14, T12, byte-exact binding)
ec042_* Heterogeneous anti-laundering (H1–H16, T16, OOC absorbing)
ec043_* Audit-not-authority exhaustive (A1–A9, T18, replay attacks)
ec044_* Authority ceiling exhaustive (C1–C14, T19, hard cap)
ec045_* Permission triples exhaustive (TR1–TR5, all 1728 triples)
ec046_* Meet GLB property exhaustive (GLB1–GLB5, T8, greatest lower bound)
ec047_* Step 11 assembler truth table (S1–S16, T8/T11, tier dominance)
ec048_* Theorem 2 greatest-satisfiable (T2-1–T2-11, T5/T10)
ec049_* Admission contract A1–A9 depth (T6/T19, bounded-time, adversarial)
ec050_* Schema version adversarial (SV1–SV12, T2, mismatch/concurrent)
proptest_*/ Property-based tests for the 4 structural guarantees
step11_assembler Assembler integration tests
python/tests/ Python integration tests (100 tests, pytest)
test_py001_permission Permission ordering, meet, from_str, hash
test_py002_compile_basic compile() outcomes: OOC/DIA/EXP/MalformedContext
test_py003_live_judgment LiveJudgment expiry, fingerprint, idempotence
test_py004_compose compose() identity inheritance, g2 token rejection
test_py005_timestamps Timestamp precision and EXP floor boundary behavior
test_py006_exceptions Exception hierarchy and message quality
test_py007_types GapRecord/Membership/NegativeControlStatus/ProofToken
test_py008_derivation Derivation steps, compiled_at, permission match
License
Apache 2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file noethers_turnstile-0.2.2.tar.gz.
File metadata
- Download URL: noethers_turnstile-0.2.2.tar.gz
- Upload date:
- Size: 58.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f8cdaec218b261347a5f706cacef3c716b6fe78928e27e5f247d405afcaa1e1
|
|
| MD5 |
09ec52256a9258771d1890edd3ce1f3c
|
|
| BLAKE2b-256 |
47ee0239d21769a673e515d6ecd146dbb82f8b38823ba46620ccce88abe005de
|
File details
Details for the file noethers_turnstile-0.2.2-cp311-cp311-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: noethers_turnstile-0.2.2-cp311-cp311-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 471.4 kB
- Tags: CPython 3.11, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd10ddf80ed299018031196e234763e4cc9db0df15a141bd869f5360b1a052ef
|
|
| MD5 |
04c778fa66cf93dc237fdfe5d04e940b
|
|
| BLAKE2b-256 |
2e17e872c14ef15b9a0ed3f74c0e4e1f1afae75a80a8c21a3d18d4ff8405a2a9
|