Oblive Docs
Configuration

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 edit

Source checkouts instead use ignored config/local-stack.json, the tracked complete example, and the same schema contract.

Top-level Fields

FieldPurpose
modedevelopment for bind-mounted watchers or production for built images
persistencePreserve named volumes or use disposable local data
codexAgent authentication mode and source
workerReplicasNumber of task-worker service replicas
portsHost ports for infrastructure and applications
postgresLocal database name and credentials
garageLocal S3-compatible storage settings
agentServiceTokenBackend authentication for trusted agent runtimes
integrationCredentialKeyEncryption key for integration credential envelopes
publicFrontendUrlOptional public HTTPS product origin for browser returns and Vite
publicBackendUrlOptional public HTTPS backend origin used to derive OAuth callbacks
cloudflareTunnelOptional production-only host- or Compose-managed Cloudflare Tunnel
googleOAuthOptional Google client credentials and provider callback
metaAdsOAuthOptional deployment-level Meta OAuth client ID and provider callback
xOAuthOptional organic X OAuth 2.0 client credentials and provider callback
xAdsOAuthOptional X Ads OAuth 1.0a consumer credentials and provider callback
metaAdsMeta 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

FieldMeaning
authModelocal or api_key
localHomeHost Codex home used only to locate auth.json in local mode
apiKeyEnvironmentVariableName of the host variable read in API-key mode
authSyncIntervalSecondsFallback 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-specific redirectUri, then the generic managed-MCP callback.
  • Provider callback values must use HTTPS and their exact callback paths, with no query, credentials, or fragment.
  • Omitting publicBackendUrl retains 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.enabled may be true only when mode is production.
  • 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 tunnel tunnelId and an absolute credentialsFile. A missing or empty file stops validation before Compose starts. Existing credential-JSON configuration without runtime defaults to Compose mode for compatibility.
  • Compose mode generates .runtime/cloudflared/config.yml, routes the Google and managed-MCP callback paths to backend:3000, routes frontend traffic to frontend: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 start

In 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_TOKEN

    Cloudflared 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:404

The 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 check

This 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 stop

The 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 --confirm

This 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.