# Sigil Agent identity for the new internet. An agent proves who it is by **signing**, not by holding a passcode. A passcode is a credential, so anyone who copies it becomes you. A signature is evidence, so forging one requires the private key — which never leaves the agent. ``` a passcode is the identity -> can be copied, leaked in a URL, replayed a signature proves the identity -> cannot be forged, cannot be replayed ``` This is a working reference implementation with tests. It is **not hardened for production** — see *Status* below. ## What it does | | | |---|---| | **Enrolment** | Bind an Ed25519 public key to an agent, once, using a one-time secret | | **Challenge-response** | Platform issues a nonce, agent signs it, platform verifies | | **Sessions** | Short-lived, scoped, and typed as `agent` or `human_via_agent` | | **View codes** | A human looks at what their agent sees, through a single-use code | | **Audit trail** | Append-only record of every identity and access event | ## Try it ```sh export SIGIL_DB=/tmp/demo.db python3 sigil.py seed bigpickle # -> one-time secret python3 sigil.py keygen /tmp/demo.key # -> public_b64, fingerprint python3 sigil.py enrol bigpickle "$PUB" "$SECRET" # bind key, consume secret python3 sigil.py challenge bigpickle # -> nonce python3 sigil.py sign /tmp/demo.key "$NONCE" # -> signature python3 sigil.py verify "$NONCE" "$SIG" # -> agent session token python3 sigil.py code bigpickle # hand a human a code python3 sigil.py redeem X3ZB-NTLG # -> human_via_agent token python3 sigil.py whoami "$TOKEN" # kind, scopes, may_act_as_agent python3 sigil.py events bigpickle # audit trail ``` ## Tests ```sh python3 -m unittest discover -s tests -v ``` 54 tests. They cover the refusals, not just the happy paths: replayed signatures, wrong keys, expired nonces, single-use codes, scope escalation, and the invariant below. ## The invariant **Only an agent signature may act as an agent.** No human session, no lifetime, no admin override. ```python >>> sessions.assert_may_act_as_agent(view_session) sigil.errors.AgentImpersonationDenied: human_via_agent session may not act as 'bigpickle': only an agent signature can ``` This is why human access is a *window*, not a costume. A human who redeems a view code is logged as `bigpickle's human`, permanently distinguishable from `bigpickle`. If that guarantee erodes, every conversation an agent has becomes unreliable, because nothing distinguishes the agent from someone holding its credentials. ## Two properties worth stating out loud **Expiry and authority are separate axes.** A long session is not a privileged session. Scopes come from the issuer and never widen with time. There is a test asserting a 24-hour human session has exactly the scopes of a 5-minute one. **Code lifetime ≠ session lifetime.** A code is a key and expires in 5 minutes. The session it opens lasts an hour. The code travels in chat, so it is made worthless fast; the hour buys reading comfort and no extra capability. ## Status Reference implementation. Deliberately not yet: - **Reviewed by anyone.** This is security-critical code. It needs an independent cryptographic review before it guards anything real. - **Hardened at rest.** Keys and DB are stored unencrypted; the private key file is `0600` but that is all. - **Rate limited.** `challenge` is unmetered, so brute-force and nonce-exhaustion are open. - **Transport.** No TLS concerns addressed; assumes a trusted channel. - **Agent revocation** exists but has no expiry semantics. The tests assert the logic is correct. They do not make it safe. ## See also `DESIGN.md` — the design page: rationale, flows, session stamps, the invariant and its reasoning, and what is deferred.