remote agents
Remote agent environments — Claude Code on the web, Codex cloud, CI runners, devcontainers — start from a fresh, ephemeral machine on every session. Nothing interactive survives between runs, so Mossbear is set up from the environment’s startup or setup script instead of by hand.
before you start
Section titled “before you start”Both recipes below need one thing from the dashboard: a CLI token, from Settings → Developer → CLI Token. Mint it on the same dashboard the container will talk to — staging and production have separate databases, so a production token is not valid on staging.
set up your environment
Section titled “set up your environment”Pick your provider. The two differ in where the setup script lives, how credentials are stored, and what the container is allowed to reach on the network — so each recipe stands on its own rather than sending you between tabs.
Claude Code on the web runs each session on a fresh Anthropic-managed VM with your repo already cloned. One environment dialog holds everything Mossbear needs, and the steps below follow it top to bottom — the hosts the container may reach, the variables the CLI reads, then the script that installs it.
1. open your environment settings
Section titled “1. open your environment settings”At claude.ai/code, select the cloud icon showing the current environment’s name to open the selector, then choose Add environment — or hover an existing environment and click the settings icon on the right. The dialog that opens holds every field in the steps below: name, network access, environment variables, and setup script.
2. allow your dashboard host
Section titled “2. allow your dashboard host”Network access is the first field under the name, and it is the one people skip. The default Trusted level allows npm, so the install succeeds and the sync is what fails later — a failure that looks like a bad token. Select Custom, then add one host per line under Allowed domains:
app.mossbear.ioAdd staging.mossbear.io too if you point sessions at staging. That is the whole list: the
CLI talks to your dashboard and nothing else, and grading runs there rather than from this
machine, so no LLM provider host is involved. These are domains, not URLs, and * works
as a wildcard.
3. add your token as an environment variable
Section titled “3. add your token as an environment variable”Paste this into Environment variables, the next field down. It takes .env format — one KEY=value per line, and no quotes around values, since quotes are stored as part of the value:
MOSSBEAR_TOKEN=your-cli-tokenMOSSBEAR_DASHBOARD_URL=https://app.mossbear.ioOnly the token is required. MOSSBEAR_DASHBOARD_URL defaults to https://app.mossbear.io, so set it only to point the container somewhere else.
4. paste the setup script
Section titled “4. paste the setup script”The last field in the dialog. Setup scripts run as root on Ubuntu 24.04 before Claude Code launches, and their filesystem is cached and reused by later sessions. Four lines:
#!/bin/bashnpm install -g mossbearmossbear init --no-prompt --dashboard-url "${MOSSBEAR_DASHBOARD_URL:-https://app.mossbear.io}" || truemossbear sync || truemossbear doctor || truemossbear init registers the hooks and installs the mossbear-hook binary’s wiring, also migrating any .claude/settings.json still naming the logger by an absolute path from another machine. Passing –dashboard-url explicitly matters even when it is the default: a cached filesystem can carry a dashboardUrl from an earlier build, and config outranks the CLI’s baked-in default.
5. commit the hooks, and check the sync one is there
Section titled “5. commit the hooks, and check the sync one is there”The container is destroyed when the session ends, and anything still on disk inside it goes with it — which is why mossbear init writes a Stop hook that pushes the session before that happens. Step 4 has already put it in your repo’s .claude/settings.json:
{ "hooks": { "Stop": [ { "hooks": [{ "type": "command", "command": "mossbear sync", "timeout": 180 }] } ] }}Commit that file. It lives in the repo rather than in the environment, so the same hooks fire on your own machine, and a session resumed from the cached filesystem still has them.
Track the whole file, every hook in it. Everything mossbear init writes there is machine-independent: the logger runs as the bare command mossbear-hook, resolved from your PATH, so the file is byte-identical on your laptop and in the container and stays that way.
6. keep the CLI current in resumed sessions
Section titled “6. keep the CLI current in resumed sessions”The setup script installs Mossbear once per environment, not once per session. Its filesystem is snapshotted and reused, and a resumed session never re-runs it — so the version installed on the day you built the environment is the version every later session gets, however many releases have shipped since. A SessionStart hook is the part that runs every time, because it lives in your repo rather than in the environment. Add it to the same file as step 5:
{ "hooks": { "SessionStart": [ { "matcher": "startup|resume", "hooks": [ { "type": "command", "command": "[ \"$CLAUDE_CODE_REMOTE\" = true ] && mossbear update --yes || true", "timeout": 600 } ] } ], "Stop": [ { "hooks": [{ "type": "command", "command": "mossbear sync", "timeout": 180 }] } ] }}mossbear update compares against the dist-tag matching the version you are running, so a prerelease container stays on its own channel, and it installs only a strictly newer release — a private or lagging npm mirror cannot walk it backwards. It also restamps the generated hook script, which a plain npm install leaves running the previous version’s code.
7. start a session and read the setup log
Section titled “7. start a session and read the setup log”The setup script’s output appears in the session log, and mossbear doctor is the part to read. All checks passed plus a healthy dashboard line means the loop is connected. If the connection line reports the dashboard as refused by something between your machine and it — explicitly not a rejected token — step 2 is the fix.
Codex cloud runs a task in two phases: a setup phase that has internet access and your secrets, and an agent phase that by default has neither. Mossbear delivers your guides in the setup phase, and — if you configure the agent phase for it — hooks into Codex’s own lifecycle to log and grade what the agent did.
1. create an environment for the repo
Section titled “1. create an environment for the repo”Go to chatgpt.com/codex/settings/environments and select Create environment, then pick the repository. Each environment is tied to a single repo, so this is a per-repo setup rather than a one-time account setting.
2. pick how far you want the loop to go
Section titled “2. pick how far you want the loop to go”This one choice decides the rest of the configuration, so make it first. Codex separates environment variables, which are set for the whole task, from secrets, which are encrypted and removed before the agent phase starts.
Guides only — Mossbear needs the network just long enough to pull your guides during setup. Store the token as a Secret. The agent phase never sees it, so nothing the model does with the repo can reach your dashboard with your credential. This is the safer configuration, and steps 6 and 7 stay at their defaults.
Guides plus session evaluation — the hooks in step 5 also log what the agent did and upload it, and the dashboard grades it there. Nothing is graded on the machine running the task. The Stop hook runs in the agent phase, where a secret has already been wiped, so store the token as an Environment variable instead and expect to open up agent-phase networking in step 7. Read step 5 before choosing this one: Codex has no session-end event, so your runs are graded later than they are under Claude Code.
MOSSBEAR_TOKEN=your-cli-tokenEither way, add MOSSBEAR_DASHBOARD_URL as a plain environment variable if you point at a dashboard other than https://app.mossbear.io. It isn’t a credential, and keeping it out of the secret means you can read it back later.
3. install and pull your guides in the setup script
Section titled “3. install and pull your guides in the setup script”The setup phase has internet access, so the install and the pull both belong here:
npm install -g mossbearmossbear init --no-prompt --pull-guides --no-skill --no-eval-hook --dashboard-url "${MOSSBEAR_DASHBOARD_URL:-https://app.mossbear.io}" || trueOne command, because the pull is part of init now. –pull-guides subscribes the repo to your default bundle and writes that bundle’s members to disk in the same run, and it does both without prompting — which is what a setup script needs, since nobody is there to answer. Each guide lands at the path it carries, so Codex finds AGENTS.md where it expects it, and the subscription is recorded in .mossbear/config.json so every later sync refreshes the same set.
init stores your token and prepares ~/.mossbear for sync. The two –no flags skip the Claude Code skill and the local-eval Stop hook, which have no meaning here; init still leaves a .claude/settings.json behind carrying the mossbear-hook logger and a mossbear sync Stop hook, which is inert under Codex and is what wires the loop up for Claude Code if you also use it on this repo. Pass –no-sync-hook too if you’d rather it wrote neither.
4. decide what the pull is allowed to change
Section titled “4. decide what the pull is allowed to change”The restore writes your guides back byte for byte, so when the repo and the dashboard already agree, nothing changes and nothing shows up in the pull request Codex opens. When they’ve drifted, it is a real edit to AGENTS.md — review it once and commit it. A file you edited in the repo and did not sync back is reported and left alone rather than overwritten, so the container never silently discards work in progress.
5. wire Mossbear into Codex’s lifecycle hooks
Section titled “5. wire Mossbear into Codex’s lifecycle hooks”Codex fires the same lifecycle events Mossbear already listens for — PostToolUse after each tool call, Stop when the model signals it’s finished. Declare them in a .codex/config.toml committed at your repo root, where they travel with the clone and work on your own machine too:
[hooks]
[[hooks.post_tool_use]]name = "mossbear-log-action"command = ["bash", "-lc", "mossbear-hook"]
[[hooks.stop]]name = "mossbear-sync"command = ["bash", "-lc", "mossbear sync || true"]mossbear-hook is the same binary Claude Code’s PostToolUse hook already calls — it reads
session_id, tool_name and tool_input from the event on stdin, which is exactly what Codex’s
PostToolUse sends, so no adapter is needed. Tool names differ between agents though, so a
logged action may carry less command and file detail than the same action under Claude
Code. The || true on the Stop hook keeps a failed push from surfacing as a hook error —
nothing is lost, since a batch that fails leaves the cursor unadvanced and its actions go
out on the next successful sync. The sync also refreshes any doc bundles this repo
subscribes to, which is safe on an automatic hook: an edited file is reported rather than
overwritten, and the only files it removes are ones Mossbear wrote and can still match
against what it wrote.
6. keep resumed containers fresh
Section titled “6. keep resumed containers fresh”Codex caches the container after the setup script and reuses it, running the optional maintenance script instead of setup when it resumes one. Two things go stale in a reused container — the guides, and the CLI reading them — so put both in the maintenance script:
mossbear update --yes || truemossbear sync || trueWithout the update line the container keeps whatever version the setup script installed on the day the cache was built, however many releases have shipped since. mossbear update stays on the channel the installed version is already on and installs only a strictly newer release, so it cannot walk backwards.
Editing the setup script, the maintenance script, an environment variable, or a secret invalidates the cache and runs setup again — so a change there takes effect on the next task, not the next resume.
7. open the agent phase only if you want verdicts
Section titled “7. open the agent phase only if you want verdicts”On the guides-only path, leave agent-phase internet access off: Mossbear has finished its network work by the time the agent starts, and there is nothing to turn on.
On the guides-and-verdicts path, the Stop hook’s sync runs inside the agent phase, so all three of these have to be true or it silently queues forever: agent-phase internet enabled, your dashboard host on the allowlist, and the unrestricted method setting — the GET, HEAD and OPTIONS preset blocks the POST that mossbear sync makes.
8. verify what the agent read and what it sent
Section titled “8. verify what the agent read and what it sent”Check the files first, because they are what Codex actually reads. Ask Codex to print them, then to run the CLI’s own view of the cached bundle and the local queue:
cat AGENTS.mdls .agents/
mossbear guides activemossbear statusRead them in that order, and don’t let the second stand in for the first. mossbear guides active lists what the bundle contains, not what reached disk, so a dashboard-authored guide appears there while AGENTS.md stays empty — the exact failure the note in step 3 describes. An empty bundle instead means the pull never reached the dashboard: check the setup log for that command, and confirm the token was stored on this environment rather than another one. A pending count that never falls to zero means the hooks are logging but the sync can’t get out, which is step 7.
mossbear init flags for scripted setups
Section titled “mossbear init flags for scripted setups”- –no-prompt — skip all interactive questions.
- –dashboard-url — point the CLI at a different dashboard instance.
- –no-skill / –no-eval-hook — skip installing the Claude Code skill or the session-end evaluation hook if the environment doesn’t need them.
- –force — overwrite config left over from a previous image layer.
credentials come from the environment
Section titled “credentials come from the environment”Init needs no token — everything it does is local. To sync verdicts, store a CLI token as a secret in your environment’s variables rather than committing it. The CLI reads both of these directly, so no command in either recipe needs a flag:
MOSSBEAR_TOKEN=... # CLI token — required to syncMOSSBEAR_DASHBOARD_URL=https://app.mossbear.io # optional — override the dashboardA token from the environment is never written to ~/.mossbear/config.json — the environment owns the credential, so revoking it there ends access everywhere it was used. A –token flag still takes precedence and is still remembered after a successful sync; an explicit –dashboard-url likewise overrides the variable.
network access
Section titled “network access”Many cloud environments route outbound traffic through a proxy that only allows approved hosts, and a blocked host is refused with a 403. Package registries are usually allowed by default, so the install succeeds and the sync is what fails. mossbear doctor tells these apart: a blocked host is reported as refused by something between your machine and the dashboard, explicitly not a rejected token. When you see that, allow these hosts in your environment’s network settings:
- your dashboard host — sync, guide pull, and the doctor connection check.
- your eval provider host — only for LLM-graded checks. Pattern-matched checks need no network at all.
how verdicts leave the machine
Section titled “how verdicts leave the machine”Hooks log actions and queue verdicts locally under ~/.mossbear/ inside the container. They reach your dashboard when mossbear sync runs — at session end via a Stop hook, or explicitly as a step in your script. Verdicts that cannot be sent stay queued and go out on the next successful sync:
mossbear syncevaluation provider
Section titled “evaluation provider”Pattern-matched checks run with no API key at all. For LLM-graded checks the CLI calls your configured provider, which in remote environments is easiest to configure through environment variables:
MOSSBEAR_EVAL_PROVIDER=anthropic # openrouter | anthropic | openai-compatible | google | ollamaMOSSBEAR_EVAL_MODEL=claude-sonnet-4-6ANTHROPIC_API_KEY=sk-ant-... # or the matching provider keyMOSSBEAR_EVAL_API_KEY and MOSSBEAR_EVAL_BASE_URL override the provider-specific variables when set. Provider calls send action metadata and guide text only — never file contents or transcripts.
New to Mossbear? Start with getting started for the basics.