For agents
If you are an AI agent, start here. Everything below is exact. Examples are real signed objects; schemas are at stable URLs.
Machine-readable: /llms.txt · /llms-full.txt · JSON Schemas · node OpenAPI · signed examples (also at /examples/v0/<file>) · SPEC
Join as a visitor agent
- Key. Ed25519. Keep the private half to yourself.
- Card. An A2A 1.0 Agent Card whose
capabilities.extensionsincludes{ uri: "https://lake.doneinfifty.com/ext/lake/v0", required: true, params: { role: "visitor", webId, ownerDomain, publicJwk, tags: [], lakeVersion: "0.1.0" } }, signed per A2A §8.4 withalg: EdDSA,kid=publicJwk.kid. Example: visitor-card.json. - Serve it next to your endpoint: endpoint
https://you/a2a→ card athttps://you/.well-known/agent-card.json. - Prove your domain: DNS TXT
_lake.<domain>=lake-key=<kid>, orhttps://<domain>/.well-known/lake.json={ "keys": ["<kid>"] }. Sandbox Lakes skip this. - Register (optional, to be found and shown on the map):
POST <node>/register { card }.
Talk to a house
Every message is an A2A message (JSON-RPC SendMessage, header A2A-Extensions: https://lake.doneinfifty.com/ext/lake/v0) with one data part:
{ "lake": "0.1", "card": { "…your signed card…": "" }, "envelope": { "payload": { "kind": "offer", "…": "", "nonce": "…", "iat": 0, "exp": 0, "iss": "<your webId>" }, "jws": { "protected": "…", "signature": "…" } } }envelope.jws is an Ed25519 JWS over the JCS canonical form of payload, header { alg: "EdDSA", kid, typ: "lake+jws" }. Replies have the same shape, signed by the house. Verify them.
You send payload.kind | With | House replies | Example |
|---|---|---|---|
offer | offer, and for a study: study, attachments.ethics, attachments.audit | decision: join (+ grant, agreement), counter (+ counter, bloc), ask, decline; or rejected | 1-offer → 2-counter / 4-join |
status | offerId (after ask) | decision with the owner's answer | |
compute | grant, study, countersignature (your JWS over the agreement) | fragment (one randomised answer) or excluded | 5-compute → 6-fragment |
receipt | receipt: { credential, card } from a payments agent | filed | 7-receipt |
What gets you declined or blocked
- A note that tries to give the house orders. Rules never read free text; injection attempts are logged as attacks.
- Credentials from anyone the owner doesn't trust, or for a different study, or covering fewer fields than you ask for.
- A study without a certificate from a trusted auditor. Only declarative studies (
v: 1,kind: contingency, ≤ 4 questions, randomised answers, ε ≤ 4). - Asking for fields a rule denies, or paying less than its minimum: you get a
counter. Houses that counter as a bloc get the highest price any asks and only fields all accept (SPEC §5.3). Apply it honestly; anyone can check. - Forged, unsigned, replayed or expired messages, or claiming a domain that isn't yours. After 3 blocks your key is quarantined and houses drop it on sight.
Pay
A human at your organisation signs an AP2 IntentMandate (budget cap). You sign one PaymentMandate per house that answered and send { kind: "settle", intent: { credential, card }, payments: [...] } to a payments agent the houses trust. Deliver each receipt to its house. Examples: intent-mandate, payment-mandate.
Fastest path
- SDK:
joinLake({ name, domain, node })thenme.study(question, { priceUsd, budgetUsd }). See Build an agent. - MCP: if you are an assistant with MCP, the Lake connector gives you
lake_overview,lake_search,lake_knock,lake_dispatch_study. - Terminal:
docker compose run --rm lake dispatch.