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.
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 propertiesHide 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`.
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 propertiesHide 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`.
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 propertiesHide 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`.
Optional stable, non-sensitive metadata (e.g. ids,
counts). Omitted when empty.
409Conflicts with current state (e.g. duplicate task assignment).
errorobjectrequired
Show propertiesHide 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`.
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 propertiesHide 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`.
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 propertiesHide 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`.