# Copy to .env and fill in: cp .env-template .env # script/server loads .env automatically; with Docker pass it via `docker run --env-file .env` # (so keep values unquoted). .env is git-ignored and excluded from the image; never commit it. # --- Server --------------------------------------------------------------- # PORT=3000 # --- Gitea OAuth login ------------------------------------------------------ # Register an OAuth2 application in Gitea (Settings > Applications) as a confidential # client, with the redirect URI /auth/callback GITEA_URL=https://gitea.example.com GITEA_CLIENT_ID= GITEA_CLIENT_SECRET= # Public URL of this app, as the browser reaches it. Use https in production: that also # marks the session cookie Secure. BASE_URL=http://localhost:3000 # Signs and encrypts session cookies. Generate with: openssl rand -hex 32 # Unset = a random key per start, so everyone is logged out on every restart. SESSION_SECRET= # --- Admin API -------------------------------------------------------------- # Comma-separated Gitea usernames allowed to use the admin API (POST /nrepl). # Empty = nobody can. ADMIN_USERS= # --- Database ----------------------------------------------------------------- # The one SQLite file the app keeps everything in: the todos, and the MCP server's OAuth state # (registered clients, and the hashed tokens of every client a user has approved). The directory # is created on first use. In Docker, mount a volume here or the todos vanish with the container # and every MCP client has to be approved again after a restart. BASE_URL (above) must be the # address MCP clients use. # DB_PATH=data/tbd.db # --- Agent (LM Studio, via LangChain4j) --------------------------------------- # The agent (tbd.agent.core, a LangChain4j streaming chat model) talks to an LM Studio server. # Two surfaces use it: the chat widget in the UI (the green square, bottom left; POST /api/chat, an # AG-UI run, logged-in users only) and the A2A server for other agents (POST /a2a, the same OAuth # access token as /mcp; see /.well-known/agent-card.json). Point it at your LM Studio instance: # server address, no /v1 (start its server, and enable "Serve on Local Network" for a remote one). # Unset LMSTUDIO_URL = both surfaces answer "not configured". # LMSTUDIO_URL=http://192.168.1.20:1234 # Model id to ask for; empty = whichever model LM Studio has loaded. # LMSTUDIO_MODEL= # Only if the server requires an API key. # LMSTUDIO_API_KEY= # Seconds the LLM server may stay silent (before it starts answering, or between two pieces of the # reply) before giving up (default 120). # LMSTUDIO_TIMEOUT_SECONDS=120 # Every turn (chat or A2A) is summarised in the server's log. To also keep the model's full # reasoning and replies, and the messages it was sent (for finding out why it loops), append a JSON # trace to this file. It holds what users typed, so keep it private. # CHAT_TRACE_FILE=data/chat-trace.jsonl # --- WebMCP origin trial ------------------------------------------------------ # Chrome's WebMCP (the UI's browser tools) is in an origin trial. Register BASE_URL's origin at # https://developer.chrome.com/origintrials and paste the token here: the server then sends it # as an `Origin-Trial` header on the page, and Chrome turns WebMCP on for everyone who opens it. # The token is only good for that exact origin (scheme, host and port), and expires with the trial. # Empty = no header (in Chrome you can still enable chrome://flags/#enable-webmcp-testing). WEBMCP_ORIGIN_TRIAL_TOKEN= # --- nREPL debug server (POST /nrepl) ----------------------------------------- # nREPL has no authentication and evaluates arbitrary code, so it listens on loopback # only by default. Changing NREPL_BIND to 0.0.0.0 exposes it to the network: only do that # behind a firewall or an SSH tunnel. # NREPL_PORT=7888 # NREPL_BIND=127.0.0.1