Documentation Index
Fetch the complete documentation index at: https://docs.second.so/llms.txt
Use this file to discover all available pages before exploring further.
Production checklist
Before going live, make sure you have:-
SECOND_AUTH_MODE=externalwith a working auth provider extension - External auth provider syncs workspace memberships, roles, and invitations
-
MONGODB_URIpointing to a managed MongoDB instance (must support replica sets for app data Change Streams) -
SECOND_PUBLIC_URLset to your public HTTPS origin - If using OAuth integrations, provider OAuth apps use
${SECOND_PUBLIC_URL}/api/oauth/callbackas the redirect URI - At least one supported runtime installed for the worker:
claude,codex, oropencode - If enabling OpenCode, use a version whose
opencode run --helpincludes--format json - Runtime provider credentials configured for the runtime(s) you enable
-
REDIS_URLpointing to a Redis instance -
INTERNAL_API_TOKENset to the same strong secret on both web and worker - HTTPS termination via a trusted reverse proxy
- MongoDB and Redis access restricted to your application network
Need help with a secure production rollout, runtime/provider setup, cost
management, or support? Contact sales@second.so.
Environment variables
Web (apps/web)
SECOND_POSTHOG_TOKEN is a PostHog project token, not a private API key.
Product analytics are enabled by default after onboarding in anonymized mode,
and events are forwarded by the web app’s same-origin analytics endpoint. If
you do not want a deployment to send PostHog analytics, set
SECOND_POSTHOG_DISABLED=1 or SECOND_TELEMETRY_DISABLED=1.
SECOND_SENTRY_DSN and NEXT_PUBLIC_SENTRY_DSN are public Sentry DSNs, not
private API keys. Error reporting is enabled by default with masked replay on
error only. If you do not want a deployment to send Sentry error reports, set
SECOND_SENTRY_DISABLED=1, SECOND_ERROR_REPORTING_DISABLED=1, or
SECOND_TELEMETRY_DISABLED=1. Source-map upload requires a private
SENTRY_AUTH_TOKEN in CI; never commit that token.
With anonymization on, Second forwards personless events only and strips user,
workspace, app, prompt, and URL identifiers. Anonymized events share a stable
local anon_... ID so they can be grouped in PostHog without being linked to
the user’s person profile. Users can turn anonymization off from the workspace
account menu. With anonymization off, Second sends a dedicated PostHog
$identify event for that onboarded user and then forwards product events with
the non-anonymized event properties. See Product analytics
for the full capture and privacy model.
Worker (apps/worker)
INTERNAL_API_TOKEN authenticates internal web↔worker calls. The worker uses it for web internal APIs (tool execution, agent completion callbacks, app data reads/writes), and the web server uses it when calling the worker HTTP API. Use a strong random secret and set the same value on both services.
The worker must never pass INTERNAL_API_TOKEN, MongoDB URLs, Redis URLs, WorkOS secrets, cookies, headers, or integration secret values into CLI runtimes. Codex CLI and OpenCode are launched with an allowlisted environment plus private per app/run HOME and config directories. The only token they receive for Second tools is a short-lived scoped MCP broker token. Codex receives an OpenAI key through app-server login instead of through the spawned process environment, and Codex shell commands get a separate shell HOME plus key/token/secret environment exclusions. In local Codex login mode, the private Codex home is seeded with only the local Codex auth.json; production deployments should prefer explicit provider keys, and local auth seeding is disabled by default under NODE_ENV=production.
Claude Code runs with subprocess environment scrubbing enabled by default. On
Linux workers, that requires the bubblewrap package (bwrap executable). Keep
it installed in custom worker images. CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=0 is an
explicit escape hatch only for externally isolated workers that accept Claude
subprocesses not getting Claude’s inner env scrubber.
Codex’s Linux workspace-write sandbox can fail inside containers when the host blocks the namespace or bwrap operations it needs. In production, Second treats the worker/container environment as the external sandbox for normal Codex build runs and sends Codex danger-full-access when the selected runtime setting is workspace-write. Local development still uses Codex workspace-write.
For local development only, Codex builder sessions keep a warm codex app-server
process per app/runtime session to reduce repeated startup cost. This is disabled
under NODE_ENV=production, does not apply to app-agent runs, and can be turned
off locally with SECOND_CODEX_APP_SERVER_WARM=0.
Redis is required for collaborative streaming and workspace coordination. It
backs live stream resume/replay, run events, and workspace event invalidations.
OAuth also uses Redis for short-lived OAuth state and single-flight refresh
locks so concurrent tool calls do not stampede the provider token endpoint.
SECOND_PERF_TRACE=1 can be enabled temporarily during incident diagnosis. It
logs route names, request IDs, elapsed timings, counts, CPU, and memory. It does
not log prompts, source files, cookies, tokens, headers, or secret values, but
it adds log volume and should normally stay off.
OAuth integrations
Self-hosted and on-prem deployments use customer-owned OAuth apps. Second does not require WorkOS Pipes, Pipedream, Composio, or any hosted OAuth broker for this path. The enterprise Gmail/Calendar setup flow is:- The builder declares OAuth metadata in
agents.jsonandintegration-setup.json: provider key, authorization URL, token URL, exact scopes, and API endpoint. - A workspace admin opens Settings → Integrations and copies Second’s redirect
URI, usually
https://your-domain.example/api/oauth/callback. - The customer’s Google Workspace admin creates a Google Cloud OAuth client for that deployment. For internal Workspace use, configure the consent screen as Internal and add the listed Gmail/Calendar scopes.
- The admin pastes the OAuth client ID and client secret into Second. In
production with WorkOS configured, the client secret is stored in WorkOS
Vault. Without WorkOS Vault, Second requires
SECOND_TOKEN_ENCRYPTION_KEYand stores an encrypted local reference. - Each end user clicks Connect for that provider. Second redirects to the
provider, receives an authorization code at
/api/oauth/callback, exchanges it server-side, and stores the refresh token through the same secret-store adapter. - When an app agent uses an OAuth tool,
/api/internal/tool-executeresolves the triggering user from the app-agent run, checks the connected account and scopes, refreshes the access token on demand if needed, injects the bearer token server-side, and calls the provider API.
SECOND_TOKEN_ENCRYPTION_KEY or a
generated gitignored key in .second-dev/; changing OAuth client credentials in
the UI does not require restarting the service.
When manually testing OAuth in local development, run Second on a plain
loopback origin instead of the portless *.second.localhost dev URL:
http://localhost:4198/api/oauth/callback. Providers such as Google only grant
special HTTP redirect handling to loopback hosts like localhost or
127.0.0.1; they do not treat generated *.second.localhost hosts as loopback
OAuth redirect URIs.
Portless is only a developer convenience for npm run dev. It is not used by
npm run start, npm run release, on-prem deployments, or the packaged
npx --yes @second-inc/cli local runtime. The CLI should use the same plain loopback
shape for OAuth-capable local runs.
Running with Docker Compose
Option A — build from source:Why WORKER_URL is required
The Next.js web server calls the worker over HTTP for agent operations and live workspace reads (for example /sessions/:appId/messages, /sessions/:appId/status, /sessions/:appId/files).
WORKER_URL must resolve from the web runtime to the worker runtime over your private network.
Architecture in production
Capacity and scaling
The application code is designed so the web tier can scale horizontally behind a load balancer: durable state lives in MongoDB, live coordination lives in Redis, and every route still authorizes by workspace before returning data. Workspace realtime uses one workspace event subscription per browser profile when BroadcastChannel/Web Locks are available, and settings pages use projected read models instead of loading large app source snapshots on navigation. The shipped local/Docker Compose setup does not autoscale. If you deploy to Kubernetes, node autoscaling and pod autoscaling are separate concerns. Managed clusters such as GKE Autopilot can add nodes for schedulable pod requests, but they do not automatically create more web pods unless your deployment defines more replicas or an HPA/KEDA policy. A single web pod may handle small teams, but production deployments should set explicit web replicas or autoscaling targets based on request latency, CPU, memory, and streaming connection count. Worker scaling needs more care than web scaling. The worker keeps active agent SDK sessions in memory, while durable run messages and source snapshots are saved through the web layer. Additional worker replicas can improve capacity, but active sessions, workspace filesystem persistence, and load-balancer routing need to be planned for the deployment model.Security notes
- Never use
SECOND_AUTH_MODE=noneon the public internet. See Authentication for details on external mode. - Production collaboration depends on the external provider mapping accepted invitations into Second’s
users,workspace_memberships, and defaultGeneralteam membership. Unknown external roles should not grant elevated access. ANTHROPIC_API_KEY,OPENAI_API_KEY, andINTERNAL_API_TOKENare secrets — treat them accordingly and never commit them to source control.- The worker’s HTTP API (
/sessions/*) should not be exposed publicly — only the web app should be able to reach it. - The worker’s scoped MCP route (
/mcp/*) is for CLI runtimes only. It requires a per-run bearer token and should still stay on an internal network. - Internal API endpoints (
/api/internal/*) bypass the browser auth proxy and rely onINTERNAL_API_TOKENfor authentication. Missing tokens fail closed in production. Keep these on an internal network. - Make sure your reverse proxy forwards and sanitizes headers correctly (
X-Forwarded-For,X-Forwarded-Proto).