- Session login for the single user Sam: server-side session store with an HMAC-signed session cookie, POST /login, POST /logout, and double-submit CSRF on every form post. Unauthenticated requests to / and /partials/* redirect to /login?next=... - Empty dashboard shell in the Family Home Lab design system: left nav (category and folder placeholders), centre list, right detail pane. - Copy tokens.css and the self-hosted Inter fonts from the family-home-lab design system into web/static; vendor htmx 2.0.4. No cross-project imports at runtime. - Dark mode via prefers-color-scheme plus a persisted toggle; phone-first layout with 44px touch targets, hairline borders, 8px grid, pill CTAs. - Config: EMAIL_AGENT_PASSWORD for the login password, falling back to EMAIL_AGENT_SESSION_SECRET so one configured secret is enough. Co-Authored-By: Paperclip <noreply@paperclip.ing>
3.8 KiB
Email Agent
Triage the family inbox from one page: all mail lands in a single Gmail account and this service classifies it, recommends actions, performs bulk actions, drafts replies, and answers the Hub.
The build spec is AGENTS.md. ORIGINAL_NOTES.md is archived
pre-planning material and is not authoritative.
Status
Phase 1.2 — Session login + empty dashboard. The Go skeleton (Phase 1.1)
plus a single-user session login, a copied Family Home Lab tokens.css, and an
HTMX dashboard shell (left nav, centre list, right detail pane) with dark mode.
Mail fetch, classification, and the rest come in later phases.
Layout
cmd/email-agent/ service entry point
internal/config/ environment-driven configuration
internal/session/ server-side session store + signed cookie values
internal/store/ SQLite open + schema (internal/store/schema.sql)
internal/server/ HTTP routes, auth middleware, HTMX handlers
internal/version/ build metadata (set via -ldflags)
web/static/ embedded design tokens, Inter fonts, htmx, app.css
web/templates/ embedded server-rendered HTML
Dockerfile multi-stage, CGO-free build
docker-compose.yml this project's own container and image
Configuration
All configuration is read from environment variables. Secrets live in
~/.config/environment.d/10-secrets.conf on the host and are passed into the
container by docker-compose; no secret value is ever committed. See
.env.example for the full list of names.
Key variables:
| Variable | Default | Purpose |
|---|---|---|
EMAIL_AGENT_ENV |
development |
development, production, or test |
EMAIL_AGENT_ADDR |
:8790 |
Listen address |
EMAIL_AGENT_PORT |
8790 |
Listen port (used when EMAIL_AGENT_ADDR is unset) |
EMAIL_AGENT_DB_PATH |
data/email_agent.db |
SQLite file |
EMAIL_AGENT_LOG_LEVEL |
info |
debug, info, warn, error |
EMAIL_AGENT_SESSION_SECRET |
(empty) | Session cookie signing key. If unset, an ephemeral key is used (sessions reset on restart) |
EMAIL_AGENT_PASSWORD |
(empty) | Login password for Sam. If unset, EMAIL_AGENT_SESSION_SECRET doubles as the password |
Run locally
go run ./cmd/email-agent
curl -s localhost:8790/health
Docker
docker compose up -d --build
curl -s localhost:8790/health
The container is email-agent, image email-agent:latest, named volume
email_agent_data mounted at /data, listening on 8790. It runs as an
unprivileged user and has a built-in healthcheck (email-agent -healthcheck).
Caddy on .35 proxies mail.lab.audasmedia.com.au to this port.
Endpoints (Phase 1)
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/health |
public | Liveness + database check, JSON |
GET |
/readyz |
public | Readiness, JSON |
GET |
/login |
public | Sign-in form (deep-indigo band) |
POST |
/login |
public | CSRF-checked password sign-in, sets the session cookie |
POST |
/logout |
session | Destroys the session, returns to /login |
GET |
/ |
session | Empty dashboard shell |
GET |
/partials/inbox |
session | HTMX fragment for the centre list |
GET |
/static/… |
public | Embedded tokens.css, app.css, Inter fonts, htmx.min.js |
Unauthenticated requests to / and /partials/… redirect to /login?next=….
Form posts use a double-submit CSRF token (email_agent_csrf cookie plus a
hidden csrf field).
Database
SQLite is opened with the pure-Go modernc.org/sqlite driver (no CGO). The
schema in internal/store/schema.sql creates the AGENTS.md §7 tables:
messages, classifications, rules, actions_log, unsubscribe, drafts,
summaries, plus schema_migrations. Schema init is idempotent and runs on
every start.