codai docs
Projects & Environments

Team environments

Share one environment with your team — roles, a Linux account and port range per member, a shared repository, the build lock and isolation modes.

Several people (and their agents) can work in the same environment. Each member gets their own identity inside the machine so one person's credentials are never readable by another, and the daemon coordinates the things that must not run twice.

Roles

RoleMay
viewerSee the environment, its members, ports and events; attach. No Linux account is provisioned for SSH.
developerEverything a viewer may, plus publish ports up to authenticated, list secret names and register their SSH key.
maintainerPlus start, stop, archive, rename, change the idle timeout, add/remove members, publish public ports, set and delete secrets, mint enrolment tokens (BYO).
ownerPlus destroy, grant or remove owner. An environment always keeps at least one owner (409 last_owner).

The environment's creator is its owner. Org owners and admins of the environment's org act as owner; plain org members see it as viewer. Anyone may leave an environment.

Add a member in the hub (environment page → Members → e-mail + role; the person needs a codai account with that address) or with POST /v1/environments/{id}/members and { "email": "[email protected]", "role": "developer" }.

Per-member identity

When the daemon runs as a root system service in per-user mode (CODAI_SERVICE_SCOPE=system, the default for managed machines), every member gets:

WhatDetail
Linux accountu- plus the first 8 hex characters of your user id — the same on every machine. Created on first use (useradd -m, group codai-dev, home 0750), shown as Linux user in the hub. Shell commands and terminals drop to this account.
Git identity~/.gitconfig with the member's name and e-mail.
Worktree~/work/<project-slug> on the default branch, or on u/<user> when that branch is already checked out by someone else. Your file operations are confined to your worktree and home.
Port rangeA block of 100 ports starting at 30000, 30100, … Exported to your commands as PORT (the first port) and CODAI_PORT_RANGE_START, CODAI_PORT_RANGE_END, CODAI_PORT_RANGE, so two members' dev servers do not collide.
SSH accessPaste your public key under Your SSH key (or PUT /v1/environments/{id}/members/me/ssh-key). The daemon writes it to your ~/.ssh/authorized_keys and the hub shows Installed. Viewers are not provisioned. Removing a member empties their managed key file; the account and home stay.

If the daemon runs without root (shared identity — the default for a user-scope BYO install), everyone runs as the same Unix user, codaid status reports identity_unsafe: true, and the agent is told to treat the machine as untrusted for secrets. Use shared mode only for a machine you use alone.

Shared repository

A project with a repo_url gets one shared bare repository at /srv/codai/repos/<slug>.git (group codai-dev), fetched once and used by every member's worktree. To clone a private repository, store a token as the environment secret CODAI_GIT_TOKEN (PUT /v1/environments/{id}/secrets/CODAI_GIT_TOKEN); the daemon passes it to git through an askpass helper, never on the command line. Delivery of the secret value to the daemon is rolling out — until your gateway exposes it, the daemon clones without a token (public repositories only).

Build lock

Two builds in one repository at the same time corrupt each other's output. A shell command sent with "lock": "build" takes an exclusive, non-blocking OS lock:

  • per repository per environment in per-user mode (every member's worktree of the same project shares one lock);
  • released automatically if the holder crashes;
  • a second caller gets { "ok": false, "error": "lock_held: <holder>" } immediately instead of waiting.

Held locks appear in the relay status (locks), on the desktop card as a lock glyph, and in codaid status. Who is attached — people and agents — is shown in the same places.

Isolation modes

Chosen per project (isolation), by whoever manages the project:

ModeWhat you getStatus
shared_vm (default)One machine; each member works as their own Linux user with their own worktree and port range.Live
containerOne rootless podman container per member (codai-<slug>-<user>), built from the repository's devcontainer.json (.devcontainer/devcontainer.json or .devcontainer.json; falls back to ubuntu:24.04), worktree mounted at /workspaces/<slug>. Shells and terminals run inside the container; file operations stay on the host path.Rolling out — needs the per-user daemon and podman on the machine
vm_per_devOne environment per developer, pinned to them (for_user_id). Creating a second answers 409 already_has_env with the id to reuse; such environments have exactly one member.Live

Supported devcontainer.json fields in container mode: image, build.{dockerfile,context,args,target}, forwardPorts, containerEnv, remoteEnv, containerUser, remoteUser, postCreateCommand (once), postStartCommand, mounts, runArgs, workspaceFolder, overrideCommand. Docker Compose is not supported.

In the hub, vm_per_dev projects group environments by developer (Your machine, Machine of …), and each developer creates their own from the project page.

On this page