This is the single setup and workflow guide for LemmaComputer. Start by choosing the outcome you need; do not combine commands from different rows.
| Goal | Checkout and profile | First command path | Workspace topology |
|---|---|---|---|
| Read, review, or run unit tests | Any clean checkout | Install dependencies only if required | No stack |
| Evaluate the product without changing code | Dedicated disposable clone using the worktree profile |
env:init |
Colocated |
| Change code or documentation | One branch in one Git worktree using the worktree profile |
worktree:init |
Colocated by default |
| Exercise remote-node routing or Claude Cowork | Initialized task worktree | Remote qualification command | Locally split with mTLS |
| Exercise customer-managed Microsoft integration | Keep worktree for code changes; use a dedicated customer-managed evaluation deployment for operator qualification |
Microsoft integration runbook | Deployment-specific |
| Qualify a real hosted release | Representative hosted infrastructure | Deployment and release tooling, not local Compose | Physically separate private nodes |
If a request says only "set it up", "run it", or "test it" and the choice would change data ownership, ports, topology, or required infrastructure, ask whether the user wants: a disposable evaluation, an isolated development worktree, local remote-node/Cowork qualification, or a production-profile deployment. Do not ask when the requested outcome already makes the choice clear.
The primary main checkout is an integration checkout. It does not own a local
development stack. A dedicated evaluation clone may be checked out at main
because it is disposable and never used to edit or integrate code; do not turn
the primary integration checkout into that evaluation clone.
Node.js development and non-containerized tests can run on macOS. The complete reference stack and managed desktop require:
- Linux
amd64/x86_64; - Node.js 22 or later;
- Docker Engine and Docker Compose v2.30.0 or later; and
- enough Docker address-pool capacity for the isolated networks.
Chrome, Visual Studio Code, and Obsidian additionally require an AppArmor 4
host that can load the repository's lemmacomputer-workspace-electron profile
and permits unprivileged user namespaces. Install and enable it using the
workspace-node runtime procedure
before starting an Electron-selected workspace.
Claude Cowork additionally requires usable /dev/kvm and
/dev/vhost-vsock. On a Mac, use a Linux x86_64 host or VM for full workspace
runtime checks. Docker Desktop emulation is not a qualified deployment path.
Use this path only for a person who wants to explore one local stack and will not change the repository:
git clone <repository-url> lemmacomputer-eval
cd lemmacomputer-eval
npm ci
npm run env:init -- --profile=worktree
npm run compose:upOpen the URL in LEMMACOMPUTER_PUBLIC_WEB_URL; the dedicated evaluation default
is http://localhost:4174:
grep LEMMACOMPUTER_PUBLIC_WEB_URL .envTo launch a managed desktop, build the large workspace image once:
npm run image:workspaceenv:init creates .env with mode 0600, fresh internal credentials, and the
selected profile. It refuses to overwrite an existing file. It does not create
unique names for parallel stacks, so run only one evaluation stack per Docker
host unless each checkout is isolated manually.
Do not use env:init --force casually. Rotating those values can invalidate
sessions, encrypted records, signatures, service trust, and persisted data.
Every change gets one branch and one Git worktree. From a clean primary checkout:
git fetch origin
mkdir -p .worktrees
git worktree add .worktrees/<task-name> \
-b <issue>-<short-name> origin/main
cd .worktrees/<task-name>
npm run worktree:init
npm run dev:doctorThe canonical task-worktree directory is the primary checkout's literal
.worktrees/ directory (plural). Do not create task worktrees as siblings of
the repository, directly in the repository root, or in an improvised directory.
Keeping one predictable containment path makes ownership, cleanup, Docker
namespace inspection, and agent handoff repeatable. The directory is ignored by
Git; each child remains a normal Git worktree with its own branch and generated
runtime identity.
When there is no issue, use a short descriptive branch name. Follow an explicit user-supplied branch name when provided.
Do not run npm ci or env:init separately in a task worktree.
worktree:init:
- refuses
main; - runs
npm ciif dependencies are absent; - creates
.envwith fresh secrets if it is absent; - selects
LEMMACOMPUTER_INSTALLATION_KIND=worktreeand development runtime behavior; - derives a stable
lemmacomputer-*worktree identity from the worktree path and branch; - assigns unique ports, Compose projects, networks, image tags, databases, and volumes; and
- prints the worktree's Web URL.
Never copy .env, database volumes, generated PKI, or runtime environment
files from another checkout. Each worktree owns its trust and persistence.
Unique localhost ports isolate each worktree's processes and Docker resources,
but they do not create separate browser cookie jars. Cookies are scoped to a
host, not a port, so a cookie set for localhost can be sent to LemmaComputer,
another worktree, or an unrelated local application served on a different
localhost port. This is a local-development limitation; real deployments use
distinct hostnames and must not rely on ports as a cookie boundary.
Use a separate browser profile for each sensitive local stack (and for unrelated localhost applications), or assign distinct development hostnames when the complete callback, trusted-origin, and certificate configuration has also been updated for those names. Do not treat the generated port allocation as browser credential isolation.
At the start of every later session:
git status --short
npm run dev:doctorAfter bringing in changes from main, run npm run env:check. If the canonical
environment gained variables, run npm run env:update; it adds missing values
without rotating existing secrets.
| Command | Mutates state? | Purpose |
|---|---|---|
npm run worktree:init |
Yes, once | Installs missing dependencies, creates the worktree-owned .env, and assigns isolated names and ports. |
npm run dev:doctor |
No | Checks branch identity, dependencies, .env, lemmacomputer-* isolation values, Docker context safety, and bind-mounted file readability. |
npm run env:check |
No | Checks .env parity and validates profile, URL, secret, and coupled configuration such as complete mTLS groups. |
npm run env:update |
Yes | Merges newly registered variables into .env while preserving existing values and reporting extras. |
npm run compose:config |
Only generated projections | Writes least-privilege .runtime-env/<service>.env files, then validates the resolved Compose model without starting containers. |
npm run image:workspace |
Yes, Docker image cache | Builds the large managed desktop image. It is needed for desktop workspaces, not merely to sign in. |
npm run compose:up |
Yes | Renders service environments, builds application images, applies explicit migration jobs, starts services, and waits for health. |
npm run compose:down |
Yes | Stops the worktree stack while preserving volumes by default. Pass -- --volumes only when deletion is intentional. |
compose:up repeats environment rendering, but env:check and
compose:config are useful separate diagnostics: they catch contract and
Compose errors before partially changing the running stack.
A new task worktree intentionally starts with no users, organizations, providers, pricing, sessions, or workspaces. Initialize it once, validate the generated namespace, then let the explicit migration jobs create the schemas:
npm run worktree:init
npm run dev:doctor
npm run env:check
npm run compose:up
grep '^LEMMACOMPUTER_PUBLIC_WEB_URL=' .envworktree:init creates fresh secrets and a stable
lemmacomputer-<10-character-id> Docker namespace. compose:up creates that
namespace's empty PostgreSQL volumes, runs the product and Better Auth
migration jobs, starts LiteLLM against its own database, and waits for health.
This path is correct only when fresh application state is intended.
The .env and Docker volumes in the same worktree are the durable local stack
identity. worktree:init is unnecessary after the first initialization and
preserves an already matching worktree environment, but it is not the command
for resuming services. Do not create a replacement worktree or add --volumes
merely to resume development. Use:
git status --short
npm run dev:doctor
npm run env:check
npm run compose:updev:doctor confirms that the checkout still owns its generated namespace.
env:check detects a changed configuration contract without rotating secrets.
compose:up reattaches the existing database volumes and applies only pending
explicit migrations. Stop it with npm run compose:down; volumes remain.
npm run compose:down -- --volumes is destructive and is reserved for an
intentionally disposable stack.
From the initialized task worktree:
npm run env:check
npm run compose:config
npm run compose:upBuild the workspace image before testing managed desktops or packaged desktop software:
npm run image:workspaceRead the worktree-specific URL instead of assuming a port:
grep LEMMACOMPUTER_PUBLIC_WEB_URL .envCreate the first account in the Web UI. A base workspace needs no application,
AI-agent, or model-provider selection: leave both catalogs clear and create the
workspace to qualify desktop provisioning and lifecycle in isolation. Configure
model-provider deployments under AI control plane -> Models & providers
only when the test selects an AI agent, then configure Pricing, Model routes,
Team rollout, and workspace policy as required by that AI path. Provider
credentials belong in the product UI, not .env.
An initialized development worktree uses the captured authentication-email transport. Sign-up, verification tokens, Better Auth records, sessions, and the personal-tenant onboarding path remain real; only delivery to an external mailbox is replaced.
- Open the worktree-specific Web URL and choose Create account.
- Enter a test name, email address, and password, then submit the form.
- After the UI confirms that the worktree captured the message, choose Open local verification email.
- The browser opens the captured message's real, same-origin verification URL. Complete sign-in and personal-workspace onboarding normally.
The same capture transport supports local password-recovery links. It consumes the latest matching message for that email and purpose; it is not a universal authentication bypass and it cannot retrieve another worktree's messages. The capture endpoint is exposed only when all three conditions hold:
LEMMACOMPUTER_INSTALLATION_KIND=worktree;LEMMACOMPUTER_RUNTIME_ENVIRONMENT=development; andLEMMACOMPUTER_AUTH_EMAIL_TRANSPORT=capture.
Production configuration rejects the capture transport and requires real
transactional email. If the local button is absent, run npm run env:check and
confirm those generated worktree values instead of adding Postmark credentials
or editing .env.example.
| Path | Owner and role |
|---|---|
compose.yaml |
Canonical ordinary topology; use it through npm run compose:*. |
compose.hosted.yaml |
Empty compatibility marker; it does not turn local Compose into hosted infrastructure. |
compose.oauth-qualification.yaml |
Tool-owned isolated OAuth qualification stack. |
compose.provider-qualification.yaml |
Tool-owned isolated provider qualification stack. |
.env |
Ignored, secret, checkout-specific operator configuration. |
.env.example |
Generated reference from scripts/deployment-config.mjs; never hand-edit. |
.env.qualification.example |
Generated inputs for qualification tooling, not a deployment environment. |
.runtime-env/ |
Ignored least-privilege service projections generated by repository commands. |
.runtime-remote-workspace-node/ |
Ignored, disposable PKI and state for local split-node qualification. |
There are three different environment surfaces; do not combine them:
.env.exampleis the complete deployment-operator contract. Every value an operator may place in deployment.envappears there exactly once, with its purpose, accepted format, secret handling, and conditional requirement..env.qualification.exampleinventories disposable inputs generated by the qualification commands. It is documentation only; do not copy it to.env..runtime-env/<service>.envand per-workspace container specifications contain service-local names derived from the deployment contract. Repository commands generate least-privilege projections so, for example, the Web service does not receive database or provider secrets.
Model-provider API keys and tenant MCP OAuth tokens are configured through the
product UI and encrypted persistence, not .env. Temporary shell controls used
by an individual diagnostic command are documented with that command and are not
deployment inputs.
Do not edit either example manually. The canonical definitions are in
scripts/deployment-config.mjs:
npm run env:example
npm run env:qualification:example
npm run env:checkThe first two commands detect generated-file drift. env:check validates the
actual .env, including profile-specific requirements and complete certificate
or OAuth credential groups. After pulling a contract change, use
npm run env:update to merge new keys without rotating existing secrets.
Maintainers regenerate a changed reference with
npm run env:example -- --write or
npm run env:qualification:example -- --write.
The generated worktree environment is sufficient for builds, automated tests, Compose validation, stack health, and fixture-based multi-tenant tests. Ask for external values only when the requested flow needs them:
| Test goal | Human supplies |
|---|---|
| Build, unit tests, Compose health, fixture-based tenant testing | Nothing |
| Real email delivery | Postmark server token, sender, and the Postmark transport setting |
| Microsoft 365 connector | Dedicated Microsoft 365 tenant ID, client ID, and client secret |
| Google or Microsoft social login | The selected provider's complete client-ID and client-secret pair |
| Local remote-node qualification | Nothing; the command generates disposable certificates |
| Production remote node | Private networking plus complete workload identities and CA-managed certificates |
Use the Microsoft integration runbook only when the task actually exercises the Microsoft 365 connector or Microsoft customer social login.
worktree:init creates an isolated database and workspace namespace. That is
the correct default for parallel task work, but it does not seed a new
worktree from another checkout's users, organizations, provider settings,
pricing, sessions, or workspace homes. Do not point two running worktrees at
the same writable Docker volumes.
When retiring a long-lived local stack and replacing it with an isolated worktree, treat the operation as an exclusive state handover:
- capture one coordinated recovery set using the backup contract, including all three databases, workspace-home volumes, matching cryptographic configuration, and image identifiers;
- stop application writers and every managed workspace in the source stack, then keep that stack stopped for the remainder of the handover;
- restore the product, Better Auth, and LiteLLM databases into volumes owned by the target worktree; keep the target's Compose identity and database credentials, and reapply the Better Auth runtime grants after a logical restore that excludes ACLs;
- either copy each stopped workspace-home volume into the target namespace or transfer that namespace exclusively to the target. Reusing the original homes is permitted only after removing the source runtime containers and preventing the source stack from restarting;
- move the public port and callback origin only after the source listener is
stopped, then run
npm run env:checkandnpm run dev:doctor; and - start the target topology and compare non-sensitive continuity counts for users, organizations, memberships, provider settings, model deployments, sessions, and workspaces before accepting the handover.
Copying database rows without their matching Better Auth, session, policy,
LiteLLM, and connector cryptographic material can leave accounts present but
sessions or encrypted credentials unusable. Do not copy another checkout's
entire .env; transfer reviewed state-bound settings while retaining the
target worktree's isolation values.
There is currently no repository command that automates this cross-worktree
handover. Until one exists, record the exact backup, restore, ownership, and
verification commands as migration evidence. Ordinary task worktrees remain
fresh and isolated; a persistent seeded development environment is a distinct
operator workflow, not an implicit side effect of worktree:init.
The oc-* prefix is a retired development Docker namespace. Changing only
LEMMACOMPUTER_COMPOSE_PROJECT_NAME makes Compose create different database
volumes; the old data still exists, but the new stack appears empty. Perform a
stateful rename as a stopped backup-and-restore operation:
-
record the current project, workspace prefix, public URL, image IDs, and non-sensitive continuity counts;
-
capture the product, Better Auth, and LiteLLM databases plus every workspace-home volume using the backup contract;
-
stop all managed workspaces. For an active remote-node qualification, run
npm run qualify:remote-workspace-node -- down, then runnpm run compose:downand verify that no old project containers remain; -
explicitly rewrite only canonical legacy isolation values:
npm run worktree:init -- --migrate-legacy-namespace npm run dev:doctor npm run env:check
The migration flag refuses a non-
oc-*project, an active remote-node qualification, or remaining legacy Compose containers. It preserves custom values such as an intentionally transferred public port or exclusive workspace-home prefix. It does not move database contents; -
create the new PostgreSQL volumes, restore all three databases, and reapply the Better Auth runtime grants described by the backup contract;
-
start the required topology with either
npm run compose:upornpm run qualify:remote-workspace-node -- up; and -
compare the same continuity counts and health endpoint before accepting the rename. Retain the stopped
oc-*database volumes until rollback is no longer required.
Never start the old and new namespaces concurrently against the same workspace-home volumes. The explicit migration flag is a namespace rewrite, not authorization to share writable persistence.
Ordinary evaluation and development are colocated. Use the remote qualifier
only when the task must exercise the real controller, relay, and mTLS boundary
or Claude Cowork. It requires an initialized, non-main task worktree; a
disposable evaluation clone is not sufficient.
Remote mode moves employee-controlled desktops to a private workspace compute node while identity, policy, provider credentials, OAuth custody, and product databases remain in the control plane. It uses the LemmaComputer Docker/KasmVNC adapter—not the commercial Kasm control plane.
| Mode | Current status |
|---|---|
Colocated worktree |
Supported local default |
| Remote-node worktree qualification | Repeatable local integration test on one physical Docker host |
customer-managed remote |
Configuration contract supported; customer supplies networking, PKI, storage, and operations |
hosted remote |
Required architecture, including Cowork, but not yet production-qualified |
The boundary is provider-neutral. A future E2B or Daytona adapter can implement the same controller, signed-policy, isolation, routing, and purge contracts without forking the product.
flowchart LR
Browser["Employee browser"]
subgraph ControlHost["Control-plane host"]
Ingress["Workspace ingress"]
Control["Control API"]
Web["Web"]
Gateway["LiteLLM"]
ControlDb[("Control and Better Auth databases")]
GatewayDb[("LiteLLM database")]
AppEndpoint["Private application endpoint"]
end
subgraph Node["Private workspace node"]
Controller["Workspace controller - only Docker socket owner"]
subgraph WorkspaceBoundary["Per-workspace boundary"]
DesktopRelay["Desktop ingress relay"]
GatewayRelay["Gateway application relay"]
ControlRelay["Control application relay"]
Egress["Governed egress proxy"]
Sandbox["KasmVNC desktop and agents"]
end
end
Browser --> Ingress --> Web --> Control
Control --> ControlDb
Gateway --> GatewayDb
Control -->|"mTLS, token, signed policy"| Controller
Controller -->|"node-local Docker API"| WorkspaceBoundary
Ingress -->|"mTLS HTTP and WebSocket"| DesktopRelay --> Sandbox
Sandbox -->|"fixed gateway alias"| GatewayRelay
Sandbox -->|"fixed Control alias"| ControlRelay
GatewayRelay -->|"mTLS"| AppEndpoint --> Gateway
ControlRelay -->|"mTLS"| AppEndpoint --> Control
Sandbox --> Egress -->|"policy-approved TLS"| Internet["Approved public destinations"]
For each running workspace the controller normally creates:
| Runtime | Count | Purpose |
|---|---|---|
| Sandbox | 1 | Desktop, applications, and local agent brokers |
| Desktop relay | 1 | Authenticated ingress to this sandbox's KasmVNC port |
| Gateway relay | 0-1 | Fixed local alias to the private LiteLLM endpoint |
| Control relay | 0-1 | Fixed local alias to the private agent bridge and authorization endpoint |
| Egress proxy | 0-1 | Signed, policy-governed public destination access |
The relays form one logical workspace network gateway but stay separate per-workspace containers. Combining ingress, private application routing, and public egress into one shared service would merge credentials, directions, and failure domains and could bridge workspaces.
/var/run/docker.sock is root-equivalent authority over the workspace node.
Only the workspace controller mounts the node-local socket:
services:
workspace-controller:
networks:
node-transport:
aliases: [workspace-node]
volumes:
- /var/run/docker.sock:/var/run/docker.sockControl never mounts or proxies that socket. It sends a bounded lifecycle request; the controller validates workload identity, bearer token, signed policy, workspace identity, and provider labels before using Docker locally. The normative node security and purge contract is in Workspace node deployment.
| Caller | Listener | Authentication and key custody |
|---|---|---|
| Control API | Workspace controller | Node server TLS, Control client certificate with expected CN, and bearer token; each leaf key stays with its workload. |
| Workspace ingress | Per-workspace desktop relay | Relay server TLS and ingress client certificate with expected CN. |
| Per-workspace application relays | Private LiteLLM and Control endpoints | Application endpoint TLS and node application-gateway client certificate with expected CN. |
| Control API | LiteLLM admin proxy | Separate administrator mTLS identity. |
Browser authentication is normal HTTPS plus the user and workspace session, not mTLS. Public egress uses ordinary Web PKI TLS after signed destination policy enforcement. Production leaf certificates must come from the deployment's private CA or workload identity system; the local qualifier's two-day authorities are disposable test material.
npm run env:init does not generate mTLS certificates. It generates the
ordinary local service secrets and signing keys, but leaves the workspace-node
and LiteLLM administration certificate fields blank in a worktree .env.
Those fields represent production workload identities and must not be filled
with a long-lived developer CA by default.
The two local mTLS qualification paths deliberately keep their certificates
outside the normal .env:
| Command | Certificate lifecycle | Storage and stack effect |
|---|---|---|
npm run qualify:remote-workspace-node -- config [--cowork] |
Uses OpenSSL to create separate two-day node and application-relay CAs and their server/client leaves. | Writes under .runtime-remote-workspace-node/ only long enough to validate both Compose projects, then deletes it. .env and the running stack are unchanged. |
npm run qualify:remote-workspace-node -- up [--cowork] |
Generates the same disposable two-day authorities for the active split-node run. | Keeps base64 certificate values, generated Compose files, and PEM files under .runtime-remote-workspace-node/ until down. It does not modify .env. |
npm run qualify:internal-mtls |
Generates separate one-day test CAs for the LiteLLM admin and workspace-controller listeners, plus valid and foreign client leaves. | Uses temporary OS directories, starts real TLS listeners with mock backends, proves rejection behavior, and deletes all material in the same test run. It does not modify Compose or .env. |
The remote-node up command exercises node, desktop-relay, and private
application-relay mTLS in the running split stack. It does not also switch that
worktree's LiteLLM admin listener to mTLS; qualify:internal-mtls tests the
LiteLLM administrator boundary separately. For a hosted deployment, both sets
of base64 PEM values are supplied by the deployment secret manager from a
private CA or workload-identity system. The complete LiteLLM production fields
and validation procedure are in
Operations.
First start and configure the ordinary worktree. For placement and desktop lifecycle testing, create a base workspace with no applications or AI agents; this deliberately removes provider configuration from the qualification's critical path. Add an AI agent and provider route only when the test itself needs the gateway or agent bridge. The qualifier reuses the worktree's users, organizations, optional providers, pricing, routes, policies, PostgreSQL volumes, and workspace-home volumes. It does not dump or copy a database.
Stop every workspace through LemmaComputer, then validate without changing containers:
npm run qualify:remote-workspace-node -- configFor Cowork, verify hardware support and include the flag:
test -c /dev/kvm
test -c /dev/vhost-vsock
npm run qualify:remote-workspace-node -- config --coworkStart the split topology:
npm run qualify:remote-workspace-node -- upCowork-enabled remote mode is selected here—not in compose.hosted.yaml and
not by manually switching the local profile:
npm run qualify:remote-workspace-node -- up --coworkThe command:
- refuses
main, non-worktree profiles, and active workspace runtime containers; - generates two-day test certificates;
- creates worktree-scoped transport, application, and desktop-ingress networks;
- starts the controller in a separate Compose project with the node-local Docker socket;
- stops and removes any already-running colocated controller, disables that service for the split stack, and selects the same placement-aware router used by hosted remote topology;
- adds test-only mTLS application endpoints for Control and LiteLLM; and
- retains the existing control stack, databases, users, configuration, and persistent volumes.
The qualifier prints its stable node id. Open /platform, register that id with
endpoint https://workspace-node:4101 and TLS server name workspace-node, then
assign the test tenant. For a legacy workspace with no persisted owner, request
the explicit backfill only after confirming that this qualification node owns
its existing local volume. Missing placement intentionally fails closed; the
qualifier does not silently rewrite tenant ownership or operator audit history.
Inspect or restore the topology with:
npm run qualify:remote-workspace-node -- status
npm run qualify:remote-workspace-node -- downdown removes only qualification-owned containers, networks, PKI, and state,
then restores the colocated worktree stack. It preserves databases and
persistent workspace volumes. If an existing qualification was started without
Cowork, stop its workspaces, run down, then run up --cowork.
- Sign in with an existing worktree account and expected organization.
- Create and open a managed base workspace with no applications or AI agents.
- Inspect that base workspace and verify its application and agent selections are empty, no model alias or gateway credential is projected, and no Gateway or Control application relay is created. The desktop ingress relay remains required so the user can open the base desktop.
- Restart and reconnect to the base workspace through the product route; this is the minimum placement and lifecycle qualification and needs no provider.
- Separately, when qualifying AI behavior, select an agent, configure its provider route, and create or restart the workspace.
- Create and open disposable and managed workspaces when both profile types are in the test scope.
- Test an allowed and denied public destination.
- Complete a governed model request and verify provider credentials are absent from the sandbox.
- Complete Hermes Desktop and Hermes CLI turns.
- Open Chrome, Visual Studio Code, and Obsidian; in each app, create persistent state, close and reopen the app, then restart/reconnect the workspace and verify that state remains.
- Inspect the workspace container and confirm it is enforcing
lemmacomputer-workspace-electron, retainsno-new-privileges, and does not add capabilities or host devices for the Electron applications. - Open Claude Desktop, verify Cowork virtualization, and complete a Cowork action.
- Restart and reconnect to the AI-enabled workspace through the product route.
- Run two workspaces concurrently and verify separate IDs, networks, relays, and home volumes.
- Stop one workspace and confirm the other remains reachable.
- Inspect audit events without exposing certificates, tokens, prompts, or provider secrets.
The two Compose projects still use one physical Docker Engine. Local qualification proves configuration, mTLS identities, route projection, Docker authority separation at the container boundary, and application behavior. It does not prove cloud security groups, cross-host DNS, load balancers, certificate issuance/rotation/revocation, managed database restore, autoscaling, node draining, cross-node latency, or failure recovery.
Hosted requires representative private-node testing with nested
virtualization, /dev/kvm, /dev/vhost-vsock, encrypted workspace storage,
real workload certificates, network deny rules, monitoring, backup/restore,
node replacement, and Claude Cowork acceptance. The hosted profile validates
the configuration contract; it is not a local infrastructure emulator.
| Symptom | Check |
|---|---|
| Environment reports an incomplete coupled group | Run npm run env:update; do not fill individual certificate fields manually. |
| Topology switch is refused | Stop every managed workspace first. |
all predefined address pools have been fully subnetted |
Remove only ownership-verified empty worktree networks or configure a non-overlapping Docker default address pool; never prune globally. |
| Node API reports TLS or identity errors | Check CA, server SAN/name, client certificate CN, bearer token, and clock. |
WORKSPACE_UPSTREAM_UNAVAILABLE |
Inspect ingress and that workspace's desktop relay and certificate projection. |
| Desktop works but model or agent bridge fails | Inspect that workspace's gateway and Control relays plus application-endpoint mTLS. |
| Cowork says virtualization is unavailable | Check both devices, nested virtualization, host memory, --cowork, and start the workspace after enabling Cowork projection. |
CONTRIBUTING.md is the command and test-suite index.
Every change runs npm run verify:quick; persistence and migration changes also
run npm run verify:db. User-visible Web changes run the smallest relevant
Playwright suite. Report actual commands and outcomes rather than relying on a
hidden hook.
The integration owner merges verified work into main. main remains
buildable but is not the running demo. A demo release requires a clean pushed
commit and:
npm run verify:release
npm run release:tag -- --pushDeploy the immutable demo-* tag and the four first-party image digests
recorded by verify:release, never a moving branch, mutable image tag, or dirty
checkout. Do not resolve migration conflicts by renumbering or editing a
migration already applied anywhere; use forward reconciliation.