This guide is for production and advanced self-hosting: long-lived Servers, multiple machines/users, system services, reverse proxies, Docker, and operator-managed networking. For personal and multi-computer setup, start with Unified installation and its release/validation status. For existing published Desktop artifacts, use the legacy installation guide; the Full Setup guide retains the advanced CLI procedures. For a few-minute one-repository trial, use the Quick Trial.
For personal or multi-computer installation, start with Unified installation. Its Windows NSIS, macOS package, and Debian 12 / Ubuntu 22.04+ .deb installer targets for x64 and arm64 are defined in the build pipeline; the six installer variants have not yet received native build and installation acceptance or a unified release. Real-machine installation, reboot persistence, GUI behavior, and upgrade have not yet been accepted on all three platforms; see the validation checklist.
The npm/runtime archive, Docker, and platform-specific procedures below are retained as advanced compatibility and historical operational guidance. They do not redefine the unified installer workflow.
webcodex— the unified CLI for project workflows, Server/Runner lifecycle, enrollment, and operations.webcodex-server— the Server process exposing REST, MCP, and Runner endpoints.webcodex-runner— the long-lived worker on the machine that owns the repositories.
Execution configuration belongs to the Runner that performs the work. The
retired Server CODEX_* settings do not select a coding-agent executable,
approval mode, timeout, or argument allowlist. Configure coding-agent providers
in the Runner's [acp] / [[acp.agents]] settings instead; see the
ACP coding-agent guide. The Server needs its
writable data directory, not a separate legacy uploads directory.
For existing published runtime/CLI artifacts, the npm thin installer/wrapper remains available:
npm install -g @yyjeqhc/webcodexSupported package platforms are Linux x64, Linux arm64, macOS x64, macOS arm64, Windows x64, and Windows arm64. Windows supports CLI + Runner, explicit foreground Server, and explicit local webcodex share --tunnel cloudflare|openai|none. Windows x64 supports managed Cloudflare acquisition; Windows ARM64 Cloudflare requires a trusted explicit/PATH binary because the pinned upstream release has no official ARM64 artifact. Native OpenAI Tunnel supports Windows x64/arm64. The legacy server install / runner install commands do not manage Windows services; the foreground examples below remain available. New environment setups default to user scope, which uses signed-in user scheduled tasks on Windows. Explicit --scope system selects SCM services with OS authorization and account requirements; existing environments retain their saved scope and owner. User scope does not promise operation before login or after logout; see Unified installation and its pending native acceptance. The npm wrapper
requires Node.js 18 or newer. The native Linux x64 artifact targets glibc 2.17
or newer.
Build from source:
cargo build --release --workspace --bins
export PATH="$PWD/target/release:$PATH"This produces webcodex, webcodex-server, and webcodex-runner.
The npm package installs all three Windows executables. Windows can run both the Server and Runner directly without Linux, WSL, or a Windows Service. Keep the foreground terminals open while the processes are in use.
On the Windows Server machine, initialize an explicit env file and start the Server from PowerShell:
$envFile = Join-Path $HOME ".config\webcodex\webcodex.env"
$dataDir = Join-Path $HOME ".local\share\webcodex"
webcodex server init --listen 127.0.0.1:8080 --data-dir $dataDir --env-file $envFile
webcodex server run --env-file $envFilePass the same --env-file explicitly when starting the foreground Server; do not rely on a managed-service default. For a local same-machine enrollment, open another PowerShell window and create a short-lived pairing code:
webcodex pairing create --server-url http://127.0.0.1:8080 --env-file $envFile --username workstation --display-name "Windows Workstation" --ttl-secs 600Then, on the Windows machine that owns the repositories, redeem only that short-lived code and run the generated Runner config in the foreground:
webcodex login http://127.0.0.1:8080 --code <wc_pair_...> --allowed-root C:\src --project C:\src\my-repo
webcodex runner run --config <login-reported-runner-config>When Server and Runner are on different machines, replace the loopback URL with the Server's reachable HTTPS URL and configure the Server listener/public URL plus a trusted reverse proxy or network route as described below. An OpenAI Secure MCP Tunnel carries ChatGPT-to-MCP traffic and is not a Runner enrollment URL. Do not copy the Server bootstrap token or env file to the Runner machine. The legacy webcodex server install/start/stop/restart/logs/uninstall and webcodex runner install commands remain unsupported on Windows; Ctrl-C or Ctrl-Break ends the foreground runtime.
To keep a Windows Server loopback-only while exposing MCP privately through an OpenAI Secure MCP Tunnel and operating an independent Runner like a normal long-lived Runner, see the Windows + OpenAI Secure MCP Tunnel deep dive. It is advanced setup/troubleshooting material; ordinary users do not need it before understanding the full setup path.
The hosted shared-key path needs no local Server, database, reverse proxy, or systemd unit, but it does require a shared key accepted by that Server (normally supplied by its operator or recovered from an existing protected profile):
cd /path/to/your/repository
webcodex connect https://your-server.example --key-file /private/path/shared-keyconnect uses the current directory as the project, writes an owner-only
profile, starts a detached Runner, and waits until the Server sees both the
Runner and the project. Use the printed /mcp URL and credential in your MCP
client. After a machine reboot, rerun the same connect or use
webcodex runner start --profile <profile>.
This is not first enrollment for a freshly self-hosted Server. For that case,
keep the bootstrap administrator token on the Server and follow the pairing /
webcodex login flow below. For automation of a shared-key deployment, prefer
--key-file <path> over --key. Do not pass both.
A first-time operator does not need OAuth, QUIC, or account credentials. The minimum production path:
- Use a Linux x64 host with systemd,
sudo, and a public HTTPS domain or trusted tunnel. - Install
@yyjeqhc/webcodex, runwebcodex server init, and install thewebcodex-serverservice. - Configure the reverse proxy and set
WEBCODEX_PUBLIC_URLto the exact public HTTPS origin. - Create a short-lived pairing code on the server and run
webcodex login <server-url> --code <code>on the machine that owns the repositories. - Install the
webcodex-runnerservice on that repository machine. - Run
webcodex ops status --strict; only then add the MCP connector.
Initialize the Server env file (creates the bootstrap WEBCODEX_TOKEN and the
listen/data settings):
sudo webcodex server init \
--listen 127.0.0.1:8080 \
--data-dir /var/lib/webcodex \
--env-file /etc/webcodex/webcodex.env \
--public-url https://your-domain.exampleserver init creates the selected data directory and the server-side bootstrap/admin token. It does not create user API tokens or Runner tokens. Its next-step guidance preserves the exact env/data paths supplied above.
Install and start the managed systemd socket/service pair:
sudo webcodex server install \
--env-file /etc/webcodex/webcodex.env \
--working-directory /var/lib/webcodex \
--bin /usr/local/bin/webcodex-server
webcodex server status --env-file /etc/webcodex/webcodex.envWhen --working-directory is omitted, server install uses WEBCODEX_DATA from the selected env file before falling back to the platform default. Install preflight rejects a missing/non-executable binary, missing working directory, unreadable env file, or unknown explicit User/Group before mutating systemd. server status likewise derives its default local HTTP probe from that env file's WEBCODEX_ADDR; an explicit --url still wins.
The managed Linux layout is intentionally split: webcodex.socket owns the
fixed WEBCODEX_ADDR listener, while webcodex.service owns only the Server
process. For ordinary binary replacement after this socket-activated layout is
established, stage the replacement and run:
sudo systemctl restart webcodex.serviceDo not restart webcodex.socket during the normal Server replacement path.
The socket remains bound while the old Server drains and the new process later
inherits the same listener, eliminating the listener/ECONNREFUSED ownership
gap under normal bounded-backlog conditions. On SIGTERM (managed restart/stop)
or Ctrl-C/SIGINT (foreground), WebCodex first makes a process-local drain fence
authoritative, then asks Salvo to stop accepting connections and gracefully close
existing HTTP connections. A request admitted before that fence may run for up to
315 seconds and flush its response; a request that still reaches the old process
after the fence receives a retriable HTTP 503 without entering its handler. The
315-second bound is derived from the 300-second ordinary HTTP hard timeout plus a
15-second response/teardown margin. The generated systemd service uses
TimeoutStopSec=330s, leaving another 15-second margin so systemd does not
SIGKILL the process before the application-owned bound.
This is an availability-preserving graceful restart, not overlapping generations. During a long drain, new TCP connections can remain queued in the systemd socket backlog until the old process exits and the new Server inherits the listener, so restart latency may approach the finite-request bound. Existing WebSocket, HTTP keep-alive, and streaming connections may still disconnect/reconnect; there is no WebSocket continuity or literal zero-interruption guarantee.
Use --overwrite on server install only when replacing an existing managed
pair. Migrating an already-active legacy direct-bind webcodex.service is a
one-time migration boundary: the installer fails closed rather than competing
for the live address. Stop the legacy Server first, then rerun the install with
--overwrite. This boundary does not provide a gap-free first migration.
MCP call waiting is a Server-side Host adaptation and is configured independently of Runner execution timeouts. Ordinary MCP Hosts use the default direct profile, so no setting is normally required:
WEBCODEX_MCP_HOST_PROFILE=direct
For a Host that provides native Code Mode/orchestration with an approximately 55-second wall-clock budget for the whole composition, select:
WEBCODEX_MCP_HOST_PROFILE=host_code_mode
# Optional: host_code_mode already defaults to 55 seconds.
WEBCODEX_MCP_HOST_BUDGET_SECS=55
To independently experiment with bounded Server-owned waiting ceilings without changing existing defaults, optionally set:
# Optional; absence retains current profile-derived timing (5s in host_code_mode).
WEBCODEX_MCP_HOST_SYNC_WAIT_MAX_SECS=8
WEBCODEX_MCP_HOST_CONTINUATION_WAIT_MAX_SECS=5
The first value bounds the synchronous wait before an already-started structured Job returns for continuation (integer 1–60 seconds). The second bounds ordinary observe_jobs / job_tail waits and generated continuations (integer 1–100 seconds). Invalid or out-of-range values fail Server startup with a variable-name-only error. Both are also limited by the selected request's Host budget minus the 5-second return guard. The configured bounds remain authoritative when an MCP client changes profiles or narrows its budget through request headers. wait_for_job_readiness still uses its distinct Host-budget policy. These settings require a Server restart, never change actual Job execution timeouts, and are not model-facing tool arguments.
Example values above are experimental overrides, not recommended defaults. Review both Host request duration and final Job outcomes before changing a deployment policy. get_runtime_status.effective_config.mcp_host shows the nonsecret deployment-effective values, not request-local narrowed values.
WEBCODEX_MCP_HOST_BUDGET_SECS describes the Host-side MCP call/composition budget, not command runtime. Tool timeout_secs remains the execution lifetime and may be much larger. WebCodex never infers the profile from clientInfo, User-Agent, or a Host product name.
host_code_mode describes orchestration supplied by the external MCP Host. It is separate from WebCodex's experimental internal Code Mode feature and its own nested-execution safeguards. runtime_status.effective_config.mcp_host reports the non-secret deployment default. Mixed clients can override their request strategy with X-WebCodex-MCP-Profile and reduce their waiting budget with X-WebCodex-MCP-Budget-Secs; configure these headers on each client connection, not as model arguments. See request-local client policy. No header means the deployment default; client brand is never auto-detected.
Tool-request tracing is an operator diagnostic and is disabled by default. Use metadata mode for lightweight lifecycle diagnostics; use full only when you explicitly need request/response payload capture:
WEBCODEX_TOOL_REQUEST_TRACE=full
WEBCODEX_TOOL_REQUEST_TRACE_DIR=/var/lib/webcodex/tool-request-traces
WEBCODEX_TOOL_REQUEST_TRACE_RETENTION_HOURS=168
WEBCODEX_TOOL_REQUEST_TRACE_MAX_TOTAL_BYTES=2147483648
full tracing can contain source text, patches, command/script input and output, user messages, and secrets that were themselves present inside captured tool payloads. Protect the trace directory like other sensitive diagnostic data and keep retention/budget bounded. Trace capture is diagnostic only; trace-write failure does not change tool execution.
The exact trace file layout, correlation ids, model-facing forensic reader, queueing, compression, and payload-validation rules are implementation/maintainer details and are intentionally omitted here.
Hosted MCP clients require a public HTTPS URL. Set
WEBCODEX_PUBLIC_URL in the Server env file and put a reverse proxy in front
of 127.0.0.1:8080. Nginx is supported; a named Cloudflare Tunnel is also a
valid front door. The same hostname must carry ordinary HTTPS requests and
/api/agents/ws (Cloudflare supports WebSocket upgrades). WebCodex also uses
this configured origin as the MCP App ui.domain for Computer and Result
resources; it never substitutes a WebCodex-operated domain for self-hosted
servers. When no public URL is configured the optional field is omitted.
WebCodex CLI does not automate reverse proxy or tunnel setup.
On the machine that owns the repositories, as the ordinary user who will run
project commands (do not use sudo):
webcodex login https://your-domain.example --code <wc_pair_...> \
--allowed-root "$HOME/git" \
--project "$HOME/git/my-repo"
webcodex runner install --scope user \
--config <login-reported-runner-config>
webcodex runner status --scope user \
--config <login-reported-runner-config>
webcodex ops status --server-url https://your-domain.example \
--token-file <login-reported-webcodex-user-token> --strictwebcodex login is the canonical client entry: it derives a unique device name, redeems the pairing code, and writes the client-side webcodex-user-token and a runner.toml. --allowed-root grants registration authority only; --project names the actual existing workspace to register. The generated project_registry_dir is a registry directory, not the workspace root. If login is performed without --project, use webcodex project register --config <login-reported-runner-config> /path/to/repo before project-bound work. Use the documented login --device and --dir options when an explicit device identity or alternate local base directory is required; there is no separate compatibility enrollment command.
The pairing code is created server/admin-side:
webcodex pairing create \
--server-url https://your-domain.example \
--env-file /etc/webcodex/webcodex.env \
--username friendname \
--display-name "Friend Name" \
--ttl-secs 600Copy only the short-lived wc_pair_* code to the client. Do not copy
WEBCODEX_TOKEN, user API tokens, Runner tokens, env files, or complete
runner.toml files between machines. Each friend should use a unique
username.
webcodex runner install supports user or system scope. Non-root users default
to user scope; root defaults to system scope.
User scope uses systemctl --user, writes the unit under
$XDG_CONFIG_HOME/systemd/user, stores config under $XDG_CONFIG_HOME/webcodex,
and needs no sudo:
webcodex runner install --scope user --profile workstation
webcodex runner status --scope user --profile workstation
webcodex runner logs --scope user --profile workstation --lines 100An enabled user unit follows that account's user manager. For unattended boot
persistence, an administrator may explicitly run
sudo loginctl enable-linger <runner-user>; WebCodex never changes lingering
automatically.
System scope uses /etc/systemd/system and requires a named non-root
--user:
sudo webcodex runner install \
--scope system \
--profile workstation \
--user <runner-user> \
--working-directory /home/<runner-user> \
--config /etc/webcodex/clients/workstation/runner.toml
sudo webcodex runner status --scope system --profile workstationA root Runner is refused unless --allow-root-runner is explicit (discouraged).
Use the same --scope for every lifecycle command. Example files live in
deploy/ (webcodex.env.example, webcodex.service.example,
webcodex-runner.toml.example, webcodex-runner.service.example,
nginx.webcodex.example.conf).
The repository includes a server-only Dockerfile and Compose deployment that
runs webcodex-server plus the admin CLI; it intentionally excludes the
Runner, project repositories, and toolchains. Official releases publish one
multi-architecture image for linux/amd64 and linux/arm64 at
ghcr.io/yyjeqhc/webcodex-server.
An ordinary Server deployment does not require a repository clone or a Rust toolchain. Download the self-contained bootstrap from the latest public Release and run it:
mkdir -p webcodex-server && cd webcodex-server
curl -fLO https://github.com/yyjeqhc/webcodex/releases/latest/download/webcodex-server-bootstrap.sh
sh webcodex-server-bootstrap.sh https://webcodex.example.comThe Release-generated bootstrap embeds a Compose definition pinned to that
Release's immutable multi-arch image digest and materializes it locally as
webcodex-server-compose.yaml. Thus even the latest/download convenience URL
produces a fixed deployment after download, without a race between multiple
Release assets. To select a specific release instead, replace
releases/latest/download with releases/download/v<VERSION>. The bootstrap
records the materialized COMPOSE_FILE and exact image reference in its private
.env; later plain docker compose commands in that directory therefore reuse
the same pinned deployment.
Bootstrap is recoverable rather than all-or-nothing. It validates the strict
HTTPS origin, Compose/source assets, host port, Docker/Compose availability, and
published image before creating an administrator secret. It then advances the
private .webcodex-bootstrap.receipt through AssetsPrepared,
SecretCommitted, ContainerStarted, ServerHealthy, and PairingReady. The
receipt contains hashes and state, not the administrator token. .env is written
through a 0600 temporary file, synced, and atomically renamed. Success is printed only after the Compose healthcheck and /healthz readiness verification succeed; a short-lived pairing code is created only after that readiness barrier.
If an install is interrupted or a startup/health check fails, keep .env and use
the same downloaded bootstrap in that directory:
sh webcodex-server-bootstrap.sh status
sh webcodex-server-bootstrap.sh resume
# Remove runtime effects while preserving the committed administrator token
# and named data volume:
sh webcodex-server-bootstrap.sh rollbackresume reuses a committed administrator token; it never regenerates one merely
because docker compose up or the health check failed. rollback deliberately
returns a post-secret installation to SecretCommitted instead of deleting
.env, because the Server may already have initialized durable data with that
token. An unmanaged or pre-transaction .env without a matching receipt is
never overwritten or deleted automatically.
For development or before the first public GHCR image has been activated, clone the source and choose the explicit build path:
git clone https://github.com/yyjeqhc/webcodex.git
cd webcodex
./deploy/docker/bootstrap.sh https://webcodex.example.com --build-from-source
# Later source rebuilds use the same explicit override:
docker compose -f compose.yaml -f compose.build.yaml up -d --buildThe default binding is 127.0.0.1:8080. Put an HTTPS reverse proxy in front;
the bootstrap has already verified the local Server and created the first
short-lived pairing code. Use that code to enroll the machines that hold your
repositories. The first ever GHCR publication creates a private package by
GitHub default; a maintainer must make that package public once before the
workflow's anonymous-pull gate can succeed. End users do not need registry
credentials after that one-time activation.
Client enrollment generates the Runner config. Important settings in
runner.toml:
| Setting | Notes |
|---|---|
server_url |
Public WebCodex URL. |
token |
Runner credential. Do not commit or print it. |
client_id |
Stable id used in agent:<client_id>:<project_id>. |
owner |
Owner principal for this Runner. |
transport |
Prefer auto with [quic] configured. |
project_registry_dir |
Directory of project registry files. |
[policy] |
Local execution boundary (allowed_roots, etc.). |
[skills].roots |
Available since v0.4.2. Optional absolute Runner-local live Skill roots. WebCodex does not modify them; supported scripts may execute via run_skill_resource; content is not copied into the managed Skill Store. |
[instructions].files |
Optional absolute Runner-local instruction files applied to every Project on this Runner. No implicit default path; the list is hot-reloadable and file contents are live. |
[shell] |
Optional shell profile definitions and bounded persistent-shell limits. |
[ssh.resources.<name>] |
Optional named SSH target for Session-bound run_shell / run_job. |
Policy defaults: missing or empty allowed_roots defaults to $HOME; an
explicit allowed_roots overrides it. Use explicit roots to narrow a Runner,
for example to one workspace tree:
[policy]
allow_raw_shell = true
allow_cwd_anywhere = false
allowed_roots = ["/root/git"]
max_timeout_secs = 3600
max_output_bytes = 262144After editing the already-running Runner's startup-bound runner.toml, use
check_runner_config(client_id=...), then pass its current_generation to
reload_runner_config(client_id=..., expected_generation=...), then inspect
get_runtime_status(client_id=...). Check never activates the candidate; reload never
writes the file. Invalid candidates preserve the active snapshot/generation, and
restart_required_fields names startup-only changes that are not claimed live
until restart. Unix service reload/SIGHUP remains a compatibility trigger for the
same reload primitive, but is not required for first-class config control. Identity,
server/auth, project source, concurrency, capabilities, and transport changes
remain restart-only where reported.
[instructions].files is explicitly hot-reloadable: after check/reload, new
Project bootstraps use the new list without Runner restart. Changing the contents
of an already-configured instruction file needs no config reload at all; the next
bootstrap re-reads it. These configured instruction paths do not widen
[policy].allowed_roots or ordinary Project filesystem authority, and native
absolute paths are not exposed in startup projection. The current manual
runner.toml configuration is Runner-level and applies to every Project on that
Runner; Desktop selection/upload UI is future work.
[plugins] is live-reloadable: generic Runner config reload and plugin_tool reload
share the same Plugin candidate admission/atomic-commit primitive. Plugin provider
tools remain Runner-local capabilities behind plugin_tool; they are never promoted
into outer MCP tools/list and do not require a Runner restart for discovery.
For a foreground test, run webcodex-runner --profile workstation. Advanced
manual config generation uses webcodex runner init.
OAuth2 remains disabled by default when a Server has no public origin. webcodex server init --public-url https://your-domain.example writes the public URL and enables OAuth with that exact issuer. Direct shared-key auth and the shared-key OAuth bridge remain disabled across that public boundary unless you explicitly add --allow-remote-shared-key.
For a hand-managed env file that intentionally enables remote shared-key auth and the shared-key OAuth bridge, configure:
WEBCODEX_PUBLIC_URL=https://your-domain.example
WEBCODEX_OAUTH2_ENABLED=true
WEBCODEX_OAUTH2_ISSUER=https://your-domain.example
WEBCODEX_SHARED_KEY_ENABLED=true
WEBCODEX_SHARED_KEY_REMOTE_ENABLED=true
WEBCODEX_OAUTH2_SHARED_KEY_BRIDGE=true
The same remote opt-in is required when direct shared-key auth is configured and an enabled QUIC Runner listener binds a non-loopback address; the default QUIC listen address is 0.0.0.0:8443.
For ordinary repository machines, no managed login is required. Connect with the MCP client's exact callback:
cd /path/to/your/repository
webcodex connect https://your-domain.example --auth oauth \
--oauth-redirect-uri https://client.example/callback --project .To let this ordinary shared-key OAuth client offer the fixed optional Computer consent set, explicitly opt in:
webcodex connect https://your-domain.example --auth oauth \
--oauth-redirect-uri https://client.example/callback \
--oauth-computer-permissions --project .The Runner continues using its hosted credential while the MCP client receives a separate OAuth credential. --oauth-computer-permissions and --oauth-local-mcp are explicit opt-ins for optional capabilities; ordinary reconnect never silently adds them. A real OAuth permission change requires the client to authorize again. ChatGPT never receives the Runner/shared-key credential, and OAuth tokens are not valid on Runner transport.
Shared-key OAuth delegation for Browser Use requires explicit --oauth-browser-permissions on connect --auth oauth. It adds only browser:read, browser:control, and browser:launch. The baseline excludes Browser scopes. Browser authority is independent of --oauth-computer-permissions and its consent checkboxes. Existing clients never expand automatically; narrow historical profiles gain only the explicitly selected class. Scope ceiling changes revoke old grants and require reauthorization. Reusing a Browser-enabled profile requires the flag again.
The OAuth authority migration adds admin_authority INTEGER NOT NULL DEFAULT 0 CHECK(admin_authority IN (0, 1)) to authorization-code, access-token and refresh-token tables. It is additive and idempotent; existing rows remain false with no backfill from user roles or current PATs. users.role remains legacy metadata, not runtime/admin authority. Narrow PATs no longer gain admin operations from an admin user role and require account:manage for OAuth client management.
Existing ChatGPT/NewWebCodex OAuth connections do not automatically gain admin authority. Reauthorize once with an admin PAT for a managed-user client owned by that same user. The new grant preserves internal admin authority through exchange and refresh, independently of later PAT revocation/expiry/scope changes. OAuth revocation and disabled-user checks remain effective. Public scopes never include admin; shared-key/project-share OAuth remain non-admin. See Authentication.
If a managed-user OAuth identity is specifically required, use the advanced webcodex login flow followed by webcodex connect ... --auth managed-oauth --oauth-redirect-uri ...; --user applies only there.
Create an OAuth client (the client_secret is returned only once; only its
hash is stored):
curl -fsS -X POST https://your-domain.example/api/oauth/clients/create \
-H "Authorization: Bearer $WEBCODEX_PAT" \
-H "Content-Type: application/json" \
-d '{"name":"ChatGPT MCP","redirect_uris":["https://chatgpt.com/connector/oauth/<callback-id>"],"allowed_scopes":["runtime:read","project:read","project:write","job:run"]}'allowed_scopes limits what an OAuth client may request. Existing clients are not silently widened when WebCodex adds new permissions. To change an existing client, submit the complete desired non-empty allow-list to POST /api/oauth/clients/update_scopes. A real change invalidates the client's old OAuth grants and requires reauthorization; submitting the same canonical list is a no-op. See Authentication for the security model.
ChatGPT MCP host-file import uses two trust tiers. An active authenticated OAuth client may import only from OpenAI attachment hosts: files.oaiusercontent.com and its subdomains, plus the narrowly matched Sediment Azure Blob accounts oaisdmntpr<region>.blob.core.windows.net; those URLs still require HTTPS, public DNS resolution with address pinning, port 443, no userinfo, no redirects, and the normal bounded download/write policy. Configure an exact server-generated OAuth client id in WEBCODEX_OAUTH2_TRUSTED_MCP_FILE_CLIENT_IDS only when that client must also import from arbitrary public HTTPS hosts under the same SSRF controls. Reprovisioning changes the client id but does not break ordinary OpenAI-host attachment import; update the setting to restore the broader Tier 1 trust. Client display names and redirect URIs never grant Tier 1 trust.
A separate local-only exception exists for an operator-controlled Server that is
bound to loopback and reached through OpenAI Secure Tunnel. Set
WEBCODEX_MCP_TRUST_LOOPBACK_API_TOKEN_FILE_IMPORT=true to trust ChatGPT
host-file rewrites only when that request is authenticated by an allowed local
credential: a normal user API token, or the configured Server bootstrap credential
used by the regular Desktop Tunnel. That Tunnel derives the credential from the local
WEBCODEX_TOKEN configuration and injects it privately; do not copy or expose it.
The flag is ignored for non-loopback binds and all other credential classes; leave it
unset on network-accessible Servers.
Only this explicitly trusted loopback provenance may accept a resolved IPv4 address
in the RFC 2544 benchmark / TUN fake-IP pool (198.18.0.0/15), and only when the
HTTPS hostname matches the existing OpenAI attachment-host allowlist. All other
private or sensitive destinations remain blocked; OAuth and remote trusted clients
continue to require public DNS answers. Address pinning, TLS hostname verification,
no-proxy/no-redirect download, file-size limits, and Project write permissions
remain in force. This does not make fake IPs reachable without a correctly
configured local TUN.
List and revoke clients with POST /api/oauth/clients/list and
POST /api/oauth/clients/revoke. OAuth uses the authorization-code flow;
dynamic client registration, OIDC, and the device-code flow are not
implemented. Keep offline_access enabled when a host offers it — it is a
protocol-level refresh-token scope and grants no extra WebCodex permission.
Connect a maintained MCP client to /mcp with a user API token or OAuth.
MCP and generic Runtime HTTP share the same ToolRuntime authority path. v0.5
has no Action adapter, OpenAPI import document or compatibility feature.
See MCP and AI Onboarding.
If ChatGPT reports a conversation-level developer-MCP FORBIDDEN error, do not
treat it as proof that the deployed Server or Runner is down. Verify the
deployment through the operator checks below and use the Host-layer decision tree
in Troubleshooting before rotating credentials, re-registering
projects, or restarting a healthy Runner.
WEBCODEX_AUTHORITY_MODE controls whether consequential runtime tools
auto-execute or require human approval:
| Value | Behavior |
|---|---|
| unset / empty | trusted_agent (default for self-hosted single-operator deployments). |
trusted_agent |
Project work, shell, jobs, git, and validation auto-execute after hard safety checks, with no approval interruptions. Push/tag/publish/release/deploy still require an explicit user task action. |
restricted |
Consequential runtime tools are denied by permission policy. There is no separate Connector command-approval queue. |
Hard safety boundaries (project roots, read-only sessions, path policy,
credential redaction, job cancel semantics) are never relaxed by
trusted_agent. v0.5 accepts only the canonical values above. Remove the retired
WEBCODEX_PERMISSION_MODE variable entirely (not even an empty assignment); its
presence fails closed rather than silently selecting the trusted default.
dev_auto_approve and require_approval are no longer mode aliases.
webcodex ops status --server-url "$SERVER_URL" --token-file "$USER_TOKEN_FILE" --strict
webcodex ops runners --server-url "$SERVER_URL" --token-file "$USER_TOKEN_FILE"
webcodex ops projects --server-url "$SERVER_URL" --token-file "$USER_TOKEN_FILE"
webcodex ops smoke-preflight --server-url "$SERVER_URL" \
--token-file "$USER_TOKEN_FILE" --project agent:workstation:my-repoops commands are read-only and never print token or env values. --strict
makes a FAIL report exit with status 2. WARN means worth reviewing but not a
deploy blocker.
Recommended production smoke sequence:
webcodex ops status ... --strictpasses.POST /api/runtime/statusreturnsservice=webcodexand the expected public URL.list_runnersshows at least one online Runner.list_projectsshowsagent:<client_id>:<project_id>ids.- Read-only project tools work on a known project.
- Write/replace/validate tests are limited to disposable smoke projects.
The Server serves the Runtime Console at /runtime. It projects ordinary runtime,
Project, Runner, Job, Workflow Session, collaboration, and recent activity state
through the same authorization path used by ToolRuntime. Project-scoped credentials
see only their ProjectGrant-visible Runner/Project set; knowing another Project or
Runner id does not widen visibility. The old Connector Project Review Console at
/console and its task/result/approval APIs are removed. Credentials are never
returned by the Runtime Console API.
observe_jobs, list_jobs, and read_job_tail are intended for trusted
single-operator deployments. They are not a tenant boundary between mutually
untrusted users. Do not expose one runtime to multiple untrusted users without
adding job-owner isolation; use separate server/runtime instances instead.
See Troubleshooting for the operational checklist and
common fixes, including existing systemd services, HTTP reachable: no,
missing client CLI on PATH, server-side pairing vs client-side enrollment,
and client online: no.