-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Summary
Add a cwsandbox join-token command that creates a Tower join token via the ATC API. This replaces the manual curl workflow documented in the Sandbox Tower Helm chart installation guide, giving operators a single command to generate tokens for new Tower deployments.
Context: Join tokens are currently created by manually calling the ATC REST API (POST /v1beta1/towers/tokens). Brandon identified the need for a CLI helper in [Slack discussion] after manually distributing tokens to staging towers. This is a natural fit for the cwsandbox CLI.
Blocked by: #10 / PR #60 (base CLI with ls and exec commands)
Motivation
When deploying the Sandbox Tower Helm chart to a new cluster, operators need a join token. Today this requires:
- Constructing a
curlPOST request with the correct endpoint, headers, and JSON body - Extracting the token from the JSON response
- Figuring out the right
kubectlandhelmcommands to apply it
A CLI command makes this workflow self-documenting and reduces mistakes during Tower onboarding.
Proposed UX
cwsandbox join-token \
--tower-id my-cluster-east \
--tower-group-id production \
--ttl 3600 \
--description "Join token for my-cluster-east"Default output prints the token value and the follow-up commands needed to use it:
Join token created successfully.
Token: <token-value>
Token ID: a1b2c3d4-...
Tower: my-cluster-east
Group: production
Expires: 2026-02-28T15:02:21Z
To use this token, run:
kubectl create namespace sandbox-system
kubectl create secret generic sandbox-tower-join-token \
--namespace sandbox-system \
--from-literal=token='<token-value>'
Then install the Helm chart:
helm install sandbox-tower coreweave/sandbox-tower \
--namespace sandbox-system \
-f sandbox-tower-values.yaml
With --json flag, output the raw API response for scripting:
{
"token": "...",
"tokenId": "a1b2c3d4-...",
"towerId": "my-cluster-east",
"towerGroupId": "production",
"expiresAt": "2026-02-28T15:02:21Z",
"organizationId": "org-..."
}Flags
| Flag | Required | Default | Description |
|---|---|---|---|
--tower-id |
Yes | - | Unique identifier for the Tower (e.g. cluster name). Must be unique per org. |
--tower-group-id |
No | "default" |
Group identifier for organizing related Towers |
--ttl |
No | 3600 (1h) |
Token TTL in seconds |
--description |
No | Auto-generated from tower-id | Human-readable description |
--label |
No | - | Key=value label, repeatable (e.g. --label env=prod --label team=infra) |
--json |
No | false |
Output raw JSON response |
API Reference
Source: aviato/proto/coreweave/aviato/v1beta1/tower_join.proto
POST /v1beta1/towers/tokens - Create
Request:
tower_id string REQUIRED, must be unique per org
tower_group_id string optional, defaults to "default"
ttl_seconds int32 optional, defaults to 3600 (1 hour)
description string optional
labels map<string, string> optional
Response:
token string the join token (single-use, treat as secret)
token_id string UUID for tracking
tower_id string
tower_group_id string
expires_at Timestamp
organization_id string
Behavior:
- One active token per tower per org. Creating a new token auto-revokes the previous active token for that tower.
- Token is single-use: marked "used" after the Tower exchanges it for mTLS certs via
POST /v1beta1/towers/join.
Token lifecycle
States: active -> used | revoked | expired
active: newly created, not yet exchangedused: Tower exchanged the token for mTLS certsrevoked: explicitly revoked by userexpired: TTL elapsed (evaluated lazily at query time)
Implementation Notes
- This is an HTTP REST call, not gRPC. The existing SDK uses gRPC for sandbox operations, but the join token endpoint is REST. Use
httpx(already a dependency) orrequests. - Authentication should reuse the existing
CWSANDBOX_API_KEYpath from_auth.py. W&B auth is not relevant for this admin operation. - The command is an admin/operator action, distinct from the sandbox lifecycle commands (
ls,exec). Barecwsandbox join-tokenis sufficient for now; if list/revoke are added later, make bare invocation an alias forjoin-token createso existing usage doesn't break. - The token value is a secret: consider warning if outputting to a terminal.
Future Work
cwsandbox join-token list- list tokens with status filtering (GET /v1beta1/towers/tokens)cwsandbox join-token revoke <token-id>- revoke active tokens (DELETE /v1beta1/towers/tokens/{token_id})
The API already supports both operations.