Developer reference
The Artist Alley source lives at github.com/Artist-Alley-Org/artist-alley. This page is the map: top-level layout, the per-domain backend pattern, the frontend layout, the infrastructure pieces, and how a change moves from a local edit to a release.
Top-level layout
Section titled “Top-level layout”artist-alley/├── app/ The Go backend + the OpenAPI spec│ ├── api/openapi.yaml HTTP contract — generates strict server + clients│ ├── cmd/ Binary entry points (server, CLI tools)│ ├── internal/ Per-domain Go packages (see below)│ ├── schema.sql Postgres schema the Go side sees│ ├── sqlc.yaml sqlc configuration│ └── web/ Embedded SvelteKit build output (release-time only)├── web/ The Svelte 5 frontend source│ ├── src/│ │ ├── routes/ SvelteKit page routes│ │ └── lib/│ │ ├── api/ Generated TS client from openapi.yaml│ │ ├── components/ Shared components│ │ ├── stores/ Svelte runes / stores│ │ ├── i18n/ Per-locale catalogues│ │ └── playlist/, whiteboard/, sprite/ Feature modules│ └── package.json├── docs/ Long-form docs (markdown) — the site pulls these│ └── adr/ Architecture Decision Records (canonical source)├── infra/│ ├── docker/ Dockerfiles + compose stacks│ ├── nginx/ Reverse-proxy config│ └── postgres/ DB init + extension setup├── scripts/ Dev + release shell scripts (test.sh, release.sh, etc.)├── docker-compose.yml The dev stack — postgres + app + web + (opt) minio + workers├── CONTRIBUTING.md High-level contribution flow└── RELEASING.md Release cut procedureBackend per-domain layout
Section titled “Backend per-domain layout”The Go backend follows a per-domain layout under app/internal/<domain>/. Each domain owns one capability area and is self-contained.
app/internal/<domain>/├── handler.go HTTP handlers + adapters between OpenAPI types and the domain model├── handler_test.go Integration tests against a real Postgres├── queries.sql sqlc-managed queries├── queries.sql.go Generated; do not edit└── service.go (optional) Cross-handler business logic that doesn't belong in a handlerCurrent domains:
| Domain | What it owns |
|---|---|
assets | Core asset entity, lifecycle, file metadata |
assettype | Asset-type lookup table |
metadata | Field definitions, asset field values, history |
collections | Collections of assets + posts, with ACLs |
posts | Posts wrapping one or many assets |
social | Comments, likes |
auth | Sessions, login, password handling |
acls | Per-row access control list checks |
users, teams | Identity surfaces |
audit | Audit-event ledger |
cache | In-memory cache registry with cross-domain invalidation |
config, sysconfig | System configuration surfaces |
db | Migrations + low-level DB helpers |
http | HTTP server bootstrap + middleware |
i18n | Per-locale catalogues + translation routing |
jobs | Async job queue |
openapi | Generated strict-server types |
preview | Thumbnail / preview generation pipeline |
setup | First-run setup wizard |
storage | Pluggable storage backend (fs / S3) |
workflow | Asset / post state machine |
abr, brushpacks | Adaptive bitrate + annotation toolset |
logging | Structured-log shipping |
Cross-domain calls go through:
- HTTP when the domains are conceptually separate services that happen to share a binary today (and will be split later for federation).
- Small interfaces when one domain needs a focused capability from another (e.g.
audit.Recorderis passed into handlers that need to emit events). - The cache registry (
cache.Registry) for cross-package invalidation that needs to propagate across domains in a federation-ready way.
What doesn’t happen: one domain importing another domain’s repository or query types directly. That coupling is the thing the per-domain layout exists to prevent.
Frontend layout
Section titled “Frontend layout”web/src/├── routes/ SvelteKit page routes — file-system-mapped URLs│ ├── +layout.svelte Root layout│ ├── browse/ Browse feed│ ├── admin/ Admin shell (capability-gated)│ ├── account/ Account preferences│ └── ...└── lib/ ├── api/ TS client generated from openapi.yaml — do not edit ├── components/ Shared components consumed across routes │ ├── viewers/ Universal asset viewer + per-kind view bodies │ ├── whiteboard/ Annotation tool (Phase 1.18.B-12 / brush packs) │ └── ... ├── stores/ Cross-route runes + Svelte stores ├── i18n/ en.json + locale catalogues └── util/ Pure functions, no UI dependenciesRunes ($state, $derived, $effect) live in .svelte.ts files. A plain .ts file using runes errors at runtime, not compile time — see coding standards.
The API client is regenerated by npm run generate:api after every openapi.yaml change. CI does not type-check the frontend; staleness goes undetected without a local run.
Infrastructure
Section titled “Infrastructure”infra/├── docker/ Dockerfiles for app, web, blender worker├── nginx/ Reverse-proxy config for production└── postgres/ Init scripts + extension setup (pg_trgm, uuid-ossp, etc.)The dev stack is brought up with docker compose up. Optional profiles:
--profile storage-s3— runs MinIO for S3-backend testing. Daily dev uses the filesystem backend; MinIO is verification-only.--profile workers— runs the Blender worker for 3D thumbnail generation.
The Go binary is the runtime. Each release ships a single artifact:
- A multi-arch (
amd64+arm64) Docker image (ghcr.io/Artist-Alley-Org/artist-alley, mirrored to Docker Hub), keyless-signed via Sigstore.
Native binaries, .deb / .rpm packages with a systemd unit, and a
Homebrew formula are a v1.0.0 target, tracked in
#242.
Docs site
Section titled “Docs site”The site at artist-alley.org is Astro Starlight and lives in a separate private repo (Artist-Alley-Org/artist-alley-site) — the site/ directory was removed from this OSS repo. At build time the site checks this OSS repo out and its sync scripts pull the doc source from docs/ and app/:
sync-adrs.mjs—docs/adr/*.md→ augmented MDX with cross-reference cards.generate-schema-docs.mjs—app/schema.sql→ per-table pages + ER diagram.sync-openapi.mjs—app/api/openapi.yaml→ rendered API reference.sync-roadmap.mjs—docs/roadmap.md→ roadmap page.sync-install-config.mjs— install config table fromdocs/install/config/aa.env.example.sync-screenshots.mjs— screenshot assets for the docs.fetch-github.mjs— GitHub snapshot for the engineering dashboard.
The first six run on pnpm dev (predev) and pnpm build (prebuild). fetch-github.mjs runs on demand via pnpm refresh-github. Generated content is gitignored; the source files are tracked.
Test + release flow
Section titled “Test + release flow”Local development
Section titled “Local development”# Bring up the stackdocker compose up -d
# Tail logsdocker compose logs -f app web
# Run a fresh Go test pass./scripts/test.sh --go
# Run a fresh frontend type-checkdocker compose exec -T web sh -c 'cd /app && npm run check'
# Run the full pass (Go + frontend)./scripts/test.shAfter a Go binary change that isn’t picking up: docker compose up -d --build --force-recreate app. Without --force-recreate, compose can keep the old binary running.
After a frontend change that isn’t picking up: docker compose restart web. Vite HMR sometimes serves stale code.
When Postgres misbehaves: docker compose logs postgres first. Migration failures, lock issues, role wipes — the Postgres log is the diagnostic, not the app log.
Release
Section titled “Release”feat/* → dev → main → tag → release.
- Working branches merge into
devvia PR (./scripts/test.shgreen required). dev → mainhappens at release boundaries via PR. Fast-forward only.- Release tag (
v0.X.Y) onmaintriggers the GitHub Actions pipeline that builds the multi-arch Docker image, signs it with Sigstore, publishes to GHCR + Docker Hub, and posts the release notes. (Binaries + native packages return with #242 at v1.0.0.) - Self-hosted runner labelled
[artist-alley]runs the heavy build steps. - Full procedure in
RELEASING.md.
Where things you’ll commonly look for live
Section titled “Where things you’ll commonly look for live”| Looking for | Path |
|---|---|
| HTTP routes and request/response shapes | app/api/openapi.yaml |
| The SQL that runs against Postgres | app/internal/<domain>/queries.sql |
| The schema you can query against | app/schema.sql |
| A specific table’s documentation | /developers/database/<table>/ |
| Migration history | app/internal/db/migrations/NNNNN_*.sql |
| Why we chose X over Y | /adr/ — the ADR catalogue |
| Where a feature is on the roadmap | /roadmap/ |
| How a frontend component looks | web/src/lib/components/<area>/ |
| A locale string | web/src/lib/i18n/<locale>.json |
| Dev-stack containers | docker-compose.yml |
| Release procedure | RELEASING.md |
| Contribution flow | CONTRIBUTING.md + coding standards |