daybook/planning/daybook_planning.md
2026-06-10 15:48:19 +01:00

8.7 KiB

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)

{
  "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):

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.