said edge — the connection to your .said brain from any device and any AI. The memory lives in your own .said file; the edge signs in, routes and relays. MCP (connect any AI): POST /u//mcp key in "Authorization: Bearer " or ?key= POST /u//k//mcp for clients that drop query strings or sign in (OAuth 2.1): add https:///u//mcp to Claude or ChatGPT; with no key it answers 401 and WWW-Authenticate points to /.well-known/oauth-protected-resource/u//mcp, then /.well-known/oauth-authorization-server, POST /oauth/register, GET /oauth/authorize (PKCE S256, resource=), the owner allows it in the Console, POST /oauth/token (code, refresh_token) one address for everyone: POST /mcp signs in the same way and answers from the signed-in person's Home memory A2A (agents talk to agents): POST /u//a2a or /u//a2a/ SendMessage, GetTask, ListTasks, CancelTask with your key GET /u//.well-known/agent-card.json (public), GET /u//a2a//.well-known/agent-card.json (with a key) Home memories (.saidhome): AI apps (OAuth sign-ins, ai- keys), agents, computer sign-ins, invited keys and any call that names an agent get only about_me, ask, remember, recent, overview, list_tags and get (A2A: the ask skill); remember and import refuse passwords, card numbers, SSNs and API keys. The owner's own key naming no agent keeps every tool (the Console, said-mcp --serve-edge): give an AI a sign-in, a link or an agent, never the owner key Claim a name: PUT /u/?local=1 empty body + your key (16+ characters): the name and its keys only; 409 if another key owns it Answer from your device: POST /u//poll {"pc","wait"} and POST /u//answer [{"id","answer"}] with the owner key while the Console or your computer is open; with no device open, an AI is told the memory is offline Changes while it is closed: a write from a key that may write is sealed to the file's own key (poll {"outbox"}) and held; the poll answer carries {"outbox":[{id,at,from,box}]}, the device folds them and POST /u//outbox/ack {"ids"}; GET /u//outbox/count (owner) says how many wait; view-only keys are refused Move an older hosted memory to your device: GET /u//download, then POST /u//device-only {"sha256"} of that file; the edge copy is removed only when the hash matches Older hosted memories (Work and Enterprise for now): PUT /u/ with a body (in parts: ?at=&total=), POST /u//sync, GET /u//download Remove a name for good: DELETE /u/ answers 204 and deletes everything the edge holds for it Who am I: GET /u//me {name, level, brain, version, acts_as, owner, device} for any valid key Names (no key, rate-limited): GET /names/ {name, brain, status: available|taken|reserved, tier: free|pro|premium|auction|enterprise, suggestion} next free name: GET /names?base=&second=&free=1 {name}: , -, then 2, 3, ...; free=1 checks .saidhome free names live under saidhome: PUT /u/.saidhome; a nested name PUT /u/. needs the owner key of GET /u//claim the signed claim record; proved is true for the key that claimed it A team on one brain (each person gets their own key; upload, sync, download and remove need the owner key): Owner: POST /u//invite {"person","level":"read"|"write","days"}, POST /u//revoke {"person"}, GET /u//people, POST /u//rotate (a new owner key; the old one stops at once) Join with an invite: said-mcp --path me.said --join "https:///u/?invite=" from a browser: GET /u//invite-info?invite= says what it is; POST /u//join {"invite"} takes it Who signs: a key for willie acts as willie, willie-ai or willie-ai-, or one of willie's machines; nothing else is accepted a person may be an address: {"person":"willie@qonsult.co.za"}; level admin switches machines and revokes people but reads no one's private notes Machines: POST /u//machines {"person","job"} mints m-<12 hex> for a person (owner or admin for anyone, a person for themselves) GET /u//machines, POST /u//machines/ {"on":false|true}; an admin's switch-off holds against the person a machine is its id and key only: an agent's calls go to the owner's device through the relay, never to a memory kept here Shared hub (brains stay on their owners' computers; the hub stores none of their content): Ask the shared brains: POST /hub//mcp a read key, as for /u//mcp (also /hub//k//mcp) What is online: GET /hub//libraries the shared brains a read key may ask; GET /hub//me, /invite-info and POST /join as for a brain Share from a computer: said-mcp --share https:///hub/ --brain wiki.said Owner: POST /hub//invite {"person","level":"read"|"share"}, POST /hub//revoke {"person"}, GET /hub//shares Sign in (proves your address; never hands out a key): GET /auth/providers, GET /auth//start?address=&return=, POST /auth/passkey/register/{start,finish} and /auth/recovery with the owner key, POST /auth/passkey/login/{start,finish}, GET /auth/me with the session Browser console: /console/ Websites (said.js v1): ; sign-in by popup (POST /oauth/register with redirect_uris ["https:///oauth/popup?origin="], PKCE, GET /oauth/popup posts the code to that origin), tokens in page memory only; GET /apps/me, POST /apps/ask {"kind":"forget"|"share"|"scope"} (the owner decides), GET /apps/events?after=, POST /apps/signout; apps never delete and never see a key Needs you (owner): GET /u//needs, POST /u//needs/answer {"id","yes"}, POST /u//capabilities {"action",...} (to the device) Every route answers an OPTIONS preflight and carries Access-Control-Allow-Origin: *, except /mcp, /oauth/token and /apps/*, which allow only an origin a registered client named; keys travel in the Authorization header. The first request to a name with a key of 16+ characters claims that name; that key is the owner key. Keep it secret.