Files
oikos/containers/126-plato.md
claudio 40691dae20 wiki: add 126-plato, decommission 125-seafile
- containers/126-plato.md: Plato (notes/discovery) on LXC 126 — stack,
  bind mount, caddy + DNS, fresh-DB bootstrap workaround
- containers/125-seafile.md: removed (LXC destroyed 2026-05-13)
- containers/index.md: 126 added; 125 moved to recently-destroyed
- infrastructure/dns.md: plato entry added, files entry removed,
  Changelog reflects both
- README.md: top-level quick-links table swaps 125 → 126
2026-05-13 08:52:55 +02:00

4.5 KiB

126 — plato

Docker host for Plato — a cross-linked notes workspace (SvelteKit SPA embedded into a Go HTTP server, SQLite-backed). LAN+mesh only, no public ingress.

At a glance

  • Hostname: plato
  • IP: 192.168.8.190
  • Privilege: privileged
  • Resources: 2 cores / 2 GiB RAM / 8 GiB rootfs / 1 GiB swap
  • Mounts: host /mnt/library/documents/plato ↔ container /opt/plato/data
  • Public hostname: plato.hubris.networkcaddy (121)192.168.8.190:8080

Stack

Single-container deploy. The repo's Dockerfile is a three-stage build (Node → Go → distroless/static-debian12:nonroot, ~23 MiB final image). The container exposes :8080 and writes its SQLite db to /data.

  • Checkout: /opt/plato/app (clone of http://192.168.8.121:3000/dtoro/Plato.git, using the cached gitea PAT in /root/.git-credentials — same pattern as caddy (121)).
  • Data: host /mnt/library/documents/plato (owned 65532:65532 to match the distroless nonroot UID) bind-mounted into the LXC at /opt/plato/data, then bound into the container at /data via a docker-compose.override.yml:
    services:
      plato:
        volumes: !override
          - /opt/plato/data:/data
        restart: unless-stopped
    
  • .env at /opt/plato/app/.env (optional, untracked) — LLM provider keys (OPENROUTER_API_KEY, ANTHROPIC_API_KEY, etc.) and PLANTUML_BASE_URL override. Absent by default; LLM features stay greyed out, PlantUML defaults to the public service.
  • Run / update: cd /opt/plato/app && git pull && docker compose up -d --build.

Fresh-DB bootstrap workaround

The schema constant in backend/internal/views/store.go (as of commit e0542c0) creates the views table without project_id, then immediately runs CREATE UNIQUE INDEX … ON views(project_id, lower(title)). On a fresh DB this fails (no such column) and Plato crash-loops with open views store: SQL logic error: no such column: project_id. ensureProjectIDColumn() adds the column on subsequent migrations, but the schema apply happens first.

Until the upstream fix lands, pre-seed the DB before first start:

docker compose stop
rm -f /opt/plato/data/plato.db
python3 - <<'PY'
import sqlite3
c = sqlite3.connect('/opt/plato/data/plato.db')
c.executescript("""
CREATE TABLE views (
  id TEXT PRIMARY KEY,
  type TEXT NOT NULL DEFAULT 'document',
  title TEXT NOT NULL,
  aliases TEXT NOT NULL DEFAULT '[]',
  content TEXT NOT NULL DEFAULT '',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  project_id TEXT NOT NULL DEFAULT ''
);
CREATE UNIQUE INDEX views_project_title_lower ON views(project_id, lower(title));
CREATE INDEX views_project_id ON views(project_id);
""")
c.commit()
PY
chown 65532:65532 /opt/plato/data/plato.db
docker compose up -d

Once the column exists, every subsequent boot's IF NOT EXISTS clauses no-op. Once Plato is fixed upstream (remove the CREATE UNIQUE INDEX line from the boot schema constant — ensureTitleIndexPerProject() already re-creates it after the migration), this preseed becomes unnecessary.

Why privileged

Matches the docker-host convention used by 120 mule-images and 122 arriman. Distroless nonroot's UID 65532 on the host bind mount maps directly through; unprivileged would shift the UID by the idmap offset and the container couldn't write /data without extra plumbing.

Caddy

plato.hubris.network {
	tls {
		dns ionos {env.IONOS_AUTH_API_TOKEN}
	}
	reverse_proxy 192.168.8.190:8080
}

No Authentik forward-auth — Plato has no auth model yet; access control is "be on the LAN or the mesh".

DNS

dnsmasq entry on 124-authentik:

address=/plato.hubris.network/192.168.8.175

Changelog

2026-05-13 — container created, Plato deployed

LXC 126 stood up on Debian 12 standard, privileged, docker-ce installed. Plato cloned from dtoro/Plato, built and started. Caddy site and dnsmasq split-horizon entry added. Recycled the IP/ID slot freed earlier the same day by the decommissioned Seafile experiment (LXC 125). Hit the fresh-DB bootstrap bug on first boot; worked around by pre-seeding the SQLite schema.