03 / Develop · Shell · Configure

Configure the Shell ​

Almost all of the Shell's behaviour is gated by environment variables. The required trio (APP_ENV_JWT_SECRET, APP_ENV_DATABASE_URL, APP_ENV_REDIS_URL) is set when you run it; everything here is opt-in. Each block is paste-ready with its defaults — drop it into api/.env, fill the blanks, and restart the API. The fully-annotated master is api/.env.example.

Sign-in providers ​

The Shell is provider-agnostic — methods live in the identity_providers table (no admin UI yet). Local password login is on by default (seeded). Google and Keycloak each upsert their provider row on boot once you set their vars and restart:

bash
# Google — auto-creates the `google` provider; scope defaults to: openid profile email
APP_ENV_GOOGLE_CLIENT_ID=
APP_ENV_GOOGLE_CLIENT_SECRET=

# Keycloak — overrides the seeded row (which ships a `change-me` secret)
APP_ENV_KEYCLOAK_URL=http://localhost:18080
APP_ENV_KEYCLOAK_REALM=veni-ai
APP_ENV_KEYCLOAK_CLIENT_ID=veni-ai-platform
APP_ENV_KEYCLOAK_CLIENT_SECRET=                                        # required — Keycloak admin → Clients → Credentials
APP_ENV_KEYCLOAK_REDIRECT_URI=http://localhost:3000/api/auth/callback

Every OAuth provider redirects back to {API_URL}/api/auth/callback; register that URI at the provider, then trigger login with GET /api/auth/login?provider=<id>. Remote-app login also needs the RSA key so satellites can verify Shell tokens.

For a generic OIDC provider there's no env bootstrap — add a row to api/scripts/seed-data/development/identity-providers.csv and make seed:

csv
myoidc,oauth2,<client-id>,<client-secret>,<issuer>/authorize,<issuer>/token,"{""scope"":""openid profile email""}",001_ACTIVE

Provider gotchas

  • Seeding is insert-only — it won't update an existing provider. Env bootstrap (Keycloak/Google) does update on each boot; otherwise edit the DB.
  • No OIDC discovery / JWKS — set authUrl and tokenUrl manually; the ID token is decoded, not locally verified.
  • type is always oauth2 — SAML / LDAP are not implemented.

Providers are also per-organization

Everything above is platform-wide. On top of it, each organization can bring its own identity provider: an org-admin configures it in Organization settings (PUT /api/auth/me/organization), which writes an org-scoped row into the same identity_providers table under the convention {type}-org-{organizationId} (e.g. keycloak-org-…, google-org-…, legacy sso-{slug}). Org-scoped rows are resolved per-org and never appear in the platform-wide provider list.

The business rule this drives: on an organization's portal / vanity domain, that org's IdP governs login — it overrides whatever provider the request asked for, because login must follow the org's config, not the caller's. The org is matched from the request host (resolveByHost); an org with no active SSO provider falls back to password sign-in ("This workspace uses password sign-in"). The org's domain and IdP live on the organizations row (portalDomain, idpProviderId).

Platform wiring ​

bash
APP_ENV_DEVELOPER_APPS=true                                        # Developer mode / My Apps (self-service)
APP_ENV_CORS_ORIGINS=http://localhost:5173,http://localhost:4175   # add every remote UI origin that calls the Shell
APP_ENV_HRM_GRPC_URL=http://localhost:3012/api                     # one line per remote you call over gRPC…
# APP_ENV_GRPC_URLS={"myapp":"http://localhost:3005/api"}          # …or a JSON map for the rest

Infrastructure & ops ​

bash
# Object storage (uploads)
APP_ENV_MINIO_ENDPOINT=http://localhost:19000
APP_ENV_MINIO_ACCESS_KEY=minioadmin
APP_ENV_MINIO_SECRET_KEY=minioadmin

# Email — invitations, password resets (blank USER/PASS = disabled)
APP_ENV_SMTP_HOST=smtp.gmail.com
APP_ENV_SMTP_PORT=587
APP_ENV_SMTP_USER=
APP_ENV_SMTP_PASS=
APP_ENV_SMTP_FROM=VENI-AI <noreply@venizia.ai>
APP_ENV_SMTP_SECURE=false                          # true for port 465
APP_ENV_INVITE_EXPIRY_HOURS=48

# Distributed tracing → Grafana Tempo (unset = off)
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

# Payments (optional)
# APP_ENV_STRIPE_SECRET_KEY=
# APP_ENV_STRIPE_PUBLISHABLE_KEY=
# APP_ENV_STRIPE_WEBHOOK_SECRET=

AI features ​

bash
APP_ENV_DASHBOARD_AI_BRIEFING_ENABLED=false        # streamed dashboard briefing; else the static greeting
# APP_ENV_ANTHROPIC_API_KEY=                        # required when the flag is true, or it auto-disables
# APP_ENV_LLM_PROVIDER=anthropic                    # which provider the AI services call: anthropic | openai | deepseek
# APP_ENV_OPENAI_API_KEY=                           # RAG embeddings + memory_search (embeddings always use OpenAI)

Missing a credential auto-disables the feature

Flags that need a key turn themselves off when it's absent rather than erroring — the AI briefing returns 404 and the UI falls back to the static greeting. Safe to flip a flag on now and add the key later.

Common snags ​

SymptomFix
A provider (Google / Keycloak) doesn't show on the login screenIts env vars are unset, or the API wasn't restarted — both upsert on boot.
An edited OIDC provider row doesn't take effectSeeding is insert-only — edit the DB, or clear and re-seed.
A flipped AI flag does nothingThe credential is missing — the feature auto-disables (a 404, not an error). Add the key and restart.

More run and database-startup symptoms are in Run → Common snags.

See also ​

  • Overview — what the Shell is and how it's built.
  • Run the Shell — get it up before turning features on.