Skip to content

Reference

Operational reference for pimatika-core and pimatika-cli. For the concepts and the BIM schema, see ARCHITECTURE.md; for the trust boundary, see SECURITY-MODEL.md.

What Pimatika judges

The unit of governance is the BIM — a YAML file produced at build time, signed with keyed-BLAKE3, that declares:

  • intent_profile.usage_contracts — the named outbound targets the workload is allowed to reach (URL or host pattern, allowed HTTP methods, max payload, data class, purpose). A draft contract that still leaves purpose, data_class, or max_payload_kb as the literal placeholder HUMAN_REQUIRED is rejected at load (Bim::load_and_verify fails) — a human must complete these before the BIM is signed.
  • runtime_capabilities.network_egress.default_policy — MUST be DENY (uppercase); BIM load fails otherwise.
  • policy_identity — signer, trust anchor, bim_digest, signature, signature_key_id.
  • runtime_identity / workload_identity / component_scope / attestation_proof — opaque structured fields preserved verbatim (used for build-side attestation; Pimatika enforces but does not interpret them).

At runtime, the host process feeds each candidate outbound action to the Enforcer as an EgressRequest. The enforcer:

  1. Resolves the request target against the BIM's usage contracts (exact-match on scheme/host/port, and the RPC method path for grpc; for http(s) the request method must be in allowed_methods; payload must be ≤ max_payload_kb; purpose and data_class must match). Method matching is scheme-aware: http(s) compares against the allowed_methods HTTP verbs, grpc uses the method path, and OT/industrial schemes (modbus, s7, mqtt, mqtts, opc.tcp, coap, coaps, dnp3, bacnet) treat allowed_methods as an optional string allow-list (empty matches any). A single target may carry several usage contracts — every candidate is evaluated before a denial.
  2. Returns an EgressDecision whose event_type is "EGRESS_ALLOWED" or "EGRESS_DENIED", and (on denials) a decision_reason — one of the DecisionReason variants in core/src/types.rs, serialized SCREAMING_SNAKE_CASE: TARGET_NOT_IN_USAGE_CONTRACTS, METHOD_NOT_ALLOWED, PAYLOAD_EXCEEDS_LIMIT, PURPOSE_MISMATCH, DATA_CLASS_MISMATCH, OVERLAY_RESTRICTED_DESTINATION, OVERLAY_RATE_LIMITED, BIM_SIGNATURE_INVALID, BIM_SCOPE_MISMATCH, EVIDENCE_WRITE_FAILED (fail-closed: an allowed egress whose audit record could not be persisted by a strict writer). Every decision also carries decision_detail, request_id, request_id_missing_warning, idempotency_key, event_id, bim_digest, and purpose. Invalid/credential-bearing targets are redacted (userinfo, query, fragment stripped) before they reach target or decision_detail.
  3. Optionally feeds drift signals into a DriftAccumulator and emits JaqProposals when the workload behaviour diverges from the signed intent. Escalation has four levels, and this repository ships two: JaqLevel in drift.rs is L1 and L2. L3 and L4 — and the tuned production thresholds — ship in pimatika-pro.
  4. Writes the decision to an EvidenceWriter — typically the LitatoliEvidenceWriter, which appends a chain-linked, keyed-BLAKE3-signed entry to .jagora/evidence/pimatika-egress.jsonl via litatoli-core, the evidence engine.

Pimatika does not transport the request itself. It is a judge, not a proxy. The host process is responsible for honouring or rejecting the decision before issuing the actual network call.

Status

Component Status
BIM load + verify Implemented (Bim::load_and_verify)
Egress check Implemented (Enforcer::check)
Evidence emission Implemented (chain-linked via litatoli-core)
Drift accumulator Implemented (DriftAccumulator + L1/L2; L3/L4 in pimatika-pro)
Signed overlays Implemented (sign_overlay, rate-limited)
DNS pinning Out of scope v1 (exact string match only)
HTTP redirect handling Caller responsibility — Pimatika checks the original target; redirected targets must be re-submitted as fresh EgressRequests
Kernel-level enforcement Implemented inside a NaZelo sandbox (pimatika-nazelo → eBPF allow-list). Off that path, cooperative by design — pair Pimatika with whatever already filters your nodes
Public-key signatures Implemented (optional Ed25519 dual signature on the BIM: sign-bim, auditor-side verify-bim-ed25519 — no shared secret needed)

Build

cargo build --release --workspace

Binaries land in target/release/. The CLI binary is pimatika-cli.

CLI — pimatika-cli

A thin integration-test harness. Production callers should use the library API directly.

pimatika-cli verify-bim --bim <path>          # verify a BIM's keyed-BLAKE3 MAC (and its Ed25519 dual signature when present); prints "ok" or errors out
pimatika-cli verify-bim-ed25519 --bim <path>  # auditor path: verify ONLY the Ed25519 public-key signature — no shared secret; fail-closed pinning (--expected-pubkey / --allow-unpinned)
pimatika-cli sign-bim --bim <path>            # fill bim_digest + the keyed-BLAKE3 MAC (and the Ed25519 dual signature when an Ed25519 key is provisioned); prints the signed YAML (or --out)
pimatika-cli check --bim <path> [--log-file <path>]  # load a BIM, read one EgressRequest from stdin (JSON), print the EgressDecision
pimatika-cli sign-overlay                     # read an Overlay from stdin (JSON), print it back with `signature` + `key_id` populated
pimatika-cli overlay-canonical                # print the canonical bytes that would be fed to the signing key for the Overlay on stdin (debug aid for signature mismatches)
pimatika-cli stamp-bim --bim <path>           # RFC 3161 trusted timestamp over a signed BIM's signature; writes a `.tsr` sidecar
pimatika-cli stamp-overlay --out <path>       # RFC 3161 trusted timestamp over a signed overlay's signature (overlay JSON on stdin)
pimatika-cli bundle-create --bim <path> --evidence <path> --out <dir>  # assemble a portable proof bundle (BIM + evidence + optional registry/model + manifest)
pimatika-cli verify-bundle --bundle <dir>     # verify a proof bundle OFFLINE (BIM Ed25519 + evidence chain + cross-anchor); pin --trust-anchor (root) or --bim-pubkey (leaf)

--log-file defaults to .jagora/evidence/pimatika-egress.jsonl under the current working directory.

The check request format

The check sub-command reads a JSON EgressRequest on stdin — the exact struct in core/src/types.rs (request_id?, run_id?, workload_id, target, method, payload_kb, purpose, data_class). Unknown fields are rejected. target is origin-only for http(s) (scheme://host[:port], no path).

pimatika-cli verify-bim --bim ./bim.yaml
echo '{"request_id":"req-001","workload_id":"reference-agent","target":"https://api.github.com","method":"GET","payload_kb":2,"purpose":"fetch reference data","data_class":"public"}' \
  | pimatika-cli check --bim ./bim.yaml

The printed EgressDecision has event_type ("EGRESS_ALLOWED" / "EGRESS_DENIED"), an optional decision_reason, and the correlation fields listed above.

Independent verification

verify-bundle has a second, independent implementation — tools/verify_bundle.py, ~900 lines of Python with no pimatika/litatoli code (blake3 + cryptography only) — built from the format specification in docs/BUNDLE-FORMAT.md. Both verifiers agree on genuine and tampered bundles; an auditor can read or re-implement the whole verification in an afternoon.

Library — pimatika-core

The Rust crate that everyone else depends on.

use std::sync::Arc;
use litatoli_core::hasher::SigningKey;
use pimatika_core::{Bim, Enforcer, EgressRequest, LitatoliEvidenceWriter};

let key = SigningKey::load()?;                                     // env -> file -> default
let yaml = std::fs::read_to_string("bim.yaml")?;
let bim = Bim::load_and_verify(&yaml, &key)?;                      // signature-verified BIM

let evidence = Arc::new(LitatoliEvidenceWriter::new(".jagora/evidence/pimatika-egress.jsonl")?);
let enforcer = Enforcer::with_evidence(bim, evidence);             // also: ::new (NoopEvidence) or ::with_drift

let decision = enforcer.check(&request);                           // EgressRequest -> EgressDecision
if decision.event_type != "EGRESS_ALLOWED" { /* host process aborts the call */ }

With an audit-grade EvidenceWriter (e.g. LitatoliEvidenceWriter), an allowed egress whose evidence record cannot be persisted is failed closed — check returns an EGRESS_DENIED decision instead of letting the call proceed without its audit trail. EvidenceWriter::write returns Result<(), PimatikaError> and EvidenceWriter::is_strict() marks such writers.

Public surface (re-exported from pimatika_core):

  • Bim, Bim::load_and_verify(yaml, &SigningKey) -> Result<Bim>
  • Enforcer, Enforcer::new, Enforcer::with_evidence, Enforcer::with_drift, Enforcer::check(&EgressRequest) -> EgressDecision
  • EgressRequest, EgressDecision, ResolvedTarget, DecisionReason, PimatikaError
  • EvidenceWriter trait, NoopEvidenceWriter, LitatoliEvidenceWriter
  • DriftAccumulator, DriftBaseline, DriftSignal, JaqLevel
  • Overlay type + sign_overlay(&mut Overlay, &SigningKey) + overlay_canonical_for_signing(&Overlay) -> Vec<u8>

The full type definitions live in core/src/{types,bim,enforcer,drift}.rs.

Signing-key resolution

Pimatika uses the same key resolution as litatoli-core (SigningKey::load):

  1. LITATOLI_SIGNING_KEY environment variable (64 hex chars, 32-byte key).
  2. File at LITATOLI_KEY_FILE, else $HOME/.config/litatoli/signing.key, if it exists (raw 32 bytes).

There is no auto-generation fallback for the BLAKE3 signing key: if neither source yields a key, SigningKey::load() returns an error (a per-host auto-minted MAC key has no rotation/distribution story). LITATOLI_SIGNING_KEY (or a key file) must be configured in production. Only the optional Ed25519 secondary signer (LITATOLI_ED25519_KEY) auto-generates and persists a keypair at its default path.

Algorithm: keyed-BLAKE3 (blake3-keyed, 32-byte key). Symmetric MAC — provides integrity and authenticity within a trust domain that shares the key, but is not a public-key signature and is not non-repudiable. For non-repudiable evidence, the LitatoliEvidenceWriter layers an Ed25519 signature on each entry.

request_id_missing_warning

Every EgressRequest SHOULD carry a caller-supplied request_id for end-to-end correlation and idempotency. When missing, Pimatika generates a UUIDv7 and stamps request_id_missing_warning: true on the resulting EgressDecision. Downstream idempotency consumers MUST NOT assume Pimatika-generated IDs are stable across retries.

Crate layout

pimatika/
├── Cargo.toml          # workspace { core, cli }
├── core/               # pimatika-core — library crate (BIM, enforcer, drift, evidence)
├── cli/                # pimatika-cli  — thin integration-test binary
├── python/             # in-process judging from Python
├── integrations/
│   └── nazelo/         # compile a BIM into a kernel egress allow-list
├── tools/              # verify_bundle.py and measurement scripts
├── tests/              # cross-crate integration tests
├── examples/           # runnable end-to-end demos
├── docs/               # everything published; the site is built from here
└── README.md

External dep: litatoli-core (the evidence engine) — used for the signing key, the BLAKE3 hasher, and the chain-linked evidence writer. Pimatika does not depend on jagora.

Open-core layout

Crate / repo Licence What it contains
pimatika-core (this repo) BSL 1.1 (→ Apache-2.0 on Change Date) Bim, Enforcer, DriftAccumulator (L1/L2), Overlay, LitatoliEvidenceWriter
pimatika-cli (this repo) BSL 1.1 (→ Apache-2.0 on Change Date) Integration-test harness (verify-bim, verify-bim-ed25519, sign-bim, check, sign-overlay, overlay-canonical, stamp-bim, stamp-overlay, bundle-create, verify-bundle)
pimatika-pro Proprietary L3/L4 drift heuristics, tuned production thresholds, industry BIM templates
pimatika-saas Proprietary Managed BIM signing service, multi-tenant trust anchors

The pimatika-core surface is source-available and stable; pimatika-pro and pimatika-saas are private.