Architecture
Go huma API, SQLite/sqlc/goose, Astro + Svelte 5 frontend, go-yoto client, and the design system.
Four frameworks enforce correctness at compile time. Understanding them before making changes is essential. They interact, and bypassing one usually breaks another.
The four frameworks
| Layer | Tool | Source of truth | Generated output |
|---|---|---|---|
| API | huma v2 | Go I/O structs in internal/api/ |
OpenAPI spec |
| Database | sqlc | SQL in internal/db/queries/*.sql |
Go in internal/db/gen/ |
| Migrations | goose | SQL in internal/db/migrations/ |
Schema with rollback |
| Config | koanf | internal/config/config.go |
Validated config struct |
| Frontend types | openapi-typescript | huma's generated OpenAPI spec | frontend/src/lib/api/types.ts |
| Frontend client | openapi-fetch | Generated types | frontend/src/lib/api/client.ts |
Repository layout
| Directory | Purpose |
|---|---|
cmd/yotoshelf/ | CLI entrypoints. The binary's own usage line lists them: serve, create-admin, backup, restore, seed-llm, seed-eval, list-devices, version |
internal/api/ | huma API operations (HTTP layer). One file per domain. |
internal/db/ | SQLite, goose migrations, sqlc queries, and generated code |
internal/config/ | koanf configuration struct and loader |
internal/ | Domain packages: auth, card, icons, jobs, labels, and others |
frontend/ | Astro + Svelte 5 frontend; embedded into the binary via go:embed |
frontend/design | Git submodule: this site's design tokens, components, and brand assets |
e2e/ | Playwright end-to-end tests |
scripts/ | CI helper scripts (file size check, i18n check) |
docs/hardware.md | Canonical Yoto hardware specs |
Tech stack
| Layer | Technologies |
|---|---|
| Backend runtime | Go 1.25, CGO-free (modernc.org/sqlite) |
| HTTP | huma v2 + chi v5 |
| Database | SQLite with sqlc (type-safe queries) and goose (migrations) |
| Auth | argon2id password hashing, gorilla/sessions, OIDC (PKCE S256) |
| Frontend build | Astro, Svelte 5 (runes, not stores), Tailwind v4 |
| UI components | shadcn-svelte |
| Icons | Phosphor duotone only |
| i18n | Paraglide JS: all user-facing strings in frontend/messages/en.json |
| Image generation | Ideogram (recommended AI provider) |
go-yoto sibling module
The Yoto API client lives in a separate module at
gitlab.com/yotoshelf/go-yoto.
It is a standalone library, not a subdirectory of this repository. Changes to Yoto API
integration belong there; changes to how YotoShelf calls it belong in internal/.
Design submodule
frontend/design is a git submodule pointing at
yotoshelf/yotoshelf.dev
(this documentation site, which is also the canonical home of the design system).
It provides design tokens, shared Svelte components, fonts, and brand assets.
The submodule must be initialised before building the frontend:
git submodule update --init
New feature workflow
This sequence is read from the repository's
CONTRIBUTING.md
when the site is built.
YotoShelf uses four tools that enforce correctness at compile time. Follow this workflow for every change:
1. Write the SQL query
Add your query to internal/db/queries/{domain}.sql:
-- name: GetCardBySlug :one
SELECT id, title, slug, status FROM cards WHERE slug = ?;
Run sqlc generate (or just generate). Type-safe Go appears in internal/db/gen/.
2. Write the huma operation
Add your endpoint to internal/api/{domain}.go:
huma.Register(api, huma.Operation{
OperationID: "getCard",
Method: http.MethodGet,
Path: "/cards/{slug}",
Summary: "Get card detail",
Tags: []string{"cards"},
}, func(ctx context.Context, input *GetCardInput) (*GetCardOutput, error) {
// Call sqlc-generated query
card, err := queries.GetCardBySlug(ctx, input.Slug)
// ...
})
The OpenAPI spec updates automatically when the server starts.
3. Generate frontend types
just generate # runs sqlc + openapi-typescript
TypeScript types appear in frontend/src/lib/api/types.ts.
4. Use the shared client
import { api } from '$lib/api/client';
const card = await api.getCard({ slug: 'my-card' });
5. Verify
just check # lint + test + build + govulncheck + npm audit + i18n
Structural conformity
Before writing any new code, find an existing example of the same pattern and follow it exactly. See the lookup table in AGENTS.md for which file to reference for each kind of change. New patterns that have no existing precedent in the codebase require human approval.