Private testing started on 6 November 2026. Got an invite code? Sign up here

Auvyra

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 config

Internal 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.ts redirects requests with no session cookie. It is a convenience, not a security boundary.
  • getCurrentUser validates the session against the database.
  • requirePermission guards 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.