Every setting of a Core installation has exactly one home. There are two kinds:
| Kind | Examples | Home | Change it with | Takes effect |
|---|---|---|---|---|
| Process settings | Public URL, ports, logging, harnesses, execution concurrency, audit retention, OAuth origins, database pool, Runtime history export | config.json in the installation directory (default ~/.oac/core) | Web domain setup or oac domain for managed HTTPS; otherwise edit the file, then run oac apply | oac apply restarts the services that read the changed settings |
| Runtime settings | Sandbox backend and size, nodes, Projects and keys, default models, executor credentials | Core's PostgreSQL database | Web, or the Core API (/core/v1) with the Core key | Saved without a Core restart; nodes prepare Runtime changes asynchronously |
Web's System page shows the installation's addresses, the default models, the sandbox configuration and, under Startup settings, the process settings read-only with the path of config.json and the apply command. Secrets live in secrets/, one copy each. The files in generated/ are derived from config.json. No configuration file defines Projects or API keys.
Process settings: config.json
The installer writes every setting that applies to the installation's mode, so the file shows each value. Installer flags listed in installation options only seed it. To change a setting, edit the file and apply it:
~/.oac/core/oac apply --dry-run # show the changed settings, files and restarts
~/.oac/core/oac applyHow oac apply works
- It validates
config.jsonand changes nothing if a value is invalid.mode,native_coreandingressare fixed after installation; to change them, install into a new directory. It also checks the listeners that a changedhost, port or managed-ingresspublic_urladds (ports), and changes nothing ifhostis not an address of this machine or another program holds one of their ports; the installation's own listeners do not count. - It writes the files Core, Web and Compose read into
generated/:compose.json,core.env,core-key-digests.json,settings.jsonand, when used,runtime-history.json, the managedCaddyfileand the native Core unit. Don't edit them. A generated file edited by hand stopsapplyuntil you move the change intoconfig.jsonand runoac apply --discard-edits, which keeps the edited copy asgenerated/<file>.edited-<time>. - It compares what it wrote with what actually runs. Each container carries a digest of its inputs (the
io.oac.inputslabel; native Core carriesOAC_INPUTS), andapplyrecreates or restarts exactly the services whose inputs differ: Core first, then Web. The Restarts column below says which services a setting affects; see stop and restart for what a restart interrupts. - While any service runs,
applyalso starts the stopped ones. Afteroac stop, it only writes the files and the installation stays stopped. - If Core refuses a value at startup,
applyprints Core's startup error. If every service was running with the previous files, it restores them and starts the services again; otherwise it reports the failure, and the nextapplyfinishes the work.
oac status reports changes to config.json that are not applied yet and generated files edited by hand.
Changing the public URL
public_url is the one origin that applications, nodes, sandboxes and self-hosted executors use. Core derives the daemon WebSocket URL, the self-hosted remote_url and each sandbox's connection address from it. With null, Core uses its loopback origin. Until public_url is set, a managed installation serves only Web, over HTTP on the host's IP address at ports.web; afterwards that address redirects to public_url.
With managed ingress, change it in Web (System → Configure domain and HTTPS) or with oac domain HOSTNAME. Both take the installation lock, verify the new certificate and address, then write config.json and apply it. With external ingress, update your reverse proxy first, then edit public_url and run oac apply.
When nodes, hosted sandboxes or self-hosted executors are bound to the current address, apply lists them and asks you to type the new URL (--confirm-public-url-change URL without a terminal). Afterwards:
- Nodes on the old address get no new sandboxes: remove them in Web and add them again.
- Existing sandboxes and executors keep working only while the old address still reaches this Core. A managed domain change replaces the previous domain route.
- Self-hosted executors must restart with the new
remote_url, and their installer refuses an installation made for the old address: create new self-hosted Sessions and connect their hosts again.
Settings
core.runtime_history.headers may hold export credentials. They stay in the 0600 config.json and the generated file Core reads, and never appear in oac output or in Core's settings snapshot. Model providers are not process settings; see Default models.
| Key | Type | Default | Modes | Change | Restarts | Meaning |
|---|---|---|---|---|---|---|
$schema | string | none | all | any time | none | Editor hint that points at the installed copy of this schema. Ignored. |
format | 1 | none | all | fixed | none | Configuration format for this release. Fixed after installation. |
mode | "all" | "core-only" | "web-only" | "all" | all | fixed | none | Which services this installation runs. |
native_core | boolean | false | all, core-only | fixed | none | Run Core as a systemd user service instead of a container. |
public_url | string or null (canonical origin; HTTP only on loopback) | null | all | oac apply | core, web | Canonical public origin of Core and Web. With managed ingress, set the DNS hostname in Web or run oac domain; certificates are automatic. With external ingress, configure your TLS reverse proxy before applying this value. |
host | string (IPv4 or IPv6 address) | "127.0.0.1" | all | oac apply | core, web | Listener IP. With managed ingress only the gateway is public; Core stays on loopback. The default combined installer listens on all IPv4 interfaces. |
ports.core | integer 1024–65535 | 8091 | all, core-only | oac apply | core (core, web with native Core) | Host port of the Core API. With native Core, Web follows it. |
ports.web | integer 1024–65535 | 8080 | all, web-only | oac apply | web | Host port of Web. |
ports.database | integer 1024–65535 | none | all, core-only | oac apply | database, core | Loopback port of PostgreSQL. Present exactly when native_core is true; the installer picks a free port. |
web.core_url | string (canonical origin; HTTP only on loopback) | none | web-only | oac apply | web | Origin of the Core that this Web connects to: HTTPS, or HTTP on a loopback host. |
log.level | "debug" | "info" | "warn" | "error" | "info" | all | oac apply | core, web | Minimum log level of Core and Web. |
log.format | "auto" | "text" | "json" | "auto" | all | oac apply | core, web | Log format. auto writes text to a terminal and JSON otherwise. |
log.add_source | boolean | false | all | oac apply | core, web | Add the source file and line to each log record. |
core.execution_concurrency | integer 1–1024 | 4 | all, core-only | oac apply | core | Concurrent execution work units in Core. Unrelated to node sandbox capacity. |
core.harnesses | array of "claude_sdk" | "codex" | "mcode" | ["claude_sdk", "codex", "mcode"] | all, core-only | oac apply | core | Harnesses that Sessions may select. |
core.default_harness | "claude_sdk" | "codex" | "mcode" | "codex" | all, core-only | oac apply | core | Harness used when a Session names none. It must be listed in core.harnesses. |
core.write_audit_retention | string (Go duration, at least 1h) | "2160h" | all, core-only | oac apply | core | How long non-creation write history is kept, as a Go duration of at least 1h. |
core.oauth_trusted_origins | array of string (canonical HTTPS origin) | [] | all, core-only | oac apply | core | Extra HTTPS origins trusted as private OAuth issuers. |
core.database_pool.max_conns | integer or null ≥ 1 | null | all, core-only | oac apply | core | Maximum database connections. null keeps the driver default, max(4, CPU count). |
core.database_pool.min_conns | integer or null ≥ 0 | null | all, core-only | oac apply | core | Minimum idle database connections. null keeps the driver default, 0. |
core.database_pool.max_conn_lifetime | string or null (Go duration) | null | all, core-only | oac apply | core | Go duration. null keeps the driver default, 1h. |
core.database_pool.max_conn_idle_time | string or null (Go duration) | null | all, core-only | oac apply | core | Go duration. null keeps the driver default, 30m. |
core.database_pool.health_check_period | string or null (Go duration) | null | all, core-only | oac apply | core | Go duration. null keeps the driver default, 1m. |
core.runtime_history | object or null | null | all, core-only | oac apply | core | Runtime history collection and OTLP export. null keeps local collection with Core's defaults. Core checks the values at startup. |
core.runtime_history.transport | string | none | all, core-only | oac apply | core | OTLP export transport: otlp_http. Required with endpoint. |
core.runtime_history.endpoint | string | none | all, core-only | oac apply | core | OTLP/HTTP metrics URL: a canonical absolute URL with a path and no credentials, query or fragment; HTTPS, or HTTP with insecure. Omit it to keep history local; transport, insecure and headers then must be absent. |
core.runtime_history.insecure | boolean | none | all, core-only | oac apply | core | Export without TLS. Required for an HTTP endpoint and refused for HTTPS. |
core.runtime_history.headers | object of string values | none | all, core-only | oac apply | core | Headers sent with each export, such as credentials. Host, Content-Length, Content-Type and Content-Encoding are refused. Never shown by oac or Core. Sensitive. |
core.runtime_history.queue_capacity | integer | none | all, core-only | oac apply | core | Capacity of the history write queue and of the export queue, up to 4096. Omitted or 0 selects 256. |
core.runtime_history.timeout_seconds | integer | none | all, core-only | oac apply | core | History write, export and query timeout in seconds, up to 30. Omitted or 0 selects 2. |
core.runtime_history.sample_interval_seconds | integer | none | all, core-only | oac apply | core | Periodic sampling interval in seconds, 5 to 300. Omitted or 0 selects 30. |
ingress | "managed" | "external" | "external" | all | fixed | none | managed provides automatic HTTPS and Web domain setup for a combined Docker installation; install.sh selects it by default. external uses your existing proxy. Fixed after installation. |
| //: # (END config-reference) |
The schema is deploy/install/config.schema.json; each installation keeps a copy in generated/config.schema.json for editors. Core serves the non-secret settings snapshot, with the path of config.json and the apply command, at GET /core/v1/installation. How Core collects and keeps Runtime history is in retained history.
Runtime settings: Web
Runtime settings live in Core's database. Change them in Web; scripts use the same Core API with the Core key.
| Setting | Where in Web | Core API | Notes |
|---|---|---|---|
| Sandbox backend: Docker, microsandbox or E2B | System → Manage sandbox configuration: the setup wizard, ending with Save configuration | /core/v1/sandbox/deployment | One backend per installation. install.sh --sandbox saves the first choice. Another backend needs Reset deployment first; see change the sandbox configuration |
| Sandbox size, Runtime release, E2B key and template build | System → Manage sandbox configuration → Change resources | /core/v1/sandbox/deployment | Web proposes the sizes in standard-sizes.json. Existing sandboxes keep their size and release. The E2B key is write-only and encrypted |
| Nodes and their capacity | Nodes: Add node; Edit node and Remove node on a node's page | /core/v1/sandbox/enrollment-tokens, /core/v1/sandbox/nodes | See Node capacity and the nodes guide |
| Projects and API keys | Projects and keys: Create project, Rename, Issue key, Revoke, Archive | /core/v1/projects | Keys are shown once; Core stores digests |
| Default model per harness | System → Default model configuration: Set | /core/v1/harnesses/{harness}/model-configuration | See Default models |
| Executor credentials of a self-hosted Session | Session log, then the Session page: Executor credentials | /core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials | See self-hosted executors |
Which harnesses are enabled, and the default one, are process settings (core.harnesses, core.default_harness); System shows them read-only. The Core administration API lists every Core API route, and the deployment contract defines the sandbox fields, limits and change rules.
Node capacity
Core approves a node's capacity when you generate its Add node command: Sandboxes at once (max_active, default 2) and, for microsandbox only, Retained sandboxes (max_retained, default 8), with max_retained >= max_active >= 1. Docker never suspends sandboxes, so Web doesn't ask for it and Core keeps max_retained equal to max_active. Change them later with Edit node. Reservations and cleanup that is not confirmed count against capacity; lowering a limit stops no running sandbox. A node's own files can't change its capacity, size or Runtime.
core.execution_concurrency is unrelated: it limits concurrent execution work in Core.
Default models
Set a default in System → Default model configuration, or use PUT /core/v1/harnesses/{harness}/model-configuration. Core encrypts provider keys with secrets/credential.key and never returns them. Model execution owns the request fields and replacement rules, and precedence says which Sessions use a default.
Compose installations
The standalone Compose template uses its Compose definition and the platform's environment as the source of process settings. An unset or empty OAC_PUBLIC_URL selects http://localhost:8080, allowing startup before a public domain is configured. Core and Web receive that same value. For public access, set OAC_PUBLIC_URL to the exact public HTTPS origin without a trailing slash and redeploy Core and Web with the same project and volumes; changing an environment variable requires container recreation, not just a restart. Configure the public origin before adding nodes or executors. The platform owns TLS and routes to gateway:8080; Web's installer-managed domain setup is unavailable.
The initialization service generates secrets and the installation ID once, then verifies them on subsequent deployments. Each secret has one persistent source; Core's key digest is derived from Web's sign-in key. Initialization never replaces missing or changed secrets on an existing installation. Core reads its existing process environment and file settings, so the installer-specific config.json, oac apply and startup settings snapshot do not apply to this deployment.
| Compose volume | Content | Readers |
|---|---|---|
database | PostgreSQL data | PostgreSQL; initialization checks whether it is empty |
database-secret | Generated database password | PostgreSQL, migrator and Core |
core-config | Credential encryption key, installation ID, Core key digest and initialization receipt | Migrator and Core |
web-secret | Generated Core sign-in key | Web and the explicit credentials tool |
core-state | Private Provider state | Core |
node-payload | Verified node installation metadata | Web |
Initialization prepares these volumes; application services receive their secret volumes read-only. The credentials tool disables container logging. Retrieve its output only in an operator terminal. Database passwords and credential encryption keys are never printed.
The Compose project name scopes the volumes. Preserve every volume together with that project's definition and public URL. Removing only secret volumes does not reset an installation; initialization refuses to start over an existing database. Core also binds the installation ID to its database. Runtime settings continue to live in Core's database.
Docker node configuration
The node installer writes Docker’s provider configuration into the node’s configuration file; these fields are separate from Core’s config.json. Deployment resources, Runtime images and capacity remain in Core’s database.
| Field | Installer value | Meaning |
|---|---|---|
host | unix:///var/run/docker.sock | Explicit Docker Engine socket |
network | oac-node-<installation-id> | Runtime container network |
seccomp_file | <node-root>/runtime/seccomp.json | Matched distribution’s seccomp profile |
nested_sandbox | true | Enables the Docker adapter’s init process and proc-mask configuration |
extra_hosts | Optional | Additional container host mappings |
The Docker adapter owns container isolation, volume layout and lifecycle behavior.
Installation directory
The installer creates the installation directory, ~/.oac/core by default, with mode 0700; the files in secrets/ are 0600.
| Path | Content | Changed by |
|---|---|---|
config.json | Process settings. The only file you edit | You, then oac apply; managed domain setup for public_url |
oac | The management command | The installer |
state.json | Installation ID, Compose project name, image IDs, source commit, the digests of generated files and whether the services have started once | The tools only |
secrets/core.key | The Core key | oac rotate-core-key |
secrets/credential.key | Encryption key for what Core stores sealed in the database: model providers, the E2B key, Vault credentials, Skills, initial files and environment setup | Nothing. Keep it with the database; oac apply refuses a changed file |
secrets/database.password | PostgreSQL password | Nothing. PostgreSQL reads it only when the database is created; oac apply refuses a changed file |
generated/ | Files derived from config.json | oac apply |
node-payload/ | The node installer and node files that Web serves at /node-install/, one directory per release | The installer |
native-installers/ | Self-hosted daemon installers for each platform, with their catalog, when the bundle carries them | The installer |
state/e2b/ | Private E2B receipts | Core |
ingress/ | Managed HTTPS: certificates and gateway state, domain setup status and the control sockets | The gateway and installation services |
native/ | Core binaries, with native Core | The installer |
.oac.lock | The installation lock | The installer and mutating oac commands |
A Web-only installation has only secrets/core.key, a copy of its Core's key, and no state/ or native-installers/. A Core-only installation has no node-payload/. Only managed ingress has ingress/. With native Core, the installation directory and the bundle must be canonical absolute paths without control characters, quotes, backslashes or wildcards.
In Docker, the Compose project is named oac-<10 hex digits> (project in state.json). Its services are database, migrate, core and web, plus gateway and installation with managed ingress. gateway routes to Core and Web and publishes ports.web, plus 80 and 443 once HTTPS is on; installation applies domain changes from Web and uses the Docker socket to do so. The volume <project>_database holds all data. Native Core runs Core as the systemd user unit <project>-core.service instead of a container. Apart from Docker's storage, nothing is written outside your home directory.
Appendix: Core environment without the installer
Core reads only its environment. The installer renders generated/core.env from config.json; if you run Core yourself (see the service guide), set these variables. Compose loads the file with env_file and systemd with EnvironmentFile, so Compose must be 2.26.0 or newer.
| Variable | Set from |
|---|---|
OAC_PUBLIC_URL | public_url, or Core's loopback origin. Core derives the daemon WebSocket URL, the self-hosted remote_url, the hosted sandbox address and the deployment's read-only core_url from it, never from request headers. Without it, Core runs no Runtime gateway and executes no Sessions |
OAC_ADDR | The installer derives the native listener from ports.core and sets :8091 in the container. Independently started Core defaults to 127.0.0.1:8091 when unset or empty |
OAC_DATABASE_URL | The installation's PostgreSQL without a password, plus core.database_pool as pool_* query parameters |
OAC_DATABASE_PASSWORD_FILE | secrets/database.password. The URL must then carry no password; migrations and the maintenance commands read the file too |
OAC_CREDENTIAL_KEY_FILE | secrets/credential.key |
OAC_CORE_KEY_DIGESTS_FILE | generated/core-key-digests.json: a JSON array with the SHA-256 of the Core key |
OAC_INSTALLATION_ID | The installation ID from state.json, a canonical UUID. It enables the sandbox deployment and node routes and requires OAC_PUBLIC_URL and OAC_CORE_KEY_DIGESTS_FILE. Core refuses an ID other than the one its database recorded, so keep the two together |
OAC_SETTINGS_FILE | generated/settings.json, the snapshot Core serves at GET /core/v1/installation; Core does not act on it |
OAC_EXECUTION_CONCURRENCY, OAC_DEFAULT_HARNESS, OAC_WRITE_AUDIT_RETENTION, OAC_OAUTH_TRUSTED_ORIGINS | The matching process settings; omitted or empty default Harness uses core.default_harness’s documented default |
OAC_HARNESSES | core.harnesses with the installer. Independently started Core enables only the default Harness when unset; comma-separated explicit names supplement it. Entries are trimmed and deduplicated; unknown names stop startup |
OAC_HISTORY_SETTINGS_FILE | generated/runtime-history.json: the core.runtime_history object, when it is set |
OAC_LOG_LEVEL, OAC_LOG_FORMAT, OAC_LOG_ADD_SOURCE | log.*; Web reads the same three |
OAC_PROVIDER_ROOT | Absolute adapter artifact root: native/ for native Core, /opt/oac in the Core image. Each adapter owns its helper paths beneath this root |
OAC_PROVIDER_STATE_ROOT | Absolute private state root: state/ for native Core, /state in the Core image. Each adapter owns its subdirectory; E2B uses e2b/, owned by Core's user with no group or other access. Back it up with the database and credential.key; don't mount it into Web or a Runtime |
OAC_NATIVE_INSTALLER_DIR | native-installers/, served to self-hosted machines; the Core image has a copy at /opt/oac/native-installers, used when this is unset. Core checks the catalog against its own release before serving it |
Core logs the file paths it loads, never environment values or file contents.
Invalid explicit OAuth trusted origins stop Core at startup. Entries must be HTTPS origins without credentials, query or a non-root path; the installer validates core.oauth_trusted_origins before generating them. Vaults owns refresh and network policy. A private issuer also needs a trusted CA: independently managed Unix Core can use Go’s SSL_CERT_FILE PEM CA-bundle override, which preserves certificate verification. Managed installation has no custom-CA setting or mount; do not edit generated/core.env.
Appendix: Web environment without the installer
The installer sets these variables from config.json; set them yourself only when you run the console without the installer. Of the installation's secrets, the installer gives the console only secrets/core.key.
| Variable | Default | Meaning |
|---|---|---|
OAC_WEB_ADDR | :8080 | Listener address |
OAC_WEB_ORIGIN | http://127.0.0.1:8080 | The exact browser-facing origin, HTTP or HTTPS, without a path. Host and origin checks use it; HTTPS makes the session cookie Secure |
OAC_WEB_UPSTREAM | http://core:8091 | Core's origin, HTTP or HTTPS, without credentials, query or path |
OAC_WEB_CORE_KEY_FILE | /admin/core.key | Absolute path of a regular file with no group or other permissions, holding the Core key: at least 32 characters, no whitespace, at most 4 KiB |
OAC_WEB_DIST | /www | Absolute directory of the built console; must contain index.html |
OAC_WEB_NODE_PAYLOAD_DIR | unset | Absolute path of the matched distribution's node payload (the installer's node-payload/). Unset, /node-install/* is not served and Add node is unavailable |
OAC_WEB_INSTALLATION_SOCKET | unset | Absolute path of the installer's domain socket. Unset, domain setup reports unsupported |
OAC_WEB_BOOTSTRAP | 0 | 1 accepts literal-IP hosts before a domain is configured. Requires an http:// origin and OAC_WEB_INSTALLATION_SOCKET |
Defaults apply when a variable is absent; an explicitly empty value is validated as supplied. An invalid OAC_WEB_* value stops the console at startup with a message naming the variable. The console also reads OAC_LOG_LEVEL, OAC_LOG_FORMAT and OAC_LOG_ADD_SOURCE (Core environment); unknown values fall back to their defaults. Use HTTPS for any browser that is not on the same machine.