decommission: destroy plato (LXC 126)
LXC 126 stopped and destroyed on hubris. Remove all live references: inventory, container doc, host file, README, containers index, auto-deploy pipeline, DNS entry, SSH access table, nfs-export mount list. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ Dedicated, single-purpose LXC that re-exports `/mnt/library` over NFSv4 to clien
|
||||
- **LAN DNS:** `nfs-export.hubris.network` → `192.168.8.200` (direct, no Caddy)
|
||||
- **Privilege:** privileged (`unprivileged: 0`) + `lxc.apparmor.profile: unconfined` — required for `nfs-kernel-server`
|
||||
- **Resources:** 1 core / 512 MiB RAM / 2 GiB rootfs / 256 MiB swap
|
||||
- **Mounts:** host `/mnt/library` ↔ container `/mnt/library` (same path on both sides — matches the bind-mount convention used by jellyfin, paperless, arriman, nextcloud, mule-images, plato, apps)
|
||||
- **Mounts:** host `/mnt/library` ↔ container `/mnt/library` (same path on both sides — matches the bind-mount convention used by jellyfin, paperless, arriman, nextcloud, mule-images, apps)
|
||||
|
||||
## What it does
|
||||
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
# 126 — `plato`
|
||||
|
||||
Docker host for [Plato](https://git.hubris.network/dtoro/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.network`](../infrastructure/dns.md) → [caddy (121)](121-caddy.md) → `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)](121-caddy.md)).
|
||||
- **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`:
|
||||
```yaml
|
||||
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:** push to `dtoro/Plato` (auto-deploys, see below) or `cd /opt/plato/app && git pull && docker compose up -d --build` for a manual rebuild.
|
||||
|
||||
## Auto-deploy
|
||||
|
||||
Push to `dtoro/Plato` `main` triggers a rebuild — same Shape B pattern as [Artifacto / mule-image](../infrastructure/auto-deploy.md). Webhook receiver at `/opt/plato-deploy/`, systemd unit `plato-deploy-webhook.service`, port `9799`, gitea hook id 8.
|
||||
|
||||
- Receiver: `http://192.168.8.190:9799/deploy`, signed payload (HMAC-SHA256, secret in `/etc/plato-deploy/secret`).
|
||||
- Logs: `journalctl -u plato-deploy-webhook -f`.
|
||||
- Health: `curl http://127.0.0.1:9799/health`.
|
||||
- Manual deploy: `/opt/plato-deploy/deploy.sh`.
|
||||
- Gitea's `app.ini` `ALLOWED_HOST_LIST` was extended with `192.168.8.190` to allow this delivery.
|
||||
|
||||
## 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](120-mule-images.md) and [122 arriman](122-arriman.md). 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](124-authentik.md):
|
||||
```
|
||||
address=/plato.hubris.network/192.168.8.175
|
||||
```
|
||||
|
||||
## Related
|
||||
- [Caddy (121)](121-caddy.md)
|
||||
- [DNS (split-horizon)](../infrastructure/dns.md)
|
||||
- [Media permissions](../infrastructure/media-permissions.md)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-05-13 — auto-deploy wired
|
||||
Shape B pipeline added (`/opt/plato-deploy/`, port `9799`, gitea hook id 8). `ALLOWED_HOST_LIST` in gitea `app.ini` extended with `192.168.8.190`. See [auto-deploy](../infrastructure/auto-deploy.md#plato).
|
||||
|
||||
### 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)](index.md#recently-destroyed-kept-for-archaeology). Hit the [fresh-DB bootstrap bug](#fresh-db-bootstrap-workaround) on first boot; worked around by pre-seeding the SQLite schema.
|
||||
@@ -15,7 +15,6 @@ All containers live on [`hubris`](../hosts/hubris.md). Each row links to the per
|
||||
| 121 | [caddy](121-caddy.md) | 192.168.8.175 | unpriv | 1 | 512 MiB | 6 GiB | — | (terminates all `*.hubris.network`) | running |
|
||||
| 122 | [arriman](122-arriman.md) | 192.168.8.132 | priv | 4 | 8 GiB | 24 GiB | `/mnt/library` | `jellyseerr` / `qbit` / `sab` | running |
|
||||
| 124 | [authentik](124-authentik.md) | 192.168.8.180 | priv | 2 | 4 GiB | 20 GiB | — | `auth.hubris.network` | running |
|
||||
| 126 | [plato](126-plato.md) | 192.168.8.190 | priv | 2 | 2 GiB | 8 GiB | `/mnt/library/documents/plato` | `plato.hubris.network` | running |
|
||||
| 128 | [trmnl](128-trmnl.md) | 192.168.8.211 | unpriv | 1 | 768 MiB | 8 GiB | — | `trmnl.hubris.network` | running |
|
||||
| 129 | [house](129-house.md) | 192.168.8.212 | unpriv | 1 | 1344 MiB | 8 GiB | — | `house.hubris.network` | running |
|
||||
|
||||
@@ -27,6 +26,7 @@ All containers live on [`hubris`](../hosts/hubris.md). Each row links to the per
|
||||
| 100 | arr (yunohost) | ~2026-04-28 | Migrated to docker stack on [arriman](122-arriman.md); planned retention window expired |
|
||||
| 106 | flaresolverr | ~2026-04-28 | Folded into the arriman docker compose |
|
||||
| 116 | heaper | 2026-05-14 | Decommissioned by user; data subtree at `/mnt/library/heaper` (224 MiB) retained |
|
||||
| 126 | plato | 2026-06-28 | Notes/discovery workspace decommissioned; data at `/mnt/library/documents/plato` retained for archaeology |
|
||||
| 123 | claudio-bot | 2026-06-04 | Replaced by Hermes Agent on mac-mini; monitoring migrated to `homelab-health-watchdog` cron. See [deprecation plan](../plans/2026-06-04_130000-deprecate-claudio-bot.md) |
|
||||
| 109 | syncthing | 2026-05-14 | Decommissioned by user; `/mnt/library/syncthing` was already empty |
|
||||
| 125 | seafile | 2026-05-13 | Seafile Pro evaluation, user disliked the product; teardown also removed `files.hubris.network` from caddy + dnsmasq |
|
||||
|
||||
Reference in New Issue
Block a user