157 lines
4.4 KiB
Markdown
157 lines
4.4 KiB
Markdown
# 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 three endpoints on its own port:
|
|
|
|
```
|
|
GET /health -> {"ok": true}
|
|
GET /capabilities -> the topics this project can answer
|
|
POST /ask -> answer a question about this project's data
|
|
```
|
|
|
|
## Capabilities (for routing)
|
|
|
|
The Hub reads this list to choose a project.
|
|
|
|
```json
|
|
{ "project": "email_agent", "topics": ["email", "inbox", "mail"], "people": ["sam"] }
|
|
```
|
|
|
|
Keep the topic list short and plain. The Hub uses these topics plus `routing.md`.
|
|
Do not put private data in this list.
|
|
|
|
### 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. `/capabilities` never writes.
|
|
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)
|
|
|
|
Some write actions need a human yes. This is one shared pattern, not one per
|
|
project.
|
|
|
|
1. The project (or the Hub) 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:
|
|
|
|
- a parental control change (cut or restore the kids' internet, change a schedule),
|
|
- a Paperclip job.
|
|
|
|
Approval is **not required** for a calendar write. Email and school may add events.
|
|
These are not dangerous.
|
|
|
|
**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`.
|