Skip to main content

Minimal control plane for multi-VM Claude Code orchestration

Project description

ocaptain

Python sprites.dev

O Captain! my Captain! our fearful Claude Code session is done, The repo has weather'd every rack, the prize we sought is won.

Minimalist multi-coding agent control plane built on sprites.dev VMs with Tailscale mesh networking. Orchestration is managed by Claude Code's built-in task list feature: work is distributed by the ocaptain CLI to cloud VMs ("ships") that work in parallel on a plan you generate with Claude.

No Kubernetes, no local sandboxes, no containers, no asking for permissions.

Table of Contents

What it does

Provisions a fleet of VMs on sprites.dev, each running an autonomous Claude Code agent. Ships sync files via Mutagen and coordinate through a shared task list—no central scheduler, just agents racing to complete work.

You (local) → ocaptain sail → sprites.dev VMs → Ships claim tasks → Code syncs back

Why?

  • Deploy parallel, autonomous Claude Code instances with one command
  • No container complexity: full Linux VMs with direct access
  • Watch Claude Code sessions in real-time via tmux
  • Task-based coordination means no conflicts, no merge hell
  • Local storage with Mutagen sync—no storage VM required
  • Built-in OpenTelemetry observability

Quickstart

Prerequisites

  1. sprites.dev account with sprite CLI installed
  2. Tailscale installed and running
  3. Mutagen installed (brew install mutagen-io/mutagen/mutagen)
  4. Claude Code long-lived OAuth token (subscription required, from claude setup-token)
  5. Tailscale OAuth secret for ephemeral auth keys

Install

# Clone and install
git clone https://github.com/smithclay/ocaptain.git
cd ocaptain
uv sync

Set credentials

# Claude Code OAuth token (from `claude setup-token`)
export CLAUDE_CODE_OAUTH_TOKEN="your-token-here"

# Tailscale OAuth secret (for ephemeral ship auth keys)
export OCAPTAIN_TAILSCALE_OAUTH_SECRET="tskey-client-xxxx"

# sprites.dev org
export OCAPTAIN_SPRITES_ORG="your-org"

# Optional: GitHub token for private repos
export GH_TOKEN="ghp_xxxx"

Check prerequisites

uv run ocaptain doctor

Make a plan

At the moment, you need to use Claude Code to create a detailed dependency plan to pass to ocaptain.

Use an existing plan

For convenience, example plans are available in the examples/generated-plans directory.

Create a custom plan

In Claude Code, run the following then restart:

/plugin marketplace add smithclay/skills
/plugin install ocaptain-skills@smithclay-skills

You're now ready to generate a detailed plan in Claude Code using /voyage-plan:

Plan a voyage with ocaptain: take an empty repository and make a to-do list app.

Launch a voyage

# A plan is required, see "Make a plan" above
uv run ocaptain sail ./examples/generated-plans/multilingual-readme

# Monitor status
uv run ocaptain status

# Attach to a ship's tmux session to watch Claude work
uv run ocaptain shell voyage-abc123 ship-0

# View aggregated logs
uv run ocaptain logs voyage-abc123 --follow

# Clone the workspace when done
uv run ocaptain clone voyage-abc123

# Scuttle the fleet
uv run ocaptain sink voyage-abc123

Architecture

flowchart TB
    subgraph Local["Local Machine"]
        CLI[ocaptain CLI]
        Voyages[(~/voyages/)]
        Telemetry[OTLP Collector]
    end

    subgraph Tailscale["Tailscale Mesh"]
        TS[100.x.x.x]
    end

    subgraph Sprites["sprites.dev"]
        subgraph Fleet["Ship VMs"]
            S0[Ship 0<br/>Claude Code]
            S1[Ship 1<br/>Claude Code]
            S2[Ship N<br/>Claude Code]
        end
    end

    CLI -->|Mutagen Sync| TS
    TS -->|Workspace + Tasks| Fleet
    Fleet -->|Telemetry| Telemetry
    S0 & S1 & S2 -->|tmux sessions| CLI

Components

Component Description
Local Voyages ~/voyages/<voyage-id>/ contains workspace, tasks, logs, and artifacts
Ship VMs sprites.dev VMs running Claude Code autonomously in tmux sessions
Tailscale Mesh Ships join tailnet with ephemeral keys for direct connectivity
Mutagen Sync Two-way file sync between laptop and ships (workspace + tasks)
Task List Shared JSON files in ~/.claude/tasks/. Ships race to claim pending tasks

Voyage Lifecycle

  1. Setup — Local voyage directory created, repo cloned, tasks seeded
  2. Bootstrap — Ship VMs provisioned in parallel, join Tailscale, install Claude
  3. Sync — Mutagen sessions established for workspace and tasks
  4. Launch — Claude starts in tmux on each ship (autonomous operation)
  5. Work — Ships claim tasks, do work, changes sync back via Mutagen
  6. Complete — All tasks done, workspace ready to clone
  7. Sink — VMs destroyed, Tailscale nodes removed

CLI Reference

ocaptain sail <plan>

Launch a new voyage from a plan directory.

uv run ocaptain sail ./plans/add-auth --ships 5
Option Description
--ships, -n Override recommended ship count
--no-telemetry Disable OTLP telemetry collection

ocaptain status [voyage_id]

Show voyage status derived from task list. Auto-selects if only one active voyage.

uv run ocaptain status
uv run ocaptain status voyage-abc123

ocaptain logs <voyage_id>

View aggregated logs from all ships.

uv run ocaptain logs voyage-abc123
uv run ocaptain logs voyage-abc123 --follow --grep "error"
Option Description
--ship, -s Filter to specific ship
--follow, -f Stream logs in real-time
--grep, -g Filter log lines by pattern
--tail, -n Show last N lines

ocaptain tasks <voyage_id>

Display task list with status, assignees, and blockers.

uv run ocaptain tasks voyage-abc123
uv run ocaptain tasks voyage-abc123 --status pending

ocaptain shell <voyage_id> [ship_id]

Attach to a ship's tmux session to observe Claude working.

uv run ocaptain shell voyage-abc123 ship-0      # Attach to ship's tmux
uv run ocaptain shell voyage-abc123 ship-0 --raw  # Direct SSH

ocaptain clone [voyage_id]

Clone the workspace from local voyage storage.

uv run ocaptain clone                    # Auto-select if one voyage
uv run ocaptain clone voyage-abc123 -d ./my-copy

ocaptain sink <voyage_id>

Destroy voyage VMs and clean up.

uv run ocaptain sink voyage-abc123       # Destroy ships
uv run ocaptain sink --all -f            # Destroy ALL ocaptain VMs
Option Description
--all Destroy ALL ocaptain VMs
--force, -f Skip confirmation

ocaptain doctor

Check system prerequisites and configuration.

uv run ocaptain doctor

ocaptain telemetry-start / telemetry-stop

Start or stop the local OTLP telemetry collector.

uv run ocaptain telemetry-start
uv run ocaptain telemetry-stop

Configuration

Environment Variables

Variable Required Description
CLAUDE_CODE_OAUTH_TOKEN Yes Claude Code authentication token
OCAPTAIN_TAILSCALE_OAUTH_SECRET Yes Tailscale OAuth secret for ephemeral keys
OCAPTAIN_SPRITES_ORG Yes sprites.dev organization name
GH_TOKEN No GitHub token for private repos
OCAPTAIN_DEFAULT_SHIPS No Default ship count (default: 3)

sprites.dev Setup

Install the sprite CLI and authenticate:

# Test connectivity
sprite list -o your-org

# Create a test sprite
sprite create -o your-org test-sprite

Tailscale Setup

  1. Create an OAuth client in the Tailscale admin console with devices:write scope
  2. Set OCAPTAIN_TAILSCALE_OAUTH_SECRET to the client secret
  3. Ensure your laptop is connected to the tailnet

Security

Token Handling

  • OAuth tokens passed via environment variables, not command args
  • Tailscale ephemeral keys expire automatically when ships are destroyed

Network Isolation

  • Ships communicate only via Tailscale mesh (no public IPs)
  • Each ship is a full Linux VM with its own filesystem
  • Mutagen sync scoped to specific directories

Repository Access

  • Private repos require GH_TOKEN
  • Token validity checked before VM provisioning
  • Clone failures surface immediately, not after fleet deploys

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ocaptain-0.0.2.tar.gz (9.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ocaptain-0.0.2-py3-none-any.whl (10.9 kB view details)

Uploaded Python 3

File details

Details for the file ocaptain-0.0.2.tar.gz.

File metadata

  • Download URL: ocaptain-0.0.2.tar.gz
  • Upload date:
  • Size: 9.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ocaptain-0.0.2.tar.gz
Algorithm Hash digest
SHA256 13619d4373afe1ef839220d4c368264a99b4b3722decab01c41d73127e65a4b0
MD5 b2d661aad05bfd6019cb425d6f4a85b9
BLAKE2b-256 3bc58a5b7ba9f2b5363467475085c947ebb9cc5346a90acee8d91aaa043633c9

See more details on using hashes here.

File details

Details for the file ocaptain-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: ocaptain-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 10.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ocaptain-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 4bd1bd4f3d7a0e1413989869a5edcc81ed15b3809eb763f1eb0ecf2754a180d5
MD5 e2b905320d06d30d78c6d3264f968b5f
BLAKE2b-256 11e3c5c0101e9329df6cf897f761855519bee872c8ee7189d830bd5af1c3a1f3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page