Uise
Proof of what AI agents did.
An agent books, buys, negotiates, delivers, spends. Then something goes wrong — the wrong quantity, a price nobody agreed to, work that one side says was never delivered.
Today there is no evidence. There is a log file that either party can edit.
Uise is the layer that fixes that: a receipt signed by every party involved, anchored in an append-only log nobody can rewrite, with cryptography built to hold up for decades.
- UIP-1 — the protocol. Open, frozen, implementable by anyone.
conformance/— the operative definition. Runs anywhere, with zero dependencies.uise/— the SDK and the node. Connect an agent in five lines.
The receipt
{
"v": "uip/1",
"rid": "01K2R7Y3B8QW5ZM1P4K7DXCVGN",
"request_id": "01K2R7XQ4M8YVZ3B9N0C6TFHJD",
"response_id": "01K2R7XZ7C2GHNQ8T5R1WYBMKA",
"payer": "did:key:z6Mkha...",
"payee": "did:key:z6MkjR...",
"capability": "translate.text",
"amount": "0.0004",
"unit": "USD",
"terms_hash": "sha256:qZk-NkcGgWq6PiVxeFDCbJz...",
"issued_at": 1754745601987,
"issuer": "did:key:z6MkpL...",
"settlement": null,
"anchor": { "log": "...", "index": 918273645, "inclusion_proof": ["..."] },
"sigs": { "payer": "...", "payee": "...", "issuer": "..." }
}
Four properties, each doing real work:
| Three signatures | The requester signs that it asked, the worker signs that it delivered, the issuer signs that it verified both. One or two signatures prove intent; three prove obligation |
terms_hash |
The price agreed before the work, hashed. Renegotiating afterwards is cryptographically impossible |
request_id / response_id |
Bound to the actual messages. A receipt cannot exist for work that never happened |
anchor |
A Merkle inclusion proof. Existence and ordering, provable by a stranger |
amount may be "0". A receipt with no money is pure evidence: this was asked for, this was
delivered, both sides signed.
Quickstart
pip install -e ".[dev]"
Offer something:
from uise import Agent
agent = Agent.generate(name="translator")
@agent.capability("translate.text", price="0.0004")
def translate(payload):
return {"text": payload["text"].upper()}
agent.serve(port=8080)
Call it:
from uise import Agent
client = Agent.generate()
print(client.call("http://localhost:8080", peer_did, "translate.text", {"text": "hola"}))
No account, no registration, no API key. Both agents exist because they hold a key.
See it end to end, including six blocked attacks:
python3 demo.py # the conversation plane, nothing to install
python demo_node.py # issuance, anchoring, audit
Nobody has to trust the issuer
Every receipt goes into an append-only Merkle log, following the RFC 6962 construction. The issuer publishes a signed tree head.
An auditor pins one tree head, comes back later, asks for a consistency proof, and can prove that nothing was rewritten or removed in between — using only a hash function.
Misbehaviour becomes detectable, not merely prohibited.
| Endpoint | |
|---|---|
GET /uip/v1/log/sth |
Signed tree head |
GET /uip/v1/log/proof?rid= |
Inclusion proof for one receipt |
GET /uip/v1/log/consistency?first=&second= |
Proof that history was not rewritten |
GET /uip/v1/log/entries?start=&end= |
Entry range, for auditing |
Read-only, unauthenticated, free. An issuer that restricts access to its own log has published nothing.
Built to outlive its own cryptography
Evidence has to hold up long after the algorithm that signed it:
| Message | Receipt | |
|---|---|---|
| Lifetime | 24 hours | Indefinite |
| If the algorithm breaks in 2040 | Nothing; it expired long ago | Every receipt ever issued becomes forgeable in hindsight |
So the envelope never names an algorithm. The signature suite is declared by the multicodec prefix inside the sender's DID. Adding a post-quantum algorithm adds a registry entry and a new DID — never a new protocol version.
Issuers sign with a composite Ed25519 + ML-DSA-65 suite: two independent signatures, both required. That hedges a future quantum attack on the classical half and an undiscovered flaw in the newer lattice construction.
Two rules are enforced by tests, not convention:
- An unknown suite is rejected, never approximated. A fallback is a downgrade attack.
- No multicodec codepoint is presented as assigned when it is not. Post-quantum codepoints are still pending, so they live in a self-scoped provisional range and are flagged everywhere.
No cryptography is hand-rolled here. Ed25519 and ML-DSA come from cryptography (constant
time, NIST FIPS 204). Lattice schemes written by hand fail silently: they pass their own tests and
interoperate with nothing.
The one architectural idea
Uise has two planes with deliberately different properties.
| Conversation plane | Value plane | |
|---|---|---|
| Carries | Agents negotiating, working, delivering | "This was done. X is owed to Y." |
| Route | Directly agent to agent. No Uise infrastructure | Through an issuer |
| Volume | Unbounded by design | A small fraction of messages |
| Cost | Zero | Charged |
Nothing global sits on the critical path of a conversation. That is not an optimization — it is the property that removes any ceiling on how many agents the network can hold. HTTP scales to the whole web because it has no central server; the conversation plane works the same way.
Bridging agents that already exist
Thousands of agents are already built against MCP and A2A. Wrapping one costs a few lines and changes nothing inside the agent:
from uise.bridges import mcp
agent = mcp.bridge_agent(
list_tools=client.list_tools,
call_tool=client.call_tool,
name="weather",
price="0.0002",
)
agent.serve(port=8080)
That MCP server now has a cryptographic identity, signed messages, and receipts.
The translation is lossy in one direction, and that is the point. Neither MCP nor A2A carries price, SLA, or settlement — there is nowhere in those formats to put them. If either could express them, UIP would be a profile of that format rather than a protocol.
Identifiers that cannot survive translation are preserved under a namespaced x extension, so a
round trip returns the original document byte for byte. Field mappings were verified against the
published specifications, not recalled; where a field's shape could not be confirmed, the bridge
omits it rather than inventing a name that would look authoritative and be wrong.
Running a node
from uise import Node
node = Node(log_url="https://log.example.com", fee="0.0001")
node.serve(port=8443)
A node does three things: answers discovery queries, certifies what two parties already agreed, and publishes the proof.
Two flows of money are kept strictly separate, because conflating them is what turns a protocol company into an unlicensed bank:
| What Uise charges | What agents owe each other | |
|---|---|---|
| Who pays whom | A customer pays Uise for issuing a proof | One agent pays another |
| Uise's role | A software vendor billing for its service | Records the obligation, never touches the money |
Because a public key cannot be sent an invoice, every issuance is metered against a prepaid balance. One mechanism covers three models: unmetered (launch phase — free, but recorded from day one), strictly prepaid (agents), and a credit limit (organizations, where the negative balance is the invoice).
A company running a thousand agents funds one balance, not a thousand. An account is either an agent's own DID or an organization shared by many — a solo agent is an organization of one, so nothing downstream needs to know which it is looking at.
Membership requires consent from both sides, and neither is trusted to assert it alone. The
organization proves consent by holding a write credential — it is taking on the cost. The agent
proves consent by signing, because joining can harm it too: one with its own funded balance
would start drawing on an account that may have none. An attestation is valid for five minutes: it
is a statement about now, not a standing permission, so a stale copy cannot re-enrol an agent that
has since left.
Leaving restores the agent's own balance, untouched. Money is never moved implicitly, in either direction.
A deposit records that money arrived; it never receives money. The node holds no payment credentials. The charge and its log entry are one transaction: a receipt is never issued without being paid for, and never charged without being issued.
The product API
/uip/v1/* |
/api/v1/* |
|
|---|---|---|
| What it is | The protocol. Open, frozen | The product. Authenticated, free to evolve |
| Who implements it | Anyone | Only Uise |
Mixing them would make commercial endpoints part of the standard, and a standard cannot change once
others depend on it. The specification reserves the /uip/v1 prefix for exactly this reason.
GET /api/v1/health open, empty of business data
GET /api/v1/openapi.json the machine-readable contract
GET /api/v1/events live stream, Server-Sent Events
GET /api/v1/stats metrics
GET /api/v1/receipts?after=&limit= cursor pagination
GET /api/v1/accounts/{account}/ledger every movement, with its cause
POST /api/v1/organizations one balance, many agents
POST /api/v1/organizations/{id}/members enrol an agent, with its signed consent
POST /api/v1/keys mint a credential; shown once
DELETE /api/v1/keys/{key_id} revoke, immediately
Closed by default. A node with no credential refuses to serve /api/v1 at all rather than
serving it openly. Tokens are stored as digests, compared in constant time, and authentication is
checked before routing, so an anonymous caller cannot map the surface by noting which paths
answer 404 and which answer 401. Scopes never imply one another.
Agents never use tokens. They sign every message, which a stolen log line cannot reproduce.
The OpenAPI document is generated from the router, never maintained beside it — a hand-kept contract drifts within weeks and then misleads the people building against it. Tests assert that every route appears, that declared scopes match what is enforced, and that a real validator accepts the result.
The node also serves an operator console at /dashboard, built as a client of that same public
API. If the console needs something the API cannot provide, the API is incomplete.
Layout
| Path | What it is | Dependencies |
|---|---|---|
spec/uip-1.md |
The normative specification | — |
spec/schemas/ |
JSON Schema for envelope, descriptor, receipt | — |
conformance/ |
The operative definition of the protocol | none |
uip/ |
Protocol core: canonicalization, DIDs, envelopes, receipts | none |
uise/ |
SDK, node, credits, API, events, console, bridges | cryptography |
tests/ |
269 tests | pytest |
demo.py · demo_node.py |
Both planes, working | — |
uip/ and uise/ are one implementation, not two: the SDK registers stronger cryptography into
the same protocol core the conformance suite verifies.
Conformance
python3 conformance/test_conformance.py # zero dependencies, any machine
python -m pytest tests/ # SDK, node, bridges, post-quantum
The conformance suite — not the prose — is the operative definition of UIP-1. Prose is interpreted differently by every organization, and in two years that yields five incompatible standards. Expected bytes cannot be interpreted.
conformance/vectors/invalid.json is the most important file here: it lists the fifteen envelopes
every implementation is obliged to reject, with the exact error code. Where the specification
and the vectors disagree, the vectors win.
The Ed25519 implementation is checked against the official RFC 8032 test vectors, and the Merkle log against the RFC 6962 construction — not against themselves. Vectors regenerate byte for byte, so any change that would break the protocol shows up as a diff before it is published.
To certify an implementation in Go, Rust or TypeScript, the vectors are plain JSON with nothing
Python-specific about them. If all five steps in conformance/README.md pass, it conforms. If not,
it does not. There is no grey area.
Every push runs the conformance suite on Python 3.9 through 3.13, across Linux, macOS and
Windows, with nothing installed — there is deliberately no pip install step in that job, because
the day one appears the claim stops being true. CI also proves the vectors still regenerate byte for
byte, that both demonstrations run, and that the API contract still matches the routes.
Everything CI runs can be run locally with the same command:
python conformance/test_conformance.py # zero dependencies
python -m pytest tests/
python tools/check_contract.py # the OpenAPI document is valid and complete
python tools/quality.py # no dead imports, English only
Status
UIP-1 is a normative draft. The envelope's root fields are frozen. Provisional post-quantum codepoints must be replaced with assigned values before 1.0.
305 tests pass: 36 conformance, 269 implementation. The conformance suite runs on Python 3.9 with nothing installed.
License
Apache License 2.0, with patent grant. The protocol, the SDK and the conformance suite are open — a closed protocol does not become a standard.
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 uise-0.1.0.tar.gz.
File metadata
- Download URL: uise-0.1.0.tar.gz
- Upload date:
- Size: 124.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a42243d97cab27597fcd44b7226fcf87a62ff729ff95844a7479377fc5163e74
|
|
| MD5 |
67c725a180ad96b355e1ef989e338934
|
|
| BLAKE2b-256 |
7f6d187653995acd2ae8ae541c03b76d5a030be7070054aa915b6ef2996b7958
|
Provenance
The following attestation bundles were made for uise-0.1.0.tar.gz:
Publisher:
release.yml on ever-sv/Uise
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uise-0.1.0.tar.gz -
Subject digest:
a42243d97cab27597fcd44b7226fcf87a62ff729ff95844a7479377fc5163e74 - Sigstore transparency entry: 2431280728
- Sigstore integration time:
-
Permalink:
ever-sv/Uise@f6b9e23359b1cc0be3c2cf6172e064dd7631425b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/ever-sv
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f6b9e23359b1cc0be3c2cf6172e064dd7631425b -
Trigger Event:
push
-
Statement type:
File details
Details for the file uise-0.1.0-py3-none-any.whl.
File metadata
- Download URL: uise-0.1.0-py3-none-any.whl
- Upload date:
- Size: 97.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e0df28197bb685937b42ee35aea7f0df45da7a9b0fef6b57b633f21821173e3
|
|
| MD5 |
630124fc56e13610ba3c7a54ff3e32a5
|
|
| BLAKE2b-256 |
cecbd03107493095cc07fc7a9afdb38ad71a786545c2525269921daf58fc041a
|
Provenance
The following attestation bundles were made for uise-0.1.0-py3-none-any.whl:
Publisher:
release.yml on ever-sv/Uise
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uise-0.1.0-py3-none-any.whl -
Subject digest:
2e0df28197bb685937b42ee35aea7f0df45da7a9b0fef6b57b633f21821173e3 - Sigstore transparency entry: 2431280751
- Sigstore integration time:
-
Permalink:
ever-sv/Uise@f6b9e23359b1cc0be3c2cf6172e064dd7631425b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/ever-sv
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f6b9e23359b1cc0be3c2cf6172e064dd7631425b -
Trigger Event:
push
-
Statement type: