Local Stack
Complete reference for the canonical machine-local JSON configuration.
The ignored config/local-stack.json file is the canonical machine-local configuration. The tracked
config/local-stack.example.json contains safe placeholders, and config/local-stack.schema.json
describes the same contract used by the launcher.
Top-level Fields
| Field | Purpose |
|---|---|
mode | development for bind-mounted watchers or production for built images |
persistence | Preserve named volumes or use disposable local data |
codex | Agent authentication mode and source |
workerReplicas | Number of task-worker service replicas |
ports | Host ports for infrastructure and applications |
postgres | Local database name and credentials |
garage | Local S3-compatible storage settings |
agentServiceToken | Backend authentication for trusted agent runtimes |
integrationCredentialKey | Encryption key for integration credential envelopes |
publicFrontendUrl | Optional public HTTPS product origin for browser returns and Vite |
googleOAuth | Optional Google client credentials and provider callback |
metaAdsOAuth | Optional deployment-level Meta OAuth client ID and provider callback |
xOAuth | Optional organic X OAuth 2.0 client credentials and provider callback |
xAdsOAuth | Optional X Ads OAuth 1.0a consumer credentials and provider callback |
metaAds | Meta Ads operational provider; defaults to direct Graph API tools |
Mode
development mounts source and runs watchers. production builds and runs the service-owned images.
This is an image/runtime mode, not a complete production deployment guide.
Persistence
Use persistent to keep named volumes across normal stop/start cycles. Use disposable behavior only
when losing local database, Redis, object-store, and Codex container state is intended.
Codex
| Field | Meaning |
|---|---|
authMode | local or api_key |
localHome | Host Codex home used only to locate auth.json in local mode |
apiKeyEnvironmentVariable | Name of the host variable read in API-key mode |
authSyncIntervalSeconds | Fallback local-auth scan interval; 10–86,400 seconds, default 60 seconds |
Local mode watches auth.json for immediate changes and uses authSyncIntervalSeconds only as a
fallback. One synchronizer writes validated newer generations into the shared container-owned
Codex state. Chat and task workers do not mount or scan the host credential themselves.
Ports
Each port is a host binding. Containers use Compose service names and their internal service ports. Do not reuse a host loopback URL inside a container.
PostgreSQL
Set a local user, password, and database. PostgreSQL with pgvector is the canonical operational store. Migrations run as a one-shot startup dependency.
Garage
Garage provides the supported local S3-compatible service. The application remains provider-neutral and consumes the S3 environment contract. Configure region, bucket, access key, secret key, RPC secret, admin token, and metrics token.
Workers and the backend use the Compose-only http://garage:3900 endpoint. This is sufficient for
Meta Ads, organic X, and X Ads media transfer: workers upload directly to Garage, then the backend
streams or chunks the private object to the provider. Garage does not need a public hostname or
CDN. A split deployment must set an S3 endpoint reachable from both worker and backend execution
networks. Production buckets should expire abandoned integration staging objects after 24 hours.
Integration Credential Key
The value must satisfy the schema’s base64 key contract. Changing it after credentials have been stored makes existing credential envelopes unreadable; disconnect or migrate integrations before rotating it.
Google OAuth
When googleOAuth is present, the generator writes the Google client values and callback URI.
googleOAuth.redirectUri is optional and must be the exact HTTPS callback ending in
/oauth/google/callback. When omitted, it falls back to the loopback backend callback generated
from ports.backend. Omit the whole block when Google Workspace is not needed.
Meta Ads OAuth and Operations
The generator always writes the generic local managed-MCP callback URI. Providers such as Apollo discover their authorization server, dynamically register the client, and retain this generic callback.
Meta Ads uses a deployment-level public OAuth client for connection and reconnection. Add its client ID when this deployment should offer Meta Ads:
{
"metaAdsOAuth": {
"clientId": "replace-with-meta-approved-oauth-client-id",
"redirectUri": "https://api.example.com/oauth/mcp/callback"
},
"metaAds": {
"operationsProvider": "graph"
}
}The Meta connection flow uses PKCE and does not require a client secret. The deployment client is shared infrastructure identity, not an ad-account credential: one configured client can authorize multiple organization integrations, while each organization keeps its own encrypted Meta token.
metaAdsOAuth.redirectUri is optional in the local-stack contract and falls back to the generic
loopback callback. Meta requires a public HTTPS callback for this client in normal local testing, so
set the exact public URL ending in /oauth/mcp/callback.
The Meta-specific callback takes precedence only for Meta. Apollo and other managed MCP providers
continue using MCP_OAUTH_REDIRECT_URI. The selected callback is stored with the one-use OAuth
state so authorization and token exchange use the identical URI even if deployment configuration
changes while consent is open.
Deployments that do not use the local-stack generator set META_ADS_OAUTH_CLIENT_ID and
META_ADS_OAUTH_REDIRECT_URI on the backend. Set META_ADS_OPERATIONS_PROVIDER=graph for Oblive’s
direct Marketing API provider. graph is also the default when the variable is absent.
hosted_mcp remains an explicit rollback mode; it restores hosted discovery and operations and may
again be limited by Meta’s account rollout.
In graph mode, the backend registers a fixed local MCP tool set and calls
graph.facebook.com/v25.0 with the encrypted organization token. Operational discovery, reads,
writes, reporting, and action execution do not contact mcp.facebook.com. The hosted endpoint is
used only when operationsProvider is explicitly hosted_mcp.
X And X Ads OAuth
The organic X and X Ads connectors are independent. Organic X uses OAuth 2.0 Authorization Code with PKCE. X Ads requires Ads API access and three-legged OAuth 1.0a; it does not reuse the organic X token or permission set.
{
"xOAuth": {
"clientId": "replace-with-x-oauth2-client-id",
"clientSecret": "replace-with-x-oauth2-client-secret",
"redirectUri": "https://api.example.com/oauth/mcp/callback"
},
"xAdsOAuth": {
"consumerKey": "replace-with-x-ads-consumer-key",
"consumerSecret": "replace-with-x-ads-consumer-secret",
"redirectUri": "https://api.example.com/oauth/mcp/callback"
}
}Both callback values are optional in the local-stack contract and fall back to the generic loopback
callback. Use the exact public HTTPS /oauth/mcp/callback registered with each X developer App for
normal external testing. For local X Ads authorization, approve
http://127.0.0.1:3000/oauth/mcp/callback in the X developer App before using the loopback default.
Set xOAuth.clientSecret for a Web App, Automated App, or Bot. Omit it for a Native App or SPA.
PKCE is used in both cases. The secret switches token exchange, refresh, and revocation to
confidential-client Basic authentication and remains backend-only.
Deployments that do not use the generator set X_OAUTH_CLIENT_ID, optional
X_OAUTH_CLIENT_SECRET, and X_OAUTH_REDIRECT_URI for organic X. Set
X_ADS_OAUTH_CONSUMER_KEY, X_ADS_OAUTH_CONSUMER_SECRET, and X_ADS_OAUTH_REDIRECT_URI for X Ads.
Both secrets remain backend-only.
Public Frontend and Provider Callbacks
Public URLs are opt-in and independent:
{
"publicFrontendUrl": "https://app.example.com",
"googleOAuth": {
"clientId": "replace-with-google-oauth-client-id.apps.googleusercontent.com",
"clientSecret": "replace-with-google-oauth-client-secret",
"redirectUri": "https://api.example.com/oauth/google/callback"
},
"metaAdsOAuth": {
"clientId": "replace-with-meta-approved-oauth-client-id",
"redirectUri": "https://api.example.com/oauth/mcp/callback"
},
"xOAuth": {
"clientId": "replace-with-x-oauth2-client-id",
"clientSecret": "replace-with-x-oauth2-client-secret",
"redirectUri": "https://api.example.com/oauth/mcp/callback"
},
"xAdsOAuth": {
"consumerKey": "replace-with-x-ads-consumer-key",
"consumerSecret": "replace-with-x-ads-consumer-secret",
"redirectUri": "https://api.example.com/oauth/mcp/callback"
},
"metaAds": {
"operationsProvider": "graph"
}
}publicFrontendUrlmust be an HTTPS origin with no path, credentials, query, or fragment.- Provider callback values must use HTTPS and their exact callback paths, with no query, credentials, or fragment.
- An omitted field retains its current loopback fallback; Google and Meta can be enabled independently.
publicFrontendUrlchanges browser return URLs and Vite’s public origin/allowed hostname only.BACKEND_URLremains Compose DNS inside containers and loopback for host-run services.
For an externally managed Cloudflare Tunnel or equivalent reverse proxy:
| Public Hostname | Local Target | Requirement |
|---|---|---|
app.example.com | http://127.0.0.1:3001 | Forward HTTP and development WebSockets |
api.example.com | http://127.0.0.1:3000 | Forward callbacks and backend HTTP |
Oblive does not create a cloudflared service or store tunnel credentials. Vite allows only the
configured app hostname; it never enables an unrestricted host allowlist. See Vite server
options.
The local control plane has no end-user authentication gate. Never expose it through an unrestricted public tunnel. Protect both hostnames with an external access policy and restrict them to intended testers.
Register the exact generated callbacks in the Google and Meta applications. After changing any
public URL, run bun run stack env and restart the frontend and backend. Existing generated files
remain authoritative until regeneration.
Generate Runtime Files
bun run stack envThis generates complete environment files for the backend, frontend, chat agent, worker agent, and containers. Edit the JSON input rather than the generated files.
Inspect Before Starting
bun run stack configUse this after changing ports, persistence, replicas, authentication, or local infrastructure credentials.
Lifecycle Commands
bun run stack up
bun run stack ps
bun run stack logs -f
bun run stack downRead-only commands such as ps, logs, and config should not rewrite environment files or trigger
watcher restarts.
Reset Local Data
bun run stack reset --confirmThis removes local stack volumes. Use it only when the loss of local state is acceptable.
Developers adding a field should follow Local Stack and Migrations.