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, and the guest-signing date), 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.

ENVIRONMENTLocal dev.env.local, .dev.varsSecretswrangler secret putSettingswrangler.toml [vars]FrontendsNEXT_PUBLIC_* at build
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

Authentication is provided by an optional integration with Clerk, and the app supports three modes depending on what you configure.

  1. Hybrid, the production setup: both the publishable key (NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, on the live and community builds; a repository secret when you deploy through GitHub Actions) and the API's verification URL (CLERK_JWKS_URL) are set, so guests and signed-in users coexist.
  2. Guest-only, the typical self-host: leave the auth variables unset. The app runs without an account system, sign-in routes say sign-in isn't enabled and offer Continue as guest, and the editor is unchanged.
  3. A partial misconfiguration (frontend configured, API not) exists only for debugging and is not meant for production: people can sign in, but the API treats everyone as a guest.

With sign-in on, also set GUEST_ID_HMAC_SECRET so guest ids are signed and nobody can claim another guest's documents when they sign up. 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.

Clerk set?Signed-inyesGuest pathunsetEditor works
Whether Clerk 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, photo importOne 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
EmailRESEND_API_KEY on the api Worker, plus RESEND_FROM on a domain verified in Resend and APP_BASE_URL set to your origin, since both default to livediagram.app (needs sign-in)
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)
Continue with GoogleNEXT_PUBLIC_GOOGLE_OAUTH_ENABLED on the live build, with Google enabled in Clerk. The bundled deploy workflow already sets it, so enable Google in Clerk or remove that line

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 Clerk publishable key and the Google Picker key, they are not read from repository secrets.

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?