Python SDK for the Daimon protocol — sovereign personal AI
Project description
daimon — Python SDK
Thin Python client over the Daimon daemon's Unix-socket JSON-RPC surface
(SPEC §6.1). Mirrors the Go cmd/daimon CLI's wire-level behaviour: one
connection per RPC, no pipelining, JSON-RPC 2.0.
Status: v0.1.0 GA on
latest(identity / memory / provider / activity verbs). v0.2.0-dev.2 pre-release on the--prechannel adds wallet + x402 payment verbs.
Install
Default install — v0.1.0 GA:
pip install daimon-protocol
Pre-release install — v0.2.0-dev.2 with wallet + x402 payments:
pip install --pre daimon-protocol
From a checkout of the Daimon repo:
pip install -e sdk/python
The PyPI distribution name is
daimon-protocolbecause the unqualifieddaimonname on PyPI is held by an unrelated dormant package, anddaimon-sdk/daimon-clientbelong to other active projects. The import name remainsdaimon—from daimon import Clientworks regardless of how you installed it.
Use
The SDK assumes a running daimon daemon on the local machine, reachable via
the same socket path the Go CLI uses ($DAIMON_HOME/daimon.sock, with the
same long-path fallback rules). Start the daemon first:
daimon unlock
Then:
from daimon import Client
client = Client() # resolves $DAIMON_HOME
print(client.identity.get()) # {"did": "did:key:..."}
# memory.Kind.Valid() accepts: "fact", "preference", "task", "observation"
mid = client.memory.write(kind="fact", content="the sky is blue")
print(mid) # {"id": "01K..."}
mem = client.memory.read(mid["id"])
hits = client.memory.search("sky") # [{...mem, "score": 0.7}, ...]
all_mems = client.memory.list() # search with empty query
# provider verbs
providers = client.provider.list()
env = client.provider.invoke(
provider="ollama",
model="llama3.2:latest",
messages=[{"role": "user", "content": "hi"}],
)
print(env["response"]["content"])
# streaming — yields delta strings; final envelope on .final
stream = client.provider.stream(
provider="ollama",
model="llama3.2:latest",
messages=[{"role": "user", "content": "count to 3"}],
)
for delta in stream:
print(delta, end="", flush=True)
print()
print("usage:", stream.final["response"]["usage"])
# activity verbs (audit trail)
client.activity.append(kind="custom.event", payload={"n": 1})
entries = client.activity.query(limit=20)
result = client.activity.verify() # {"verified": N, "ok": True}
Wallet + payments (v0.2 pre-release)
Available in 0.2.0.dev2 (pip install --pre daimon-protocol). The
wallet keystore is auto-created by the daemon on first daimon unlock
— the 24-word BIP-39 mnemonic is surfaced exactly once in that
unlock's RPC response. Wallets are MetaMask-compatible: importing the
mnemonic into MetaMask reproduces the same address the daimon derived.
# Derive a fresh EVM wallet for Base mainnet
w = client.wallet.create(chain="evm:base")
print(w["address"]) # 0x... (EIP-55 checksummed)
# List wallets in the keystore
for w in client.wallet.list():
print(f"{w['chain']:20s} {w['address']}")
# Quick lookup by chain
addr = client.wallet.address(chain="evm:base")
# Compute the address that WOULD be derived at a given HD index
# WITHOUT persisting anything. Handy for verifying a recovered seed:
# `client.wallet.derive(chain="evm:base", index=0)["address"]`
# should match what MetaMask / Phantom shows for the same seed at
# the same path. No audit row, no wallet-list mutation.
predicted = client.wallet.derive(chain="evm:base", index=0)
print(predicted["address"], "at", predicted["path"])
# Re-display the BIP-39 mnemonic, password-gated. Returns the
# 24-word list. Useful for verifying the backup was written down
# correctly, or exporting the seed to MetaMask / Phantom / Rabby.
# Wrong password raises RPCError with code -32008 (CodeWrongPassword),
# distinct from -32001 (CodeIdentityLocked) — the daemon IS unlocked,
# the password check is a separate attestation.
words = client.wallet.show_mnemonic(password="my-unlock-password")
assert len(words) == 24
# Pay an x402-protected URL end-to-end. The daimon parses the
# 402's PAYMENT-REQUIRED header, signs EIP-3009 transferWithAuthorization
# with the matching wallet, and retries with PAYMENT-SIGNATURE.
# ceiling_smallest_unit caps the payment in USDC smallest-unit (6 dec):
# 100000 == $0.10.
resp = client.payment.pay(
url="https://protected.example.com/api/data",
method="POST",
body=b'{"prompt": "hi"}',
ceiling_smallest_unit=100_000,
)
print(resp["status_code"], resp["body"])
if resp["payment_response"]:
pr = resp["payment_response"]
print(f"settled: tx={pr['transaction']} payer={pr['payer']}")
The audit log gains wallet.created, payment.signed, and
payment.settled rows automatically — every wallet generation and
every payment chains into the same Ed25519-signed log that carries
the v0.1 memory and provider rows. Walk the whole chain with
client.activity.verify().
Typed RPC error codes for the wallet + payment surface propagate
via RPCError.code:
-32006— payment exceeds local ceiling. The daimon refused to sign; no on-the-wire signature was emitted.-32007— no wallet in the keystore matches the resource's PaymentRequirements (chain not in registry, or wallet for that chain not yet created).-32008—show_mnemonicwas called with the wrong password. Distinct from-32001(CodeIdentityLocked) so callers can branch on it without their "daemon is locked, rundaimon unlock" rewrite kicking in when really the user just mistyped the password.
See examples/x402-smoke for an
end-to-end runnable example against a local mock x402 server.
Recovering a daimon from an existing seed
If you have a 24- (or 12-) word BIP-39 phrase already — a backup
you wrote down, or a seed you'd like to import from MetaMask /
Phantom / Rabby — the CLI daimon wallet recover writes a fresh
wallet keystore from that phrase. It's an offline-only operation,
so there's no SDK wrapper: the daimon daemon must be stopped (or
have never run against this $DAIMON_HOME) when you run it,
because a live seed swap on a running daemon would orphan every
wallet derived from the previous seed.
daimon wallet recover
# Recovery phrase: (hidden input, paste your 12 or 24 words)
# Choose a password: (must match your daimon unlock password)
# Confirm password:
# Wallet keystore written.
# Next: `daimon unlock` to bring up the daemon against this seed.
After recover, the daemon's next unlock loads the imported
keystore instead of generating a fresh mnemonic, and every wallet
you create with client.wallet.create(...) derives from the
imported seed. The canonical abandon ... about 12-word vector
produces 0x9858EfFD232B4033E47d90003D41EC34EcaEda94 at
m/44'/60'/0'/0/0 — the same address every BIP-39 derivation
tool produces for that seed, so cross-wallet recovery is trivially
verifiable.
Errors
from daimon import DaemonNotRunning, DaemonLocked, RPCError
try:
client.memory.write(kind="fact", content="x")
except DaemonNotRunning:
# daimon binary isn't serving on this $DAIMON_HOME
...
except DaemonLocked:
# daemon is running but `daimon unlock` hasn't been called
...
except RPCError as e:
# any other JSON-RPC error from the daemon
print(e.code, e.message, e.data)
Development
cd sdk/python
pip install -e .[dev]
pytest
The test suite uses a stub Unix-socket daemon (no real keys, no real storage) plus optional smoke tests against a live daimon when one is running.
See also
- TypeScript SDK — sister SDK, same wire shape, async-iterator streaming surface.
examples/streaming— cross-language streaming reference: both SDKs round-trip token deltas through one daemon, audit chain verified three ways.CHANGELOG.md— release notes per version.
Project details
Release history Release notifications | RSS feed
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 daimon_protocol-0.2.0.dev2.tar.gz.
File metadata
- Download URL: daimon_protocol-0.2.0.dev2.tar.gz
- Upload date:
- Size: 33.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4d19edb76f6478f4746d4f313439d4e592c49154b3bfeed125064d4d01328e6
|
|
| MD5 |
78badd66a8e3c830ffd0b093fd6872df
|
|
| BLAKE2b-256 |
0d36cc5e1b3d11b6f420eb15cd8c1718a848519c8eade50557c86827a3c8ca44
|
File details
Details for the file daimon_protocol-0.2.0.dev2-py3-none-any.whl.
File metadata
- Download URL: daimon_protocol-0.2.0.dev2-py3-none-any.whl
- Upload date:
- Size: 24.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9738bb947bbf06edc753c5093278615119f4af34a36bc65ab76d74e2ad3d185d
|
|
| MD5 |
0154f651ef3e4c1f841e347e2d93a57b
|
|
| BLAKE2b-256 |
21cec90a92b663bc1d13904b2213875a81567a7272bf00ee89f024ef3a643acf
|