Local Stack
Complete reference for the canonical machine-local JSON configuration.
The portable installer creates ~/.oblive/local-stack.json as the canonical machine-local
configuration and places local-stack.schema.json beside it for editor completion and validation.
The configuration is mode 0600 and contains generated local secrets. Find or edit it with:
oblive config path
oblive config editSource checkouts instead use ignored config/local-stack.json, the tracked complete example, and
the same schema contract.
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 |
publicBackendUrl | Optional public HTTPS backend origin used to derive OAuth callbacks |
cloudflareTunnel | Optional production-only host- or Compose-managed Cloudflare Tunnel |
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
Portable deployments require production and run release-pinned images without build directives.
Source development uses development, adds the local build and bind-mount overlays, and runs
watchers. This is an image/runtime mode, not a complete public 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 is derived from publicBackendUrl, then falls back to
the loopback backend callback generated from ports.backend. Omit the whole block when neither
Google Workspace nor Google Analytics is needed.
For Google Analytics, enable the Google Analytics Admin API (analyticsadmin.googleapis.com) and
Google Analytics Data API (analyticsdata.googleapis.com) in the Cloud project that owns this OAuth
client. The Admin API provides property discovery and configuration; the Data API serves reports.
The connected Google identity also needs Viewer or higher access to the required GA4 properties.
Meta Ads OAuth and Operations
The generator always writes the generic managed-MCP callback URI. It is derived from
publicBackendUrl when configured and otherwise uses loopback. 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
managed-MCP callback. Set it only when Meta must use a different registered callback from the one
derived from publicBackendUrl.
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
managed-MCP callback. Set them only when an X developer App must use a different registered callback
from the one derived from publicBackendUrl. For loopback-only X Ads authorization, approve
http://127.0.0.1:3000/oauth/mcp/callback in the X developer App.
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, Backend, and Cloudflare Tunnel
For a Mac that already runs a remotely managed Cloudflare Tunnel, use the host runtime. It keeps Cloudflared under the operating system’s service manager and prevents the stack from starting a second tunnel process:
{
"mode": "production",
"publicFrontendUrl": "https://oblive.example.com",
"publicBackendUrl": "https://oblive.example.com",
"cloudflareTunnel": {
"enabled": true,
"runtime": "host"
},
"googleOAuth": {
"clientId": "replace-with-google-oauth-client-id.apps.googleusercontent.com",
"clientSecret": "replace-with-google-oauth-client-secret"
},
"metaAdsOAuth": {
"clientId": "replace-with-meta-approved-oauth-client-id"
},
"xOAuth": {
"clientId": "replace-with-x-oauth2-client-id",
"clientSecret": "replace-with-x-oauth2-client-secret"
},
"xAdsOAuth": {
"consumerKey": "replace-with-x-ads-consumer-key",
"consumerSecret": "replace-with-x-ads-consumer-secret"
},
"metaAds": {
"operationsProvider": "graph"
}
}Host mode defaults to CLOUDFLARE_TUNNEL_TOKEN for first-time service installation. Set
tokenEnvironmentVariable in cloudflareTunnel when deployment policy requires another variable
name.
For a locally managed credential-JSON tunnel that the Oblive stack should run, use Compose mode:
{
"mode": "production",
"publicFrontendUrl": "https://oblive.example.com",
"publicBackendUrl": "https://oblive.example.com",
"cloudflareTunnel": {
"enabled": true,
"runtime": "compose",
"tunnelId": "6ff42ae2-765d-4adf-8112-31c55c1551ef",
"credentialsFile": "/Users/example/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json"
}
}- Both public URL fields must be HTTPS origins with no path, credentials, query, or fragment.
- The generic managed-MCP callback is
<publicBackendUrl>/oauth/mcp/callback; Apollo and other managed providers use it unless they declare a runtime-specific callback. - Google uses
googleOAuth.redirectUri, then<publicBackendUrl>/oauth/google/callback, then the generated loopback callback. Meta, organic X, and X Ads use their provider-specificredirectUri, then the generic managed-MCP callback. - Provider callback values must use HTTPS and their exact callback paths, with no query, credentials, or fragment.
- Omitting
publicBackendUrlretains the current loopback callback behavior. - Public URL fields never change
BACKEND_URL. Service traffic remains on Compose DNS inside containers and loopback for host-run processes.
The tunnel contract is deliberately narrow:
cloudflareTunnel.enabledmay betrueonly whenmodeisproduction.- Both public URLs are then required and must be HTTPS origins without explicit ports.
runtime: "host"requires the operating-system Cloudflared service to be running. It never adds the Cloudflare Compose overlay or expects a credential JSON file.runtime: "compose"requires a named tunneltunnelIdand an absolutecredentialsFile. A missing or empty file stops validation before Compose starts. Existing credential-JSON configuration withoutruntimedefaults to Compose mode for compatibility.- Compose mode generates
.runtime/cloudflared/config.yml, routes the Google and managed-MCP callback paths tobackend:3000, routes frontend traffic tofrontend:3001, mounts the credential read-only, and returns 404 for unmatched traffic.
After saving the production configuration, configure the selected runtime and start the complete topology:
oblive configure cloudflared
oblive stack startIn host mode, oblive configure cloudflared behaves safely with an existing daemon:
-
If the service is already active, the command reuses it and does not require a token.
-
If Cloudflared is missing on macOS, the command installs it with Homebrew.
-
If the binary exists but no service is installed, export the configured token variable once:
export CLOUDFLARE_TUNNEL_TOKEN='<token copied from the Cloudflare tunnel setup command>' oblive configure cloudflared unset CLOUDFLARE_TUNNEL_TOKENCloudflared stores what its system service needs using its own protected service files. Oblive never copies the token into
local-stack.json,.runtime, or the release bundle. -
If a service definition exists but is stopped, the command starts it instead of trying to install a duplicate service. Do not uninstall a working system service just to run this command.
oblive stack start validates that the host service is running only when host mode is enabled. In
Compose mode, oblive configure cloudflared validates the local credential and installs the pinned
container image; startup adds the Cloudflare overlay and waits for its readiness endpoint. Neither
mode creates a Cloudflare account, tunnel, DNS record, dashboard ingress, or access policy.
For host mode, place the callback rule before the frontend catch-all in the remotely managed tunnel’s dashboard ingress configuration:
ingress:
- hostname: oblive.example.com
path: ^/oauth/(google|mcp)/callback$
service: http://127.0.0.1:3000
- hostname: oblive.example.com
service: http://127.0.0.1:3001
- service: http_status:404The second rule must forward development WebSockets. Separate app and API hostnames remain
supported: set publicFrontendUrl to the app origin, set publicBackendUrl to the API origin, and
route them to ports 3001 and 3000 respectively. Provider-specific callback overrides are for
exceptional registrations, not an integration-level public/private mode.
A remotely managed token tunnel stores ingress in the Cloudflare dashboard rather than a local YAML
file. Select runtime: "host" for that topology. Use runtime: "compose" only for locally managed
credential-JSON tunnels. Vite allows only the configured app hostname and never enables an
unrestricted host allowlist. See Vite server options. Use
a stable named tunnel for OAuth; a random trycloudflare.com quick-tunnel hostname changes when the
tunnel is recreated and invalidates registered callbacks.
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 provider applications. After changing any public URL,
run oblive config check and oblive restart --no-open. Existing generated files remain
authoritative until validation regenerates them.
Validate and Generate Runtime Files
oblive config checkThis validates the JSON and resolved Compose topology, then generates complete container environment files for the selected semantic release. Edit the JSON input rather than the generated files. Use it after changing ports, persistence, replicas, authentication, or local infrastructure credentials.
Lifecycle Commands
oblive stack start
oblive stack status
oblive stack logs --follow
oblive stack stopThe existing top-level oblive start, status, logs, and stop commands remain aliases.
status and logs do not change the selected release or rewrite configuration.
Reset Local Data
oblive 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.