Ir al contenido

Architecture

Esta página aún no está disponible en tu idioma.

The production shape is deliberately small: three containers and one Go binary.

flowchart LR
  subgraph host[Single host]
    nginx[nginx<br/>TLS + static]
    app[Go binary<br/>API + embedded SPA]
    pg[(Postgres)]
    storage[(Filesystem<br/>or S3)]
    nginx -->|reverse proxy| app
    app --> pg
    app --> storage
  end
  user[Artist / Reviewer / Operator] -->|HTTPS| nginx
  • Go binary serves the JSON API and embeds the SvelteKit SPA via go:embed. One artifact. One process. Same binary supports the embed_web build tag for production (frontend baked in) and a dev mode that proxies the Vite dev server.
  • Postgres holds every record Artist Alley owns — assets, metadata fields + values, posts, members, collections, comments, likes, sessions, audit, system_config. Migrations live in app/internal/db/migrations/ and run on boot.
  • Storage is content-addressed: every byte blob is keyed by its SHA-256, so dedupe across the install is free. Two backends:
    • fs — directory on the host (mounted into the container)
    • s3 — any S3-compatible API (AWS, R2, B2, MinIO)
  • nginx in front for TLS termination and static serving. Optional: the binary serves HTTP fine on its own behind another proxy if you prefer.

The JSON API is defined in app/api/openapi.yaml. That file is the contract: server types and handler interfaces are generated from it by oapi-codegen. Frontend code (SvelteKit) generates its TypeScript types from the same source via openapi-typescript.

Edits to the spec are the only way to change the public API surface — handlers won’t compile if the Go signatures drift from the spec, and npm run check fails on the frontend if its generated types diverge.

The rendered reference lives at API reference and is re-rendered on every site build.

The Postgres schema is declared in app/internal/db/migrations/*.sql and surfaced here at Database schema. Migrations are the source of truth — sqlc type-checks Go queries against them at code-generation time, so type drift gets caught before code ships.

The schema is mirrored in app/schema.sql, kept in lockstep with the single squashed baseline migration (00001_baseline_v0_1.sql, per ADR 0046); sqlc type-checks Go queries against it at code-generation time.

Two layers:

  • Process config (host vs container, ports, secrets) — driven by environment variables, see the install guide configuration reference.
  • Install-wide settings (site name, SMTP, auth providers, AI providers, brand fonts, themes) — stored as JSON-typed rows in the system_config table, mutated through the admin UI, surfaced via the /admin/system/* API. The appearance key drives the per-install font slot picker.

Federation-aware: every system_config row stays local to its instance. Per-user prefs (theme, language, AI provider override) ride on user_profiles which carries origin_server_id.

  • Go for the server — one static binary, fast startup, good concurrency, and native HTTP/2 + SSE for streaming flows like the planned live presenter sessions.
  • SvelteKit for the SPA — small bundle, file-based routing that lines up with the API. Build output is static (adapter-static), so the binary just serves the files.
  • Postgres only — one durable database to back up, one query language across every entity. No separate cache, vector store, or search index. Full-text search is a TSVECTOR column with a trigger that rebuilds it on writes; vector similarity uses pgvector in the same database.
  • go:embed for the SPA — operators don’t need to think about static asset deployment. The binary is the whole frontend.
  • Content-addressed storage — uploading the same bytes twice stores them once. Move the install to a new disk by rsync-ing the storage directory; integrity is recoverable by re-hashing.

For the rationale behind each big call see Architecture Decisions.