Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.boxd.sh/llms.txt

Use this file to discover all available pages before exploring further.

The boxd CLI, SSH server, and SDKs are all clients of one thing: the public gRPC API at boxd.sh:9443. If your language doesnโ€™t have a boxd SDK yet, generate one from the proto file and talk to it directly.

Endpoint

boxd.sh:9443
  • Transport: HTTP/2 cleartext (h2c). No TLS on this port โ€” auth is short-lived JWTs.
  • Reflection: gRPC server reflection is enabled, so tools like grpcurl work without the proto file.
  • Service: boxd.api.v1.BoxdApi

The proto

Copy the full service definition into a local api.proto, then generate stubs with protoc, buf, grpc_tools, or @grpc/proto-loader. The proto is proto3 with no external imports โ€” generation is one command. You donโ€™t strictly need the file: since server reflection is on, grpcurl and buf curl work directly against the endpoint, and grpcurl -plaintext boxd.sh:9443 describe boxd.api.v1.BoxdApi dumps the schema on demand.

Authentication

Two-step: long-lived API key โ†’ short-lived JWT โ†’ bearer token on every gRPC call.

1. Create an API key

API keys are issued from the boxd console. Sign in at boxd.sh, open the API keys page, and create one. The raw key is shown once โ€” copy it immediately. Format: bxd_ followed by ~40 base62 characters. You can also create them via the CLI once youโ€™re logged in:
boxd login                              # GitHub OAuth
boxd api-key create --name=my-app

2. Exchange for a JWT

Send the API key to the exchange endpoint over HTTPS:
curl -X POST https://boxd.sh/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"api_key":"bxd_..."}'
{
  "token": "eyJhbGciOi...",
  "expires_at": 1735689600,
  "user_id": "gh-username"
}
The JWT is short-lived (default ~1 hour). Re-exchange before expiry. Rate-limited per source IP.

3. Send it on every gRPC call

authorization: Bearer eyJhbGciOi...
Standard gRPC metadata. Most generated clients expose this as a per-call interceptor or call option.

Hello world

Easiest way to verify auth and reachability โ€” no proto file needed (server reflection is on).
# Set your JWT
export BOXD_JWT="$(curl -s -X POST https://boxd.sh/api/v1/auth/token \
  -H 'Content-Type: application/json' \
  -d '{"api_key":"bxd_..."}' | jq -r .token)"

# Whoami
grpcurl -plaintext \
  -H "authorization: Bearer $BOXD_JWT" \
  boxd.sh:9443 boxd.api.v1.BoxdApi/Whoami

# Create a VM
grpcurl -plaintext \
  -H "authorization: Bearer $BOXD_JWT" \
  -d '{"name": "hello-grpc"}' \
  boxd.sh:9443 boxd.api.v1.BoxdApi/CreateVm

# List VMs
grpcurl -plaintext \
  -H "authorization: Bearer $BOXD_JWT" \
  boxd.sh:9443 boxd.api.v1.BoxdApi/ListVms
Install: brew install grpcurl or github.com/fullstorydev/grpcurl.

Errors

Standard gRPC status codes. The most common ones youโ€™ll hit:
CodeWhen
UNAUTHENTICATEDMissing/malformed/expired JWT, or authorization metadata not set
NOT_FOUNDVM, disk, template, or domain doesnโ€™t exist
RESOURCE_EXHAUSTEDPer-user VM quota reached
INVALID_ARGUMENTBad request shape (e.g. invalid VM name, conflicting fields)
INTERNALServer-side error
The error message in Status.message() is human-readable and safe to surface to users.

Streaming RPCs

Two RPCs use streams:
  • StreamLogs โ€” server-streaming, emits LogChunk messages as the VM produces output. Set follow=true to keep the stream open after current logs flush.
  • Exec โ€” bidirectional. First message must contain vm_id and command; subsequent client messages with stdin=true pipe stdin; server messages return stdout/stderr data. Final server message has exit_code set.

Whatโ€™s next

External CLI

Same API, no codegen โ€” useful for shell scripting and one-offs.

Primitives: Machines

Concepts behind CreateVm / ForkVm / SuspendVm.