Monolith
The monolith is the FastAPI + SvelteKit application suite behind most of this homelab. It combines a personal knowledge graph, a Discord chat agent, the EmberVM-backed software factory, and small apps (including Grimoire, hikes, trips, stars, and ships) over a shared Postgres data plane. Separate private, public, and agent compositions ship only the routes and code each audience needs; jomcgi.dev is the public surface.
Architecture
The backend is a FastAPI app (app/main.py) organized into domains, one
directory per feature area, each owning its own routes, models, and tests
colocated as *_test.py files next to the code they cover. Domains talk to a
shared Postgres database (shared/) through SQLModel, and a Postgres-backed
scheduler (scheduler/) drives background jobs (ingest, retention, digests,
backfills) without a separate workflow engine.
The frontend is a SvelteKit app (frontend/) that renders both the public
website and the private app surfaces. Public routes proxy to the backend's
public API through +page.server.js loaders so pages render server-side and
stay cacheable at the edge; private routes talk to the authenticated API.
The deployed application has distinct audience surfaces:
- Public tier: read-only, unauthenticated routes served at jomcgi.dev
(health, public apps, docs, and published factory and knowledge views). It is
a pruned binary on the restricted
public_readerrole, with a narrowly scoped writer for Grimoire chat. - Private tier: authenticated apps, APIs, Discord integration, knowledge editing, and factory controls behind the private ingress.
- Agent tier: a separate pruned MCP server for EmberVM guests. It exposes the bounded knowledge and Kubernetes observation tools described in ARCHITECTURE.md, not the private catalogue.
- Friends surface: the moving planner and Grimoire campaign lobby, each protected by its own Authentik policy. Grimoire supports invitation-only account registration, campaign ownership, and player invitations.
The obsolete DR jobs and Canada Campsites frontend pages are retired. Their
former /app/dr-jobs and /app/campsites URLs use the standard path-aware
404 page, including saved filter links. The backend domains, records, refresh
jobs and backups remain available independently. The retained DR jobs digest
still includes its former page link, which now resolves to that 404.
The retired private Firecracker and Qwen demo pages, the Semgrep performance dashboard, the Semgrep MCP tool and the public Semgrep exhibit are not part of any tier. Public EmberVM pages and their probes remain independent. The committed Firecracker story measurements and Qwen replay remain published. Their captures depended on the retired private demos API and the removed SigNoz span store and cannot be regenerated. The Qwen capture scripts are removed.
The tier boundaries are enforced through separate compositions, database roles, and ingress policy; see ARCHITECTURE.md sections 1 to 3. The hazard model for this boundary is STPA.md.
The shipped Factory selects bounded issue work, asks a per-task Planner
to build a DAG, and has an Executor run its role-specific nodes. Legacy
factory_conductor names refer to that task planner, not to the proposed
operator-facing Conductor. The maintained
role and record contract
specifies the top-level Conductor without presenting it as shipped. The
expanded MCP review and steering interface remains follow-up work in
#5788.
Trust and safety
Discord engagement runs behind a per-server, per-user trust ledger. Narrow regex heuristics, an asynchronous LLM intent classifier, and a shadow-first random forest feed one score. The thresholds are environment-overridable; a pardon restores the score and relabels recent events, so a wrong lockout becomes corrective training data. Current state: ARCHITECTURE.md.
Key subdirectories
| Path | What it is |
|---|---|
app/ |
FastAPI entrypoint, app wiring, lifespan, logging, OpenTelemetry setup |
frontend/ |
SvelteKit app: public website, private app UIs |
chart/ |
Helm chart for the service (templates, migrations, dashboards) |
deploy/ |
ArgoCD Application, Helm values, and GitOps wiring for this cluster |
knowledge/ |
Evidence ingestion, extraction, retrieval, public views, and knowledge interventions |
chat/ |
Discord bot, trust and safety, triggers, reminders, summaries, and session adapter |
factory/ |
Factory execution, task planning, DAG orchestration, operator controls, and public snapshots |
scheduler/ |
Postgres-backed job scheduler shared by all domains |
shared/ |
Cross-domain database session/engine setup and test helpers |
grimoire/, hikes/, ships/, stars/, trips/, worldcup/ |
Individual apps, each with its own routes and models |
campsites/, dr_jobs/ |
Retained backend APIs, data models and jobs; frontend pages retired |
e2e/ |
End-to-end tests spanning the frontend and backend together |
Deployment
The monolith is packaged as a Helm chart (chart/) and published as an OCI
artifact. Images are built amd64-only with apko in CI. A
merge to main publishes the chart and writes its new version back to the
repository. Production runs on the GKE hub, where Kargo promotes new chart
versions to the monolith and monolith-public ArgoCD Applications. The home
deployment was retired in #6914; deploy/application.yaml remains only as
publication metadata for chart-version write-back. See
Platform architecture: GitOps and delivery
for the current pipeline.
Database schema changes go through Atlas migrations checked in under
chart/migrations/, applied by an in-cluster Atlas operator rather than at
application startup.
Public Ember exhibits use the recorded Firecracker restore replay. The live Postgres and Bazel demo front doors, their code (ember_public keeps only the agent-lane probes) and their combined synthetic schedule are retired. Public notes provide graph browsing; the notes chat (chat_public, now only its retention jobs) is retired and the homepage points visitors to the factory activity visuals. Production agent lane probes remain on the private tier.