# The `/ask` Contract **Purpose:** one small, uniform interface between the Hub and every data project. **Applies to:** email agent, Compass/NHS, internet control. Not Artemis (action only). **Status:** agreed shape. Build both sides to this. --- ## Why Each project is the expert on its own data. The Hub is the expert on routing and reply shape. The Hub must **never** learn a project's data model. One contract keeps the Hub thin and lets every project be built and used alone. ## The rule > A project answers questions about **its own** domain. It reads only. It never > changes state on `/ask`. Writes (cut internet, add to calendar, book interview) go through the project's own interface, or through an approval flow. Never through `/ask`. ## Endpoint Every data project exposes two endpoints on its own port: ``` GET /health -> {"ok": true} POST /ask -> answer a question about this project's data ``` ### Request ```json { "question": "what does Harry have on today?", "asker": "sam", "person": "harry", "source": "esp32" } ``` | Field | Required | Meaning | |---|---|---| | `question` | yes | The words the user said. Plain text. | | `asker` | yes | Who asked. Used for permission. | | `person` | no | Who the question is about. Defaults to `asker`. | | `source` | no | Where it came from. `esp32`, `phone`, `chat`, `admin`. | ### Response ```json { "answer": "Harry has maths and a music lesson today.", "data": {}, "expires": "2026-10-10T09:00:00Z" } ``` | Field | Required | Meaning | |---|---|---| | `answer` | yes | One short sentence. Safe to speak aloud. | | `data` | no | Structured detail. The Hub may ignore it. | | `expires` | no | When the answer stops being true. | ### Cannot answer ```json { "answer": null, "reason": "no data for that person" } ``` Never guess. Never invent. Say `reason` instead. ## Rules for the project 1. **Read only.** `/ask` never writes anything. 2. **Answer for one person at most.** Check `asker` may see `person`. 3. **One short sentence.** No lists unless asked. 4. **Fast.** Answer in under 3 seconds, or return `answer: null`. 5. **No secrets in the answer.** No tokens, no internal paths. ## Rules for the Hub 1. **Forward the question.** Do not parse the project's data. 2. **Set a timeout.** 3 seconds. If slow, say so and stop waiting. 3. **Pass identity.** Always send `asker`. Never send another person's question without permission. 4. **Reformat only.** The Hub chooses text, speech, or notification. It does not change the meaning. 5. **One project down is not an outage.** Say "that service is not answering". ## Notify (project to Hub) A project may tell the Hub something, so the Hub can speak or notify. ``` POST /notify { "from": "email_agent", "level": "important", "message": "3 emails need a decision.", "person": "sam" } ``` | Field | Meaning | |---|---| | `from` | The project name. | | `level` | `info`, `important`, or `urgent`. | | `message` | One short sentence for the user. | | `person` | Who should hear it. | ## Approval (write actions) A write action needs a human yes. This is one shared pattern, not one per project. 1. The project sends an approval request: what, who, why, and a one-time id. 2. The admin app shows **"Did you ask for this?"**. 3. On tap, the admin app calls the project to execute. 4. The project executes once and returns the result. Approval is required for: parental control changes, calendar writes from email or school, and any Paperclip job. **A spoken keycode is not the lock.** Only the admin app confirms. ## Authentication Service to service. One bearer token per project, stored in `~/.config/environment.d/10-secrets.conf`. The Hub sends the target project's token. No project trusts an unsigned request. ## Versioning The contract is version 1. Add fields; do not remove or rename them. A breaking change needs a new path, for example `/ask/v2`.