Skip to content

Workspaces: create, size, reach

One container per developer or agent, on its organisation's own network on your host (or, if your organisation turns it on, a member's own computer: egress there is observed, not enforced). This page covers every flag and setting that shapes one, from create to destroy.

Create a workspace · command buildersynthetic
Workspace options
The only member who can open it.
on the host
$ bin/workspace-agent create --tenant acme --workspace dev01 \
    --owner dev@example.test --repo acme/api
$ bin/workspace-agent status --tenant acme --workspace dev01

Container
ws-acme-dev01uid 1000, no sudo, no-new-privileges, an init process
Network
agentstack-t-acmeacme's own network; other organisations cannot reach it
Home
ws-acme-dev01-home/home/dev, survives recreation
Limits
2g memory · 2 CPUs · 512 processes
Idle
suspend after 30 min idlechecked every 5 minutes; the volume is kept
Routes
/ws/acme/dev01/…terminal, IDE and preview, all behind the front door
Preview
/ws/acme/dev01/preview/3000/
Owner
dev@example.test
Repository
acme/apicloned and pushed through the git broker
File transfer
offno download or upload; copy-tool ports refused

Change a field and the command and the list of what the host makes update together. Turn the firewall switch off to see the refusal: workspace-agent will not create a workspace on a network the firewall has not verified.

What it is

A workspace is a Docker container made by bin/workspace-agent. A workspace with managed Docker reaches the host's Docker daemon only through bin/docker-broker, a per-workspace broker that is deny-by-default. It runs as a non-root user with memory, CPU and process limits on its organisation's network, keeps /home/dev on a volume, and publishes nothing to the host: the terminal (ttyd with tmux), the browser IDE (code-server) and preview ports are reached only through the front door, which checks the session and the owner on every request.

It is

  • A container with a browser terminal, optional VS Code in the browser, app preview, an on-demand headless browser, and Claude Code, Codex and Antigravity installed.
  • Isolated per organisation by network: two organisations can both have a dev01 and cannot reach each other.
  • Suspended when idle and started again on the next visit, with the home volume intact.
  • Given its own Postgres by default, with a read-only Database tab, and able to run docker and docker compose once the organisation allows Docker.

It is not

  • A virtual machine. Containers share the host kernel; there is no cap-drop, read-only root, custom seccomp or user-namespace remap.
  • A managed database service. Each new workspace gets its own Postgres by default (since orxy V1.6); you can choose none, or connect your own server. Go, Rust, Java, .NET and Deno are not preinstalled: an admin picks versions on demand, and the host installs them. The gh CLI is not in the image.
  • Something developers create for themselves by default: admins create workspaces, or an agent may request one when the portal channel is on.

How it works

  1. Where. On your host. If a platform operator grants it and the organisation turns it on, a member's own computer can host that member's workspaces too, with weaker controls; see the members' computers task.
  2. Create. On your host, workspace-agent create checks the organisation's network is covered by the egress firewall and has a DNS gateway, refuses if either is missing, then runs the container with its limits, labels and volume.
  3. Route. It writes a front-door route for /ws/<org>/<workspace>/. The door authenticates every request and routes to the terminal, the IDE or a preview port.
  4. Credentials. The git broker and model-proxy sockets are mounted in; under the default guarded profile the container holds no GitHub token and no real model key.
  5. Idle. A sweep every 5 minutes (idle_sweep_interval) counts connections to each running workspace and stops one that has been idle longer than its own timeout.
  6. Reassign. assign changes the owner, repositories or idle timeout in place: no restart, no rebuild, nobody loses a session.

Defaults

Settings and flags that shape a workspace
SettingScopeDefaultAllowed values
Set when the workspace is created
--memoryEnv default WORKSPACE_MEMORY. Swap is capped to the same value.workspaceplatform env2ga Docker size
--cpusEnv default WORKSPACE_CPUS.workspaceplatform env2a CPU count
Process limitEnv WORKSPACE_PIDS.platform env512a number
--idle-timeoutEnv default WORKSPACE_IDLE_TIMEOUT. assign retimes a live workspace.workspace1800 s (30 min)seconds; 0 = never suspend
--ideVS Code in the browser (code-server).workspaceoffflagAbout 1 GB more memory per active user.
--preview-portEnv default WORKSPACE_PREVIEW_PORT.workspace30001024–65535, not 7681 or 8443
--dockerChecked against the organisation's workspace_docker_allowed before anything is built; recorded, so every rebuild keeps it.workspaceoffoff · managed · fullRefused on a member's own computer.
--postgresIts own Postgres server beside the workspace, on a private network only that workspace can reach. DATABASE_URL and the alias postgres are set.workspaceon in the console's create forms and the APIflagOn the command line, add the flag. None, or Connect my server, remain choices.
--languagesNode.js, Go, Rust, Java, .NET or Deno versions, installed by the host.workspacenoneversions the organisation's workspace_languages_allowed permits
--repo, --ownerworkspacenoneowner/name (repeatable; first is primary) · email
Policy keys
workspace_file_transferFiles pane, IDE upload/download, scp/sftp/rsync, and at both direct GitHub.platformorganisationoffoff · download · upload · bothAn organisation may withdraw a direction, never grant one.
ide_attach_enabledDesktop VS Code, JetBrains Gateway or ssh over a 12-hour token.platformorganisationoff until an admin turns it ontrue · falseApplies to workspaces created after the change.
workspace_max_port_visibilityHighest visibility a developer may give a port.platformorganisationorgowner-only · org · publicPublic share links are off until raised to public.
workspace_share_link_max_ttlplatformorganisation86400 s (1 day)300–2592000; an organisation may only shorten it
workspace_startup_tasksRun devcontainer.json postCreateCommand/postStartCommand, workspace-tasks.json and dotfiles.platformoff until an admin turns it ontrue · falsedevcontainer image and features are always refused.
workspace_portal_api_enabledAgents may list, start and stop their owner's workspaces and request a new one.platformorganisationoff until an admin turns it ontrue · false
ai_in_apps_enabledApps built in a workspace call Claude or OpenAI through the broker; API-key credentials only.platformorganisationoff until an admin turns it ontrue · false
Docker, languages and members' computers
workspace_docker_allowedWhether a workspace may run Docker, and how. An ordering: off < managed < full.platformorganisationoffoff · managed · fullAn organisation admin may choose off or managed; only a platform administrator may allow full.
docker_full_runtimeThe runtime full Docker uses.platformautoauto · sysbox · privileged · noneauto picks Sysbox when the host has it and never resolves to privileged.
docker_registries_allowedRegistries a managed workspace may pull from.platformorganisationdocker.io, ghcr.ioregistry host list
docker_max_containersContainers per workspace.platformorganisation81–32; an organisation may only lower it
docker_egressWhether a workspace's Docker containers reach the internet.platformorganisationoffoff · proxyoff: containers reach only the workspace database and each other. proxy: the workspace's own allowlist.
workspace_languages_allowedWhich language versions an admin may pick.platformorganisationallall · a list · noneNarrowing stops new choices; installed versions stay until the workspace's list is edited.
personal_hosts_allowedSet for an organisation by a platform operator: may its members' own computers host workspaces at all.organisationofftrue · false
personal_hosts_enabledThe organisation's own switch, on the Where workspaces run card.organisationoff until an admin turns it ontrue · falseCan be turned on only while the operator's grant holds. Turning it off stops new workspaces there at once.
Quotas, idle and logs
max_workspacesSet for an organisation by a platform operator; the organisation can read it, not change it.organisation0 (no limit)0–10000
idle_sweep_intervalHow often idle workspaces are looked for; not the timeout itself.platform300 s0–86400; 0 suspends nothing
workspace_logs_containerDevelopers see their container logs in the Logs pane.platformorganisationontrue · false

Scopes. “platform” is bin/policy set --key …; “organisation” adds --scope tenant --target <org>. An organisation with no value of its own inherits the platform's. There is no per-workspace or per-member scope for these keys; per-workspace values are the create flags.

Configure it

The operator CLI

on the host
$ bin/workspace-agent create  --tenant acme --workspace dev01 \
    --owner dev@example.test --repo acme/api [--ide]
$ bin/workspace-agent assign  --tenant acme --workspace dev01 \
    [--owner …] [--repo …] [--add-repo …] [--remove-repo …] [--idle-timeout SEC]
$ bin/workspace-agent start   --tenant acme --workspace dev01
$ bin/workspace-agent stop    --tenant acme --workspace dev01
$ bin/workspace-agent status  --tenant acme --workspace dev01
$ bin/workspace-agent list [--json]
$ bin/workspace-agent reap    [--tenant acme] [--workspace dev01]
$ bin/workspace-agent destroy --tenant acme --workspace dev01 [--keep-volume]
# read-only views
$ bin/workspace-agent ports   --tenant acme --workspace dev01 --json
$ bin/workspace-agent logs    --tenant acme --workspace dev01 --tail 200
$ bin/workspace-agent reap-status --json

Useful flags: --dry-run to print the plan, --actor EMAIL to name who acted on the audit trail, --json on the read-only views. Exit codes: 0 success, 1 usage or a precondition, 2 the operation failed, 3 another lifecycle operation is in flight (try again).

In the console

Create

OrganisationsacmeWorkspaces & repo access

Organisation settings

OrganisationsacmeSettings

File transfer, Desktop IDE attach, Share links, Port visibility, Workspace requests, Workspace Docker, Workspace languages and Where workspaces run cards. The Computers list shows members' computers.
Across the host

ConsoleWorkspaces

Developer

PortalHomeacme / dev01

Files, Search, Shells, Chat, Git, Tools & Info, Logs, Database, Docker (when the workspace has Docker), IDE and preview.
Members' computers

PortalYour computers

Add a computer, copy its install command, and open the workspaces on it.

Tasks

Give a new developer a workspace

  1. Invite them to the organisation

    OrganisationsacmeMembers

    Invitations last 7 days. If outbound mail is not configured, the console shows the link for you to send.

  2. Create the workspace

    on the host
    $ bin/workspace-agent create --tenant acme --workspace dev01 \
        --owner dev@example.test --repo acme/api --ide

    Expected: exit 0. status then shows the container running on agentstack-t-acme.

  3. They open it

    After accepting the invitation they sign in and open acme / dev01 from the portal. The terminal opens first; the IDE tab appears because of --ide.

Hand a workspace to someone else without a restart

  1. Reassign the owner and add a repository

    on the host
    $ bin/workspace-agent assign --tenant acme --workspace dev01 \
        --owner dev2@example.test --add-repo acme/web

    Expected: no container restart. The previous owner can no longer open it; list --json shows the new owner.

  2. Mind --repo on assign

    On assign, --repo states the complete set: any repository you leave out is revoked. Use --add-repo and --remove-repo to change one at a time.

Let one organisation download files

  1. Widen the platform value first

    An organisation may withdraw a direction but never grant one, so the host has to allow it.

    on the host
    $ bin/policy set --key workspace_file_transfer --value download
  2. Keep it off everywhere else

    Organisations with no value of their own inherit the platform's. Set off on each one that should not have it.

    on the host
    $ bin/policy set --scope tenant --target acme-ops --key workspace_file_transfer --value off
    $ bin/policy get-for --tenant acme-ops --key workspace_file_transfer

    Expected: get-for reports off for acme-ops and download for acme.

  3. Check it from the workspace

    The Files pane applies the change at once; the browser IDE at the workspace's next start. Every transfer is on the audit trail with its path and byte count.

Turn on desktop IDE attach

  1. Turn it on for the organisation

    The console switch leads with what it costs: turning it on lets developers copy source to their machines. The host needs an https address.

    on the host
    $ bin/policy set --scope tenant --target acme --key ide_attach_enabled --value true
  2. Recreate the workspace

    Attach applies to workspaces created after the change. Keep the home volume.

    on the host
    $ bin/workspace-agent destroy --tenant acme --workspace dev01 --keep-volume
    $ bin/workspace-agent create  --tenant acme --workspace dev01 \
        --owner dev@example.test --repo acme/api
  3. The developer connects

    They register a public key under Developer tools, take a 12-hour token from the Connect pane, and add this to ~/.ssh/config:

    ~/.ssh/config
    Host acme-dev01
        HostName acme-dev01
        User dev
        IdentityFile ~/.ssh/id_ed25519
        ProxyCommand workspace-ssh --url https://kodela.example.com/ws/acme/dev01/ssh --token-file ~/.config/kodela/acme-dev01.token

    Expected: ssh acme-dev01 opens a shell; VS Code Remote-SSH or JetBrains Gateway connect to the same alias. JetBrains Gateway also needs file transfer upload or both.

Preview an app and share it

  1. Start it with the preview helper

    in the workspace
    $ preview --port 5173 npm run dev
    $ preview --status

    Expected: a link to /ws/acme/dev01/preview/5173/ that any active member of acme can open (the default visibility ceiling is org).

  2. Allow public links, if you want them

    on the host
    $ bin/policy set --key workspace_max_port_visibility --value public
    $ bin/policy set --scope tenant --target acme --key workspace_share_link_max_ttl --value 3600

    Expected: the owner can mark the port public in the Networking panel and mint a share link, clamped to an hour. The link is shown once; only its hash is stored.

Let a workspace run Docker

  1. Allow managed Docker for the organisation

    OrganisationsacmeSettingsWorkspace Docker

    An organisation admin may choose off or managed (workspace_docker_allowed). Only a platform administrator may allow full.

  2. Create the workspace with Docker

    on the host
    $ bin/workspace-agent create --tenant acme --workspace dev01 \
        --owner dev@example.test --repo acme/api --docker managed

    Or choose Docker in either console create form. Expected: the workspace has a Docker tab beside Database, and docker and docker compose work in its terminal. Every call goes through that workspace's Docker broker; containers run beside the workspace, on its own internal network, and it sees only its own containers, networks, volumes and builds.

  3. Run an app

    A compose app opens on the workspace's own address, and the Run box can start it. Published ports are not bound on the host; Preview reaches them through the front door. The Docker tab lists this workspace's containers, running ones first, with log, stop and restart, and the images it may see. Starting and stopping Docker apps is on the audit trail.

  4. Decide what the containers may reach

    By default they reach only the workspace database and each other. Set docker_egress to proxy to give them the workspace's own allowlist; add registries with docker_registries_allowed.

  5. Full Docker, only if you must

    --docker full runs the workspace's own Docker daemon. It needs a platform administrator and a supported runtime: Sysbox, chosen automatically when the host has it. privileged must be set explicitly by an operator (docker_full_runtime); under it anyone in the workspace has host-root-equivalent power, the egress firewall and no-credentials guarantees no longer hold for that workspace, and every create is audited.

Use the workspace database

  1. Keep the default

    Each new workspace gets its own Postgres by default in every console create form and the API; None and Connect my server remain choices. On the command line, add --postgres to workspace-agent create. The password stays on the host and is never shown.

  2. Read its structure

    WorkspaceDatabase

    Expected: the tables and columns, read-only. The Database connections panel lives in this tab. The chat knows the database's structure, and the Library can connect a project to it by writing a git-ignored .env inside the workspace.

  3. Run a compose app against it

    When a compose project declares its own Postgres, starting it from the Run box runs the app against the workspace database instead. Postgres images only; an app that reads its own DB_HOST-style settings needs changing; a docker compose up typed in the terminal is not redirected.

  4. Add it to an existing workspace

    Only an organisation admin or a platform operator may, and it is a confirmed recreate.

Give a workspace another language version

  1. Pick versions when creating or editing it

    Admins choose Node.js, Go, Rust, Java, .NET or Deno versions in the Language picker, or with --languages on workspace-agent create. The host downloads each version, checks the vendor's published checksum and copies it into the workspace home, so the workspace reaches no new host.

  2. Narrow the choice, if you want to

    OrganisationsacmeSettingsWorkspace languages

    workspace_languages_allowed is all by default. Package registries (crates, Maven, NuGet) still need the egress allowlist.

Let members host workspaces on their own computers

Weaker controls on a member's computer. Workspaces run on your Kodela host. If a platform operator grants it and your organisation turns it on, a member's own computer can host their workspaces too. There, internet access is observed, not enforced; DNS filtering is not enforced; the source on its disk is readable by whoever uses it; and Docker is not available. Tested end to end only on Windows (Linux laptops up to enrolment; Mac only in a simulation).

  1. A platform operator grants it to the organisation

    on the host
    $ bin/policy set --scope tenant --target acme --key personal_hosts_allowed --value true
  2. The organisation turns it on

    OrganisationsacmeSettingsWhere workspaces run

    This is personal_hosts_enabled. Turning it on without the operator's grant is refused.

  3. The member adds their computer

    PortalYour computers

    The page gives one command to paste, with the computer's id and enrolment token filled in. On Linux or WSL it pipes /api/hosts/install.sh into bash; on Windows it runs install.ps1 in PowerShell. The same scripts are bin/personal-host-install.sh and bin/personal-host-install.ps1 in the repository:

    on the member's computer
    # Linux or WSL
    $ bin/personal-host-install.sh --control-plane <console address> --host-id <id> --token <token> [--header …] [--insecure]
    # Windows PowerShell
    > bin/personal-host-install.ps1 -ControlPlane <console address> -HostId <id> -Token <token> [-Engine rancher] [-Distro …] [-Insecure] [-Yes]

    Expected: a small agent downloads, checks its sha256, runs a preflight, enrols and connects out. It needs no sudo, and the computer can sit behind NAT. On Windows, workspaces use WSL2 and Rancher Desktop; the installer checks free disk space and offers to set both up, and the one administrator step, turning WSL on, is named and the script stops. On a Mac the agent runs in a container beside the workspaces, with Docker Desktop, OrbStack or Colima.

  4. The member opens a workspace there

    Your computers shows each workspace as a tile with its state and an Open button. Chat, files, search, git, approved commands, terminal, IDE and preview work there, with no git token or AI key stored on the computer: git, Claude, Codex and the portal go through the platform's brokers. Every create, start, stop and removal is decided and recorded on the platform.

  5. Know how access ends

    Removing a computer asks first, releases its workspaces and takes effect at once: its key stops working. A computer that has not connected within its lease (30 days by default, set by an operator) is disabled as if it were removed; a computer in use renews the lease each time it connects. Offboarding a member archives the workspaces on their computer. The agent updates itself.

Cap how many workspaces an organisation may have

  1. Set the quota as an operator

    on the host
    $ bin/policy set --scope tenant --target acme --key max_workspaces --value 10

    Expected: once acme has ten, a create from the console is refused with a sentence naming the limit.

Verify it works

Inside the workspacesynthetic

dev@acme-dev01:~$ workspace-info --text workspace acme/dev01 · owner dev@example.test egress through the proxy · observe file_transfer off git broker · acme/api preview port 3000 · not listening idle not measured (the idle timeout is a host setting)

Good looks like: workspace-agent status shows it running; workspace-info (JSON for agents, --text for people) reports what the workspace may do and says not measured rather than guessing; the terminal opens at /ws/acme/dev01/ only for the owner; bin/egress-coverage exits 0. The layout above is illustrative; the real command prints more sections (ports, runtimes, skills, privileges).

Troubleshooting

Create refusedegress firewall is missing - refusing uncontrolled egress

Cause: the firewall chain is not installed. Fix: sudo bin/egress-firewall up, then verify. A variant names the one network that is not covered.

Create refusedcannot find the gateway of agentstack-t-acme for DNS - refusing to create a workspace that would resolve through an unfiltered path

Cause: the organisation's DNS filter is not up. Fix: bin/dns-filter sync, then bin/dns-filter status.

Create refused… is not running - refusing to create a tenant without a reachable front door

Cause: the front door is down. Fix: start it (see Front door & TLS), then create again.

Quota reachedacme is at its limit of 10 workspaces. A platform operator sets this; you can destroy one to make room.

Fix: destroy an unused workspace, or raise max_workspaces as an operator.

Download button refusedfile download is disabled by policy

Cause: workspace_file_transfer is off or upload for this organisation. Fix: the file-transfer task.

Organisation cannot widen file transferthis host allows file transfer 'off', so an organisation cannot set 'download' - a customer may withdraw a direction and never grant one. Ask a platform operator to widen it first.

Fix: widen the platform value first, then narrow the others.

Desktop attach refuseddesktop IDE attach is off for this organisation - an organisation admin turns it on (ide_attach_enabled), then the workspace is recreated

Fix: the attach task. Turning it off stops new tokens at once; tokens already issued last up to 12 hours, and deleting a developer's registered key cuts their next connection.

Attach networks run outcould not create attach network …: no free /28 is left in the attach range …

Fix: set AGENT_STACK_ATTACH_POOL to a larger unused private range, or destroy attach workspaces nobody uses.

Start refusedhome is not provisioned (…) - refusing to start ws-acme-dev01 against a possibly-lost home

Cause: the home volume is missing or unmarked. Fix: restore it with bin/backup restore <name> --workspace acme/dev01 before starting (see Operate).

A docker command is refused in the workspace

Cause: the Docker broker refuses, each with a sentence: privileged containers, added capabilities and devices; host network, pid, ipc and userns namespaces; binds outside the workspace; other workspaces' networks and the platform's own; registries not on docker_registries_allowed; and more than docker_max_containers. Fix: change the compose file, or widen the registry list or the container limit for the organisation.

A new workspace has no database

Cause: the default is skipped on a member's own computer, on a host without the Postgres image, and for a workspace named postgres; the result says why, and choosing Postgres explicitly there is refused. Fix: on your host, make the Postgres image available, or name the workspace differently.

The installer on a member's computer stops

Cause: over plain http it must be given --insecure (-Insecure on Windows); on Windows, WSL may need turning on by an administrator, and Podman is refused as not available yet. Fix: serve the console over https, turn WSL on, or use Rancher Desktop.

Exit code 3

Cause: another create, start, stop or destroy is running for this workspace. Fix: wait and run it again.

Limits and what is not built yet

  • Containers, not VMs. No custom seccomp, read-only root, cap-drop or user-namespace remap. If your threat model needs a VM boundary, Kodela is not that today.
  • Copy tools. Below file transfer both, on workspaces on your host, in both egress modes: copy-tool ports (22, 2222, 873, 20, 21, 989, 990, 69) are refused before connecting, and on any other port the egress proxy recognises ssh, rsync and ftp by their opening bytes and refuses before any of the copy is relayed; the firewall drops anything that bypasses the proxy. Not covered: members' own computers (egress there is observed, not enforced); a copy tool hidden inside an ordinary HTTPS connection to an allowed site, and HTTPS uploads to an allowed site, which are egress-allowlist questions; and a workspace under privileged full Docker, where the firewall guarantee no longer holds.
  • Desktop IDE attach lets source reach laptops. File transfer governs scp, sftp and rsync over attach, but cannot stop code shown in an attached editor or terminal from leaving; the admin sees that warning before turning it on. Turning attach off stops new tokens at once; an issued token works until it expires (up to 12 hours).
  • devcontainer image and features are refused; only startup commands, dotfiles and curated extensions are honoured.
  • Go, Rust, Java, .NET and Deno are not preinstalled; versions are installed on demand by the host, and their package registries still need the egress allowlist. The gh CLI is not in the image.
  • The workspace database. Each new workspace gets its own Postgres by default (none, or connect your own server, are still choices), with a read-only Database tab. The tab never runs a query a person types and never reads an external server's schema. Where the default cannot apply, such as on a member's own computer, the workspace says why. The compose redirect covers Postgres images only, and not a docker compose up typed in the terminal.
  • Docker. Docker in a workspace is off until your organisation allows it (workspace_docker_allowed: off, managed or full). Managed Docker runs every call through a per-workspace broker that refuses privileged containers, host namespaces, outside binds and unlisted registries; containers get no internet by default (docker_egress). Pulled images are one shared cache on the host, so a workspace can tell an image was already pulled. Full Docker needs a platform administrator and a supported runtime (Sysbox); under privileged the firewall and no-credentials guarantees no longer hold for that workspace. Not available on members' computers.
  • Members' computers. Off unless a platform operator grants it and the organisation turns it on. There, internet access is observed, not enforced; DNS filtering is not enforced; the source on its disk is readable by whoever uses it; and Docker is not available. Tested end to end only on Windows (Linux laptops up to enrolment; Mac only in a simulation). The computer's own report about itself is shown as unverified and never used for a decision.
  • No workspaces-per-host figure. Plan about 2 GB of memory per active workspace. There is no rate limit on workspace creation.
  • The mobile web workspace has an open layout bug at phone widths.

Verified against the main branch, 4 Oct 2026.