BYOC compute applies to the hosted service (Hosted by us). It lets an organisation run the provisioner and sandbox workers that build workspaces and run sandboxes in its own cloud account and region, while the control plane stays in the hosted cell. The workers are instances in your network; your cloud provider bills you for them directly.
A self-hosted installation already runs its workers on your own infrastructure and does not use BYOC connections. On a cell where BYOC is not configured, its routes answer 503 byoc_not_configured, and GET /api/v1/byoc/status answers configured: false.
| Cloud | How Govern acts in your account |
|---|---|
| AWS | EC2 instances, through a cross-account role it assumes with an external ID it generated |
| Microsoft Azure | Azure VMs, through workload identity federation with a per-connection OpenID Connect issuer |
What holds in every cloud
- No long-lived customer secret. In AWS, Govern assumes your role from the cell's own identity and holds only 15-minute sessions, in memory. In Azure, it signs a five-minute token of the connection's own issuer and exchanges it for a token of your managed identity. You never hand over a key, password or client secret.
- Least privilege, proven. The setup template grants only what the reconciler does, scoped to the connection, and verification makes a call that must be refused and requires that it is refused.
- mTLS to the cell. Each worker gets a short-lived client certificate (24 hours by default) from the cell's BYOC CA and reaches the cell only over TLS 1.3 with it.
- Your sandboxes stay on your workers. An organisation with BYOC sandbox workers is placed only on them, even while they are unhealthy, and a BYOC worker never receives another organisation's sandbox.
- Egress controls and audit are unchanged. Sandboxes on a BYOC worker run under the same network policy as on a shared worker; every connection, worker and certificate change is in your audit trail.
- Data stays in the cell's jurisdiction. A connection may use only regions inside it.
Set up an AWS connection
- Create a connection (
POST /api/v1/byoc/connections,byoc.connections:manage): a name, the AWS account id and region, optionally the instance type (defaultm7i.xlarge) and worker counts (default one provisioner and one sandbox worker). Govern generates the external ID. - Download and deploy the template (
GET /api/v1/byoc/connections/{id}/setup). The CloudFormation stack creates the cross-account role, a worker instance role with no permissions unless you passProvisionerPolicyArns, a security group with no inbound rules, and a launch template with IMDSv2 required, an encrypted gp3 root volume, no public IP and your subnet. Parameters:VpcId,SubnetId,WorkerImageId,WorkerInstanceType,WorkerVolumeSize,ProvisionerPolicyArns. The console shows theaws cloudformation deploy ... --capabilities CAPABILITY_IAMcommand. - Attach the stack's
RoleArnoutput (POST .../attachwithrole_arn). Govern runs its checks: the role is assumable, assuming it without the external ID is denied, the account matches, the launch template exists, and an out-of-scope call is denied. If one fails, the answer is422 verification_failedwith each check's detail, and the connection never becomes active. - Govern launches the workers. They enroll and appear under Workers.
Set up an Azure connection
Create a connection with the Microsoft Entra tenant id, subscription id, region, optionally the resource group (default
keystone-byoc-<name>), VM size (defaultStandard_D4s_v5) and worker counts.Deploy the subscription-scope template (needs Owner or User Access Administrator on the subscription):
az deployment sub create --subscription <subscription> --location <region> --name keystone-byoc-<name> \ --template-file keystone-byoc-<name>.json \ --parameters subnetId=<subnet-resource-id> workerImageId=<image-resource-id> --query properties.outputsIt creates the resource group, a managed identity with a federated credential that trusts exactly the connection's issuer, subject and audience, a worker identity with no permissions, a network security group that denies inbound traffic, and five custom roles, each at the narrowest scope that works. The identity cannot create a public IP or read secrets.
Attach the outputs (
POST .../attachwithclient_id,subnet_id,image_id). The worker image must be a Generation 2 Azure Compute Gallery image that declares Trusted Launch support.Govern launches the workers.
Running
- The reconciler runs every 30 seconds on one replica: it re-proves access, records what the cloud reports, stops instances above the desired counts, revoked or never enrolled within 20 minutes, and launches missing workers (at most 5 per pass).
- If access fails (the role cannot be assumed, the stack was deleted), the connection moves to
errorand nothing is launched or stopped until access returns. Your misconfiguration never becomes an operator incident. - Workers already running are never stopped for billing reasons; only you stop them, by scaling down or disconnecting.
- Disconnecting stops every instance, then revokes the workers and their certificates. If you deleted the stack first, Govern finishes the disconnect anyway; any instance still carrying the connection's tag is yours to terminate.
GET /api/v1/byoc/connections/{id}/health is computed on each read: overall (setup, healthy, degraded, failing, disconnected), setup progress, checks (workers connected, recent reconcile, certificates near expiry, CA expiry), worker counts and this month's meter. The console pages are under Compute, "Your cloud accounts".
Orchestration is metered as vCPU-hours of the worker instances, as the provider reports them (meter compute.byoc_vcpu).
Permissions and audit
| Permission | Granted to |
|---|---|
byoc.connections:read | Owner, Admin, Read-only |
byoc.connections:manage | Owner and Admin |
byoc.usage:read | Owner, Admin, Billing, Read-only |
Audit events: byoc.connection.created, .updated, .verified, .verification_failed, .disconnect_requested, .disconnected, .unreachable, .recovered; byoc.worker.launched, .terminated, .launch_refused, .enrolled, .enrollment_refused, .registered, .certificate_renewed, .revoked.
Cell configuration
These settings belong to the operator of the cell that offers BYOC; a self-hosted installation leaves them empty.
| Variable | Meaning |
|---|---|
KEYSTONE_BYOC_LISTEN_ADDR | The mTLS listener, for example :9443; unset, BYOC is off |
KEYSTONE_BYOC_ENDPOINT | The URL workers dial (https://host:port) |
KEYSTONE_BYOC_PRINCIPAL_ARN | The cell's IAM role that customer roles trust; unset, no AWS connections |
KEYSTONE_BYOC_OIDC_ORIGIN, KEYSTONE_BYOC_OIDC_KEY_FILE | Together: the public origin serving the per-connection issuers and their RSA key (at least 2048 bits); set, Azure connections are offered. One without the other stops the server |
KEYSTONE_BYOC_OIDC_PREVIOUS_KEY_FILE | The previous issuer key, still published while rotating |
KEYSTONE_BYOC_TLS_CERT_FILE, KEYSTONE_BYOC_TLS_KEY_FILE | The listener's certificate; reloaded on change, expiry watched by selfguard |
KEYSTONE_BYOC_CA_CERT_FILE, KEYSTONE_BYOC_CA_KEY_FILE | The BYOC CA (ECDSA P-256 or P-384, or Ed25519) |
KEYSTONE_BYOC_SERVER_CA_FILE | The bundle workers verify the listener with |
KEYSTONE_BYOC_CERT_TTL | Worker certificate life (default 24h, 1h to 168h) |
KEYSTONE_BYOC_INSTANCE_TYPE, KEYSTONE_BYOC_AZURE_VM_SIZE | Default worker sizes |
KEYSTONE_BYOC_STS_ENDPOINT, KEYSTONE_BYOC_EC2_ENDPOINT | VPC interface endpoints ({region} is replaced) |
KEYSTONE_BYOC_AZURE_LOGIN_ENDPOINT, KEYSTONE_BYOC_AZURE_ARM_ENDPOINT | Sovereign cloud endpoints |
KEYSTONE_BYOC_RESERVED_ACCOUNT_IDS, KEYSTONE_BYOC_RESERVED_AZURE_TENANT_IDS | Neural Inverse's own accounts and tenants, which no connection may name |
A partial configuration stops the server at start: it never runs half-configured.
Not available yet
- Google Cloud connections and self-hosted runners for other environments are described in the design but are not covered on this page.
- Rating the meter on invoices comes later.