Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/features/provider-live-smoke.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ hermetic lifecycle tests, `scripts/live-smoke.sh`, dedicated live runners, and
`//go:build smoke` tests. Regenerate it with
`node scripts/generate-provider-matrix.mjs`; docs CI rejects drift.

Current coverage: 72 providers; 4 with convention-named hermetic lifecycle tests, 52 with a live runner, 3 with tagged Go smoke tests, and 19 with none of those lifecycle surfaces.
Current coverage: 73 providers; 4 with convention-named hermetic lifecycle tests, 53 with a live runner, 4 with tagged Go smoke tests, and 19 with none of those lifecycle surfaces.

| Provider | Hermetic lifecycle | Live runner | Tagged Go smoke |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -203,6 +203,7 @@ Current coverage: 72 providers; 4 with convention-named hermetic lifecycle tests
| [nvidia-brev](../providers/nvidia-brev.md) | — | dedicated + matrix | — |
| [opencomputer](../providers/opencomputer.md) | — | — | — |
| [opensandbox](../providers/opensandbox.md) | — | dedicated + matrix | — |
| [orgo](../providers/orgo.md) | — | matrix | yes |
| [ovh](../providers/ovh.md) | — | dedicated + matrix | — |
| [parallels](../providers/parallels.md) | — | — | — |
| [phala](../providers/phala.md) | — | dedicated + matrix | — |
Expand Down
3 changes: 2 additions & 1 deletion docs/providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ selection metadata. Regenerate it with `node scripts/generate-provider-matrix.mj
`scripts/check-docs.sh` fails when provider registration, metadata, docs paths, or
this generated table drift.

Current built-in surface: 72 providers (42 SSH lease, 28 delegated run, 2 service control).
Current built-in surface: 73 providers (42 SSH lease, 29 delegated run, 2 service control).

Access terms:

Expand Down Expand Up @@ -119,6 +119,7 @@ Access terms:
| [nvidia-brev](nvidia-brev.md) (`brev`, `nvidia`) | built-in; `ssh-lease` · gpu-cloud | Crabbox-managed SSH; `crabbox-sync` · direct only; features: `ssh`, `crabbox-sync`, `cleanup` | `linux`; NVIDIA Brev GPU workspace | `provider-managed`; GPU: yes | NVIDIA Brev CLI; delete by default; optional stop | Managed NVIDIA GPU workspace over SSH | Requires Brev CLI auth, quota, and available GPU capacity |
| [opencomputer](opencomputer.md) (`oc`, `open-computer`) | built-in; `delegated-run` · delegated-sandbox | No SSH; `archive-sync` · direct only; features: `archive-sync`, `run-session` | `linux`; OpenComputer Linux VM | `provider-managed`; GPU: unknown | OpenComputer; VM delete | Hosted delegated Linux VM execution | REST execution contract, not an SSH lease |
| [opensandbox](opensandbox.md) | built-in; `delegated-run` · delegated-sandbox | No SSH; `archive-sync` · direct only; features: `archive-sync`, `cleanup`, `run-session` | `linux`; OpenSandbox sandbox | `provider-managed`; GPU: unknown | OpenSandbox; sandbox delete | Hosted delegated sandbox through an open SDK | Requires compatible OpenSandbox control and exec endpoints |
| [orgo](orgo.md) (`orgo-ai`) | built-in; `delegated-run` · delegated-sandbox | No SSH; `provider-owned` · direct only; features: none | `linux`; Orgo cloud computer | `provider-managed`; GPU: unknown | Orgo; computer and temporary workspace delete | Managed Linux computer execution through an HTTP API | No normal SSH lease or Crabbox workspace sync |
| [ovh](ovh.md) | built-in; `ssh-lease` · direct-cloud | Crabbox-managed SSH; `crabbox-sync` · direct only; features: `ssh`, `crabbox-sync`, `cleanup`, `tailscale` | `linux`; OVHcloud Public Cloud instance | `cloud`; GPU: optional | Crabbox; instance, key, and local claim delete | OVHcloud Public Cloud Linux VM | Direct-only; credentials use OVH signed requests and local claims |
| [parallels](parallels.md) | built-in; `ssh-lease` · local-vm | Crabbox-managed SSH; `crabbox-sync` · direct only; features: `ssh`, `crabbox-sync`, `cleanup`, `desktop`, `browser`, `code`, `workspace-checkpoint`, `workspace-fork`, `workspace-restore`, `provider-snapshot` | `linux`, `macos`, `windows/normal`, `windows/wsl2`; Parallels linked-clone VM | `local`; GPU: no | Crabbox; clone delete | Local macOS, Linux, or Windows VM with snapshots | Requires prepared Parallels source VMs and SSH |
| [phala](phala.md) (`phala-cloud`, `dstack`) | built-in; `ssh-lease` · direct-cloud | Crabbox-managed SSH; `crabbox-sync` · direct only; features: `ssh`, `crabbox-sync`, `cleanup` | `linux`; Phala Cloud confidential Intel TDX CVM | `provider-managed`; GPU: no | Phala phala CLI; CVM delete | Short-lived confidential Linux compute over SSH | Requires the phala CLI and its stored auth; verifies Intel TDX attestation by default (needs outbound Intel PCS network; --phala-skip-attestation to opt out) |
Expand Down
146 changes: 146 additions & 0 deletions docs/providers/orgo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Orgo Provider

Read when:

- choosing `provider: orgo` (alias `orgo-ai`);
- running a command on an [Orgo](https://www.orgo.ai/) cloud computer;
- changing `internal/providers/orgo`.

Orgo provides Linux cloud computers with an HTTP control API. Crabbox uses the
REST API directly: it creates a workspace when needed, creates a computer, runs
commands through `POST /computers/{id}/bash`, and deletes resources on cleanup.

This is a delegated-run provider. Crabbox does not provision an SSH lease, does
not rsync the local workspace, and does not use the coordinator broker.

## When to use

Use Orgo when you want a managed Linux desktop/computer surface with API-driven
shell execution and fast startup. Pick an SSH-lease provider such as AWS,
Hetzner, Daytona, or Static SSH when you need Crabbox-managed SSH, rsync,
Actions hydration, or interactive `crabbox ssh`.

Orgo is Linux-only in Crabbox. Desktop, browser, code-server, normal SSH,
Actions hydration, and brokered coordinator routing are not available through
this adapter.

## Commands

```sh
crabbox run --provider orgo --no-sync -- echo crabbox-orgo-ok
crabbox warmup --provider orgo --slug orgo-smoke
crabbox run --provider orgo --id orgo-smoke --no-sync -- uname -a
crabbox status --provider orgo --id orgo-smoke --wait
crabbox stop --provider orgo orgo-smoke
crabbox list --provider orgo
crabbox doctor --provider orgo
```

Without `--id`, `run` creates a new computer, runs the command, then deletes the
computer unless `--keep` is set. With `--id <computer-id-or-slug>`, it runs the
command on the existing computer and leaves it running.

`warmup` creates a persistent computer and records a local Crabbox slug so later
commands can use `--id <slug>`.

## Auth

Resolve the Orgo API key in this precedence:

1. `CRABBOX_ORGO_API_KEY` — explicit per-run override, CI-friendly.
2. `orgo.apiKey` — trusted user or explicit `CRABBOX_CONFIG` value; repository
config cannot supply provider secrets.
3. `ORGO_API_KEY` — canonical Orgo environment variable.

The API key is never exposed as a CLI flag; do not pass secrets on the command
line.

## Config

```yaml
provider: orgo
target: linux
orgo:
apiBase: https://www.orgo.ai/api
workspaceID: 550e8400-e29b-41d4-a716-446655440000
ramGB: 4
cpus: 1
diskGB: 8
resolution: 1280x720x24
```

If `workspaceID` is omitted, Crabbox creates a temporary workspace for each new
computer and deletes that workspace during cleanup. For workspace-scoped Orgo
keys, set `workspaceID` because scoped keys cannot create workspaces.

Provider flags, each overriding the matching `orgo.*` config key:

```text
--orgo-api-base
--orgo-workspace-id
--orgo-ram
--orgo-cpu
--orgo-disk
--orgo-resolution
```

Environment overrides:

- `CRABBOX_ORGO_API_BASE`, then `ORGO_API_BASE_URL`
- `CRABBOX_ORGO_WORKSPACE_ID`, then `ORGO_WORKSPACE_ID`
- `CRABBOX_ORGO_RAM_GB`
- `CRABBOX_ORGO_CPUS`
- `CRABBOX_ORGO_DISK_GB`
- `CRABBOX_ORGO_RESOLUTION`

Defaults: API base `https://www.orgo.ai/api`, 4 GB RAM, 1 CPU, 8 GB disk,
and resolution `1280x720x24`.

Repository config cannot redirect an inherited Orgo API key. Set
`CRABBOX_ORGO_API_BASE` or pass `--orgo-api-base` to explicitly approve a
custom credential destination. Non-loopback API endpoints must use HTTPS.

## Lifecycle

`crabbox run`:

1. With `--id <computer-id-or-slug>`, resolve the computer and call the bash
endpoint.
2. Otherwise, create a workspace when needed, create a computer, claim a local
Crabbox slug, run the command, then delete the computer and any temporary
workspace unless `--keep` is set.

`--keep-on-failure` preserves a newly created computer when the command fails,
so it can be inspected with Orgo tooling before manual cleanup.

## Capabilities

- SSH: no — Orgo command execution is HTTP API delegated.
- Crabbox sync: no — use `--no-sync`.
- Desktop / browser / code: no through Crabbox flags.
- Actions hydration: no.
- Coordinator: no — direct CLI-to-Orgo API calls only.

## Live Smoke

Keep the API key in the environment:

```sh
export CRABBOX_ORGO_API_KEY=your-orgo-api-key
go build -trimpath -o bin/crabbox ./cmd/crabbox

bin/crabbox doctor --provider orgo
bin/crabbox run --provider orgo --no-sync -- echo crabbox-orgo-ok
```

Or run the shared live provider harness:

```sh
CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=orgo scripts/live-smoke.sh
```

Related docs:

- [Provider reference](README.md)
- [Provider authoring](../features/provider-authoring.md)
- [Orgo API reference](https://docs.orgo.ai/api-reference/introduction)
14 changes: 14 additions & 0 deletions docs/providers/provider-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,20 @@
"caveat": "Requires compatible OpenSandbox control and exec endpoints",
"docs": "opensandbox.md"
},
"orgo": {
"status": "built-in",
"category": "delegated-sandbox",
"substrate": "Orgo cloud computer",
"location": "provider-managed",
"ssh": "no",
"sync": "provider-owned",
"gpu": "unknown",
"lifecycle": "Orgo",
"cleanup": "computer and temporary workspace delete",
"bestFit": "Managed Linux computer execution through an HTTP API",
"caveat": "No normal SSH lease or Crabbox workspace sync",
"docs": "orgo.md"
},
"parallels": {
"status": "built-in",
"category": "local-vm",
Expand Down
74 changes: 74 additions & 0 deletions internal/cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ type Config struct {
hostingerUserExplicit bool
hostingerWorkRootExplicit bool
Wandb WandbConfig
Orgo OrgoConfig
Islo IsloConfig
isloImageExplicit bool
isloVCPUsExplicit bool
Expand Down Expand Up @@ -724,6 +725,18 @@ type WandbConfig struct {
MaxLifetimeSeconds int
}

// OrgoConfig drives the Orgo delegated-run provider. The API key is resolved
// from env/config only; it must not be passed on the command line.
type OrgoConfig struct {
APIKey string
APIBase string
WorkspaceID string
RAMGB int
CPUs int
DiskGB int
Resolution string
}

type IsloConfig struct {
APIKey string
BaseURL string
Expand Down Expand Up @@ -2816,6 +2829,13 @@ func baseConfig() Config {
WorkRoot: "/tmp/crabbox",
WakeOnSSH: true,
},
Orgo: OrgoConfig{
APIBase: "https://www.orgo.ai/api",
RAMGB: 4,
CPUs: 1,
DiskGB: 8,
Resolution: "1280x720x24",
},
Daytona: DaytonaConfig{
APIURL: "https://app.daytona.io/api",
User: "daytona",
Expand Down Expand Up @@ -3196,6 +3216,7 @@ type fileConfig struct {
NvidiaBrev *fileNvidiaBrevConfig `yaml:"nvidiaBrev,omitempty"`
Hostinger *fileHostingerConfig `yaml:"hostinger,omitempty"`
Wandb *fileWandbConfig `yaml:"wandb,omitempty"`
Orgo *fileOrgoConfig `yaml:"orgo,omitempty"`
Islo *fileIsloConfig `yaml:"islo,omitempty"`
Freestyle *fileFreestyleConfig `yaml:"freestyle,omitempty"`
Tenki *fileTenkiConfig `yaml:"tenki,omitempty"`
Expand Down Expand Up @@ -3849,6 +3870,16 @@ type fileWandbConfig struct {
MaxLifetimeSeconds int `yaml:"maxLifetimeSeconds,omitempty"`
}

type fileOrgoConfig struct {
APIKey string `yaml:"apiKey,omitempty"`
APIBase string `yaml:"apiBase,omitempty"`
WorkspaceID string `yaml:"workspaceID,omitempty"`
RAMGB int `yaml:"ramGB,omitempty"`
CPUs int `yaml:"cpus,omitempty"`
DiskGB int `yaml:"diskGB,omitempty"`
Resolution string `yaml:"resolution,omitempty"`
}

type fileIsloConfig struct {
BaseURL string `yaml:"baseUrl,omitempty"`
Image string `yaml:"image,omitempty"`
Expand Down Expand Up @@ -6317,6 +6348,31 @@ func applyFileConfigWithTrust(cfg *Config, file fileConfig, trusted bool) error
cfg.Wandb.MaxLifetimeSeconds = file.Wandb.MaxLifetimeSeconds
}
}
if file.Orgo != nil {
if trusted && file.Orgo.APIKey != "" {
cfg.Orgo.APIKey = file.Orgo.APIKey
cfg.credentialProvenance.orgoAPIKey = credentialSource
}
if file.Orgo.APIBase != "" {
cfg.Orgo.APIBase = file.Orgo.APIBase
cfg.credentialProvenance.orgoAPIBase = credentialSource
}
if file.Orgo.WorkspaceID != "" {
cfg.Orgo.WorkspaceID = file.Orgo.WorkspaceID
}
if file.Orgo.RAMGB > 0 {
cfg.Orgo.RAMGB = file.Orgo.RAMGB
}
if file.Orgo.CPUs > 0 {
cfg.Orgo.CPUs = file.Orgo.CPUs
}
if file.Orgo.DiskGB > 0 {
cfg.Orgo.DiskGB = file.Orgo.DiskGB
}
if file.Orgo.Resolution != "" {
cfg.Orgo.Resolution = file.Orgo.Resolution
}
}
if file.Islo != nil {
if file.Islo.BaseURL != "" {
cfg.Islo.BaseURL = file.Islo.BaseURL
Expand Down Expand Up @@ -8407,6 +8463,24 @@ func applyEnv(cfg *Config) error {
cfg.Wandb.APIKey = getenv("CRABBOX_WANDB_API_KEY", cfg.Wandb.APIKey)
cfg.Wandb.DefaultImage = getenv("CRABBOX_WANDB_DEFAULT_IMAGE", getenv("WANDB_DEFAULT_IMAGE", cfg.Wandb.DefaultImage))
cfg.Wandb.MaxLifetimeSeconds = getenvInt("CRABBOX_WANDB_MAX_LIFETIME_SECONDS", getenvInt("WANDB_MAX_LIFETIME_SECONDS", cfg.Wandb.MaxLifetimeSeconds))
if value := os.Getenv("CRABBOX_ORGO_API_KEY"); value != "" {
cfg.Orgo.APIKey = value
cfg.credentialProvenance.orgoAPIKey = credentialSourceEnvironment
} else if cfg.Orgo.APIKey == "" {
if value := os.Getenv("ORGO_API_KEY"); value != "" {
cfg.Orgo.APIKey = value
cfg.credentialProvenance.orgoAPIKey = credentialSourceEnvironment
}
}
if value, ok := firstNonEmptyEnv("CRABBOX_ORGO_API_BASE", "ORGO_API_BASE_URL"); ok {
cfg.Orgo.APIBase = value
cfg.credentialProvenance.orgoAPIBase = credentialSourceEnvironment
}
cfg.Orgo.WorkspaceID = getenv("CRABBOX_ORGO_WORKSPACE_ID", getenv("ORGO_WORKSPACE_ID", cfg.Orgo.WorkspaceID))
cfg.Orgo.RAMGB = getenvInt("CRABBOX_ORGO_RAM_GB", cfg.Orgo.RAMGB)
cfg.Orgo.CPUs = getenvInt("CRABBOX_ORGO_CPUS", cfg.Orgo.CPUs)
cfg.Orgo.DiskGB = getenvInt("CRABBOX_ORGO_DISK_GB", cfg.Orgo.DiskGB)
cfg.Orgo.Resolution = getenv("CRABBOX_ORGO_RESOLUTION", cfg.Orgo.Resolution)
if value, ok := firstNonEmptyEnv("CRABBOX_ISLO_API_KEY", "ISLO_API_KEY"); ok {
cfg.Islo.APIKey = value
cfg.credentialProvenance.isloAPIKey = credentialSourceEnvironment
Expand Down
10 changes: 10 additions & 0 deletions internal/cli/credential_provenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type credentialDestinationProvenance struct {
railwayAPIToken credentialValueSource
fastAPICloudAPIURL credentialValueSource
fastAPICloudToken credentialValueSource
orgoAPIBase credentialValueSource
orgoAPIKey credentialValueSource
runpodAPIURL credentialValueSource
runpodAPIKey credentialValueSource
vastAPIURL credentialValueSource
Expand Down Expand Up @@ -168,6 +170,9 @@ func markCredentialDestinationFlagSources(cfg *Config, fs *flag.FlagSet) {
if flagWasSet(fs, "fastapi-cloud-url") {
provenance.fastAPICloudAPIURL = credentialSourceFlag
}
if flagWasSet(fs, "orgo-api-base") {
provenance.orgoAPIBase = credentialSourceFlag
}
if flagWasSet(fs, "runpod-url") {
provenance.runpodAPIURL = credentialSourceFlag
}
Expand Down Expand Up @@ -312,6 +317,11 @@ func validateProviderCredentialDestination(cfg Config) error {
inheritedCredential(sourcedCredential{cfg.FastAPICloud.Token, provenance.fastAPICloudToken}) {
return repositoryCredentialDestinationError("fastapi-cloud", "fastapiCloud.apiUrl", "CRABBOX_FASTAPI_CLOUD_API_URL or --fastapi-cloud-url")
}
case "orgo":
if provenance.orgoAPIBase == credentialSourceRepository &&
inheritedCredential(sourcedCredential{cfg.Orgo.APIKey, provenance.orgoAPIKey}) {
return repositoryCredentialDestinationError("orgo", "orgo.apiBase", "CRABBOX_ORGO_API_BASE or --orgo-api-base")
}
case "runpod":
if provenance.runpodAPIURL == credentialSourceRepository &&
inheritedCredential(sourcedCredential{cfg.Runpod.APIKey, provenance.runpodAPIKey}) {
Expand Down
Loading