CLI

Running ox in constrained environments

"Ephemeral" is not a mode. It's a label for a constellation of capabilities the sandbox provides or withholds. Claude Code Cloud, Devin, GitHub Actions, and Codespaces are not all the same shape — one has persistent disk and an 8-hour lifetime, one writes disk to a snapshot but can run a daemon, one is gone in five minutes. ox probes each capability once at startup and lets every subsystem ask for what it actually needs.

This page covers the capability model, what works and what doesn't, how to set ox up on each major platform, and the exact error strings runbooks should grep for.

The capability model

When ox starts, it probes the environment for four capabilities and a lifetime hint:

CapabilityQuestionSet by
PersistDiskWill writes to ~/.sageox/ survive the next ox invocation?filesystem probe + OX_PERSIST_DISK override
LongLivedHelperCan a background helper outlive a single CLI invocation?lifetime hint + OX_NO_DAEMON override
BrowserCan ox open an interactive URL for device-code auth?TTY + DISPLAY/BROWSER + known-headless detection
NetworkIs egress open, allowlisted, proxied, or offline?platform-derived; OX_NETWORK override

Subsystems consult capabilities directly. The local CodeDB indexer only runs when PersistDisk && LongLivedHelper. ox login's browser flow only attempts to launch when Browser is true; otherwise it refuses with a clear "set SAGEOX_TOKEN" message. Session staging picks ~/.sageox/sessions/... when PersistDisk is true, otherwise $TMPDIR/ox-sessions/....

Capability matrix per platform

PlatformPersistDiskLongLivedHelperBrowserNetworkLifetime
Local laptopyesyesyesopenpersistent
GitHub Codespacesyesyesnoopenhours
Claude Code Cloudnoyesnoallowlisthours
Devinnoyesnoopenhours
GitHub Actions runnernononoopenminutes

Codespaces is no longer treated as ephemeral. A Codespace has persistent disk and a multi-hour lifetime — it behaves like a slow laptop. The previous CODESPACES=true auto-detection forced the constrained path and threw away the persistent disk Codespaces paid for. First-run does the setup, second-run reuses it.

--ephemeral is deprecated and will be removed. It only sets the env var inside one ox process — POSIX prevents writing back to the calling shell, so subsequent ox commands lose the signal and silently degrade. The canonical pattern is to export OX_EPHEMERAL=1 into the session env (e.g. write it to $CLAUDE_ENV_FILE before running ox), which is what every recipe on this page already does. The flag is redundant at best and a footgun at worst. The CLI accepts it for one release with a stderr deprecation warning, then removes it.

Override knobs

You can override the probe at the capability level:

VariableEffect
OX_PERSIST_DISK=0Force the in-memory / $TMPDIR paths even when ~/.sageox/ is writable
OX_NO_DAEMON=1Disable any background helper; run everything inline in the CLI process
OX_NETWORK=allowlistTell ox it is running behind an egress allowlist (skip probes that would spuriously fail)
OX_EPHEMERAL=1Coarse shortcut: sets PersistDisk=false, LongLivedHelper=false

Use the capability-level overrides when the auto-probe gets it wrong (e.g. a brand-new sandbox ox doesn't recognize). Use OX_EPHEMERAL=1 when you just want "treat me as fully constrained" without spelling out each axis.

Prerequisites

  • A Personal Access Token exported as SAGEOX_TOKEN.
  • A static ox binary on PATH. The install script at https://install.sageox.ai/ox ships a single self-contained Go binary with no runtime dependencies.

What works, what doesn't

Subcommand behavior in constrained environments. "Designed-for" subcommands are the supported entrypoints; "degraded" still works but with a noted trade-off; "refused" exits non-zero with a clear pointer.

SubcommandPersistDisk=falseLongLivedHelper=falseBrowser=false
ox agent primedesigned-fordesigned-fordesigned-for
ox distill --since=<window>designed-fordesigned-fordesigned-for
ox statusdesigned-fordesigned-fordesigned-for
ox doctordesigned-fordesigned-fordesigned-for
ox query "..."works (refetches each call)worksdesigned-for
ox code searchdegraded (remote-only; slower)degraded (remote-only)designed-for
ox import <video>works (stages to $TMPDIR, syncs at exit)works (sync upload at exit)designed-for
ox kb writesdegraded (warns; writes lost at exit)worksdesigned-for
ox loginworksworksrefused — exit non-zero, pointer to PATs
ox daemonrefusedrefusedworks

Session uploads are idempotent on (agent_id, session_id). Retrying a failed upload is safe — the second call is a no-op if the first actually succeeded.

Error contract

These strings are part of the CLI's contract surface. Operator runbooks grep for them; they will not be reworded without a deprecation pass.

StringCauseFix
SAGEOX_TOKEN expired or invalidPAT revoked, expired, or wrong environmentRotate at Settings → Tokens; update secret store
SAGEOX_TOKEN not set; ox login is unavailable in this environmentNo PAT and no browser capabilityExport SAGEOX_TOKEN (or run on a host with a browser)
failed to clone team context: dial tcp: i/o timeoutNetwork allowlist gapAdd team-context git host to platform's allowed hosts
ox: 'daemon' requires a long-lived helper and is unavailable hereox daemon invoked in a sandbox without LongLivedHelperUse the inline command path — daemon is not required
ox: 'login' requires a browser and is unavailable hereox login invoked without Browser capabilityAuthenticate with SAGEOX_TOKEN
OX_EPHEMERAL=1 set but you appear to be on a persistent machineStray export from a previous CI experimentUnset OX_EPHEMERAL in your shell

Exit codes follow sysexits.h: usage errors are 64, auth failures are 77.

Claude Code Cloud

Anthropic's Managed Agents run Claude Code in an isolated sandbox defined by an environment YAML.

Environment file

# .claude/environment.yml
packages:
  go: ["github.com/sageox/ox/cmd/ox@latest"]

secrets:
  SAGEOX_TOKEN: oxp_...    # paste your PAT here, or reference a secret

hooks:
  SessionStart: |
    ox agent prime
  • packages.go installs ox from source into the sandbox image. Pin to a specific version (@v0.42.0) for reproducibility once you have one you like.
  • secrets.SAGEOX_TOKEN is injected at runtime — it does not appear in the image layer. Prefer Anthropic's Secrets dashboard over inlining the value.
  • hooks.SessionStart runs at the start of every Claude session. ox auto-detects the constrained environment from CLAUDE_CODE_REMOTE and configures itself accordingly — no OX_EPHEMERAL export needed.

Networking

Claude Code Cloud defaults to limited egress. Add the SageOx hosts to your allowed_hosts:

network:
  mode: limited
  allowed_hosts:
    - api.sageox.ai
    - install.sageox.ai
    - <your-team-context-git-host>   # gitlab.com, github.com, etc.

If you forget the team-context git host, ox agent prime will fail with a clone error pointing at the missing domain. Add it and retry.

Verify

Start a Claude session and look for these lines in the SessionStart log:

ox agent prime: constrained environment (CLAUDE_CODE_REMOTE)
ox agent prime: authenticated as <your-email>
ox agent prime: team context loaded (<N> files)

If you see SAGEOX_TOKEN expired or invalid, rotate the PAT — see Personal Access Tokens.

Devin

Cognition's Devin repo setup splits configuration into three sections: setup (bakes into the snapshot), startup (runs every session), and secrets.

Setup commands

One-time, runs when Devin builds the workspace snapshot:

curl -fsSL https://install.sageox.ai/ox | sh
ox --version    # sanity check that install succeeded

Startup commands

Runs at the start of every Devin session, on top of the snapshot:

ox agent prime

ox auto-detects the Devin sandbox from DEVIN_TASK_ID and configures itself accordingly.

Secrets

In the Devin Secrets dashboard, add:

SAGEOX_TOKEN=oxp_...

Devin injects this into the environment before startup commands run. Never paste the token into the startup-commands box — that value is logged.

Notes

  • Devin keeps the snapshot warm across sessions, so ox agent prime runs against a pre-installed binary. If you bump ox, re-run the setup commands or invalidate the snapshot.

GitHub Actions

For CI use cases — generating release notes, distilling sessions into team context, running scheduled queries:

# .github/workflows/ox-distill.yml
name: ox distill
on:
  schedule:
    - cron: "0 6 * * *"     # daily at 06:00 UTC
  workflow_dispatch:

jobs:
  distill:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install ox
        run: curl -fsSL https://install.sageox.ai/ox | sh

      - name: Prime and distill
        env:
          SAGEOX_TOKEN: ${{ secrets.SAGEOX_TOKEN }}
        run: |
          ox agent prime
          ox distill --since=24h

For self-hosted runners with a persistent filesystem, ox will detect the persistent disk and use its normal local-state mode — you'll get better performance from the local CodeDB index without any config change.

GitHub Codespaces

A Codespace has persistent disk and a multi-hour lifetime. Treat it as a slow laptop, not a sandbox:

# one-time workspace setup
curl -fsSL https://install.sageox.ai/ox | sh
ox login            # one-time, opens browser via Codespaces port-forward
ox init             # connects the workspace to your repo

After the first-run setup, every subsequent terminal session in the Codespace has ox configured from local state — no PAT needed, no per-session priming.

If you'd rather skip the device-code flow, export SAGEOX_TOKEN from a Codespaces secret instead of ox login. Both work; the device flow gives you the same credential refresh story as your laptop.

Troubleshooting

SAGEOX_TOKEN expired or invalid

The PAT has expired, been revoked, or is malformed. Create a new one at Settings → Tokens and update the secret in your agent platform. See Personal Access Tokens.

401 Unauthorized against a custom endpoint

ox defaults to https://api.sageox.ai. If you set SAGEOX_ENDPOINT to a staging endpoint, confirm the PAT was issued against that same environment. PATs are bound to the endpoint they were issued for.

failed to clone team context: dial tcp: i/o timeout

Network allowlist gap. The sandbox blocked the git host. For Claude Code Cloud, add the host to network.allowed_hosts. For Devin, check the workspace network settings. For GitHub Actions, this usually means a private team-context repo requires an SSH key — use HTTPS with a Git PAT instead.

ox: command not found

The install script failed silently, often because curl was missing from a minimal base image. Use the platform's package manager to install curl first, then re-run the install script. For Devin, add the install step to the setup commands so it runs in a snapshot with a fuller toolchain.

OX_EPHEMERAL=1 set but you appear to be on a persistent machine

You exported OX_EPHEMERAL=1 in a previous shell session and forgot. Unset it to restore full functionality. ox doctor will flag this on every invocation until you do.

Auto-detection misfired

If ox runs in constrained mode when you expect full mode (or vice versa), use the capability-level overrides above (OX_PERSIST_DISK, OX_NO_DAEMON, OX_NETWORK) rather than the coarse OX_EPHEMERAL=1. Run ox doctor to see which detection rule fired and what the resulting capabilities are.