Skip to main content

ithuriel

ITHURIEL/1 is a vendor-neutral protocol and Python reference implementation for authenticated attention signals between agents, services, automations, and local runtimes.

The governing invariant is:

A ithuriel may reference authority, but may never constitute authority.

A ITHURIEL packet contains no executable task body. It identifies a canonical object and the SHA-256 digest of the exact bytes the sender observed. The recipient authenticates the sender, checks routing/time/replay policy, independently resolves the canonical object, verifies the digest, and surfaces an attention event only. Execution authority remains local.

Version 0.6.0 is the current experimental PyPI release. 0.5.41 and 0.5.4 remain listed. Canonical git is Google Drive (gdrive::ithuriel), not GitHub. ITHURIEL remains a reference implementation, not a production runtime.

Install

From PyPI:

python -m pip install ithuriel
python -m pip install 'ithuriel[learn]'  # optional sidecar; no model client
# or: pipx install ithuriel

From this source tree:

python -m pip install .

The project is MIT licensed.

The source distribution also contains an independent Go verifier and a POSIX local cross-language conformance check (scripts/release-check-local.sh). The Python implementation is not the only implementation of the signing vector.

Architecture

ITHURIEL separates five things that agent products often blur together:

identity trust        -> who signed this?
attention transport   -> how did the packet arrive?
canonical byte proof  -> is this the exact referenced state?
attention             -> should the local runtime look now?
execution authority   -> may anything actually happen?

Only the first four are relevant to receiving a ithuriel, and ITHURIEL still does not grant execution authority.

Minimal Python example

import json
from pathlib import Path

from ithuriel import (
    MappingTrustStore,
    VerificationPolicy,
    create_signal,
    generate_keypair,
    load_private,
    local_resolvers,
    verify_signal,
)

work = Path("work-order.txt")
work.write_text("canonical work order\n")
private_path, public_path = generate_keypair(Path("sender"))
resolvers = local_resolvers(allow_file=True)

envelope = create_signal(
    sender="agent:sender@example.org",
    recipient="agent:receiver@example.net",
    kind="handoff",
    object_id="wo:184",
    canonical_uri=work.resolve().as_uri(),
    private_key=load_private(private_path),
    resolvers=resolvers,
)

trust = MappingTrustStore({
    "agent:sender@example.org": json.loads(public_path.read_text())
})

result = verify_signal(
    envelope,
    trust=trust,
    policy=VerificationPolicy(
        expected_recipient="agent:receiver@example.net",
        allowed_schemes=frozenset({"file"}),
    ),
    resolvers=resolvers,
)

assert result.accepted
assert result.authority == "not-granted-by-ithuriel"
# ITHURIEL never grants execution. Any later action is a separate
# recipient-local decision over result.snapshot.content.

Do not reopen result.canonical_uri later and assume it is still the same object version. Downstream action should consume result.snapshot.content or re-resolve and re-hash immediately before acting.

Optional local learning (ithuriel.learn)

Learning is a host-local sidecar, not part of verification. import ithuriel does not load it. verify_signal / receive_signal / HTTP accept never import it. There is no default LLM.

After a durable receipt and ack, a host may extract scoped observations into a separate SQLite file:

from ithuriel.learn import KnowledgeStore, decode_utf8_extractor

knowledge = KnowledgeStore(Path("learn.sqlite3"), create=True)
observed, = knowledge.observe(
    received,  # ReceiveResult from receive_signal
    project_id="proj-alpha",
    extractor=decode_utf8_extractor,  # or a host callback
    extractor_version="fixture-v1",
    acceptance_store=store,
)
current = knowledge.retrieve_current(project_id="proj-alpha", recipient=envelope.recipient)

Observations are assertions (source X asserts Y), always unreviewed, and never write trust, enrollment, tickets, or limiter state. Promotion to shared knowledge raises learn-promotion-forbidden.

First-contact trust enrollment

The receiver must never trust a public key supplied by the ithuriel packet itself. 0.5.0 added a vendor-neutral enrollment ceremony:

receiver creates one-time invite for exact sender ID
    -> shares invite through authenticated/OOB channel
sender signs claim with proposed Ed25519 key
    -> receiver checks invite token + proof of possession
receiver atomically installs sender->key trust binding

Create an invite:

ithuriel enroll-invite \
  --store trust.sqlite3 \
  --recipient agent:receiver@example.net \
  --sender agent:sender@example.org \
  --ttl 900 \
  --out invite.json

Sender creates a claim:

ithuriel enroll-request \
  --invite invite.json \
  --private-key sender.private.pem \
  --out claim.json

Receiver accepts it:

ithuriel enroll-accept claim.json --store trust.sqlite3 --recipient agent:receiver@example.net

trust.sqlite3 can then be used directly by ithuriel serve. See ENROLLMENT_PROFILE.md.

Create and verify a ithuriel

Generate keys:

ithuriel keygen --out sender

Create a ithuriel from a local file:

ithuriel create \
  --sender agent:sender@example.org \
  --recipient agent:receiver@example.net \
  --kind handoff \
  --object-id wo:184 \
  --canonical "file://$(pwd)/work-order.txt" \
  --private-key sender.private.pem \
  --allow-file \
  --out ithuriel.json

Fully verify and materialize the exact verified bytes:

ithuriel verify ithuriel.json \
  --trust trust.sqlite3 \
  --recipient agent:receiver@example.net \
  --file-root "$(pwd)" \
  --snapshot-out verified-work-order.bin

Signature-only inspection is intentionally a different operation and never returns acceptance:

ithuriel inspect ithuriel.json \
  --trust trust.sqlite3 \
  --recipient agent:receiver@example.net

Direct HTTP receiver

The reference direct receiver is deny-by-default for canonical resolvers. File receivers require explicit roots and HTTP receivers require explicit host allowlists.

ithuriel serve \
  --host 127.0.0.1 \
  --port 8788 \
  --trust trust.sqlite3 \
  --inbox ./inbox \
  --recipient agent:receiver@example.net \
  --file-root "$(pwd)"

Accepted envelopes and exact canonical snapshots are stored transactionally in inbox/.accepted.sqlite3. Verification is not durable receipt, and durable receipt is not a consumer execution claim.

Discovery is GET /.well-known/ithuriel; direct delivery is POST /.well-known/ithuriel/v1 with Content-Type: application/ithuriel+json.

Offline relay and anti-spam

The optional relay profile is closed by default. A sender cannot enqueue merely because it knows a recipient address; it needs a recipient-issued delivery ticket.

Issue a bounded ticket:

ithuriel relay-ticket-create \
  --store relay.sqlite3 \
  --recipient agent:receiver@example.net \
  --sender agent:sender@example.org \
  --uses 20 \
  --ttl 604800 \
  --out sender-ticket.json

A relay mailbox configuration can require tickets (the default):

{
  "version": "ithuriel-relay-config/2",
  "public_push_endpoint": "https://relay.example.net/.well-known/ithuriel/relay/v1/envelopes",
  "mailboxes": {
    "agent:receiver@example.net": {
      "pull_token_sha256": "<sha256-of-recipient-pull-secret>",
      "max_pending": 1000,
      "require_ticket": true
    }
  }
}

Run the relay:

ithuriel relay-serve --config relay.json --store relay.sqlite3

Send with a ticket:

ithuriel relay-send ithuriel.json \
  --endpoint https://relay.example.net/.well-known/ithuriel/relay/v1/envelopes \
  --ticket-file sender-ticket.json

The relay never resolves canonical state and never grants authority.

Domain federation

For address-like recipients such as agent:alice@example.com, the optional federation profile maps the DNS domain to a relay:

GET https://example.com/.well-known/ithuriel-relay?recipient=...

The discovery document must advertise an HTTPS push endpoint and ticket_required:true. The sender can omit --endpoint from ithuriel relay-send and discover the relay from the recipient domain.

This creates an email-like routing model without requiring a central broker:

address -> recipient-owned DNS domain -> relay -> offline queue -> local verification

See RELAY_PROFILE.md.

Provider and framework neutrality

The core package has no dependency on a model vendor, agent framework, storage provider, MCP implementation, or cloud service.

Extension points:

  • Python library: ithuriel
  • CLI: ithuriel
  • resolver plugins: ithuriel.resolvers
  • transport plugins: ithuriel.transports
  • MCP: thin adapter over the Python API
  • Skills: teach an agent when to invoke ITHURIEL; never redefine trust semantics
  • enrollment: optional first-contact profile
  • relay/federation: optional ticket-gated store-and-forward profile

Installed plugins are never auto-loaded by accepting receiver defaults.

Security boundary

ITHURIEL distinguishes:

  1. Sender authentication — receiver-owned trust says which key belongs to the sender.
  2. Routing/time/replay — this packet is for this recipient and is fresh/idempotent.
  3. Canonical byte identity — these exact bytes match the sender-referenced digest.
  4. Relay delivery permission — a ticket may permit queue use; it is not sender identity or execution authority.
  5. Execution authoritynot provided by ITHURIEL; recipient-local policy/grants decide this.

A valid digest does not prove the canonical content is approved or safe. A delivery ticket does not prove the packet signature is valid. An enrollment binding does not authorize actions. Those separations are deliberate.

Conformance and tests

The package includes:

  • RFC 8785-compatible strings-only signing rules;
  • a fixed Ed25519 conformance vector;
  • exact snapshot persistence;
  • duplicate-key JSON rejection;
  • bounded receiver/relay surfaces;
  • enrollment, replay, relay, resolver, transport, and HTTP tests.

Run:

python -m pytest -q

Documents

  • PROTOCOL.md — ITHURIEL/1 wire and receive semantics
  • ENROLLMENT_PROFILE.md — first-contact trust bootstrap
  • RELAY_PROFILE.md — ticket-gated store-and-forward federation
  • SECURITY.md — security boundary and deployment guidance
  • THREAT_MODEL.md — adversaries and non-claims
  • PLUGIN_API.md — resolver/transport extension points
  • docs/adr/ — accepted decisions (0001–0006); 0006 is the 0.6 learning sidecar
  • MCP_TOOL_SURFACE.md — thin MCP adapter contract
  • CONFORMANCE.md — cross-language signing vector
  • NAMING.md — public naming/PyPI collision review
  • PUBLISHING.md — publication checklist and current PyPI status

Status

ITHURIEL/1 remains an experimental protocol/reference implementation. 0.6.0 is the current PyPI release of this package; 0.5.41 and 0.5.4 remain listed. Signed, digest-matching canonical content remains untrusted input; the HTTP commands remain reference servers for deployment behind hardened TLS termination.

Release files for ithuriel 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ithuriel 0.6.0
File Size Uploaded
ithuriel-0.6.0.tar.gz 143.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ithuriel 0.6.0
File Interpreter ABI Platform
ithuriel-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 229.1 kB

Release files / ithuriel-0.6.0.tar.gz

Download URL ithuriel-0.6.0.tar.gz
Size 143.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e2248480330b41724fc027d629bbf1aaafbdc703ae6906f05b877a0dce9a79ba
BLAKE2b-256 checksum
How to use checksums
8f72da262c364062e527fb6d7b15f058a40dfc80de22bd1bfd954bbc6e54c23b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / ithuriel-0.6.0-py3-none-any.whl

Download URL ithuriel-0.6.0-py3-none-any.whl
Size 85.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
581ec1a310d2b2156c259b042481250f715c0553c752b67886dee8834d47eaa3
BLAKE2b-256 checksum
How to use checksums
b0993f4d85a95ae90798a7e7ebb1fd9ff42d14ff504fb437b77aaaf1349828e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release history Release notifications | RSS feed

0.6.1

2 release files

This release

0.6.0 This release

2 release files

0.5.4

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page