# Design Decisions

Rationale behind YotoShelf's key design choices.

## go-yoto: separate module

The Yoto API client lives in a separate Go module at [gitlab.com/yotoshelf/go-yoto](https://gitlab.com/yotoshelf/go-yoto) rather than inside the YotoShelf monorepo. This makes it independently importable by other Go projects without pulling in YotoShelf's full dependency tree. During development, the main module references it via a `replace` directive.

## Local users + OIDC (not OIDC-only)

YotoShelf supports both local user accounts (admin-created, argon2id passwords) and optional OIDC login. OIDC-only would require every self-hoster to run a separate identity provider — a significant barrier. Local accounts are the baseline; OIDC is configured when an existing IdP is available. Local users can be migrated to OIDC later via admin account association.

## Collections-centric design

Collections are the primary organising concept — not a layer added on top of a flat card library. Every card must be created in a collection. Publishing is collection-aware. "All Cards" is a UI filter, not a real collection.

This matches how people think about their content: "Family cards", "School", "Audiobooks". It also provides a natural access control boundary (collection-level roles) without needing per-card permissions.

## SQLite as single source of truth

SQLite is the authoritative store for all UX operations. `card.yaml` is a derived export, not a primary input. This avoids sync conflicts between two authoritative sources and makes the web UI simpler. The filesystem watcher that imports external `card.yaml` edits is a power-user escape hatch, not a core workflow.

Self-hosters overwhelmingly run a single node. SQLite is battle-tested, zero-infrastructure, and trivially backed up. The `modernc.org/sqlite` pure-Go driver removes the CGo dependency, which simplifies the container build and enables static linking.

## Cover vs label terminology

YotoShelf uses "cover" for the 1500×1500 card face image (displayed in the app and on the Yoto device) and "label" for the printable sticker template. These are distinct concepts with different dimensions and use cases. The original Yoto app conflates them; YotoShelf does not.

## Audio pipeline

ffmpeg handles transcoding locally rather than uploading the source file and letting Yoto transcode it. This gives YotoShelf control over quality settings, progress feedback, and error handling. Yoto's own transcoding pipeline has undocumented quality limits that can produce audible artifacts on long-form audio.

## Federation posture

YotoShelf does not federate or sync between instances. Each instance is standalone. Sharing between instances (and with external users) is handled via share links — time-limited URLs for label PDFs and collection views. Full federation would require a discovery mechanism, trust model, and upgrade path that are out of scope for a self-hosted personal tool.

## BFF pattern (no token exposure to frontend)

The Go backend acts as a Backend-for-Frontend: it owns all authentication state and the Yoto OAuth2 tokens. The Svelte frontend makes API calls through the Go server, which proxies to Yoto with the stored credentials. This means the frontend never sees a Yoto token — even if the frontend is compromised, the attacker cannot extract and reuse the Yoto credentials offline.
