Skip to content

Configuration

Secrets come from the luna-secrets Secret through secretKeyRef. They are never literals in the manifest and never logged. The backend runs with no configuration at all, but most features then report Unavailable or refuse honestly rather than degrade in silence.

Identity and sessions

VariableDefaultPurpose
LUNA_OWNER_EMAIL(empty: reject all)The allowlist. See Access and sessions.
LUNA_OWNER_EMAIL_FILE(unset)A file holding the allowlist, re-read every 5 s. Wins over LUNA_OWNER_EMAIL.
LUNA_GOOGLE_CLIENT_ID(unset: sign-in not configured)The Google OAuth client id. It is both the OIDC audience and the client id baked into the Flutter build. No client secret is used anywhere, and none should ever be committed.
LUNA_OIDC_ISSUERGoogleOverrides the OIDC issuer.
CORE_ENVELOPE_KEKrequiredThe platform key-encryption key. LUNA’s appfs key is derived from it. Without it there is no session store and nothing persists.
LUNA_JWT_SECRET(none, no fallback)No longer signs sessions. Still read as the root of the key that signs avatar media URLs, so keep it in the manifest.

Storage

VariableDefaultPurpose
OBJECTSTORE_ENDPOINT, OBJECTSTORE_BUCKET, other OBJECTSTORE_*(unset)The platform object store, holding the appfs root and avatar media. The full set is documented by the store library.
LUNA_APPFS_DIRa temp directoryThe local encrypted appfs root. With an object store, it is only the pre-connect fallback for memory and sessions. Without an object store, it is the durable root for everything (local development).
LUNA_TIMEZONEUTCIANA timezone that sets the day boundary for streaks and points.

Inference

VariableDefaultPurpose
INFERENCE_REMOTE_URL(unset: chat fails)The shared mistral.rs text plane, used for chat only (and model-backed plan and meal work).
INFERENCE_CONTEXT_SIZEengine defaultThe context window the prompt budget is measured against. History is trimmed oldest-first to fit.
LUNA_VISION_REMOTE_URL(unset: vision RPCs fail FailedPrecondition)The dedicated vision tier. A comma-separated list is tried in order. A single in-cluster .svc. name also gets its -direct sibling added as a failover.
LUNA_VISION_MODELdefaultModel name sent to the vision tier.
LUNA_EMBED_URL(unset: keyword-only memory)The embedding endpoint for semantic recall. When it is unset, the boot log says semantic recall INACTIVE.
LUNA_EMBED_MODELdefaultEmbedding model name on every request.
LUNA_EMBED_API_KEY(unset)Optional key for the embedding endpoint.
EMBEDDING_FORMATqwen3-embeddingThe embedding instruction format: qwen3-embedding, nomic-v1.5 or raw. An unknown name stops the server at boot. Changing it opens a new, empty memory index per account, and nothing here re-embeds old memories.
LUNA_EMBEDDING_DIM768Width of every memory index.
LUNA_AGENT_CONFIGagents/luna.iniPer-function sampling settings (sections such as chat, plan-draft, meal-suggest). A missing file is non-fatal: engine defaults apply.
LUNA_JUDGEMENTonThe judging ladder over model proposals. off, false, 0, no or disabled switches it off. Off is not a pass: proposals are shown unjudged, and health-adjacent ones still say “not verified”.
LUNA_ADMIT_MAX(unset: direct dispatch)Turns on the in-process admission scheduler for vision and photo-plan calls, ordered by level tier. A non-numeric value fails startup. Leave it off while chasing a timeout.
LUNA_ADMIT_QUEUE4 when LUNA_ADMIT_MAX is setThe number of callers that may wait behind the admission slots. Never unbounded.
LUNA_GATE_MAX_INFLIGHT, LUNA_GATE_FIRST_TOKEN_SECONDS(unset: inert)The shared inference library’s pre-submission backpressure gate. It refuses a request whose remaining deadline can’t cover the wait plus the decode.

Voice

VariableDefaultPurpose
VOXTRAL_REMOTE_URL(unset)The in-cluster Voxtral speech-to-text endpoint. When it is unset, Transcribe answers voice transcription not configured (VOXTRAL_REMOTE_URL unset).
TTSD_URLhttp://ttsd.inference-system.svc.cluster.local:5005/v1/audio/speechThe platform’s ttsd speech service. It works with no configuration. Set it to "" to disable server speech, and clients then use on-device TTS.

Limits

VariableDefaultPurpose
LUNA_RATE_RPS / LUNA_RATE_BURST5 / 20Transport rate limit per session. The two defaults apply independently.
LUNA_CHAT_TURNS_PER_HOUR30Model chat turns per hour. Deterministic plan actions and habit logging are free. 0 disables the cap.
LUNA_VISION_CALLS_PER_HOUR12Vision calls per hour: meal photos, form review and plan-from-photos. 0 disables the cap.
LUNA_VOICE_CALLS_PER_HOUR120Transcription plus speech calls per hour. 0 disables the cap.

Fixed limits, not configurable: at most 3 images per vision request, 6 MiB per image, 16 MiB of audio per transcription, and a 17 MiB gRPC message cap. Deadlines are 6 minutes for chat, 8 for vision and plans, and 4 for voice.

Serving and observability

VariableDefaultPurpose
PORT0 (auto-assign)The listen port for gRPC, gRPC-Web, /media/ and the web bundle, all multiplexed on one port.
LUNA_WEB_DIR(unset: no static serving)Directory of the Flutter web bundle, served from the same origin as the API. The image sets /app/web.
CORS_ALLOWED_ORIGINS*Comma-separated gRPC-Web origins. Same-origin serving needs no CORS in production.
APP_VERSION(unset)The version stamped on logs.
CORE_HEALTH_URL(unset: no reporting)Where agent health is reported to the CORE console. It carries a bearer token (CORE_HEALTH_TOKEN).

Outbound transport check

Before it opens its listener, the server checks every outbound endpoint: INFERENCE_REMOTE_URL, LUNA_VISION_REMOTE_URL, VOXTRAL_REMOTE_URL, LUNA_EMBED_URL, TTSD_URL, CORE_HEALTH_URL and OBJECTSTORE_ENDPOINT. Cluster-internal plaintext is allowed. Anything else that would carry personal data off the cluster unencrypted stops the process, and the error lists every bad endpoint at once:

luna: N outbound endpoint(s) would carry personal data in cleartext off this cluster:
  - …

Flutter build-time defines

DefineDefaultPurpose
LUNA_BACKEND_URLweb: the serving origin (or a runtime config.json); native release: https://luna.runink.org; native debug: http://localhost:50051The backend base URL
LUNA_GOOGLE_CLIENT_IDthe production client idThe Google Identity Services client

Store builds must go through flutter/tool/build_release.sh appbundle|ipa, which obfuscates the code and writes split debug symbols. Keep those symbols for each release, because they are the only way to read a crash.