Skip to content
Fuse
Esc
navigateopen⌘Jpreview

Attach to environment

Opens an interactive stream to a process inside the guest. Upgrades the connection to fuse-attach/1 and relays a raw duplex byte stream between the client and a process in the guest. This is the transport behind fuse environment shell.

This endpoint is not really describable in OpenAPI. What is modelled below is only the handshake — the request that opens the stream, and the 101 that accepts it. Everything of substance happens afterwards, on a hijacked socket that carries framed bytes rather than JSON. Treat the 101 as the end of the part this document can speak to. Attach is deliberately absent from the TypeScript and Python SDKs; only the Go SDK and the CLI speak it, because only an interactive terminal needs it.

Handshake. The request must carry Upgrade: fuse-attach/1 over HTTP/1.1, and should send Connection: Upgrade alongside it as HTTP/1.1 requires — though this server only inspects the Upgrade header, so a request omitting Connection is still accepted and hijacked. A request without the Upgrade header is rejected with 400 invalid_argument. The server replies 101 Switching Protocols and echoes the Upgrade header, after which the connection is no longer HTTP. HTTP/2 cannot be used: the connection has to be hijackable.

Attach requires the master token, for the same reason exec does: it is a root shell in the guest.

Why the spec is in the query string. An upgrade is a GET, so there is no body to put it in. Repeated cmd parameters preserve argv boundaries, which means an argument containing spaces survives the round trip without a quoting convention:

GET /v1/environments/vm-123/attach?tty=1&rows=24&cols=80&cmd=sh&cmd=-c&cmd=echo+hi

Frame protocol (fuse-attach/1). Once upgraded, both directions carry length-prefixed frames. The orchestrator does not interpret them; it relays bytes verbatim between the client and the host agent, which is what lets the same protocol run on both hops.

frame = [type:1][reserved:3][length:4 big-endian][payload:length]
type name direction payload
0 stdin client → server raw bytes
1 stdout server → client raw bytes
2 stderr server → client raw bytes
3 resize client → server {"rows":24,"cols":80}
4 exit server → client {"exit_code":0}

A single frame’s payload is capped at 1 MiB (1048576 bytes), so a bogus length on the wire cannot be turned into an allocation. The exit frame carries the guest command’s own status and is the last frame of a session; as with exec, a non-zero exit_code is a normal outcome and not an error.

GET/v1/environments/{vmId}/attach
Authorization
AuthorizationBearer token · headerrequired
`Authorization: Bearer <token>` — either the static master token (`ORCH_AUTH_TOKEN`) or a revocable API key issued via /v1/api-keys (API keys require Postgres; without a database only the master token is accepted). Master-only surfaces (exec, attach, API key management) refuse API keys with 403. **Authentication can be disabled entirely.** When neither `ORCH_AUTH_TOKEN` nor an API key store (`DATABASE_URL`) is configured — the default local-development posture — the bearer middleware becomes a pass-through: every operation this document marks as secured is reachable with no credentials, requests carry no principal, and master-only surfaces therefore admit everyone. Never run that configuration on a reachable network; set `ORCH_REQUIRE_AUTH=true` to make the orchestrator refuse to start without a master token.
or
fuse_sessionAPI key · cookierequired
HttpOnly session cookie set by POST /login for browser callers. Consulted only when no Authorization header is present.
Path parameters
vmIdstringrequired
VM identifier assigned by the orchestrator.
Query parameters
ttystringrequired
Must be `1` or `true`. Attach is pty-only; a request without it is rejected with 400 invalid_argument (`attach requires tty=1; use ?action=exec for non-interactive commands`).
Allowed:1true
rowsinteger
Initial pty height. Only seeds the window size at startup; later resizes travel as type 3 frames on the stream itself. Values outside the range are ignored rather than rejected.
min 1 · max 65535
colsinteger
Initial pty width. Same handling as `rows`.
min 1 · max 65535
cmdstring[]
The argv to run, one array element per repeated `cmd` parameter. Omit it entirely to get the guest's login shell.
Responses
101Upgrade accepted. The connection leaves HTTP and carries fuse-attach/1 frames in both directions until either end closes it.
400The request did not carry `Upgrade: fuse-attach/1`, or did not set `tty=1`.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted only by CIDR-allowlist rejections; master-only refusals (exec, attach, API key management) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplemented
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty.
403The caller authenticated, but not as the master operator. Master-only surfaces (exec, attach, API key management) refuse API keys; those 403 bodies carry code `unauthorized`. CIDR allowlist rejections also return 403 but with code `forbidden`.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted only by CIDR-allowlist rejections; master-only refusals (exec, attach, API key management) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplemented
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty.
404Resource does not exist. Note the distinct code `route_not_found`, returned when the URL matches no registered route at all (wrong host, port, or path prefix) rather than a known route with a missing resource.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted only by CIDR-allowlist rejections; master-only refusals (exec, attach, API key management) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplemented
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty.
409Conflicts with current state (e.g. duplicate task assignment).
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted only by CIDR-allowlist rejections; master-only refusals (exec, attach, API key management) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplemented
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty.
500The guest stream could not be opened, or the connection was not hijackable. Reported as HTTP because every error that can be expressed as HTTP is raised before the upgrade; once the socket is hijacked there is no ResponseWriter left to report through.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted only by CIDR-allowlist rejections; master-only refusals (exec, attach, API key management) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplemented
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty.
501The operation is not supported by the backing provider (e.g. an exec or attach against an environment with no real guest).
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted only by CIDR-allowlist rejections; master-only refusals (exec, attach, API key management) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplemented
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty.
Request
curl -X GET "http://localhost:8080/v1/environments/string/attach?tty=1" \
  -H "Authorization: Bearer YOUR_TOKEN"
Response
Upgrade accepted. The connection leaves HTTP and carries fuse-attach/1 frames in both directions until either end closes it.