Architecture
How the repository fits together.
Auvyra is a pnpm monorepo.
apps/web The product: station website, dashboard and API
apps/website The official website and documentation
apps/docs Documentation content, rendered by apps/website
packages/ui Design tokens and shadcn/ui-style components
packages/database Repository contracts, Zod schemas, adapters, migrations
packages/azuracast Typed AzuraCast client with SSRF protection
packages/config Environment validation, encryption, shared TypeScript configInternal packages ship TypeScript source and are compiled by the app.
Two apps
apps/web is the open-source product: the station website, dashboard and API. It is all a self-hosted install runs.
apps/website is the official Auvyra website and this documentation, hosted by the project. It has no database, and nothing in apps/web depends on it.
Station branding is applied as CSS variables on the station layout, so it cannot reach the dashboard. Values are validated hex colors and allowlisted font keys, so stored data never becomes arbitrary CSS.
The player lives in the station layout, which survives client-side navigation, so its single audio element keeps playing between pages.
One deployable
A station runs a single container. apps/docs is a content package used by the official website.
Data flow
Application code depends only on the interfaces in packages/database. createDatabase loads the adapter named by DATABASE_PROVIDER with a dynamic import, so an installation only loads the driver it uses.
Authentication
Sessions are opaque random tokens. The cookie holds the token and the sessions repository holds its SHA-256 digest, so all persistence goes through the shared database layer and works the same on every engine.
proxy.tsredirects requests with no session cookie. It is a convenience, not a security boundary.getCurrentUservalidates the session against the database.requirePermissionguards server actions, and every dashboard page checks access on the server.
Auth.js was considered. Its Credentials provider only supports JWT sessions, which would bypass the Sessions repository and make revoking a session impossible.
Business rules
Rules live in apps/web/lib/services, not in pages or server actions. A service takes the database and current user as arguments and returns a FormResult, so permissions, cross-station checks and sanitization are all tested directly. Server actions only resolve the session and call a service.
Secrets
packages/config encrypts credentials with AES-256-GCM. The ciphertext is bound to its record as additional authenticated data, so it cannot be copied onto another station. Browser-facing types such as NowPlayingView contain no URLs or keys.