Omnigent on Islo
Islo sandboxes give you disposable cloud machines for running Omnigent hosts, two ways:
- CLI-launched:
omnigent sandbox create/connectprovisions a sandbox from your terminal, ships your local checkout into it, and registers it as a host with your server. - Server-managed: the server provisions a sandbox automatically when
a session is created with
"host_type": "managed"and terminates it when the session is deleted.
Sandboxes boot from the official prebaked host image, so startup is
seconds. Unlike Modal and Daytona, the Islo launcher talks to the Islo
HTTP API directly through httpx (already an Omnigent dependency), so
there is no provider SDK extra to install — just an API key.
What makes Islo different from the other providers, and shapes the rest of this guide:
- A credential gateway. Islo can inject LLM/API keys into a sandbox's outbound traffic at the network layer, so the raw key never reaches the sandbox process. This is a first-class, recommended path for model credentials (see Model credentials) and has no equivalent on Modal or Daytona.
- No local port forward. Islo can't forward a sandbox→laptop callback
port, so the interactive in-sandbox
omnigent login/ App OAuth step is skipped automatically (as on Modal and Daytona). - No lifetime cap. Islo sandboxes run until deleted (like Daytona, unlike Modal's 24 h).
Prerequisites
Install the Islo CLI and create an API key, then make it available where the launcher runs — your shell for the CLI flow, the server process for managed sandboxes:
curl -fsSL https://islo.dev/install.sh | sh # install the islo CLI
islo login # browser OAuth (one-time)
islo api-key create omnigent --show # prints an islo_key_… value
export ISLO_API_KEY=islo_key_…
# Optional: a non-default API endpoint
# export ISLO_BASE_URL=https://api.islo.dev
ISLO_API_KEY is exchanged for a short-lived session token at
POST /auth/token; the token is cached until shortly before expiry. The
key is the only required credential — no SDK, no ~/.config file.
Note
Islo cannot forward a local callback port into the sandbox. The interactive
omnigent loginbrowser flow (and the in-sandbox App OAuth callback) needs a sandbox→laptop port forward, which Islo doesn't provide — so the CLI skips that step automatically, exactly as it does for Modal and Daytona. For a server that requires authentication, inject the credentials instead (see Connecting to an authenticated server).
The host image
Sandboxes boot from ghcr.io/omnigent-ai/omnigent-host:latest, published
by CI from the host target of
deploy/docker/Dockerfile with Omnigent and its
dependencies preinstalled — including the coding-harness CLIs (claude,
codex, pi, kiro-cli), so agents on any harness run without an in-sandbox
install.
To use a different image (a fork, or extra tooling baked in), build the same target and push it anywhere Islo can pull from:
docker build -f deploy/docker/Dockerfile --target host \
--platform linux/amd64 \
-t docker.io/<you>/omnigent-host:latest .
docker push docker.io/<you>/omnigent-host:latest
Then point Omnigent at it — OMNIGENT_ISLO_HOST_IMAGE for the CLI flow,
or sandbox.islo.image in the server config for the managed flow. For a
private registry, configure the pull credentials on the Islo side (Islo
pulls the image, not Omnigent).
Important
Native terminals need
bubblewrap. Theclaude-native/codex-native/kiro-native/piharnesses wrap each agent terminal in a bubblewrap (bwrap) OS-sandbox, and on Linux that isolation is mandatory and fail-loud — a host image without thebwrapbinary makes those terminals fail to start (linux_bwrap sandbox requires the 'bwrap' binary on PATH). ThehostDockerfile target installsbubblewrap; if you bring your own image, install it there too. See Troubleshooting.
CLI-launched sandboxes
Provision a sandbox and ship your local checkout into it:
omnigent sandbox create --provider islo
This pulls the host image, builds wheels from your local checkout, and overlays them on top — so the sandbox runs your code, not whatever the image was built from. Then register it as a host with your server:
omnigent sandbox connect --provider islo \
--sandbox-id <id-printed-by-create> \
--server https://your-host
connect runs omnigent host inside the sandbox and holds the
connection open in your terminal — Ctrl-C tears it down. New sessions
targeting that host now run in the sandbox.
Running multiple sandboxes against one server? Pass a unique
--host-name <label> to each connect — the server keys hosts on
(owner, name), and sandboxes that share a hostname collide.
Sandboxes are disposable. When your code changes, create a new one — and
delete the old one (Islo sandboxes have no lifetime cap, so an abandoned
sandbox keeps billing until removed via islo rm <id> or the
dashboard).
To inject LLM/git credentials into a CLI-launched sandbox, set
OMNIGENT_ISLO_SANDBOX_ENV in your shell to a comma-separated list of
variable names (e.g. ANTHROPIC_API_KEY,GIT_TOKEN) before running
create — the named variables are copied from your environment into the
sandbox at provision time. A listed name that is not set fails the
launch loudly (it would otherwise surface much later as an opaque harness
auth failure inside the sandbox).
Connecting to an authenticated server
connect runs omnigent host inside the sandbox, and that host must
present credentials when it dials back to a server that requires
authentication. The interactive omnigent login browser flow can't run
inside an Islo sandbox (no callback port forward), so inject the keys for
the relevant server instead — name them in OMNIGENT_ISLO_SANDBOX_ENV
before create:
export OMNIGENT_ISLO_SANDBOX_ENV=DATABRICKS_HOST,DATABRICKS_TOKEN
omnigent sandbox create --provider islo
The in-sandbox host mints a fresh bearer token from those credentials on
every connect and reconnect. For a Databricks-fronted server, inject
DATABRICKS_HOST plus either DATABRICKS_TOKEN (a PAT) or
DATABRICKS_CLIENT_ID / DATABRICKS_CLIENT_SECRET (an OAuth service
principal — re-minting keeps a long-lived sandbox connected past any
single token's expiry).
A server with no authentication on the host tunnel needs none of this, and neither do server-managed sandboxes — those authenticate with a server-minted per-launch token automatically.
Server-managed sandboxes
Add a sandbox: section to the server config (omnigent server -c config.yaml, or <data_dir>/config.yaml):
sandbox:
provider: islo
server_url: https://your-host # public URL sandboxes dial back to
server_url must be reachable from Islo's cloud — a public HTTPS URL,
not localhost. The server itself needs ISLO_API_KEY (and optional
ISLO_BASE_URL) in its environment. Sessions created with
host_type: "managed" (the API call or the Web UI's New Sandbox option)
then run on a fresh Islo sandbox; the create returns immediately and
provisioning happens in the background, exactly like the Modal managed
flow — including repository
workspaces, the first-message rendezvous, and dead-sandbox relaunch.
curl -X POST https://your-host/v1/sessions \
-H "Content-Type: application/json" \
-d '{"agent_id": "agent_...", "host_type": "managed"}'
Each managed sandbox authenticates back with a server-minted, per-launch token (7-day TTL — see Lifecycle); no user credentials enter the sandbox for the server connection.
Managed hosts and server auth
How the dial-back authenticates depends on how the server does auth, and there is one interaction worth knowing before you deploy. A managed sandbox opens two kinds of connections back to the server:
- the host tunnel (
/v1/hosts/<id>/tunnel), which the per-launch token authenticates directly — the server mints it, scopes it to one host, and resolves the owner from it. This always works. - one runner tunnel per session (
/v1/runners/<token>/tunnel), opened by the runner subprocess the host spawns. The runner authenticates with whatever server credential it can resolve — a proxy-injected identity (header / OIDC), or a storedomnigent logintoken (local hosts only; a fresh managed sandbox has none) — not the per-launch host token.
The consequence:
- Header / OIDC-proxy auth, or single-user (no-auth) servers — the
runner tunnel needs no extra identity, so managed hosts work out of the
box. Verified end-to-end on a single-user server: a session created
with
host_type: "managed"provisioned an Islo sandbox from the bwrap image, the launcher cleared the seededapiKeyHelper, the host and runner tunnels connected, and a native Claude terminal ran on the injectedCLAUDE_CODE_OAUTH_TOKENsubscription. - The built-in
accountsprovider (OMNIGENT_AUTH_ENABLED=1) — the runner tunnel additionally requires a user identity, which the per-launch host token does not carry, so the runner dial-back is refused (403) even though the host tunnel connects. This is a framework-level managed-host interaction shared by all sandbox providers (Modal / Daytona / Islo), not specific to Islo.
So for a managed Islo deployment, front the server with header or OIDC
auth (a reverse proxy / IdP injects the user identity on every request,
including the runner WebSocket — see
deploy/README.md#auth), or run it single-user. The
accounts provider is fine for CLI-launched hosts (you omnigent login,
and that token is what the in-sandbox host forwards), but not yet for the
managed runner dial-back.
Optional islo: settings:
sandbox:
provider: islo
server_url: https://your-host
islo:
image: docker.io/<you>/omnigent-host:latest # default: official image
env: [OPENAI_API_KEY, GIT_TOKEN] # copy from server env
base_url: https://api.islo.dev # non-default API endpoint
gateway_profile: default # Islo gateway for egress + credential injection
snapshot_name: warm-host # boot from a prebaked snapshot
workdir: /root/workspace # sandbox working directory
vcpus: 2
memory_mb: 4096
disk_gb: 20
Model credentials (LLM keys)
A fresh sandbox has no model credentials of your own. Islo offers two distinct ways to give the agent a model — and they interact, so pick one deliberately per harness.
Option A — Islo gateway integration (recommended)
This is the Islo-native path with no Modal/Daytona equivalent. Islo gateways "automatically attach API keys, tokens, and secrets to outbound requests" at the network layer — "credentials never reach the sandbox process." You connect a provider once, server-side, and every sandbox picks it up:
islo login --tool claude # OAuth-connect Anthropic (alias: --tool anthropic)
islo login --tool openai # …and/or OpenAI
islo status # shows connected integrations
This is not Claude-specific — it's how Islo supplies model
credentials to every harness. Islo pre-seeds each sandbox with a
phantom placeholder key (islo_phantom_…) in whatever location the
harness reads, per provider:
| Harness | Phantom key location | Provider endpoint |
|---|---|---|
Claude Code (claude-native, claude-sdk, pi) |
apiKeyHelper in ~/.claude/settings.json |
api.anthropic.com |
| Codex / OpenAI agents | OPENAI_API_KEY env var |
api.openai.com |
The harness sends that placeholder to its provider endpoint; the gateway intercepts the request and swaps it for your connected credential before forwarding. The raw key never lands in the sandbox, and the connection is team-wide — other members don't need their own. (We observed both phantom keys pre-seeded in a single sandbox.)
These integrations connect provider API keys (per-token billing), not
plan/subscription auth — --tool claude gives an Anthropic API key, not a
Claude Pro/Max subscription; --tool openai gives an OpenAI API key, not
a ChatGPT plan. To use a subscription or plan token on any harness (a
Claude Pro/Max token, a Codex access token), use
Option B.
Important
If
islo statusshows "No integrations connected" for a provider, its phantom key resolves to nothing — the harness falls back to a failing request (Claude reports "API Usage Billing" and retries). Connect the integration for each provider whose harness you use.
Path A under managed hosts
This is where the gateway shines: when the server launches sandboxes, you configure no model credential on the Omnigent side at all. The flow:
admin (once): islo login --tool claude → connects Anthropic to the Islo ACCOUNT
│
server ──ISLO_API_KEY──▶ Islo API "create sandbox" ──▶ sandbox under that account
│ Islo pre-seeds the
│ phantom apiKeyHelper
▼
agent's claude → api.anthropic.com (phantom key) ──▶ Islo gateway swaps in the real key
The Omnigent server only ever holds ISLO_API_KEY — the credential it
uses to create sandboxes. Because every managed sandbox is created under
that Islo account, and integrations are connected at the account/team
level, each one inherits the connected Claude credential through the
gateway automatically. The only Omnigent-side knob is which gateway a
managed sandbox uses:
sandbox:
provider: islo
server_url: https://your-host
islo:
gateway_profile: default # the Islo gateway carrying the connected integration
Two consequences worth internalizing:
- No model secret lives in the Omnigent server's config or
environment — nothing to leak there. Contrast Option B under managed
hosts,
where the key sits in
sandbox.islo.env(copied from the server's env into each sandbox). - The integration must be connected on the same Islo account the
server's
ISLO_API_KEYbelongs to. If your server runs under a dedicated service/CI Islo account, runislo login --tool claudewhile authenticated as that account — not a personal laptop login.
Option B — Omnigent env injection (your own key or a subscription)
Bring your own credential by naming it in OMNIGENT_ISLO_SANDBOX_ENV
(CLI) or sandbox.islo.env (managed); the launcher copies the value from
the launching environment into the sandbox, and the in-sandbox host
forwards the standard harness credential vars to its runners:
| Variable | Enables |
|---|---|
ANTHROPIC_API_KEY |
Claude models on the Anthropic API |
ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL |
Anthropic-compatible gateways (LiteLLM, Bedrock/Vertex bridges, corporate proxies) |
CLAUDE_CODE_OAUTH_TOKEN |
claude-code with a Claude subscription (no API key) |
OPENAI_API_KEY / OPENAI_BASE_URL |
OpenAI or any OpenAI-compatible endpoint (OpenRouter, vLLM, Ollama, …) |
CODEX_ACCESS_TOKEN |
codex with a ChatGPT Business/Enterprise workspace |
GEMINI_API_KEY |
Gemini on the Google AI API |
The full per-plan recipes (subscriptions, gateways, open-source models)
are identical to Modal — see the variable table and
recipes. For a
Claude subscription specifically, run claude setup-token on your own
machine (one-time browser auth) and inject the resulting long-lived token
as CLAUDE_CODE_OAUTH_TOKEN. For env vars beyond the standard set, inject
OMNIGENT_RUNNER_ENV_PASSTHROUGH=NAME1,NAME2.
Note
Your injected Claude credential automatically wins over Islo's phantom helper. Islo seeds an
apiKeyHelperinto every sandbox, and Claude Code would normally prefer it over aCLAUDE_CODE_OAUTH_TOKEN/ANTHROPIC_API_KEYin the environment. So when you inject one of those, the Islo launcher strips the seededapiKeyHelperat provision time — for both CLI-launched and server-managed sandboxes — leaving your credential as the sole auth path. No manual step, nothing to run inside the sandbox.
Codex/OpenAI needs nothing special either — its phantom is the
OPENAI_API_KEY env var, which your injected OPENAI_API_KEY simply
overrides.
Choosing between A and B
| Option A — gateway | Option B — env injection | |
|---|---|---|
| Credential | provider API key (Anthropic, OpenAI, …) | your API key or subscription/plan token |
| Billing | per-token API | API key, or your subscription/plan |
| Key in sandbox? | No (gateway injects out-of-band) | Yes (in the sandbox env) |
| Scope | account/team-wide, all sandboxes | per-sandbox |
| Managed-host config | just gateway_profile; no secret on server |
key in sandbox.islo.env |
| Best for | managed / production, API-key billing | your own subscription or key; CLI or managed |
| Catch | needs a connected integration | the injected var must be set where the launcher runs |
Pick one per harness — connect Islo's integration or inject your own
credential, never both. Both work in either launch flow: the launcher
strips Islo's seeded apiKeyHelper automatically when you inject a Claude
credential. The same two patterns apply to Codex/OpenAI (islo login --tool openai, or inject OPENAI_API_KEY / CODEX_ACCESS_TOKEN).
Git credentials (private repositories)
Inject an HTTPS token as GIT_TOKEN (GitLab: add GIT_USERNAME=oauth2)
via OMNIGENT_ISLO_SANDBOX_ENV / sandbox.islo.env. The host image's git
credential helper answers HTTPS auth from it for both the launch-time
clone and the agent's later fetch / push, writing nothing to disk. Use
HTTPS repository URLs. Details by provider match the Modal git
guide.
Security considerations
- Path A keeps the model key out of the sandbox — a real advantage. With the gateway, the agent process only ever sees the phantom placeholder; the real key is injected at Islo's network edge. A prompt-injected or compromised agent can't exfiltrate a key it never holds. This is the strongest credential posture of the three providers for model keys, and the reason to prefer Option A where API-key billing is acceptable.
- The gateway terminates TLS to inject. Credential injection means
Islo's gateway sits in the path of the agent's outbound LLM traffic and
re-originates it — so that traffic (prompts, completions, tool output
sent to the model) is visible at Islo's edge. Acceptable for most teams,
but weigh it for highly sensitive workloads, and scope the
gateway_profileto exactly the egress you intend. - Option B puts the key in the sandbox. A subscription token or API key
named in
sandbox.islo.envis copied into the sandbox environment at provision time and lives there for the sandbox's life. Prefer scoped, short-lived credentials, and rely on the per-terminalbwrapsandbox (below) to keep the agent away from it. - The agent terminal is sandboxed away from those secrets. Native
harness terminals run under a bubblewrap OS-sandbox that masks dotfiles
(
~/.ssh,~/.aws, the injected~/.omnigentserver token) and pins the agent to its workspace — defense-in-depth inside the Islo sandbox, independent of Islo's own isolation. This is why the image must shipbwrap(see the host image). - All managed sandboxes share one Islo org +
ISLO_API_KEY. Cross-user isolation rides on Islo's sandbox boundaries, and the shared org key can enumerate and delete any sandbox — the same single-tenant-org shape as the Modal and Daytona providers. Scope the org to this workload. - The launch token's lifetime is 7 days. Islo sandboxes have no platform lifetime cap, so the per-launch host token must outlive a long-running sandbox across reconnects (a longer replay window than Modal's ~24 h; same as Daytona). A relaunch mints a fresh one.
Lifecycle notes
- No platform lifetime cap. Unlike Modal's 24-hour limit, Islo
sandboxes run until deleted. The managed flow deletes a sandbox when its
session is deleted, and the dead-sandbox relaunch path replaces one that
crashed or was removed out-of-band. CLI-launched sandboxes you delete
yourself (
islo rm <id>). - Resources. Sandboxes default to 2 vCPUs and 4 GiB of memory;
override per managed launch with
vcpus/memory_mb/disk_gb. - Warm starts. Set
sandbox.islo.snapshot_nameto boot from a prebaked Islo snapshot instead of a cold image pull. - Provider-side lifecycle (list / status / delete / stop) — use the
isloCLI (islo ls,islo rm <id>) or the dashboard directly.
Cost
Islo bills usage with no seat licenses or idle fees: ~$0.07/CPU-hour, ~$0.04/GB-hour of memory, ~$0.0007/GB-hour of disk — about $0.25/hour for the default 2 vCPU / 4 GiB sandbox while it runs. New accounts get $50 of free credits. Rates: islo.dev.
Troubleshooting
- Native Claude/Codex terminal fails with
linux_bwrap sandbox requires the 'bwrap' binary on PATH. The native harnesses wrap each agent terminal in a bubblewrap OS-sandbox; the host image must shipbubblewrap. ThehostDockerfile target installs it — rebuild from a current image, or for a one-off on a CLI-launched sandbox runapt-get install -y bubblewrapinside it. - Claude shows "API Usage Billing" / "both
CLAUDE_CODE_OAUTH_TOKENandapiKeyHelperset." You injected your own Claude credential (Option B) but Islo's phantomapiKeyHelperis still present — the launcher strips it automatically at provision, so this means the strip didn't run: confirm the credential is named inOMNIGENT_ISLO_SANDBOX_ENV/sandbox.islo.env(the signal the launcher keys on), and check the provision log for the "clearing Islo's seeded apiKeyHelper" line. - Requests retry then fail with no obvious error.
islo statusshows no connected integration, so the phantomapiKeyHelperresolves to nothing. Connect one (Option A) or switch to Option B. - "managed host did not come online within 120s." Check that
server_urlis publicly reachable from Islo's cloud, then inspect the in-sandbox host log:~/.omnigent/logs/host-runner/*.log. - Agent has no credentials. Verify the injected var names match the
forwarded set above (or are named in
OMNIGENT_RUNNER_ENV_PASSTHROUGH), and that each name was actually set in the launching environment.
Environment variable reference
| Variable | Where it's read | Purpose |
|---|---|---|
ISLO_API_KEY |
CLI machine / server | Islo API credentials (required) |
ISLO_BASE_URL |
CLI machine / server | Non-default Islo API endpoint (default https://api.islo.dev) |
OMNIGENT_ISLO_HOST_IMAGE |
CLI machine / server | Override the host image ref (sandbox.islo.image takes precedence for managed) |
OMNIGENT_ISLO_SANDBOX_ENV |
CLI machine / server | Comma-separated launcher-side env var names to inject (sandbox.islo.env takes precedence for managed) |
OMNIGENT_RUNNER_ENV_PASSTHROUGH |
inside the sandbox (injected) | Extra env var names the host forwards to runners |
GIT_TOKEN / GIT_USERNAME |
inside the sandbox (injected) | HTTPS credentials for private repository clone / fetch / push |