evOo-local/.env.example
cynarAI 4c619b25b9
fix(api): keep the onboarding scan off the host unless the owner opts in (#98)
* fix(api): keep the onboarding scan off the host unless the owner opts in

The first-run scan walked the home directory of the API process user for
every account that signed up, with no consent, and its status endpoint
accepted any scan id. Any account could read the repository paths and
remotes of the server owner.

The scan now stays simulated unless the deployment configures
ONBOARDING_SCAN_HOME and the requesting account is the deployment owner;
the previous HOME fallback is gone. Scan records remember who started
them and the status endpoint answers only that account.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

* ci: give each runner on a shared host its own E2E ports

Two Linux runners live on the same host. Every Web E2E job first frees
"stale" listeners on the fixed ports 3110, 3111, 5180 and 4321 with
fuser -k, so a job starting on the second runner killed the Vite server
of the job already running on the first one. The victim died with
SIGKILL (exit 137), and its remaining tests failed on reloads that could
no longer reach a server. This is what made the Web E2E run on this PR
red, and it explains the recurring reload failures on main whenever two
runs overlapped.

The workflow now derives API_PORT, WEB_PORT and WWW_PORT from the runner
name (suffix -N means an offset of N times 100), and the port-freeing
script follows those settings instead of a hard-coded list. The harness,
Vite, the API and the marketing site already read these variables.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
(cherry picked from commit 2f05ab6a817e5198e2a740706976108b7b1edde1)

* test(testkit): cover the env-driven E2E ports instead of a fixed list

The ports test parsed a hard-coded array that no longer exists. It now
runs the script on a fake host whose fuser finds nothing, and checks the
default ports, the per-runner override, the rejection of a non-numeric
setting, and that the workflow picks the ports before freeing them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
(cherry picked from commit cd0aedafcdec1b24dea924d8924b3b29af29d598)

* ci: keep every runner off the default E2E ports

Branches that still free the fixed ports 3110, 3111, 5180 and 4321 keep
running on the shared host (main, the nightly run, other open PRs) until
they carry the per-runner ports. A job of ours on the runner without a
numeric suffix used exactly those defaults, so such a neighbour could
still kill it, and its own port freeing could kill the neighbour. The
unsuffixed runner now starts at offset 100, so no job from this workflow
touches the defaults in either direction.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
(cherry picked from commit ddf5febdb3072425442ef375ca0be63fbe09e7f1)

* fix(web): recover lazy routes and overlays from a failed module fetch

When the browser aborts a module request while a page loads, the lazy
import rejects and the route throws, leaving a blank page. Chromium does
this whenever a network interface appears or disappears mid-request
(net::ERR_NETWORK_CHANGED), which happens on a laptop that changes
networks and on the CI host every time a container starts or stops. A
Web E2E run hit exactly this: two modules of the onboarding page failed
to load and the suite waited on an empty document.

Lazy routes and overlays now load through a helper that retries the
import once and, when the browser keeps rejecting it from its module
map, reloads the page a single time before surfacing the error.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
(cherry picked from commit d5dacb12f14380071ef14ba8845d18c9d39e3fac)

* fix(web): reload once when the module graph is aborted while booting

The lazy-route recovery cannot help when the browser aborts a module of
the static import graph: nothing of the app runs, the root stays empty,
and the only trace is a console error. A Web E2E run hit this right
after a page reload, when four modules of the shell failed with
net::ERR_NETWORK_CHANGED and the shell never rendered.

An inline script in the HTML entry now listens for the error event of
the module script and reloads the page once, guarded by a session flag
that the entry module clears after a successful boot. The existing
preload recovery stays in place; it only covers Vite's chunk preloads of
production builds.

A new E2E spec aborts one module request during boot and one lazy route
request, and expects the sign-in page to render in both cases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
(cherry picked from commit e5b7e036e1a60e7e270592c4b9382ee23ed45ccb)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-02 21:48:46 +02:00

239 lines
12 KiB
Plaintext

NODE_ENV=development
# Compose Postgres role (source-checkout and images stacks). Generate with:
# openssl rand -hex 16
# Use a URI-safe value (hex from openssl is fine). Special characters such as
# @ : / ? # % break DATABASE_URL interpolation inside Compose.
# Put the same value in DATABASE_URL when using the optional host publish overlay.
# Existing pgdata volumes keep user/password/db from first init; keep those
# values in .env, or ALTER ROLE / rename in place. Recreate the volume only
# after a backup (or when disposable); down -v deletes all Postgres state.
POSTGRES_USER=rakazo
POSTGRES_PASSWORD=
POSTGRES_DB=rakazo
# Host-side clients only (pnpm db:migrate / pnpm dev). Requires the postgres-host
# overlay so 127.0.0.1:5433 is published. Inside Compose, api/worker ignore this
# and connect to the postgres service with POSTGRES_* above.
DATABASE_URL=postgres://rakazo:REPLACE_WITH_POSTGRES_PASSWORD@127.0.0.1:5433/rakazo
BETTER_AUTH_SECRET=replace-with-32-plus-character-secret
# Local loopback: localhost and 127.0.0.1 are interchangeable browser hosts.
BETTER_AUTH_URL=http://127.0.0.1:5173
API_URL=http://127.0.0.1:3100
# Listener address. Keep loopback unless a container or trusted reverse proxy must reach the API.
API_HOST=127.0.0.1
WEB_ORIGIN=http://127.0.0.1:5173
PUBLIC_POSTHOG_KEY=
PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
SIGNUPS_ENABLED=true
SIGNUP_ALLOWLIST=
# First-run discovery. Leave unset and the onboarding scan never touches the host filesystem.
# Set a home-style directory (its Projects, Documents, src, ... subfolders are scanned) to let the
# deployment owner's own onboarding discover local projects. Other accounts never scan the host.
# ONBOARDING_SCAN_HOME=
ENCRYPTION_KEY=replace-with-64-random-hex-characters
DATA_DIR=./data
SANDBOX_SUPERVISOR_URL=http://127.0.0.1:7091
# Dedicated credentials (distinct from BETTER_AUTH_SECRET). Required for Docker sandboxes /
# screen proxy outside local development.
SANDBOX_SUPERVISOR_TOKEN=replace-with-32-plus-character-supervisor-token
# Per-bot-computer ceilings. Defaults 2g / 2 CPUs / 2048 pids; "0", "none" or "unlimited" removes
# one. Memory also caps swap, and must be at least 6m (Docker's own minimum).
RAKAZO_COMPUTER_MEMORY=
RAKAZO_COMPUTER_CPUS=
RAKAZO_COMPUTER_PIDS_LIMIT=
SCREEN_PROXY_SECRET=replace-with-32-plus-character-screen-proxy-secret
SANDBOX_PROVIDER=docker
# none | emulator | cursor. Live agents require a key and an explicitly authorized Space.
CLOUD_AGENT_PROVIDER=none
CURSOR_API_KEY=
# Grant this Space access to the repositories connected to the Cursor credential.
CLOUD_AGENT_SPACE_ID=
# Optional Docker daemon socket. Defaults to /var/run/docker.sock (Unix) or
# //./pipe/docker_engine (Windows). Set for rootless Docker, Colima, or custom paths.
# DOCKER_HOST takes precedence over DOCKER_SOCKET when both are set.
# DOCKER_SOCKET=
# Enable for a host-run supervisor using a local Docker Desktop daemon: control
# uses a random token-guarded 127.0.0.1 port because container IPs are unreachable.
# Leave unset for Compose, remote Docker daemons, or native Linux Docker Engine.
# Changing this setting replaces incompatible computers on their next provision;
# their mounted homes are preserved.
# SANDBOX_CONTROL_VIA_LOOPBACK=true
DAYTONA_API_KEY=
DAYTONA_API_URL=
DAYTONA_TARGET=
BOX_API_KEY=
# Optional Box API base URL; defaults to https://ascii.dev/api/box/v1.
BOX_API_URL=
AGENT_RUNTIME=pi
# Opt-in Pi JSONL session recording under DATA_DIR/pi-sessions. Default off:
# transcripts are unencrypted on disk and unsuitable for multi-tenant hosted DATA_DIR.
# PI_SESSION_RECORDING=true
WAKEUP_DRIVER=graphile
# Keep app-owned PostgreSQL pools bounded. Leave DB_POOL_MAX unset so each
# process keeps its default (api 4, worker 8). A set value applies to both —
# size it for the worker (concurrency + LISTEN + leadership + Prisma headroom).
# DB_POOL_MAX=8
GRAPHILE_WORKER_CONCURRENCY=4
# Pause or stop computers after this many idle ms. Minimum 30000.
SANDBOX_IDLE_MS=600000
# Comma-separated model ids on centrally managed local / openai-compatible endpoints that accept images.
# User-connected OpenAI-compatible models use the per-connection Supports images setting instead.
# Declared ids get input ["text","image"] so screenshot computer tools stay available.
RAKAZO_LOCAL_VISION_MODELS=
RAKAZO_OPENAI_COMPATIBLE_VISION_MODELS=
SANDBOX_COMMAND_TIMEOUT_MS=300000
# Optional limit on active bot desktops per Docker Team Computer.
# 0 leaves capacity to the machine; desktops and Chrome start only when needed.
SANDBOX_TEAM_SCREEN_LIMIT=0
# Optional limit on total concurrent bot computer containers per space in Docker sandboxes.
# 0 (or unset) means unlimited. Set a positive integer to cap containers per space.
SANDBOX_MAX_COMPUTERS_PER_SPACE=0
# Optional per-turn tool-call fuse for the Pi agent runtime. Unset, empty, or 0
# means unlimited (default). Set a positive integer to soft-stop a turn that
# exceeds the budget and still emit a final assistant message.
MAX_TOOL_CALLS_PER_TURN=
# Deployment-wide fallback model. PI_DEFAULT_PROVIDER picks which key below is used.
OPENROUTER_API_KEY=
ANTHROPIC_API_KEY=
PI_DEFAULT_PROVIDER=openrouter
# Vision-capable default for chat and computer screenshots.
# Computer-use tests can override this with COMPUTER_E2E_MODEL.
PI_DEFAULT_MODEL=openai/gpt-5.6-luna
# Optional local OpenAI-compatible models. Leave blank to disable the provider.
RAKAZO_LOCAL_MODELS=
RAKAZO_LOCAL_MODELS_URL=http://127.0.0.1:11434/v1
RAKAZO_LOCAL_CONTEXT_WINDOW=32768
RAKAZO_LOCAL_MAX_TOKENS=4096
# Allow user-connected OpenAI-compatible endpoints on public hostnames (default: private/loopback only).
RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC=
E2B_API_KEY=
COMPOSIO_API_KEY=
# Optional integrations.sh-compatible catalog base URL (Rakazo appends api/search).
# Example: https://integrations.sh/ or a self-hosted mirror base, not the full search endpoint.
INTEGRATIONS_CATALOG_URL=
# Optional alternative managed connector provider.
PIPEDREAM_CLIENT_ID=
PIPEDREAM_CLIENT_SECRET=
PIPEDREAM_PROJECT_ID=
PIPEDREAM_ENVIRONMENT=development
# Optional messaging surface (Chat SDK). Each platform mounts when its full
# credential set is present; webhooks arrive at /api/v1/messaging/webhook/<provider>.
# Users link a chat app to their account from the web (Messaging settings).
# Set true to instead let unknown senders auto-provision their own chat-only
# accounts (their runs bill the deployment model key):
MESSAGING_OPEN_SIGNUP=false
# Sendblue (iMessage/SMS — all four required):
SENDBLUE_API_KEY_ID=
SENDBLUE_API_SECRET=
SENDBLUE_SIGNING_SECRET=
SENDBLUE_PHONE_NUMBER=
# Slack (both required). Channels and rooms use the shared messaging team-chat
# path when TEAM_CHAT_BOT_ID is set; Slack is one adapter, not a separate stack.
SLACK_BOT_TOKEN=
SLACK_SIGNING_SECRET=
# Gateway bot for provider-neutral team/external rooms (Slack, later Teams, etc.).
# Alias SLACK_RAKAZO_BOT_ID is accepted for older configs.
TEAM_CHAT_BOT_ID=
# Optional separate judge model for ambient room replies (both or neither):
TEAM_CHAT_JUDGE_PROVIDER=
TEAM_CHAT_JUDGE_MODEL=
# WhatsApp Business Cloud (all four required):
WHATSAPP_ACCESS_TOKEN=
WHATSAPP_PHONE_NUMBER_ID=
WHATSAPP_APP_SECRET=
WHATSAPP_VERIFY_TOKEN=
# Telegram (both required). Inbound is webhook-only at
# /api/v1/messaging/webhook/telegram with header X-Telegram-Bot-Api-Secret-Token;
# the worker must not long-poll getUpdates.
TELEGRAM_BOT_TOKEN=
TELEGRAM_WEBHOOK_SECRET_TOKEN=
# Feishu/Lark (app id, secret, and verification token required). Inbound is
# webhook-only at /api/v1/messaging/webhook/lark — do not enable long
# connection in the developer console or the HTTP webhook will miss events.
# Create a custom app with the Bot capability, subscribe to im.message.receive_v1,
# and set the event subscription URL to that path. LARK_DOMAIN=feishu uses
# open.feishu.cn (default); lark uses open.larksuite.com. Encrypt key is
# optional unless event encryption is enabled in the console.
LARK_APP_ID=
LARK_APP_SECRET=
LARK_VERIFICATION_TOKEN=
LARK_ENCRYPT_KEY=
LARK_DOMAIN=
SUPERMEMORY_API_KEY=
# Optional. Defaults to https://api.supermemory.ai. Self-hosted: http://localhost:6767
SUPERMEMORY_API_URL=
# Transactional account email. Works with Resend, Amazon SES, or any SMTP server.
SMTP_URL=
EMAIL_FROM=
# Local development only: capture password-reset email and print its link to the API console.
EMAIL_EMULATOR=
VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=
# Structured logs to stdout (json in production, pretty in development unless overridden).
LOG_LEVEL=info
LOG_FORMAT=
# Optional Axiom ingest. Needs both token and dataset. Failures never affect the product.
AXIOM_TOKEN=
AXIOM_DATASET=
# Optional region: AXIOM_EDGE is a hostname (eu-central-1.aws.edge.axiom.co).
# AXIOM_EDGE_URL is an https URL and wins when both are set.
AXIOM_EDGE=
AXIOM_EDGE_URL=
# Optional. GET /health returns this as `revision` so you can confirm the deployed commit.
GIT_SHA=
# --- Compose production deployments ---
# Absolute path of this checkout as the Docker *daemon* sees it. Production Compose defaults to
# /srv/rakazo on Linux; override it for any other host path so the updater bind mount resolves.
# Linux override: RAKAZO_DEPLOY_DIR=/opt/rakazo
# Docker Desktop on Windows (C:\Users\you\rakazo):
# RAKAZO_DEPLOY_DIR=/run/desktop/mnt/host/c/Users/you/rakazo
RAKAZO_DEPLOY_DIR=
# Compose files the updater reconciles, in overlay order, separated like Compose's own
# COMPOSE_FILE (":" unless COMPOSE_PATH_SEPARATOR says otherwise). Leave unset for the base stack.
# RAKAZO_COMPOSE_FILE=infra/compose/docker-compose.prod.yml:ops/compose/overlay.yml
RAKAZO_COMPOSE_FILE=
# Extra services an update pulls, recreates and rolls back, comma separated. Name any overlay
# service built from the application image, or it keeps running the previous code after an update.
# Appended to api, worker, web; the updater itself is refused.
# RAKAZO_UPDATE_SERVICES=supervisor
RAKAZO_UPDATE_SERVICES=
# Published images (defaults match this repository). Tag `local` is built from the checkout.
RAKAZO_IMAGE=ghcr.io/elie222/rakazo/app
RAKAZO_IMAGE_TAG=local
RAKAZO_IMAGE_TAG_PREVIOUS=
RAKAZO_UPDATER_IMAGE=ghcr.io/elie222/rakazo/updater
RAKAZO_UPDATER_IMAGE_TAG=local
# Required only when starting the opt-in `updater` Compose profile; the sidecar refuses to start
# without it. Use a dedicated random value, not BETTER_AUTH_SECRET,
# SANDBOX_SUPERVISOR_TOKEN, or SCREEN_PROXY_SECRET;
# ≥32 chars in production. Leave empty when the sidecar is disabled.
RAKAZO_UPDATER_URL=
RAKAZO_UPDATER_TOKEN=
# --- evOo Mission Control (Cockpit on port 3088) ---
PORT=3088
EVOO_COCKPIT_HOST=127.0.0.1
# Required when EVOO_COCKPIT_HOST binds beyond loopback (e.g. 0.0.0.0 for LAN/Tailscale).
# Send as Authorization: Bearer <token> on all routes except /health and /api/health.
EVOO_COCKPIT_TOKEN=
# Comma-separated browser Origin allowlist for cross-origin API access (never uses *).
# Loopback binds default to http://127.0.0.1:5173 and http://localhost:5173 when unset.
# Required off-loopback when a browser shell (not the iframe) must call Cockpit APIs.
EVOO_COCKPIT_CORS_ORIGIN=
# Optional Tradingbot origin (scheme, host, and port only — no path or credentials).
# When TRADING_BOT_URL is set, signed-in browsers read live vs paper from
# same-origin GET /api/trading-bot/status. Leave unset for SaaS; evOo runs without a bot.
# TRADING_BOT_URL=http://127.0.0.1:8000
# Optional build-time override. When set, the web app polls
# ${VITE_TRADING_BOT_URL}/bot/api/status directly. That call is cross-origin, so the
# bot must allow this web origin. Leave unset to use the API proxy above.
# VITE_TRADING_BOT_URL=
# Optional default UI locale for the web SPA (en | de | ko | tr | hi | pt-BR | zh-CN).
# Overridden by localStorage key rakazo.uiLocale when the user picks a language in Settings.
# VITE_DEFAULT_UI_LOCALE=en
# Expo: English or Simplified Chinese (en | zh-CN). Overridden by SecureStore key
# rakazo.uiLocale from Account → Language.
# EXPO_PUBLIC_DEFAULT_UI_LOCALE=en