daybook/docs/api.md
ritte 37cd96fa7a Add multi-user auth, settings area, and unified label feed
Turns Daybook into a multi-user tool and reworks the feed UI:

- Multi-user: Django session login (username/password + remember-me),
  per-user personal API tokens (DRF authtoken), whole app gated behind
  login. Each artifact has an owner; feeds are private per user.
- Rename persona -> source (free-text label for whatever posted an
  artifact: agent-profile, script, or person). source + tags form one
  unified label system; single label filter matches tags, source has
  its own filter/column/sidebar.
- Settings area (gear/user menu, top-right): Account (view/regenerate
  token) and Demo Data (in-app button seeding one artifact per kind).
  User account management via Django admin (superusers only).
- Feed rework: rolling 7-day window with sticky inline date separators
  and infinite scroll; left sidebar is now a Sources list with per-source
  unread counts; artifact type shown as text after title; source in its
  own column.
- Unread tracking: read_at on Artifact, marked read when previewed,
  bold titles for unread, sidebar unread badges, old banner removed.
- Tag colours: global shared registry, hash-based default with optional
  per-tag override via tag_colors on POST/PATCH.
- Ingest: token resolves owner; same source name under two users' tokens
  stays isolated. daybook_post.py renamed persona->source, requires a
  real token, --check accepts a bearer token. Docs updated to match.

Migrations 0004-0008 rename persona, add owner (nullable -> backfill to
first superuser -> required), and add read_at. Fresh installs no-op the
backfill.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 09:06:46 +02:00

383 lines
10 KiB
Markdown

# Daybook API
Daybook is built around agents publishing artifacts. Agents send content; Daybook assigns IDs, stores files, normalizes browser views, and indexes metadata.
Base URL while running locally:
```text
http://127.0.0.1:8001
```
Authentication for write endpoints (POST/PATCH) — a personal per-user API token, from Settings → Account in the app:
```http
Authorization: Bearer <your-personal-token>
```
Every artifact belongs to the user whose token authenticated the POST — feeds are private per user.
Read endpoints for artifacts (`GET /api/artifacts`, `GET /api/artifacts/{id}`) require a
logged-in browser session, not a bearer token — visit the app in a browser and log in first.
`GET /api/capabilities` accepts **either** a session or a bearer token, so agents can check
connectivity before posting.
## Artifact Kinds
Currently supported kinds:
| Kind | Upload type | Rendered behavior | Raw support |
|---|---|---|---|
| `html_fragment` | JSON text | Wrapped with a Daybook theme | No |
| `html_full` | JSON text | Stored as complete HTML | Yes |
| `markdown` | JSON text | Rendered to HTML and original `.md` is preserved | Yes |
| `deck` | JSON text | Wrapped as a simple HTML slide/deck page | No |
| `image` | Multipart file | Browser image preview | No |
| `pdf` | Multipart file | Browser PDF preview | No |
| `binary` | Multipart file | Stored and downloadable | No |
## Source Fields
Every artifact can identify who or what produced it. `source` is a free-text label —
an agent-profile name, a script name, a person's name, whatever posted it.
In the feed UI, `source` gets its own column and drives the left sidebar; `tags` render
as coloured chips next to the title. The API exposes both separately, plus a combined
`labels` list (source + tags, each with a resolved colour) for convenience.
| Field | Required | Default | Notes |
|---|---:|---|---|
| `source` | Yes | None | Label identifying what posted this, slugged for storage. |
| `source_type` | No | `agent` | One of `agent`, `person`, `system`. |
| `source_name` | No | `source` | Human-readable source name. |
| `source_id` | No | Empty string | Stable external agent/person/system ID. |
| `metadata` | No | `{}` | Free-form object for model/run/tool metadata. |
Example source metadata:
```json
{
"source": "hermes-research",
"source_type": "agent",
"source_name": "Hermes Agent",
"source_id": "agent-hermes-prod",
"metadata": {
"model": "gpt-5.5",
"run_id": "run-2026-06-10-001",
"tool": "nightly-brief"
}
}
```
## Create Text Artifact
Endpoint:
```http
POST /api/artifacts
Content-Type: application/json
Authorization: Bearer <your-personal-token>
```
Required fields:
| Field | Notes |
|---|---|
| `source` | Required. |
| `kind` | One of `html_fragment`, `html_full`, `markdown`, `deck`. |
| `title` | Required. |
| `content` | Required text content. |
Optional fields:
| Field | Notes |
|---|---|
| `theme` | Defaults by kind. Common values: `report`, `tasks`, `deck`, `base`. |
| `tags` | Array or comma-separated string. |
| `tag_colors` | Object mapping tag name → `#RRGGBB`. Sets a colour override for those tags. |
| `source_prompt` | Prompt or instruction that led to the artifact. |
| `source_type` | Defaults to `agent`. |
| `source_name` | Defaults to `source`. |
| `source_id` | Optional stable source ID. |
| `metadata` | Optional object. |
### Tag colours
Tags are coloured automatically: the server hashes the tag name into a fixed palette,
so the same tag always gets the same colour without agents doing anything.
To force a specific colour, pass `tag_colors`:
```json
{"tags": ["urgent", "billing"], "tag_colors": {"urgent": "#d1453b"}}
```
Colour overrides are **global and shared across all users** — setting a colour for
`urgent` changes it everywhere, for everyone. Values must be `#RRGGBB`; anything else
returns `400`. Overrides can also be set via `PATCH` on an existing artifact.
PowerShell example:
```powershell
$body = @{
source = "hermes-research"
source_type = "agent"
source_name = "Hermes Agent"
source_id = "agent-hermes-local"
kind = "markdown"
theme = "report"
title = "Morning Brief"
content = "# Morning Brief`n`n- Review overnight output`n- Plan next actions"
tags = @("brief", "daily")
source_prompt = "Prepare a concise morning brief."
metadata = @{
model = "gpt-5.5"
run_id = "brief-001"
}
} | ConvertTo-Json -Depth 5
Invoke-RestMethod `
-Uri "http://127.0.0.1:8001/api/artifacts" `
-Method Post `
-Headers @{ Authorization = "Bearer <your-personal-token>" } `
-ContentType "application/json" `
-Body $body
```
Response shape:
```json
{
"id": "a1b2c3d4",
"url": "http://127.0.0.1:8001/a/a1b2c3d4",
"api_url": "http://127.0.0.1:8001/api/artifacts/a1b2c3d4",
"raw_url": "http://127.0.0.1:8001/a/a1b2c3d4?raw=1",
"raw_available": true,
"source": "hermes-research",
"title": "Morning Brief",
"kind": "markdown",
"theme": "report",
"day": "2026-06-10",
"created_at": "2026-06-10T15:00:00+00:00",
"tags": ["brief", "daily"],
"labels": [
{"name": "hermes-research", "color": "#4f6df5"},
{"name": "brief", "color": "#e0578c"},
{"name": "daily", "color": "#2fa88f"}
],
"source_prompt": "Prepare a concise morning brief.",
"source_type": "agent",
"source_name": "Hermes Agent",
"source_id": "agent-hermes-local",
"metadata": {
"model": "gpt-5.5",
"run_id": "brief-001"
},
"read_at": null
}
```
`read_at` is `null` until the artifact is opened in the feed viewer, then holds the
timestamp it was first read. Unread artifacts render in bold and count toward the
per-source unread badges in the sidebar.
## Create File Artifact
Endpoint:
```http
POST /api/artifacts
Content-Type: multipart/form-data
Authorization: Bearer <your-personal-token>
```
Required fields:
| Field | Notes |
|---|---|
| `source` | Required. |
| `title` | Required. |
| `file` | Required file part. |
File kind is inferred from content type:
| Content type | Artifact kind |
|---|---|
| `image/*` | `image` |
| `application/pdf` | `pdf` |
| Anything else | `binary` |
PowerShell/curl example:
```powershell
curl.exe -X POST "http://127.0.0.1:8001/api/artifacts" `
-H "Authorization: Bearer <your-personal-token>" `
-F "source=orion" `
-F "source_type=agent" `
-F "source_name=Orion Agent" `
-F "title=Architecture Diagram" `
-F "tags=diagram,architecture" `
-F "file=@C:\path\to\diagram.svg;type=image/svg+xml"
```
## List Artifacts
Endpoint:
```http
GET /api/artifacts
```
Requires a logged-in browser session. Returns only the current user's artifacts.
Supported filters (combine freely; all are ANDed):
| Query param | Meaning |
|---|---|
| `source` | Exact match on the artifact's `source`. |
| `label` | Matches any of the artifact's `tags`. |
| `day` | Exact day, `YYYY-MM-DD`. |
| `kind` | Exact artifact kind. |
| `q` | Case-insensitive title fragment. |
`source` and `label` are separate filters: `source` matches only the source field,
`label` matches only the tags list.
Examples:
```powershell
Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts"
Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts?source=hermes-research"
Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts?kind=markdown&q=brief"
Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts?day=2026-06-10&label=daily"
```
## Get Artifact Metadata
Endpoint:
```http
GET /api/artifacts/{id}
```
Example:
```powershell
Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts/a1b2c3d4"
```
## Update Artifact Metadata
Endpoint:
```http
PATCH /api/artifacts/{id}
Content-Type: application/json
Authorization: Bearer <your-personal-token>
```
Editable fields:
| Field | Notes |
|---|---|
| `title` | Must not be blank. |
| `tags` | Array or comma-separated string. |
| `tag_colors` | Object mapping tag name → `#RRGGBB`. Updates the global colour registry. |
| `source_prompt` | String. |
| `source_type` | `agent`, `person`, or `system`. |
| `source_name` | String. |
| `source_id` | String. |
| `metadata` | Object. Replaces existing metadata. |
You can only PATCH artifacts your own token owns; others return `404`.
Not editable yet:
| Field | Reason |
|---|---|
| `content` | Content replacement/revisions are intentionally deferred. |
| `source` | Affects storage layout and feed identity. |
| `kind` | Affects renderer and storage behavior. |
| `day` | Affects time-ordered feed and storage layout. |
PowerShell example:
```powershell
$body = @{
title = "Morning Brief - Reviewed"
tags = @("brief", "daily", "reviewed")
metadata = @{
model = "gpt-5.5"
run_id = "brief-001"
reviewed = $true
}
} | ConvertTo-Json -Depth 5
Invoke-RestMethod `
-Uri "http://127.0.0.1:8001/api/artifacts/a1b2c3d4" `
-Method Patch `
-Headers @{ Authorization = "Bearer <your-personal-token>" } `
-ContentType "application/json" `
-Body $body
```
## Rendered And Raw Content
Rendered browser view:
```http
GET /a/{id}
```
Raw/source view:
```http
GET /a/{id}?raw=1
```
Raw currently exists for:
| Kind | Raw content |
|---|---|
| `markdown` | Preserved `.md` source. |
| `html_full` | Stored HTML file served as text. |
If raw content is unavailable, Daybook returns a small HTML fallback page.
## Capabilities
Endpoint:
```http
GET /api/capabilities
```
Example response:
```json
{
"artifact_kinds": ["html_fragment", "html_full", "markdown", "deck", "image", "pdf", "binary"],
"filters": ["source", "label", "day", "kind", "q"],
"views": ["rendered", "raw"],
"raw_supported_for": ["markdown", "html_full"],
"source_types": ["agent", "person", "system"],
"auth": "bearer_token"
}
```
Agents should call this endpoint if they need to adapt to the current Daybook instance, or
simply to check the instance is reachable and their token is valid. Unlike the other read
endpoints, it accepts a bearer token as well as a browser session:
```bash
curl -H "Authorization: Bearer <your-personal-token>" http://127.0.0.1:8001/api/capabilities
```
## Current MVP Constraints
- Each user has a personal ingest API token; there is no shared token.
- Reader/browser access requires login; feeds are private per user.
- Content modification/revisions are deferred.
- Office document conversion is not implemented.
- Manual browser upload/drag-and-drop is deferred.
- Pagination/cursors are not implemented yet.