Personal vs Business
Luna ships in two variants — Personal for single-user companion use and Business for team deployments with JWT auth, rate limiting, and professional persona.
Overview
Set LUNA_VARIANT in .env to choose the deployment mode. The variant affects authentication, proactive behaviour, away-state logic, persona tone, and rate limiting.
LUNA_VARIANT=personal # default — single-user companion
# or
LUNA_VARIANT=business # team deployment with JWT and rate limitingFeature comparison
| Feature | Personal | Business |
|---|---|---|
| Authentication | None (localhost-only) | JWT bearer tokens, configurable expiry |
| Multi-user | Single user | Multiple users, per-user memory |
| Rate limiting | Disabled | Configurable per-minute + burst limits |
| Away detection | Enabled (farewell phrases) | Disabled |
| Proactive messages | Companion check-ins, state alerts | Task/event notifications only |
| Tone | Warm, casual, companion | Professional, configurable |
| Voice pipeline | Full (desktop wake-word) | Configurable / optional |
| Personality RL | Full (per-user drift) | Bounded (professional floor) |
| Spotify / desktop | Enabled | Disabled by default |
| Persona name | LUNA_NAME | LUNA_NAME + BUSINESS_NAME |
| Database | SQLite (default) | PostgreSQL recommended |
Personal variant
Designed for single-user, always-on companion use on a personal desktop or laptop. Prioritises warmth, proactivity, and natural conversation flow.
LUNA_VARIANT=personal
# Persona
LUNA_NAME=Luna
USER_NAME=Alex # Luna uses this name when addressing the user
# No auth required — backend binds to localhost only
HOST=127.0.0.1
PORT=8899Personal-only features
- Away detection — farewell phrases trigger the away screen.
- Companion check-in — LLM-generated messages after 25–180 quiet minutes.
- State-aware proactive — morning brief, late-night nudge, back-from-work greeting.
- Commitment follow-up — remembers interviews/exams and asks how they went.
- Spotify control — play, pause, queue via voice or chat.
- Desktop automation — launch apps, take screenshots, control volume/brightness.
Business variant
Designed for team deployments — multiple users sharing a Luna instance, with JWT authentication and a professional persona anchored to the organisation.
LUNA_VARIANT=business
# Required for multi-user auth
JWT_SECRET=your-long-random-secret-here
JWT_EXPIRY_HOURS=720 # 30 days
# Rate limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_PER_MINUTE=60
RATE_LIMIT_BURST=20
# Organisation persona
BUSINESS_NAME=Acme Corp
BUSINESS_DESCRIPTION=a SaaS company building developer tools
BUSINESS_TONE=professional # professional | friendly | technical | concise
LUNA_NAME=Luna
# Production database recommended
DB_URL=postgresql+psycopg2://user:pass@db.example.com:5432/luna
DB_POOL_SIZE=10
DB_MAX_OVERFLOW=20
# Bind to all interfaces (behind a reverse proxy)
HOST=0.0.0.0
PORT=8899Business-only features
- JWT authentication — every API request must carry a valid bearer token.
- Per-user memory isolation — facts, personality state, and conversation history are scoped to the authenticated user.
- Rate limiting — per-user per-minute limits with burst allowance.
- Professional tone floor — personality RL drift is bounded so Luna never becomes overly casual.
- Organisation context — system prompt includes business name, description, and tone.
Authentication (JWT)
In the business variant, the backend generates JWT tokens signed withJWT_SECRET. All protected endpoints require:
Authorization: Bearer <token>Generate a token
curl -X POST http://localhost:8899/api/auth/token \
-H "Content-Type: application/json" \
-d '{"username": "alice", "password": "..."}'
# Response:
{ "access_token": "eyJ0eXA...", "token_type": "bearer" }Token configuration
| Env var | Default | Description |
|---|---|---|
JWT_SECRET | (empty) | HMAC secret for signing tokens. Set a long random string in production. |
JWT_EXPIRY_HOURS | 720 | Token lifetime in hours (default: 30 days). |
JWT_SECRET is empty in business variant, the backend will refuse to start — authentication without a secret is insecure.Rate limiting
Rate limiting uses a sliding-window algorithm per authenticated user. Requests over the limit receive 429 Too Many Requests.
| Env var | Default | Description |
|---|---|---|
RATE_LIMIT_ENABLED | false | Enable/disable rate limiting. |
RATE_LIMIT_PER_MINUTE | 60 | Max requests per user per minute. |
RATE_LIMIT_BURST | 20 | Additional burst allowance above the per-minute limit. |
Persona configuration
Both variants support persona customisation:
| Env var | Personal default | Business default | Description |
|---|---|---|---|
LUNA_NAME | L.U.N.A. | L.U.N.A. | The name Luna introduces herself as. |
USER_NAME | friend | user | How Luna addresses the user (personal only). |
BUSINESS_NAME | — | (empty) | Organisation name injected into the system prompt. |
BUSINESS_DESCRIPTION | — | (empty) | One-sentence org description for context. |
BUSINESS_TONE | — | professional | professional | friendly | technical | concise |
LUNA_NAME=Aria
BUSINESS_NAME=Meridian Labs
BUSINESS_DESCRIPTION=a biotech company developing AI-assisted diagnostics
BUSINESS_TONE=technicalSwitching variants
- Change
LUNA_VARIANTin.env. - If switching from personal → business, set
JWT_SECRETand optionally add.env.businessfor variant-specific overrides. - Restart the backend:
python -m backend.main. - Run
alembic upgrade headif switching to a new database.
.env.personal, .env.business) are loaded on top of .env when the matching variant is active. Put variant-specific overrides there to avoid cluttering the base .env.