- Drop the "Open design questions (Phase 1)" section — answered in docs/design.md. - Replace "Tech stack: TBD by inventor" with the actual stack (Go + pgx + html/template + HTMX, msupabase schema projax, Dokploy). - Replace "Worker preferences: inventor / coder shifts" with the current state (PRD landed, Phase 1 implementation underway, Phase 2 and 3 deferred per docs/design.md). - Point readers at docs/design.md as the live spec, and add a layout map so a fresh agent knows where each concern lives. No code touched; this is the last commit in the Phase 1 branch before merge to main per head's review (msg #1775).
projax
m's personal data backbone for self-management — areas of life, projects within them, and aggregated views over tasks that live elsewhere. Subsumes scattered state currently held in mai.projects, CalDAV task lists, Gitea issues, and mBrian topic hubs.
Spec: docs/design.md. Project conventions: CLAUDE.md.
Run locally
export PROJAX_DB_URL=postgres://postgres:<pw>@<msupabase-host>:6789/postgres?sslmode=disable
go run ./cmd/projax
Defaults:
PROJAX_LISTEN_ADDR=:8080PROJAX_AUTO_MIGRATE=on(set tooffto skip on-start migration apply)
Visit http://localhost:8080/. Routes:
| Route | Purpose |
|---|---|
GET / |
Tree of areas + projects, plus orphan mai.projects |
GET /i/{path} |
Item detail; editable for projax, read-only for mai |
POST /i/{path} |
Save edits to a projax-native item |
POST /i/{path}/promote |
Promote a mai.projects orphan into a projax item |
GET /new?parent={path} |
Create a new item (area at root, project under parent) |
POST /new |
Submit |
GET /admin/classify |
Orphan list with inline HTMX promote |
GET /healthz |
DB ping |
GET /static/style.css |
Embedded CSS |
Test
DB-backed integration tests are skipped automatically when no PROJAX_DB_URL / SUPABASE_DATABASE_URL is set:
SUPABASE_DATABASE_URL=postgres://... go test ./...
Covers: migration idempotency, path-trigger semantics (nest, rename, re-parent, cycle, structural rules), items_unified source split + promotion hiding, every HTTP handler, and a Promote round-trip.
Deploy (Dokploy on mlake)
deploy/dokploy.yaml is a reference manifest. Translate to the Dokploy UI:
- Create an app
projaxwithDockerfilebuild context = repo root. - Set domain
projax.msbls.de(Tailscale-only — do not publish through public reverse proxy). - Secret
PROJAX_DB_URLpointing at msupabase's Tailscale address on port 6789 with thepostgresuser. - Health check path
/healthz. - Single replica.
The image is a distroless static container running as nonroot. Total image size is well under 20 MiB because everything (templates, CSS, migrations) is embed-bundled.
Trust model (v1)
Single-user, Tailscale-only. No authentication layer. The deployment relies on:
- Dokploy app exposed only to Tailscale (no public DNS / reverse proxy outside Tailscale).
- msupabase reachable only inside the same Tailscale network.
PROJAX_DB_URLis a Dokploy secret, not in the repo.
If projax later needs auth (multi-device, shared with people, etc.), the natural fit is the same Supabase auth used by flexsiebels — defer until projax has actually outgrown the Tailscale fence.
Schema
projax.items (id, kind[], title, slug, path, parent_id, content_md,
aliases[], metadata jsonb, status, pinned, archived,
start_time, end_time, created_at, updated_at, deleted_at)
projax.item_links (item_id, ref_type, ref_id, rel, note, metadata, created_at)
projax.items_unified VIEW = projax.items UNION ALL adapter over mai.projects
A BEFORE trigger maintains items.path via parent walk and enforces structural rules (areas at root, projects not at root, no cycles). An AFTER trigger rewrites descendant paths on rename / re-parent.
A mai.projects row drops out of items_unified as soon as any projax.item_links row with ref_type='mai-project' points back at it — that's how the Promote flow makes the duplicate disappear without ever mutating mai.projects.
Migrations live in db/migrations/, are embedded into the binary, and applied lexicographically on boot.