Configuration and Optional Auth

The environment variables that shape a self-hosted instance, and how sign-in degrades gracefully to pure-guest mode.

A self-hosted livediagram is configured through environment variables, secrets and each Worker's wrangler.toml, not application code. A few wrangler.toml values ship with livediagram.app's own settings (the database and KV ids, the allowed AI origins, the MCP issuer and consent URL), so a fork replaces those with its own. The most important thing to understand is that the optional features are genuinely optional: leave them unset and the editor still works perfectly, just in a simpler mode. This page covers how configuration is supplied and the main toggles you will care about.

How configuration is supplied

Secrets and settings never live in source code, since the repository is public. Instead, each Worker reads them at runtime.

  • Local development uses a gitignored .env.local file per frontend app and a gitignored .dev.vars file per Worker.
  • Workers receive secret values via wrangler secret put, and non-secret settings as [vars] in the Worker's wrangler.toml. Set plain settings in the file rather than the Cloudflare dashboard: every deploy replaces them with what the file declares.
  • Static frontends only ever bundle values explicitly marked as publishable, prefixed NEXT_PUBLIC_ so it is obvious they are safe to ship to the browser, and baked in at build time. Server-only secrets stay on the Worker side and never reach client code.

Each app that needs configuration ships an example file documenting which variables it expects, so you can see at a glance what to set.

Workers read secrets and settings at runtime and frontends bake publishable values in at build time, so no secrets ever live in the public source.

Optional sign-in

Sign-in runs inside your own api Worker, with its accounts in your own database, so it needs no outside service and no build-time key: the editor asks the api whether sign-in is on and which methods it offers. There are two modes.

  1. Sign-in on: set BETTER_AUTH_SECRET and at least one sign-in method, so guests and signed-in people coexist.
  2. Guest-only, the typical self-host: leave the secret unset. The app runs without an account system, sign-in pages say sign-in isn't enabled here and offer Continue as guest, and the editor is unchanged.
VariableWhereWhat it does
BETTER_AUTH_SECRETapi Worker secretTurns sign-in on. At least 32 random characters (openssl rand -base64 32). It also signs guest ids, so nobody can claim another guest's documents when they sign in. Rotate it with BETTER_AUTH_SECRETS beside it, never by replacing it
APP_BASE_URLapi [vars]The origin people open the editor on. Required for sign-in, and used for links in emails
GOOGLE_AUTH_CLIENT_IDapi [vars]Your Google OAuth client, for Continue with Google. Register https://<your-host>/api/auth/callback/google as its redirect URI
GOOGLE_AUTH_CLIENT_SECRETapi Worker secretThe same client's secret; Google sign-in needs both
EMAIL binding or RESEND_API_KEYapi WorkerAn email sender, for sign-in with an email code: Cloudflare Email Service, or Resend. Set EMAIL_FROM to an address on your sending domain
EMAIL_OUTBOXapi [vars]1 keeps codes in your database instead of sending them, for previews and testing only. Never on a real deployment

Accounts unlock the features that need one: teams, API tokens, and, if you set them up, email, the Google Drive mirror and the MCP server.

Guest-only is what lets a self-hoster run with zero outbound runtime traffic beyond their own hosting if they want to.

Guest-only fallback

The API identifies the owner of a request in one of two equivalent ways: a verified sign-in token, or a per-browser guest id sent as a header. When sign-in is not configured, every request simply uses the guest path, and the editor stays fully usable, with persistence, sharing, and real-time collaboration all working.

Whether sign-in is configured or not, requests resolve to a working editor.

Other optional features

Every other optional feature follows the same pattern: unset means off, and the editor hides what it cannot use.

FeatureTurn it on with
Image uploads, thumbnailsCreate the livediagram-images R2 bucket the api wrangler.toml already binds (or delete that binding to leave uploads off). Optional per-owner caps: IMAGE_MAX_PER_OWNER, IMAGE_MAX_BYTES_PER_OWNER
AI assistant, server-side photo reading (without it, photos are read in the browser)One AI key on the api Worker: OPENAI_API_KEY, GOOGLE_AI_STUDIO_API_KEY, or AI_API_KEY with AI_BASE_URL and AI_MODEL. Also set AI_ALLOWED_ORIGINS to your own origin or remove it: it ships with livediagram.app's, and any other origin gets a 403
EmailThe EMAIL binding (Cloudflare Email Service) or RESEND_API_KEY on the api Worker, plus EMAIL_FROM on your sending domain and APP_BASE_URL set to your origin, since both default to docs.nu
Google Drive mirrorGOOGLE_CLIENT_ID on the api Worker and NEXT_PUBLIC_GOOGLE_CLIENT_ID on the live build; GOOGLE_CLIENT_SECRET and DRIVE_TOKEN_KEY keep it syncing (needs sign-in). See Google Drive and MCP
MCP server for AI toolsDeploy the mcp Worker with its own KV namespace and hostname, then point CONSENT_BASE_URL (mcp Worker) at your editor's origin, and NEXT_PUBLIC_MCP_ORIGIN (live build) and OAUTH_ISSUER (api Worker) at your MCP host (needs sign-in). See Google Drive and MCP
TelemetryTELEMETRY_ENABLED on the api Worker and NEXT_PUBLIC_TELEMETRY_ENABLED on every frontend build
WorkbenchesNothing extra beyond sign-in, but set APP_BASE_URL on the api Worker to the origin that serves the app, or pairing and frame links point at livediagram.app (needs sign-in)

The NEXT_PUBLIC_* values above are baked in at build time. When you deploy through the bundled GitHub Actions workflow, add them to the workflow's build step (or set mcp_origin in deploy.yml); apart from the Google Picker key, they are not read from repository secrets. Sign-in needs none: the workflow syncs BETTER_AUTH_SECRET and GOOGLE_AUTH_CLIENT_SECRET to the api Worker from repository secrets of the same name.

The Community is the one feature that is on by default; set COMMUNITY_ENABLED to false on the api Worker to switch it off.

For every variable, where it lives and the steps around it (OAuth redirect URIs, KV namespaces, the AI model knobs), follow the repository's docs/operations/self-hosting.md and each app's .env.example file.

Was this article helpful?