Initial Daybook MVP
This commit is contained in:
commit
3974276c9b
25 changed files with 1647 additions and 0 deletions
216
planning/daybook_planning.md
Normal file
216
planning/daybook_planning.md
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
# Daybook — Project Planning
|
||||
|
||||
## Purpose
|
||||
|
||||
**Daybook** is a self-hosted web application where AI agents publish their
|
||||
outputs (task lists, research reports, slide decks, images, etc.) and a human
|
||||
browses them in a **time-ordered, agent-filterable feed** — viewing everything
|
||||
**in-browser without downloading or "opening" files**.
|
||||
|
||||
The defining feature is the integration: an agent calls a REST API and posts
|
||||
*content*, not files. For HTML it can send just a body fragment and Daybook
|
||||
supplies the CSS. The agent never deals with styling, file naming, or storage.
|
||||
|
||||
## Core principles
|
||||
|
||||
- **View, don't download.** Everything renders inline in the browser.
|
||||
- **Agent-agnostic.** Any agent/persona can publish; the human filters by persona.
|
||||
- **Time-ordered.** The primary axis is the day; flipping back to yesterday is trivial.
|
||||
- **Minimal agent burden.** Agents send content + a few metadata fields. The server
|
||||
owns styling, storage layout, IDs, and dates.
|
||||
- **Self-hosted.** A single deployable unit running on the user's own machine/server.
|
||||
- **Dumb viewer, smart ingest.** Normalize every input to three browser-native forms
|
||||
(HTML, image, PDF) at upload time, so the viewer never needs plugins.
|
||||
|
||||
## Actors
|
||||
|
||||
- **Agent (publisher):** an authenticated machine client (e.g. Claude Code, a
|
||||
persona script). POSTs artifacts via REST.
|
||||
- **Human (reader):** browses, filters, and views artifacts; later edits markdown
|
||||
and shares links.
|
||||
|
||||
## Scope
|
||||
|
||||
### MVP
|
||||
- REST ingest endpoint accepting: HTML fragment, full HTML, markdown, image, PDF, binary.
|
||||
- Server-side **named CSS themes**; automatic wrapping of HTML fragments.
|
||||
- Storage in a `persona/day/id` layout plus a metadata table.
|
||||
- Web viewer: reverse-chronological feed, persona filter, in-browser rendering.
|
||||
|
||||
### Later
|
||||
- In-browser **markdown editing** with write-back (the name "Daybook" anticipates this).
|
||||
- Slide decks via a **reveal.js** theme; `pptx`/`docx`/`xlsx` → PDF fallback.
|
||||
- **Share links** (password + expiry) for individual artifacts.
|
||||
- Tags, search, and full-text indexing.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Agent ──POST /api/artifacts──▶ Daybook (Django + DRF)
|
||||
│
|
||||
├─ normalize-on-ingest
|
||||
│ fragment → wrap with theme CSS → .html
|
||||
│ markdown → render → .html
|
||||
│ deck → reveal.js scaffold → .html
|
||||
│ office → LibreOffice headless → .pdf
|
||||
│ image/pdf → store as-is
|
||||
│
|
||||
├─ write file to artifacts/{persona}/{day}/{id}.{ext}
|
||||
└─ insert Artifact row (metadata index)
|
||||
|
||||
Human ──GET / (feed)──▶ list Artifact rows (reverse-chron, persona filter)
|
||||
──GET /a/{id}──▶ render via <iframe> (html/pdf) or <img> (image)
|
||||
```
|
||||
|
||||
The viewer only ever handles **HTML, image, or PDF**, because everything is
|
||||
collapsed into those three at ingest. No client-side office/pptx rendering.
|
||||
|
||||
## Data model
|
||||
|
||||
`Artifact`
|
||||
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `id` | str (8-char) | short unique id, used in URLs (`/a/{id}`) |
|
||||
| `persona` | str (indexed) | the agent that published it; primary filter |
|
||||
| `title` | str | display title |
|
||||
| `kind` | str | `html_fragment` \| `html_full` \| `markdown` \| `deck` \| `image` \| `pdf` \| `binary` |
|
||||
| `theme` | str | named CSS theme used at render (nullable) |
|
||||
| `day` | date (indexed) | publication day; feed grouping key |
|
||||
| `created_at` | datetime | exact timestamp |
|
||||
| `path` | str | relative path to stored, normalized file |
|
||||
| `original_kind` | str | what the agent sent, pre-normalization (audit) |
|
||||
| `tags` | json/list | later: filtering/search |
|
||||
| `source_prompt` | text | optional: the prompt that produced it |
|
||||
|
||||
Share fields (`share_token`, `share_expires`, `share_password_hash`) added in the
|
||||
share-links milestone, not MVP.
|
||||
|
||||
## REST API (MVP contract)
|
||||
|
||||
All ingest endpoints require an API token (per-persona; see Open Questions).
|
||||
|
||||
### `POST /api/artifacts` (JSON — text content)
|
||||
```json
|
||||
{
|
||||
"persona": "hermes",
|
||||
"kind": "html_fragment", // or html_full | markdown | deck
|
||||
"theme": "report", // optional; defaults per kind
|
||||
"title": "Daily Tasks 2026-06-10",
|
||||
"content": "<h1>Today</h1><ul><li>...</li></ul>",
|
||||
"tags": ["tasks"], // optional
|
||||
"source_prompt": "..." // optional
|
||||
}
|
||||
```
|
||||
Response: `{ "id": "a1b2c3d4", "url": "/a/a1b2c3d4" }`
|
||||
|
||||
### `POST /api/artifacts` (multipart — binary)
|
||||
Fields: `persona`, `title`, `tags`; file part `file` (image / pdf / office).
|
||||
Office/deck files are converted to PDF on ingest.
|
||||
|
||||
### `GET /api/artifacts?persona=&day=&tag=`
|
||||
Returns metadata list (reverse-chronological). Drives the feed.
|
||||
|
||||
### `GET /a/{id}`
|
||||
Serves the rendered artifact (HTML/PDF/image) for inline viewing.
|
||||
|
||||
## Theming (server-side CSS)
|
||||
|
||||
The server owns styling so agents can send markup only. Themes live as static
|
||||
files and are selected by name in the POST body:
|
||||
|
||||
```
|
||||
static/themes/report.css
|
||||
static/themes/tasks.css
|
||||
static/themes/deck.css # reveal.js theme
|
||||
static/themes/base.css # default
|
||||
```
|
||||
|
||||
Fragment wrapping (conceptual):
|
||||
|
||||
```python
|
||||
WRAP = """<!doctype html><html><head><meta charset="utf-8">
|
||||
<link rel="stylesheet" href="/static/themes/{theme}.css">
|
||||
<title>{title}</title></head>
|
||||
<body class="artifact">{body}</body></html>"""
|
||||
```
|
||||
|
||||
- `html_fragment` → wrapped with the chosen theme.
|
||||
- `markdown` → rendered to HTML, then wrapped.
|
||||
- `deck` → wrapped in reveal.js scaffold (agent sends `<section>` blocks).
|
||||
- `html_full` → stored verbatim (agent owns everything).
|
||||
|
||||
## Ingest normalization pipeline
|
||||
|
||||
| Agent sends | Step | Stored as | Viewer |
|
||||
|---|---|---|---|
|
||||
| HTML fragment | wrap + theme | `.html` | iframe |
|
||||
| Full HTML | verbatim | `.html` | iframe |
|
||||
| Markdown | render + wrap | `.html` | iframe |
|
||||
| Deck (sections) | reveal.js wrap | `.html` | iframe |
|
||||
| Image | verbatim | image | `<img>` |
|
||||
| PDF | verbatim | `.pdf` | native / pdf.js |
|
||||
| pptx/docx/xlsx | LibreOffice → PDF | `.pdf` | native / pdf.js |
|
||||
|
||||
## Viewer
|
||||
|
||||
- **Feed view (`/`):** reverse-chronological list grouped by day, with a persona
|
||||
filter (dropdown or sidebar) and, later, tag/search. Each entry shows title,
|
||||
persona, time, and kind.
|
||||
- **Artifact view (`/a/{id}`):** renders inline — `<iframe>` for HTML/PDF,
|
||||
`<img>` for images. No download step required (download remains available).
|
||||
- Markdown edit (later): the stored source markdown is editable in-browser and
|
||||
re-rendered on save; keep the original `.md` alongside the rendered `.html`.
|
||||
|
||||
## Storage layout
|
||||
|
||||
```
|
||||
artifacts/
|
||||
{persona}/
|
||||
{YYYY-MM-DD}/
|
||||
{id}.html | {id}.pdf | {id}.png ...
|
||||
{id}.md # kept when source is markdown (for editing)
|
||||
```
|
||||
|
||||
Files on disk stay greppable, portable, and trivially backed up; the DB is an
|
||||
index over them, not the source of truth for content.
|
||||
|
||||
## Tech stack
|
||||
|
||||
- **Django + Django REST Framework** — API, metadata, viewer.
|
||||
- **SQLite** (dev) → **PostgreSQL** (later) for the metadata index.
|
||||
- **Files on disk** for artifact content.
|
||||
- **LibreOffice headless** in the container for office/deck → PDF.
|
||||
- **markdown** (python-markdown) for md rendering; **reveal.js** for decks.
|
||||
- **pdf.js** optional, only if the native PDF viewer is unsatisfactory.
|
||||
- **Docker** — single image, self-hosted.
|
||||
|
||||
## Milestones
|
||||
|
||||
1. **M1 — Core ingest + storage.** `Artifact` model, JSON POST for
|
||||
`html_fragment`/`html_full`/`markdown`, theme wrapping, disk write, `/a/{id}` view.
|
||||
2. **M2 — Viewer.** Feed (`/`) with reverse-chron listing and persona filter;
|
||||
inline iframe/img rendering.
|
||||
3. **M3 — Binary + conversion.** Multipart upload; image/PDF passthrough;
|
||||
LibreOffice headless for office/deck → PDF.
|
||||
4. **M4 — Decks.** reveal.js theme + `deck` kind.
|
||||
5. **M5 — Editing.** In-browser markdown edit + re-render.
|
||||
6. **M6 — Sharing.** Share tokens with password + expiry.
|
||||
7. **M7 — Search/tags.** Tag filter and full-text search.
|
||||
|
||||
## Future features
|
||||
|
||||
- Per-persona dashboards / stats.
|
||||
- Webhooks or "latest artifact" endpoints for chaining agents.
|
||||
- Retention/archival policies per persona.
|
||||
- Multi-user reader auth with per-persona visibility.
|
||||
|
||||
## Open questions / decisions
|
||||
|
||||
- **Output standardization:** push agents toward HTML/markdown/reveal.js
|
||||
(cheap, robust) vs. accept native office files (LibreOffice conversion is the
|
||||
heaviest, flakiest path). Recommendation: prefer the former, keep conversion
|
||||
as a fallback.
|
||||
- **Agent auth:** one API token per persona, or a shared token + persona field?
|
||||
- **Reader auth:** single-user (simplest) vs. multi-user from the start?
|
||||
- **ID scheme:** random short id vs. date-prefixed sortable id.
|
||||
Loading…
Add table
Add a link
Reference in a new issue