The scheme
- Generate (or load) an Ed25519 keypair. The raw 32-byte public key,
hex-encoded, is
delegate_pubkey_hex. - Build the canonical byte string exactly:
— one
role:content\nline per message inmessages, in order, then a final linemodel:...\nowner:...\nns:...with no trailing newline. - Sign those exact bytes with Ed25519 — no prehashing, no re-encoding the
string first. Sign the raw UTF-8 bytes directly. The raw 64-byte
signature, hex-encoded, is
signature_hex. - Send
delegate_pubkey_hexandsignature_hexas plain JSON fields alongsidemessages/model/owner_address/namespace.
chat-relayer/src/relayer/src/http.rs::canonical_chat_req
(builds the identical string) and src/relayer/src/auth.rs::verify_signature
(decodes both hex fields, requires exactly 32 and 64 bytes respectively,
rejects anything that doesn’t decode or verify with 401 Unauthorized — not
a 400, so check auth first if a client gets rejected before reaching the
model).
Reference implementation
Worked example
Canonical bytes signed for a first message:\n, not real newlines plus a trailing one)
Resulting request:
owner_address is permanent
owner_address is a key into long-term, cross-session memory — it is
not reset between requests, sessions, or days. Re-running a test with
the same owner_address surfaces accumulated recalled_facts from every
prior call, which is correct behavior (that’s the point of the
cross-session memory layer), but it makes
isolated test runs hard to read. Use a fresh, unique owner_address (e.g. a
UUID) per test run if you want a clean before/after comparison.
Full chat-relayer API reference
Endpoint, request/response shapes, and the two automatic memory layers