Eliminates the SQLITE_BUSY VACUUM bug from #1283 by making cmd/server
truly read-only. The bug surfaced when supervisord launched both
ingestor + server in one container: the ingestor took the write lock for
INSERTs, then the server's VACUUM-on-startup immediately failed with
SQLITE_BUSY. Same race latently affected three other server-side writes.
Four write operations moved out of cmd/server/:
1. VACUUM / auto_vacuum migration (cmd/server/vacuum.go, entire file)
→ cmd/ingestor/db.go Store.CheckAutoVacuum (already existed;
ingestor runs it BEFORE the MQTT subscriber starts so there is
no contention with concurrent writes).
2. PruneOldPackets (DELETE FROM transmissions)
cmd/server/db.go → cmd/ingestor/maintenance.go (new file,
Store.PruneOldPackets) + main.go scheduler.
3. PruneOldMetrics (DELETE FROM observer_metrics)
cmd/server/db.go → cmd/ingestor/db.go Store.PruneOldMetrics
(already existed).
4. RemoveStaleObservers (UPDATE observers SET inactive = 1)
cmd/server/db.go → cmd/ingestor/db.go Store.RemoveStaleObservers
(already existed).
Server-side changes:
- vacuum.go deleted; checkAutoVacuum / runIncrementalVacuum gone.
- cmd/server/db.go: PruneOldPackets, PruneOldMetrics, RemoveStaleObservers
deleted.
- cmd/server/main.go: packet/metrics/observer prune schedulers removed;
the neighbor-edge prune scheduler (PruneNeighborEdges) is intentionally
left in place — outside scope of #1283, tracked separately.
- routes.go + openapi.go: /api/admin/prune endpoint removed (prune is
scheduled by the ingestor now; operators restart the ingestor for an
ad-hoc pass).
Ingestor changes:
- New cmd/ingestor/maintenance.go with Store.PruneOldPackets.
- cmd/ingestor/config.go gains RetentionConfig.PacketDays and
Config.PacketDaysOrZero().
- cmd/ingestor/main.go runs PruneOldPackets at startup (if
packetDays > 0) and on a 24h ticker.
Docs:
- AGENTS.md: documents the read/write separation invariant.
- config.example.json: notes that retention + vacuumOnStartup are
consumed by the ingestor.
TDD:
- Red: bb1d749a — invariant tests + Store.PruneOldPackets stub.
- Green: this commit — real implementation + server-side removals.
Note: cachedRW() still has three out-of-scope callers in cmd/server
(neighbor_persist.go, ensure_indexes.go, from_pubkey_migration.go).
Those are pre-existing write paths not covered by issue #1283 and are
left untouched per the issue scope. Future work can relocate them
under the same invariant.