Go REST API that manages multi-cloud sandbox provisioning for the Red Hat Demo Platform (RHDP). It allocates cloud resources (AWS accounts, OpenShift namespaces, DNS zones, IBM resource groups) to requesters grouped into placements.
This repository contains:
-
sandbox-api: The backend API for all things sandbox
-
sandbox-cli: CLI client for admin and operator tasks (preferred over shell scripts)
-
sandbox-issue-jwt: CLI to generate a new signed JWT login token — DEPRECATED, use
sandbox-cli jwtinstead -
sandbox-list: Interact with the AWS Sandbox DB in a read-only way
-
sandbox-metrics: Prometheus endpoint exposing metrics
-
sandbox-rotate-vault: Program to re-encrypt IAM keys using a new secret key (ansible-vault, AES256)
You will need Go 1.22 or later.
make build # All binaries (sandbox-api, sandbox-cli, etc.) make sandbox-api # API server only make sandbox-cli # CLI client only
make run-local-pg # Start local PostgreSQL make migrate # Run DB migrations make tokens # Generate dev JWT tokens make run-api # Start API server
sandbox-cli is the preferred tool for admin and operator tasks. It replaces the legacy shell scripts in tools/ with a single binary that handles authentication, config persistence, and error formatting.
# First-time setup
sandbox-cli login --server https://sandbox-api.example.com --token <your-login-token>
# Verify connection (shows client/server version, environment, DB migration)
sandbox-cli status
# JWT token management
sandbox-cli jwt list
sandbox-cli jwt issue --name ops-user --role shared-cluster-manager
sandbox-cli jwt issue --name admin-user --role admin
sandbox-cli jwt activity <id>
sandbox-cli jwt invalidate <id>
# Cluster management
sandbox-cli cluster list
sandbox-cli cluster get <name>
sandbox-cli cluster create <name> < cluster-config.json
sandbox-cli cluster create <name> --force < cluster-config.json
sandbox-cli cluster onboard [name] [--purpose dev] [--annotations '{}'] [--config file.json]
sandbox-cli cluster enable <name>
sandbox-cli cluster disable <name>
sandbox-cli cluster health <name>
sandbox-cli cluster offboard <name> [--force]
sandbox-cli cluster offboard-status <name>
sandbox-cli cluster delete <name>
# Placement testing
sandbox-cli placement dry-run --selector purpose=dev
sandbox-cli placement dry-run --selector purpose=dev,cloud=aws-shared
sandbox-cli placement dry-run --selector purpose=dev --preference region=us-east-1
# Session management
sandbox-cli logout
# Version info
sandbox-cli version [--json]
Config is saved to ~/.local/sandbox-cli/config.json (permissions 0600). The CLI auto-refreshes access tokens when they expire.
Configuration priority: CLI flags > environment variables > saved config.
| Source | Server URL | Login Token |
|---|---|---|
Flag |
|
|
Env var |
|
|
Config |
saved from |
saved from |
sandbox-cli status detects the environment from the server URL:
=== Client === Version: 1.0.0 Commit: abc123de Built: 2026-03-10 14:30:00 UTC Config: /home/user/.local/sandbox-cli/config.json === Connection === Server: https://sandbox-api-dev.example.com Environment: development Auth: valid (expires 2026-03-10 15:30:00 UTC) === Server === Version: 1.0.0 Commit: abc123de Built: 2026-03-10 14:00:00 UTC DB Migration: 22
The tools/ directory contains some legacy shell scripts. Use sandbox-cli equivalents when available:
| Script | sandbox-cli equivalent |
|---|---|
|
|
|
|
|
|
|
|
|
|
| Role | Access |
|---|---|
|
Full access to all endpoints |
|
Placement and account operations (create, get, delete, lifecycle) |
|
Manage OCP clusters (own clusters with full details, others with shared view) |
All roles can access: GET /version, GET /health, POST /placements/dry-run, GET /requests/{id}/status.
The app role can additionally: create/get/delete placements, manage accounts, read reservations.
The shared-cluster-manager role can:
-
Create/update clusters via POST/PUT
-
List all clusters (full details for own clusters, shared view for others)
-
Get any cluster (full details for own, shared view for others)
-
Offboard clusters it created
The shared view includes: name, api_url, ingress_domain, annotations, valid, quotas, limits, max_placements, created_by, timestamps. It excludes: credentials (token, kubeconfig), additional_vars, deployer SA token config, internal data.
The shared-cluster-manager cannot access: placement CRUD, account operations, admin-only endpoints (enable/disable, health, delete, JWT management, DNS, IBM, reservations).
-
GET /api/v1/login— Exchange login token for access token -
POST /api/v1/admin/jwt— Issue login JWT (admin) -
GET /api/v1/admin/jwt— List tokens (admin) -
GET /api/v1/admin/jwt/{id}/activity— Token audit log (admin) -
PUT /api/v1/admin/jwt/{id}/invalidate— Revoke token (admin)
-
POST /api/v1/placements— Create placement (202 async) -
POST /api/v1/placements/dry-run— Check availability -
GET /api/v1/placements— List all (admin) -
GET /api/v1/placements/{uuid}— Get status + resources -
DELETE /api/v1/placements/{uuid}— Delete (cascading cleanup)
-
GET /api/v1/ocp-shared-cluster-configurations— List all (admin: full, manager: own full + others shared view) -
GET /api/v1/ocp-shared-cluster-configurations/{name}— Get config (admin: full, manager: own full + others shared view) -
POST /api/v1/ocp-shared-cluster-configurations— Create (admin, manager) -
PUT /api/v1/ocp-shared-cluster-configurations/{name}— Upsert (admin, manager) -
DELETE /api/v1/ocp-shared-cluster-configurations/{name}— Delete (admin) -
DELETE /api/v1/ocp-shared-cluster-configurations/{name}/offboard— Offboard (admin, manager for own) -
GET /api/v1/ocp-shared-cluster-configurations/{name}/offboard— Offboard status (admin, manager) -
PUT /api/v1/ocp-shared-cluster-configurations/{name}/enable— Enable (admin) -
PUT /api/v1/ocp-shared-cluster-configurations/{name}/disable— Disable (admin) -
GET /api/v1/ocp-shared-cluster-configurations/{name}/health— Health check (admin)
-
CRUD on
/api/v1/dns-account-configurations -
CRUD on
/api/v1/ibm-resource-group-configurations
cmd/ sandbox-api/ HTTP server (handlers, middleware, GraphQL) sandbox-cli/ CLI client (Cobra-based) sandbox-issue-jwt/ JWT token generator sandbox-list/ Read-only account listing sandbox-metrics/ Prometheus exporter sandbox-replicate/ DynamoDB -> PostgreSQL replication (Lambda) sandbox-rotate-vault/ Credential re-encryption internal/ api/v1/ Request/response types cli/ sandbox-cli implementation models/ Domain models, DB queries, cloud integrations config/ App config dynamodb/ AWS DynamoDB provider log/ Structured logging (slog) db/migrations/ SQL migrations (golang-migrate) tests/ Hurl integration tests + Python functional tests tools/ Legacy shell scripts (prefer sandbox-cli) docs/api-reference/ OpenAPI 3.0 spec (swagger.yaml) deploy/ Helm charts
| Variable | Description |
|---|---|
|
PostgreSQL connection string |
|
HMAC-SHA256 signing key |
|
Credential encryption key |
|
IAM user for STS AssumeRole |
|
IAM secret |
|
DynamoDB access |
|
HTTP listen port (default: 8080) |
|
Async job workers (default: 5) |
|
Audit log retention period (default: |
|
Enable debug logging |
Create a secret file and run helm.
sandbox_api_secrets:
database:
url: postgres://...
dynamodb_aws_access_key_id: ...
dynamodb_aws_secret_access_key: ...
# Secret to generate and validate JWT tokens
auth:
jwt_auth_secret: ...
# ansible-vault (AES256) key to encrypt the secret in the DB
vault:
vault_secret: ...
# AWS user that can assume role into the accounts
aws:
assume_aws_access_key_id: ...
assume_aws_secret_access_key: ...
helm install -f secrets.yaml sandbox-api deploy/helm-api/
helm upgrade -f secrets.yaml sandbox-api deploy/helm-api/
To initialize or update the PostgreSQL schema:
oc run admin-$$ \ --image=quay.io/rhpds/sandbox-admin:latest -i -t \ --restart=Never --rm -- /bin/bash (1) DATABASE_URL=postgres://postgres:PASSWORD@RDS_ADDRESS.us-west-2.rds.amazonaws.com:5432/sandbox_api_dev?sslmode=require migrate \ -source github://rhpds/sandbox/db/migrations#main \ -database $DATABASE_URL up
-
Use the rhpds/sandbox-admin image which contains all the necessary binaries and tools.
oc run admin-$$ --image=quay.io/rhpds/sandbox-admin:latest -i -t --restart=Never --rm -- /bin/bash
export DATABASE_URL=postgres://postgres:PASSWORD@RDS_ADDRESS.us-west-2.rds.amazonaws.com:5432/sandbox_api_dev?sslmode=require
./sandbox-issue-jwt
JWT Auth secret: Enter Claims in the JSON format:
for example: {"kind": "login", "name": "gucore", "role": "admin"}
{"kind": "login", "name": "gucore", "role": "admin"}
token:
[TOKEN HERE]
logintoken=[TOKEN]
curl -H "Authorization: Bearer ${logintoken}" sandbox-api:8080/api/v1/login
token=[ACCESS TOKEN]
# check access
curl -H "Authorization: Bearer ${token}" sandbox-api:8080/api/v1/health
All files used for local development are prefixed by .dev and are ignored by Git, see .gitignore.
make run-local-pg # run postgresql locally in a Container
make migrate # Run the DB migrations to setup the db schema
# Set the following secrets, notice the heading space ' ' to avoid shell history
# IAM secrets to access AWS sandboxes
export ASSUMEROLE_AWS_SECRET_ACCESS_KEY=...
export ASSUMEROLE_AWS_ACCESS_KEY_ID=...
# IAM secrets to access dynamodb table that contains info of the AWS sandboxes
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
# AES key to encrypt sensible data in the different databases
# If you're using the dynamoDB dev database for AWS sandboxes (which you probably are)
# Then this needs to match the one in use on the DEV environment
export VAULT_SECRET=...
make tokens # issue some JWT token for access
make run-api # (1)
air # (2)-
When iterating, you will be stopping and relaunching this step
-
You can use cosmtrek/air instead. That will watch local files and rebuild + launch the API automatically if any changes are made.
Use sandbox-cli for cluster onboarding and offboarding. See sandbox-cli documentation for full details.
# Onboard: connects to the target OCP cluster, creates SA + token, registers with API
sandbox-cli cluster onboard [name] [--purpose dev] [--annotations '{}']
# Offboard: disables cluster, cleans up placements, removes from API
sandbox-cli cluster offboard <name> [--force]
The role of the lambda function is to replicate any changes made to the DynamoDB table into a PostgreSQL database.
-
Clone this repository
git clone --depth 1 https://github.com/rhpds/sandbox sandbox
-
If it doesn’t exist yet, create an IAM user in AWS with read-only access to DynamoDB
-
Create the secret file containing the key for the IAM user
aws_sandbox_readonly.yamlaws_sandbox_metrics_secrets: readonly: aws_access_key_id: ... aws_secret_access_key: ...
-
Install the helm chart
helm install sandbox-metrics sandbox/deploy/helm-metrics/ -f aws_sandbox_readonly.yaml
Use ansible playbooks.
See conan.