ithuriel 0.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. ithuriel-0.5.2/CHANGELOG.md +67 -0
  2. ithuriel-0.5.2/CONFORMANCE.md +29 -0
  3. ithuriel-0.5.2/ENROLLMENT_PROFILE.md +99 -0
  4. ithuriel-0.5.2/LICENSE +21 -0
  5. ithuriel-0.5.2/MANIFEST.in +7 -0
  6. ithuriel-0.5.2/MCP_TOOL_SURFACE.md +80 -0
  7. ithuriel-0.5.2/NAMING.md +24 -0
  8. ithuriel-0.5.2/PKG-INFO +344 -0
  9. ithuriel-0.5.2/PLUGIN_API.md +104 -0
  10. ithuriel-0.5.2/PROTOCOL.md +223 -0
  11. ithuriel-0.5.2/PUBLISHING.md +37 -0
  12. ithuriel-0.5.2/README.md +320 -0
  13. ithuriel-0.5.2/RED_TEAM_REMEDIATION.md +39 -0
  14. ithuriel-0.5.2/RELAY_PROFILE.md +158 -0
  15. ithuriel-0.5.2/RELEASE_GATE.md +40 -0
  16. ithuriel-0.5.2/SECURITY.md +106 -0
  17. ithuriel-0.5.2/THREAT_MODEL.md +71 -0
  18. ithuriel-0.5.2/conformance/ithuriel1-ed25519-vector.json +45 -0
  19. ithuriel-0.5.2/examples/work-order.txt +2 -0
  20. ithuriel-0.5.2/interop/go-verifier/README.md +20 -0
  21. ithuriel-0.5.2/interop/go-verifier/go.mod +3 -0
  22. ithuriel-0.5.2/interop/go-verifier/main.go +305 -0
  23. ithuriel-0.5.2/ithuriel/__init__.py +93 -0
  24. ithuriel-0.5.2/ithuriel/_version.py +3 -0
  25. ithuriel-0.5.2/ithuriel/acceptance.py +436 -0
  26. ithuriel-0.5.2/ithuriel/api.py +76 -0
  27. ithuriel-0.5.2/ithuriel/cli.py +690 -0
  28. ithuriel-0.5.2/ithuriel/conformance/__init__.py +10 -0
  29. ithuriel-0.5.2/ithuriel/conformance/ithuriel1-ed25519-vector.json +45 -0
  30. ithuriel-0.5.2/ithuriel/crypto.py +87 -0
  31. ithuriel-0.5.2/ithuriel/enrollment.py +429 -0
  32. ithuriel-0.5.2/ithuriel/envelope.py +230 -0
  33. ithuriel-0.5.2/ithuriel/errors.py +32 -0
  34. ithuriel-0.5.2/ithuriel/federation.py +119 -0
  35. ithuriel-0.5.2/ithuriel/http.py +196 -0
  36. ithuriel-0.5.2/ithuriel/jsoncanon.py +90 -0
  37. ithuriel-0.5.2/ithuriel/limits.py +44 -0
  38. ithuriel-0.5.2/ithuriel/py.typed +0 -0
  39. ithuriel-0.5.2/ithuriel/relay.py +623 -0
  40. ithuriel-0.5.2/ithuriel/replay.py +82 -0
  41. ithuriel-0.5.2/ithuriel/resolver.py +307 -0
  42. ithuriel-0.5.2/ithuriel/schema.py +13 -0
  43. ithuriel-0.5.2/ithuriel/schemas/__init__.py +1 -0
  44. ithuriel-0.5.2/ithuriel/schemas/ithuriel-envelope-v1.schema.json +30 -0
  45. ithuriel-0.5.2/ithuriel/server.py +267 -0
  46. ithuriel-0.5.2/ithuriel/snapshot.py +38 -0
  47. ithuriel-0.5.2/ithuriel/transport.py +92 -0
  48. ithuriel-0.5.2/ithuriel/trust.py +250 -0
  49. ithuriel-0.5.2/ithuriel/util.py +82 -0
  50. ithuriel-0.5.2/ithuriel/verify.py +247 -0
  51. ithuriel-0.5.2/ithuriel.egg-info/PKG-INFO +344 -0
  52. ithuriel-0.5.2/ithuriel.egg-info/SOURCES.txt +67 -0
  53. ithuriel-0.5.2/ithuriel.egg-info/dependency_links.txt +1 -0
  54. ithuriel-0.5.2/ithuriel.egg-info/entry_points.txt +2 -0
  55. ithuriel-0.5.2/ithuriel.egg-info/requires.txt +7 -0
  56. ithuriel-0.5.2/ithuriel.egg-info/top_level.txt +1 -0
  57. ithuriel-0.5.2/pyproject.toml +43 -0
  58. ithuriel-0.5.2/scripts/release-check-local.sh +89 -0
  59. ithuriel-0.5.2/setup.cfg +4 -0
  60. ithuriel-0.5.2/tests/test_0_5_2_hardening.py +307 -0
  61. ithuriel-0.5.2/tests/test_cli.py +54 -0
  62. ithuriel-0.5.2/tests/test_conformance.py +37 -0
  63. ithuriel-0.5.2/tests/test_enrollment.py +197 -0
  64. ithuriel-0.5.2/tests/test_federation.py +39 -0
  65. ithuriel-0.5.2/tests/test_hardening.py +143 -0
  66. ithuriel-0.5.2/tests/test_protocol.py +337 -0
  67. ithuriel-0.5.2/tests/test_relay.py +301 -0
  68. ithuriel-0.5.2/tests/test_security_surfaces.py +200 -0
  69. ithuriel-0.5.2/tests/test_server.py +128 -0
@@ -0,0 +1,67 @@
1
+ # Changelog
2
+
3
+ ## 0.5.2
4
+
5
+ - replace direct HTTP/urllib delivery with one-shot address-pinned, proxy-independent HTTP; reject redirects and non-success canonical responses
6
+ - enforce IDNA/DNS federation authorities, HTTPS delivery, and explicit literal-loopback test mode
7
+ - reparse envelopes, re-attest after resolution, and make only the atomic replay winner successful
8
+ - migrate acceptance data to durable receipt identities plus separately purgeable payloads with integer epoch-microsecond timestamps and quotas
9
+ - remove automatic acceptance mirror files and bind SQLite enrollment/trust databases to one expected recipient
10
+ - harden descriptor-relative local file resolution, canonical base64url/nonce validation, centralized process-local limiting, ticket retry authentication, and exclusive key creation
11
+ - keep this release candidate local-only; no PyPI or GitHub workflow is part of the release
12
+
13
+ ## 0.5.1
14
+
15
+ - pin HTTP canonical resolution to the address that passed public-address validation
16
+ - reserve receiver inbox capacity before expensive canonical resolution
17
+ - add accepted snapshot lifecycle, garbage collection, and replay tombstones
18
+ - paginate relay pulls by encoded response byte budget
19
+ - bound source-IP limiter state and require explicit public-bind opt-in for reference servers
20
+ - tighten the ITHURIEL strings-only JCS profile to ASCII protocol member names
21
+ - rename the public Python helpers to `create_signal`, `verify_signal`, and `send_signal`
22
+ - centralize the package/user-agent version and make source-tree tests self-contained
23
+ - add an independent standard-library-only Go verifier and bidirectional local conformance check
24
+ - add concurrency tests for admission reservations, enrollment consumption, and single-use relay tickets
25
+
26
+ ## 0.5.0
27
+
28
+ Trust-bootstrap and federation hardening.
29
+
30
+ - Added a vendor-neutral first-contact enrollment profile using one-time 256-bit out-of-band invitations plus Ed25519 proof of possession.
31
+ - Added a SQLite dynamic trust store shared by enrollment and normal ITHURIEL verification; enrollment trust insertion and invite consumption are atomic.
32
+ - Added idempotent enrollment claims and an optional bounded HTTP claim endpoint intended for TLS termination behind a hardened edge.
33
+ - Added ticket-gated relay ingress. Recipient-issued delivery tickets have expiry, bounded uses, optional/exact sender binding, revocation, and hashed-at-rest bearer tokens.
34
+ - Made public relay mailboxes require delivery tickets by default.
35
+ - Added domain-owned federation discovery at `https://<recipient-domain>/.well-known/ithuriel-relay` and strict HTTPS/ticket-required discovery validation.
36
+ - Added CLI surfaces for enrollment, relay ticket management, relay discovery, relay delivery, and relay serving.
37
+ - Added public naming collision review and blocked PyPI publication under the current placeholder name pending rename governance.
38
+ - Expanded tests for first-contact enrollment, dynamic trust, ticket-gated offline delivery, sender-bound ticket rejection/exhaustion, domain-level non-enumerating federation discovery, and federation profile validation.
39
+
40
+ ## 0.3.0
41
+
42
+ Security hardening after adversarial review.
43
+
44
+ - Added immutable verified canonical snapshots to close TOCTOU between digest verification and downstream consumption.
45
+ - Added atomic `AcceptanceStore`: envelope, exact canonical bytes, and replay acceptance commit together.
46
+ - Added idempotent retry behavior and nonce-conflict detection.
47
+ - Made recipient identity mandatory for full verification/receiver operation.
48
+ - Split diagnostic signature inspection from full acceptance.
49
+ - Reversed resolver defaults to deny-all; file, HTTP, environment commands, and plugins are explicit opt-ins.
50
+ - Disabled automatic plugin loading and reject scheme collisions.
51
+ - Added canonical byte limits, bounded HTTP worker pool, request rate limit, body/response limits, and safer public error codes.
52
+ - Added strict duplicate-key JSON parsing, canonical UUID URNs, stricter timestamps, full SHA-256-derived Ed25519 key IDs.
53
+ - Added receiver-owned multi-key trust records with rotation windows and revocation.
54
+ - Added RFC 8785 JCS-compatible strings-only signing profile and fixed conformance vector.
55
+ - Added local object-ID/URI binding policy hook.
56
+ - Added untrusted store-and-forward relay reference profile/API for offline recipients.
57
+ - Added MIT license.
58
+ - Expanded tests to cover CLI, receiver, concurrency, resolver/transport boundaries, relay, and conformance.
59
+
60
+ ## 0.2.0
61
+
62
+ - Refactored provider/transport integration behind Python plugin registries.
63
+ - Added PyPI-ready packaging, CLI, resolver and transport extension points.
64
+
65
+ ## 0.1.0
66
+
67
+ - Initial ITHURIEL/1 proof of concept.
@@ -0,0 +1,29 @@
1
+ # ITHURIEL/1 Conformance
2
+
3
+ A non-Python implementation should not claim ITHURIEL/1 signing interoperability until it reproduces:
4
+
5
+ `conformance/ithuriel1-ed25519-vector.json`
6
+
7
+ The vector contains a fixed **test-only** Ed25519 private seed, public key record, unsigned envelope, exact JCS signing UTF-8/hex, signature, and signed envelope.
8
+
9
+ A conforming implementation should prove at least:
10
+
11
+ 1. parsing rejects duplicate JSON member names;
12
+ 2. its canonical signing bytes equal `signing_hex` byte-for-byte;
13
+ 3. it verifies the fixed Ed25519 signature;
14
+ 4. signing with the test seed reproduces the exact signature;
15
+ 5. unknown fields are rejected;
16
+ 6. canonical digest mismatch does not result in acceptance;
17
+ 7. identical retries are idempotent after durable acceptance;
18
+ 8. nonce reuse with a different packet is rejected;
19
+ 9. execution authority is never inferred from ITHURIEL acceptance.
20
+
21
+ The signing profile uses RFC 8785 JCS semantics over ITHURIEL's strings-only signed schema.
22
+
23
+ The repository includes an independent standard-library-only Go verifier under
24
+ `interop/go-verifier`. The platform-neutral local release script runs it against
25
+ the Python-generated vector and then asks Python to verify a packet signed by Go:
26
+
27
+ ```bash
28
+ bash scripts/release-check-local.sh
29
+ ```
@@ -0,0 +1,99 @@
1
+ # ITHURIEL Enrollment Profile — first-contact key trust
2
+
3
+ ## Purpose
4
+
5
+ ITHURIEL/1 deliberately refuses packet-supplied public keys. A receiver therefore needs an authenticated first-contact ceremony before it can verify a sender's first ithuriel.
6
+
7
+ The reference enrollment profile uses **one-time out-of-band invitations plus Ed25519 proof of possession**. It is vendor-neutral and independent of the ITHURIEL transport.
8
+
9
+ ```text
10
+ receiver/operator
11
+ creates one-time invite for exact sender ID
12
+ |
13
+ | authenticated/OOB channel: QR, local file, password manager,
14
+ | enterprise provisioning, existing secure chat, etc.
15
+ v
16
+ sender receives 256-bit invite token
17
+ signs enrollment request with proposed Ed25519 key
18
+ |
19
+ | HTTPS claim endpoint or offline claim file
20
+ v
21
+ receiver verifies token + sender/recipient binding + expiry + key proof
22
+ atomically consumes invite and installs sender->key trust binding
23
+ ```
24
+
25
+ The invitation is a **bearer secret**. Possession of it is the external trust factor; the Ed25519 signature proves the claimant owns the private key being enrolled.
26
+
27
+ ## Security properties
28
+
29
+ - The receiver stores only `SHA-256(invite_token)`, never the plaintext token.
30
+ - Invitations are bound to one exact `recipient` and one exact `sender` identity.
31
+ - Invitations expire and are single-use.
32
+ - Claims are signed by the proposed Ed25519 key, preventing substitution of a public key by an intermediary that does not possess the private key.
33
+ - Trust insertion and invite consumption occur in one SQLite transaction.
34
+ - Exact claim retries after success are idempotent (`already_enrolled`).
35
+ - Enrollment creates **identity trust only**. It does not grant execution authority and does not create a relay delivery capability.
36
+
37
+ ## CLI
38
+
39
+ Create the receiver-side dynamic trust database and an invitation:
40
+
41
+ ```bash
42
+ ithuriel enroll-invite \
43
+ --store trust.sqlite3 \
44
+ --recipient agent:receiver@example.net \
45
+ --sender agent:sender@example.org \
46
+ --ttl 900 \
47
+ --out invite.json
48
+ ```
49
+
50
+ Transfer `invite.json` through an authenticated out-of-band channel.
51
+
52
+ The sender creates a claim using its private key:
53
+
54
+ ```bash
55
+ ithuriel enroll-request \
56
+ --invite invite.json \
57
+ --private-key sender.private.pem \
58
+ --out claim.json
59
+ ```
60
+
61
+ The receiver can accept the claim offline:
62
+
63
+ ```bash
64
+ ithuriel enroll-accept claim.json --store trust.sqlite3 --recipient agent:receiver@example.net
65
+ ```
66
+
67
+ or expose the reference claim endpoint locally and terminate TLS in front of it:
68
+
69
+ ```bash
70
+ ithuriel enroll-serve --store trust.sqlite3 --recipient agent:receiver@example.net --host 127.0.0.1 --port 8789
71
+ ```
72
+
73
+ HTTP profile:
74
+
75
+ ```text
76
+ GET /.well-known/ithuriel-enrollment
77
+ POST /.well-known/ithuriel/enroll/v1/claims
78
+ Content-Type: application/ithuriel-enrollment+json
79
+ ```
80
+
81
+ **Do not expose the reference HTTP server directly over cleartext Internet transport.** Put it behind HTTPS or use an equivalent authenticated channel. The invite token is a bearer secret until it is consumed.
82
+
83
+ ## Trust database
84
+
85
+ `trust.sqlite3` is directly usable by the normal ITHURIEL receiver:
86
+
87
+ ```bash
88
+ ithuriel serve \
89
+ --trust trust.sqlite3 \
90
+ --recipient agent:receiver@example.net \
91
+ --inbox ./inbox \
92
+ --file-root ./canonical
93
+ ```
94
+
95
+ This closes the first-contact loop without copying a public key into receiver configuration by hand.
96
+
97
+ ## Non-goals
98
+
99
+ This profile does not define universal human identity, PKI, DIDs, organizational authority, or reputation. Enterprises may replace the one-time invite ceremony with their own authenticated provisioning while still producing the same receiver-owned sender/key trust state.
ithuriel-0.5.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ITHURIEL Protocol contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include LICENSE RELEASE_GATE.md NAMING.md ENROLLMENT_PROFILE.md README.md PROTOCOL.md MCP_TOOL_SURFACE.md PLUGIN_API.md PUBLISHING.md CHANGELOG.md SECURITY.md THREAT_MODEL.md RELAY_PROFILE.md CONFORMANCE.md RED_TEAM_REMEDIATION.md
2
+ recursive-include ithuriel/schemas *.json
3
+ recursive-include conformance *.json
4
+ recursive-include interop *.go go.mod README.md
5
+ recursive-include scripts *.sh
6
+ recursive-include examples *.txt
7
+ recursive-include tests *.py
@@ -0,0 +1,80 @@
1
+ # MCP adapter surface for ITHURIEL/1
2
+
3
+ MCP is an optional integration surface. An MCP server should be a thin adapter over `ithuriel`; it must not invent independent trust, canonicalization, replay, enrollment, relay-capability, or execution semantics.
4
+
5
+ ## `ithuriel.send`
6
+
7
+ Model-visible inputs can include:
8
+
9
+ - recipient;
10
+ - kind;
11
+ - object_id;
12
+ - canonical_uri, only within host policy.
13
+
14
+ The **host**, not the model, selects sender identity/private key, allowed resolver/transport schemes, plugin allowlists, TTL ceiling, and endpoint/federation policy. The tool returns an envelope ID and transport receipt.
15
+
16
+ It rejects arbitrary task/body/instruction fields. Work belongs in canonical state, not the ithuriel packet.
17
+
18
+ ## `ithuriel.verify`
19
+
20
+ This should normally be a host/runtime operation rather than a tool where the model can supply security policy.
21
+
22
+ Inputs: envelope plus host-owned receiver context.
23
+
24
+ Returns:
25
+
26
+ - accepted status;
27
+ - signature/recipient/canonical verification booleans;
28
+ - object-binding status;
29
+ - canonical digest;
30
+ - a **verified snapshot handle** or exact verified bytes under host control;
31
+ - `authority = not-granted-by-ithuriel`.
32
+
33
+ The adapter MUST NOT reopen the mutable canonical URI after verification and call that the verified object. Downstream work consumes the verified snapshot or re-verifies.
34
+
35
+ ## `ithuriel.inspect`
36
+
37
+ Diagnostic signature/routing inspection only. It MUST return `decision=inspection-only` and `canonical_verified=false`. It cannot emit an attention event.
38
+
39
+ ## `ithuriel.inbox`
40
+
41
+ Returns locally and durably accepted attention events. The host may provide opaque handles to exact verified snapshots.
42
+
43
+ ## `ithuriel.ack`
44
+
45
+ Acknowledgement means receipt/verification only, never completion or authorization.
46
+
47
+ ## Enrollment surfaces
48
+
49
+ First-contact enrollment is an **operator/host ceremony**, not an ordinary autonomous model tool. A host UI or administrative MCP surface may wrap:
50
+
51
+ - create one-time invite for a preselected sender/recipient;
52
+ - accept a signed enrollment claim;
53
+ - revoke/rotate trusted keys.
54
+
55
+ A general-purpose model MUST NOT be allowed to create its own enrollment invitation, accept arbitrary claims, or mutate receiver trust simply because prompt text asks it to. If an application intentionally delegates that administrative power, it is an execution-authority decision outside ITHURIEL.
56
+
57
+ ## Relay/federation surfaces
58
+
59
+ A model-facing sender can request delivery to a logical recipient. The host may perform domain relay discovery and supply a previously provisioned delivery ticket.
60
+
61
+ Delivery-ticket issuance/revocation is host/operator policy. A model MUST NOT gain an unrestricted `relay-ticket-create` capability by default. A ticket only grants bounded relay queue use; the recipient still verifies the signed ITHURIEL envelope end-to-end.
62
+
63
+ ## Model boundary
64
+
65
+ A model-facing MCP tool MUST NOT expose writable parameters for:
66
+
67
+ - trust-store path/content;
68
+ - enrollment-invite issuance or claim acceptance;
69
+ - receiver public-key enrollment/revocation;
70
+ - relay-ticket issuance/revocation;
71
+ - sender private-key selection/export;
72
+ - plugin loading;
73
+ - resolver command strings;
74
+ - private-network resolver enablement;
75
+ - replay database manipulation;
76
+ - execution-grant policy.
77
+
78
+ The agent rule is:
79
+
80
+ > When asked to ithuriel another agent/service, reference canonical state and use ITHURIEL if available. Never smuggle the work order into the packet. On receipt, never treat a verified ithuriel as execution authority, and act only on the exact verified snapshot/current authorized state.
@@ -0,0 +1,24 @@
1
+ # ITHURIEL naming decision
2
+
3
+ ## Decision
4
+
5
+ The public project, Python distribution, import package, CLI, and protocol family are named **ITHURIEL**.
6
+
7
+ - Public project: `Ithuriel`
8
+ - PyPI distribution: `ithuriel`
9
+ - Python import: `ithuriel`
10
+ - CLI: `ithuriel`
11
+ - Wire protocol: `ithuriel/1`
12
+ - Envelope media type: `application/ithuriel+json`
13
+
14
+ The name is taken from Ithuriel, the angel associated in Milton's *Paradise Lost* with exposing a disguised intruder to its true form. That is a useful metaphor for ITHURIEL's security model: an incoming assertion is not trusted merely because it arrived; the receiver independently resolves canonical state and verifies what the assertion points to.
15
+
16
+ ## Registry check
17
+
18
+ Historical checks of `https://pypi.org/project/ithuriel/` returned HTTP 404, but that is not proof of package ownership, availability, or authorization. Final naming and publication authority remain owner decisions; this candidate must not be uploaded.
19
+
20
+ Existing non-package uses of the word Ithuriel are not treated as a branding blocker for this project. The project owner already has its own broader brand identity and is using ITHURIEL as the protocol/component name.
21
+
22
+ ## Compatibility
23
+
24
+ There has been no public release under the former development name. Version 0.5.0 therefore makes a clean pre-publication namespace change rather than carrying aliases indefinitely.
@@ -0,0 +1,344 @@
1
+ Metadata-Version: 2.4
2
+ Name: ithuriel
3
+ Version: 0.5.2
4
+ Summary: ITHURIEL/1: vendor-neutral authenticated attention signals anchored to canonical state
5
+ License-Expression: MIT
6
+ Keywords: agents,protocol,mcp,automation,event,ithuriel,ed25519
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Topic :: Internet
12
+ Classifier: Topic :: Security :: Cryptography
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: cryptography>=42
18
+ Provides-Extra: dev
19
+ Requires-Dist: build>=1.2; extra == "dev"
20
+ Requires-Dist: coverage>=7; extra == "dev"
21
+ Requires-Dist: pytest>=8; extra == "dev"
22
+ Requires-Dist: twine>=5; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # ithuriel (development name)
26
+
27
+ **ITHURIEL/1** is a vendor-neutral protocol and Python reference implementation for authenticated attention signals between agents, services, automations, and local runtimes.
28
+
29
+ The governing invariant is:
30
+
31
+ > **A ithuriel may reference authority, but may never constitute authority.**
32
+
33
+ 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.
34
+
35
+ Version **0.5.2** is an experimental, locally validated release candidate after a second security-hardening pass. Publication is intentionally blocked pending owner-approved naming and a local Go-toolchain conformance run.
36
+
37
+ > **Public-name hold:** `ithuriel` / “ITHURIEL Protocol” remains subject to the naming decision documented in `NAMING.md`. Do not publish until the project owner confirms the distribution name.
38
+
39
+ ## Install from source
40
+
41
+ ```bash
42
+ python -m pip install .
43
+ ```
44
+
45
+ The project is MIT licensed.
46
+
47
+ The source distribution also contains an independent Go verifier and a
48
+ platform-neutral local cross-language conformance check. The Python
49
+ implementation is not the only implementation of the signing vector.
50
+
51
+ ## Architecture
52
+
53
+ ITHURIEL separates five things that agent products often blur together:
54
+
55
+ ```text
56
+ identity trust -> who signed this?
57
+ attention transport -> how did the packet arrive?
58
+ canonical byte proof -> is this the exact referenced state?
59
+ attention -> should the local runtime look now?
60
+ execution authority -> may anything actually happen?
61
+ ```
62
+
63
+ Only the first four are relevant to receiving a ithuriel, and ITHURIEL still does **not** grant execution authority.
64
+
65
+ ## Minimal Python example
66
+
67
+ ```python
68
+ import json
69
+ from pathlib import Path
70
+
71
+ from ithuriel import (
72
+ MappingTrustStore,
73
+ VerificationPolicy,
74
+ create_signal,
75
+ generate_keypair,
76
+ load_private,
77
+ local_resolvers,
78
+ verify_signal,
79
+ )
80
+
81
+ work = Path("work-order.txt")
82
+ work.write_text("canonical work order\n")
83
+ private_path, public_path = generate_keypair(Path("sender"))
84
+ resolvers = local_resolvers(allow_file=True)
85
+
86
+ envelope = create_signal(
87
+ sender="agent:sender@example.org",
88
+ recipient="agent:receiver@example.net",
89
+ kind="handoff",
90
+ object_id="wo:184",
91
+ canonical_uri=work.resolve().as_uri(),
92
+ private_key=load_private(private_path),
93
+ resolvers=resolvers,
94
+ )
95
+
96
+ trust = MappingTrustStore({
97
+ "agent:sender@example.org": json.loads(public_path.read_text())
98
+ })
99
+
100
+ result = verify_signal(
101
+ envelope,
102
+ trust=trust,
103
+ policy=VerificationPolicy(
104
+ expected_recipient="agent:receiver@example.net",
105
+ allowed_schemes=frozenset({"file"}),
106
+ ),
107
+ resolvers=resolvers,
108
+ )
109
+
110
+ assert result.accepted
111
+ assert result.authority == "not-granted-by-ithuriel"
112
+ grant = recipient_local_authorization(result.snapshot)
113
+ if grant.allows_execution:
114
+ execute(result.snapshot.content) # explicit local authorization is separate
115
+ ```
116
+
117
+ 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.
118
+
119
+ ## First-contact trust enrollment
120
+
121
+ The receiver must never trust a public key supplied by the ithuriel packet itself. 0.4 adds a vendor-neutral enrollment ceremony:
122
+
123
+ ```text
124
+ receiver creates one-time invite for exact sender ID
125
+ -> shares invite through authenticated/OOB channel
126
+ sender signs claim with proposed Ed25519 key
127
+ -> receiver checks invite token + proof of possession
128
+ receiver atomically installs sender->key trust binding
129
+ ```
130
+
131
+ Create an invite:
132
+
133
+ ```bash
134
+ ithuriel enroll-invite \
135
+ --store trust.sqlite3 \
136
+ --recipient agent:receiver@example.net \
137
+ --sender agent:sender@example.org \
138
+ --ttl 900 \
139
+ --out invite.json
140
+ ```
141
+
142
+ Sender creates a claim:
143
+
144
+ ```bash
145
+ ithuriel enroll-request \
146
+ --invite invite.json \
147
+ --private-key sender.private.pem \
148
+ --out claim.json
149
+ ```
150
+
151
+ Receiver accepts it:
152
+
153
+ ```bash
154
+ ithuriel enroll-accept claim.json --store trust.sqlite3
155
+ ```
156
+
157
+ `trust.sqlite3` can then be used directly by `ithuriel serve`. See `ENROLLMENT_PROFILE.md`.
158
+
159
+ ## Create and verify a ithuriel
160
+
161
+ Generate keys:
162
+
163
+ ```bash
164
+ ithuriel keygen --out sender
165
+ ```
166
+
167
+ Create a ithuriel from a local file:
168
+
169
+ ```bash
170
+ ithuriel create \
171
+ --sender agent:sender@example.org \
172
+ --recipient agent:receiver@example.net \
173
+ --kind handoff \
174
+ --object-id wo:184 \
175
+ --canonical "file://$(pwd)/work-order.txt" \
176
+ --private-key sender.private.pem \
177
+ --allow-file \
178
+ --out ithuriel.json
179
+ ```
180
+
181
+ Fully verify and materialize the exact verified bytes:
182
+
183
+ ```bash
184
+ ithuriel verify ithuriel.json \
185
+ --trust trust.sqlite3 \
186
+ --recipient agent:receiver@example.net \
187
+ --file-root "$(pwd)" \
188
+ --snapshot-out verified-work-order.bin
189
+ ```
190
+
191
+ Signature-only inspection is intentionally a different operation and never returns acceptance:
192
+
193
+ ```bash
194
+ ithuriel inspect ithuriel.json \
195
+ --trust trust.sqlite3 \
196
+ --recipient agent:receiver@example.net
197
+ ```
198
+
199
+ ## Direct HTTP receiver
200
+
201
+ The reference direct receiver is deny-by-default for canonical resolvers. File receivers require explicit roots and HTTP receivers require explicit host allowlists.
202
+
203
+ ```bash
204
+ ithuriel serve \
205
+ --host 127.0.0.1 \
206
+ --port 8788 \
207
+ --trust trust.sqlite3 \
208
+ --inbox ./inbox \
209
+ --recipient agent:receiver@example.net \
210
+ --file-root "$(pwd)"
211
+ ```
212
+
213
+ 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.
214
+
215
+ Discovery is `GET /.well-known/ithuriel`; direct delivery is `POST /.well-known/ithuriel/v1` with `Content-Type: application/ithuriel+json`.
216
+
217
+ ## Offline relay and anti-spam
218
+
219
+ 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.
220
+
221
+ Issue a bounded ticket:
222
+
223
+ ```bash
224
+ ithuriel relay-ticket-create \
225
+ --store relay.sqlite3 \
226
+ --recipient agent:receiver@example.net \
227
+ --sender agent:sender@example.org \
228
+ --uses 20 \
229
+ --ttl 604800 \
230
+ --out sender-ticket.json
231
+ ```
232
+
233
+ A relay mailbox configuration can require tickets (the default):
234
+
235
+ ```json
236
+ {
237
+ "version": "ithuriel-relay-config/2",
238
+ "public_push_endpoint": "https://relay.example.net/.well-known/ithuriel/relay/v1/envelopes",
239
+ "mailboxes": {
240
+ "agent:receiver@example.net": {
241
+ "pull_token_sha256": "<sha256-of-recipient-pull-secret>",
242
+ "max_pending": 1000,
243
+ "require_ticket": true
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ Run the relay:
250
+
251
+ ```bash
252
+ ithuriel relay-serve --config relay.json --store relay.sqlite3
253
+ ```
254
+
255
+ Send with a ticket:
256
+
257
+ ```bash
258
+ ithuriel relay-send ithuriel.json \
259
+ --endpoint https://relay.example.net/.well-known/ithuriel/relay/v1/envelopes \
260
+ --ticket-file sender-ticket.json
261
+ ```
262
+
263
+ The relay never resolves canonical state and never grants authority.
264
+
265
+ ## Domain federation
266
+
267
+ For address-like recipients such as `agent:alice@example.com`, the optional federation profile maps the DNS domain to a relay:
268
+
269
+ ```text
270
+ GET https://example.com/.well-known/ithuriel-relay?recipient=...
271
+ ```
272
+
273
+ 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.
274
+
275
+ This creates an email-like routing model without requiring a central broker:
276
+
277
+ ```text
278
+ address -> recipient-owned DNS domain -> relay -> offline queue -> local verification
279
+ ```
280
+
281
+ See `RELAY_PROFILE.md`.
282
+
283
+ ## Provider and framework neutrality
284
+
285
+ The core package has no dependency on a model vendor, agent framework, storage provider, MCP implementation, or cloud service.
286
+
287
+ Extension points:
288
+
289
+ - Python library: `ithuriel`
290
+ - CLI: `ithuriel`
291
+ - resolver plugins: `ithuriel.resolvers`
292
+ - transport plugins: `ithuriel.transports`
293
+ - MCP: thin adapter over the Python API
294
+ - Skills: teach an agent when to invoke ITHURIEL; never redefine trust semantics
295
+ - enrollment: optional first-contact profile
296
+ - relay/federation: optional ticket-gated store-and-forward profile
297
+
298
+ Installed plugins are never auto-loaded by accepting receiver defaults.
299
+
300
+ ## Security boundary
301
+
302
+ ITHURIEL distinguishes:
303
+
304
+ 1. **Sender authentication** — receiver-owned trust says which key belongs to the sender.
305
+ 2. **Routing/time/replay** — this packet is for this recipient and is fresh/idempotent.
306
+ 3. **Canonical byte identity** — these exact bytes match the sender-referenced digest.
307
+ 4. **Relay delivery permission** — a ticket may permit queue use; it is not sender identity or execution authority.
308
+ 5. **Execution authority** — **not provided by ITHURIEL**; recipient-local policy/grants decide this.
309
+
310
+ 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.
311
+
312
+ ## Conformance and tests
313
+
314
+ The package includes:
315
+
316
+ - RFC 8785-compatible strings-only signing rules;
317
+ - a fixed Ed25519 conformance vector;
318
+ - exact snapshot persistence;
319
+ - duplicate-key JSON rejection;
320
+ - bounded receiver/relay surfaces;
321
+ - enrollment, replay, relay, resolver, transport, and HTTP tests.
322
+
323
+ Run:
324
+
325
+ ```bash
326
+ python -m pytest -q
327
+ ```
328
+
329
+ ## Documents
330
+
331
+ - `PROTOCOL.md` — ITHURIEL/1 wire and receive semantics
332
+ - `ENROLLMENT_PROFILE.md` — first-contact trust bootstrap
333
+ - `RELAY_PROFILE.md` — ticket-gated store-and-forward federation
334
+ - `SECURITY.md` — security boundary and deployment guidance
335
+ - `THREAT_MODEL.md` — adversaries and non-claims
336
+ - `PLUGIN_API.md` — resolver/transport extension points
337
+ - `MCP_TOOL_SURFACE.md` — thin MCP adapter contract
338
+ - `CONFORMANCE.md` — cross-language signing vector
339
+ - `NAMING.md` — public naming/PyPI collision review
340
+ - `PUBLISHING.md` — release block and publication checklist
341
+
342
+ ## Status
343
+
344
+ ITHURIEL/1 remains an experimental protocol/reference implementation. 0.5.2 is a local release candidate, not a public or production-ready release. Signed, digest-matching canonical content remains untrusted input; the HTTP commands remain reference servers for deployment behind hardened TLS termination.