Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Devin Outposts on Tensorlake

Run Devin Outposts sessions on Tensorlake sandboxes. The orchestrator watches a Devin Outposts queue, claims sessions for one outpost, creates or resumes a Tensorlake Firecracker microVM for each session, launches the pinned devin-remote binary inside that sandbox, then releases and suspends or terminates the sandbox when the session sleeps or ends.

Devin's agent loop (inference and planning) keeps running in Devin's cloud. Command execution, file edits, and repository access happen inside a Tensorlake sandbox that you own and can inspect.

The orchestrator runs inside a Tensorlake sandbox: a launcher command on your machine starts it in a long-lived dispatcher sandbox, so no long-running orchestrator process remains on your machine. Observe it with --status and --logs. See Run the orchestrator. A Makefile wraps every command below (make connect, make build, make build-dispatcher, make orchestrator-sandbox, make status, make logs).

Prerequisites

  • A Devin account with Outposts enabled.
  • A Tensorlake account and API key that can build images and manage sandboxes, from cloud.tensorlake.ai.
  • Python 3.10+ on the machine that runs the setup and launcher commands (connect, image builds, sandbox launch). The orchestrator itself runs inside a Tensorlake sandbox.
  • A Devin organization administrator who can approve the outpost connection and manage service users.

Tensorlake sandboxes are Linux Firecracker microVMs, so an outpost served this way is Linux only. Run one orchestrator process per outpost.

Install the package:

python3 -m venv .venv
. .venv/bin/activate
pip install -e .
cp .env.example .env

outposts-connect fills in the Devin settings. Add your Tensorlake API key before building the image. After the build, copy the printed image name into IMAGE_NAME in .env before starting the orchestrator.

Connect Devin

Run the connection command on the same machine as your browser:

outposts-connect --platform linux

The command opens Devin's connection page. A Devin organization administrator confirms the outpost name and platform and clicks Connect. Devin redirects the browser to a temporary listener on http://localhost:8765/callback; the command exchanges that one-time code directly with Devin and writes DEVIN_OUTPOSTS_TOKEN, DEVIN_API_URL, and OUTPOST_ID to this project's .env. The listener then exits. The machine token does not pass through the browser, and nothing is written to a global environment store.

The command and browser must run on the same machine because the callback uses localhost. If the machine you launch the orchestrator sandbox from is headless, run the connection on a workstation and transfer .env securely, or use manual setup. See Cognition's Outposts partner integration documentation for the authorization flow.

Token scopes

Token Scope Used for
DEVIN_OUTPOSTS_TOKEN account.outposts.machine Created with a service user by outposts-connect; used for queue list, watch, claim, and release. Sandboxes never receive this token; each remote gets its claim's connect token.
TENSORLAKE_API_KEY Tensorlake API access Image build and sandbox lifecycle calls. The Tensorlake SDK reads it from the environment; it is never placed on a command line.
GIT_TOKEN Repo-read access, only if REPOS needs it Optional repo pre-clone step.

DEVIN_OUTPOSTS_TOKEN is intentionally named after Devin's Outposts machine-serving contract. Do not treat it as interchangeable with a generic DEVIN_API_TOKEN from unrelated Devin API scripts.

Manual outpost setup

If the connection flow is unavailable, install the Devin CLI and create an outpost with a Devin v3 service-user token (prefix cog_) whose service user has the Use outpost machine and Manage outposts permissions:

Token Scope Used for
Devin outpost-create token account.outposts.orchestrator One-time outpost creation with devin worker outpost create. Remove it from your shell after the outpost exists.
export DEVIN_API_URL="https://api.devin.ai"
export DEVIN_OUTPOST_CREATE_TOKEN="replace-with-outpost-create-token"

devin worker outpost create tensorlake-linux \
  --platform linux \
  --description "Tensorlake Linux sandboxes for Devin Outposts" \
  --api-url "$DEVIN_API_URL" \
  --token "$DEVIN_OUTPOST_CREATE_TOKEN"

Do not add /opbeta to DEVIN_API_URL; the queue client appends that path. Copy the returned outpost ID into OUTPOST_ID in .env, and set DEVIN_OUTPOSTS_TOKEN to a service-user key with the account.outposts.machine scope.

Build the sandbox image

The orchestrator creates each serving sandbox from a registered Tensorlake image. build-devin-outposts-image installs the required pieces on top of a Tensorlake Ubuntu base: ca-certificates, curl, git, and the /opt/.devin and /var/log/devin directories. It also attempts to install the GitHub CLI and Chromium on a best-effort basis. The command prints the recipe hash and image name.

The image name is derived from a hash of the build recipe as devin-outposts-tl-{sha8}, so editing a step produces a new name and re-running with an unchanged recipe reuses the existing image. Set IMAGE_NAME before the build to pin an explicit name instead.

set -a
. ./.env
set +a

build-devin-outposts-image

If the console script is unavailable but the package dependencies are installed, run this instead:

python -m devin_outposts.build_image

Copy the printed image name into IMAGE_NAME in .env before starting the orchestrator. The build allocates the CPU, memory, and disk from SANDBOX_CPUS, SANDBOX_MEMORY_MB, and SANDBOX_DISK_MB (defaults: 2 vCPU, 8 GiB, 10 GiB), and each concurrently served session runs one sandbox.

The gh and Chromium steps are best-effort so the image always builds. If your sessions must build or test code or drive a browser, harden those steps in devin_outposts/build_image.py and add language toolchains there. The base image is tensorlake/ubuntu-systemd; override it with IMAGE_BASE.

Run the orchestrator

After .env has DEVIN_OUTPOSTS_TOKEN, TENSORLAKE_API_KEY, OUTPOST_ID, and IMAGE_NAME, launch the orchestrator. DEVIN_API_URL is optional and defaults to https://api.devin.ai.

The orchestrator runs inside a long-lived Tensorlake sandbox launched from a dispatcher image. Your machine runs the build and launcher commands, but it does not need to keep an orchestrator process alive. Build that image once, then launch:

build-devin-outposts-dispatcher-image     # once; or: make build-dispatcher
devin-outposts-orchestrator-sandbox        # launch/resume; or: make orchestrator-sandbox

Observe and manage it:

devin-outposts-orchestrator-sandbox --status      # make status
devin-outposts-orchestrator-sandbox --logs         # make logs
devin-outposts-orchestrator-sandbox --terminate    # make stop

Devin Outposts is watch-based: the orchestrator must be running and watching the queue to claim sessions, so this is not truly scale-to-zero. Tensorlake keeps a named sandbox up to your plan's maximum idle window and then suspends it. While it is suspended, it cannot claim sessions. devin-outposts-orchestrator-sandbox is idempotent: it resumes the sandbox and makes sure the process is running. Schedule that command with cron or a Tensorlake schedule to restore the dispatcher after the idle ceiling. The dispatcher sandbox holds TENSORLAKE_API_KEY and the Devin machine token. It also receives GIT_TOKEN when configured, so treat it as a trusted control-plane host.

Create a session

Create Devin sessions from the Devin UI or Slack and select the outpost you created. The orchestrator will claim matching sessions from that outpost.

Fetch session logs from a serving sandbox

The orchestrator log (make logs) only shows lifecycle events (claim, sandbox created, serving, released). The session's processing output — every tool call devin-remote executes — is written inside the serving sandbox to /tmp/devin-outposts/<session>.log. Use session_logs.py to read it:

python session_logs.py                 # list this outpost's serving sandboxes
python session_logs.py <dvo-name>      # dump the last 500 lines
python session_logs.py <dvo-name> -f   # stream live (ctrl-C to stop)

The sandbox name is the dvo-... string the orchestrator prints when it claims a session. Live mode attaches a tail -F over a PTY websocket — the SDK's streaming channel — and follows the session lifecycle: it reconnects when the websocket idles out, waits while the sandbox is suspended (Devin put the session to sleep) and re-attaches on resume, and exits when the sandbox is terminated because the session ended.

Code map

  • devin_outposts.config — defines the environment contract for orchestration.
  • devin_outposts.connect — authorizes an outpost through Devin and writes its machine credentials to .env.
  • devin_outposts.queue — provides the thin typed client for the early-access queue API.
  • devin_outposts.queue_shapes — normalizes drifting beta queue payload shapes.
  • devin_outposts.sandbox — names sandboxes and looks them up by name across restarts (Tensorlake has no labels).
  • devin_outposts.worker — prepares and supervises the remote via sandbox.run.
  • devin_outposts.worker_linux — launches the pinned, checksum-verified Linux remote.
  • devin_outposts.orchestrator — reconciles queue state with sandbox and worker lifecycle; runs inside the dispatcher sandbox.
  • devin_outposts.build_image — builds the hash-named per-session worker image.
  • devin_outposts.dispatcher_image — builds the image that runs the orchestrator inside a sandbox.
  • devin_outposts.launch_orchestrator_sandbox — launches/observes the orchestrator sandbox from your machine.

See docs/ARCHITECTURE.md for the component and lifecycle design.

Configuration

Variable Required Notes
DEVIN_OUTPOSTS_TOKEN Yes Service-user key scoped for machine serving, account.outposts.machine. Sent as a bearer token to the queue API. Sandboxes never receive it; each remote gets only its claim's connect token.
DEVIN_API_URL No Devin API base without /opbeta. Defaults to https://api.devin.ai.
TENSORLAKE_API_KEY Yes Read by the Tensorlake SDK for image build and sandbox lifecycle.
OUTPOST_ID Yes The outpost ID written by outposts-connect or returned by manual creation. Run one orchestrator process per outpost.
IMAGE_NAME Yes for the orchestrator Registered Tensorlake image used to create serving sandboxes. build-devin-outposts-image derives this when the variable is blank; the orchestrator needs the actual name in .env.
TENSORLAKE_ORGANIZATION_ID No Pin the org when your account spans more than one.
TENSORLAKE_PROJECT_ID No Pin the project when your account spans more than one.
TENSORLAKE_NAMESPACE No Pin the namespace; defaults to the SDK default.
MAX_CONCURRENT_SESSIONS No Default 5. The orchestrator does not serve more sessions than this at once.
ACCEPTOR_ID No Leave blank for a generated ID that persists in STATE_DIR. Set it only when you need a stable, unique name for this instance. Do not share one acceptor ID across instances.
STATE_DIR No Stores the watch cursor, generated acceptor ID, and the sandbox→session index. The launcher pins it to /root/.devin-outposts-orchestrator inside the dispatcher sandbox, so any value set here is ignored.
SANDBOX_CPUS No vCPUs per serving sandbox. Default 2. Match your image build.
SANDBOX_MEMORY_MB No Memory per serving sandbox. Default 8192. Tensorlake allows 1024–8192 MB per CPU core.
SANDBOX_DISK_MB No Disk per serving sandbox. Default 10240.
SANDBOX_TIMEOUT_SECS No Idle threshold before Tensorlake auto-suspends an idle named sandbox. Default 1800. 0 requests your plan maximum. The supervise loop sends traffic more often than this, so a serving sandbox stays awake.
SANDBOX_NO_INTERNET No true blocks outbound internet from serving sandboxes. Keep false: the remote needs its outbound connection to Devin's gateway.
SANDBOX_USER No User the remote runs as. Default root.
SANDBOX_HOME No Home directory for the remote and its state. Default /root.
WORKDIR No Working directory for repo clones and the remote launch. Default /root/workspace.
DEVIN_CHROME_PATH No Browser path inside sandboxes. Default /usr/bin/chromium.
REPOS No Comma-separated clone URLs for optional pre-clone into WORKDIR.
GIT_USERNAME No Optional username for private repos cloned from REPOS.
GIT_TOKEN No Optional token for private repos cloned from REPOS. The launcher forwards it to the trusted dispatcher sandbox.

Lifecycle and state machine

Queue state Orchestrator action
phase=pending, session_status=pending or running Claim with this ACCEPTOR_ID, create the sandbox (or resume an existing one), clone REPOS when configured, then launch the pinned devin-remote.
phase=claimed, acceptor_id matches this orchestrator Re-attach on startup. If the sandbox or worker is gone, release the claim and suspend or terminate according to the latest queue status.
session_status=suspended Release the claim and suspend the sandbox. Tensorlake preserves the filesystem, memory, and running processes so the same session resumes in place.
session_status=terminated Release any held claim and terminate the sandbox.
Queue entry missing or delete event Treat as suspended: suspended sessions leave the queue entirely, so release any held claim and suspend the sandbox while keeping its state.
Claim conflict 409 Another acceptor won the compare-and-set claim. The orchestrator leaves the session alone.

Tensorlake suspend/resume is the key difference from stop/start on other providers: a resumed session picks up with its memory and processes intact, not just its disk. docs/ARCHITECTURE.md covers the watch loop, serve sequence, reattach after restart, and the janitor.

Sandbox names and the session index

Tensorlake sandboxes have no labels, and Sandbox.connect resolves by sandbox ID rather than name, so the deterministic sandbox name is the reconciliation key:

  • Name: dvo-{outpost8}-{session}-{hash} — an 8-char hash of the outpost ID as a prefix, a sanitized session fragment, and a 16-char hash of the session ID.
  • The outpost prefix lets the janitor scope a Sandbox.list sweep to this outpost; the session hash keeps distinct sessions from colliding.
  • A sandbox_index.json in STATE_DIR maps sandbox name → session ID so the janitor and reattach logic can map a listed sandbox back to its Devin session after an orchestrator restart.

Repo handling

Use REPOS when the worker needs repository state before the remote launches.

REPOS=https://github.com/example/private-app.git,https://github.com/example/shared-lib.git
GIT_USERNAME=replace-with-git-username
GIT_TOKEN=replace-with-git-token

When REPOS is configured, the orchestrator creates WORKDIR and clones those URLs before starting the worker. Credentials are passed to git through a GIT_ASKPASS helper written into the sandbox, so the token never appears on a command line or in git config. Treat GIT_TOKEN like any other secret. The launcher forwards it to the dispatcher sandbox and then to each serving sandbox that needs to clone a configured repository.

Validation checklist

Run this before relying on the orchestrator in your own environment:

  1. Build the image with build-devin-outposts-image and copy IMAGE_NAME.
  2. Create a fresh sandbox from that image and verify git --version. If your workflow depends on the best-effort tools, verify gh --version and Chromium as well.
  3. Create a Devin session on the outpost.
  4. Confirm the orchestrator claims it, creates a sandbox, starts the worker, and completes a smoke prompt that writes a file under WORKDIR.
  5. Let the session sleep, then message it again. Confirm the same sandbox resumes and the file still exists.
  6. Terminate the session. Confirm the orchestrator terminates the sandbox.
  7. Set a stable ACCEPTOR_ID in .env, terminate the dispatcher sandbox during an active session (devin-outposts-orchestrator-sandbox --terminate), relaunch it, and confirm the orchestrator re-attaches to entries claimed by that ACCEPTOR_ID. (Terminating the dispatcher discards its generated state, so a pinned acceptor ID is required for this check.)

Caveats

  • Outposts APIs are in early access. The queue client is intentionally thin; expect endpoint shapes to change.
  • The current Devin session-create API may not expose outpost selection. If so, create sessions from the Devin UI or Slack and select the outpost there.
  • The remote runs as root in a per-session isolated microVM by default. Set SANDBOX_USER/SANDBOX_HOME if your image ships a different user.
  • The default image installs gh and Chromium best-effort. Validate the browser and desktop tools against your tenant before relying on Devin's browser or computer-use features, and add language toolchains for build/test sessions.
  • The image recipe (devin_outposts/build_image.py) is the main thing to verify against your Tensorlake tenant, along with the sandbox SANDBOX_USER/HOME defaults.

Secret handling

  • Keep real values in .env, a trusted launch environment, or a secret manager.
  • Keep .env.example placeholders fake.
  • Do not paste tokens into session prompts, logs, docs, generated artifacts, or issue comments.
  • Secrets are passed into subprocesses and the remote through environment variables, never argv.

Related

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages