bnna Platform API
REST API served by bnpd. All responses are JSON {"result":…,"timings":[…]} unless noted.
Errors return {"error":"…","timings":[…]}.
Base URL: https://op.bnna.net
Authentication
Three credential types are accepted:
csvauth Bearer token — platform operator token from users.tsv.
Authorization: Bearer <token>
PVE API token — Proxmox root@pam token, validated against known clusters.
Authorization: PVEAPIToken=root@pam!<name>=<secret>
bnna tenant token — scoped to one account, issued via SSO exchange or created directly.
Only valid on /api/accounts/{slug}/… routes where slug matches the token's account.
Authorization: Bearer bn_<token>
Health and Version
GET /health → "ok" (plain text, no auth)
GET /api/version → {"server":"bnna","version":"…"}
GET /api/platform/token/inspect → token metadata
Accounts
Tenant accounts map to Proxmox resource pools.
GET /api/platform/accounts list all accounts
POST /api/platform/accounts create account
GET /api/platform/accounts/{slug} get account
POST /api/platform/accounts/{slug}/adopt adopt existing Proxmox pool
POST /api/platform/accounts/{slug}/suspend suspend (blocks writes)
POST /api/platform/accounts/{slug}/enable re-enable
POST /api/platform/accounts/{slug}/disable disable (blocks all access)
DELETE /api/platform/accounts/{slug} GC account and resources
GET /api/platform/accounts/{slug}/health resource health check
GET /api/platform/accounts/{slug}/inventory full inventory
GET /api/platform/accounts/{slug}/gc dry-run GC report
GET /api/platform/accounts/{slug}/settings account settings
PUT /api/platform/accounts/{slug}/settings/{key} upsert setting
DELETE /api/platform/accounts/{slug}/settings/{key} delete setting
PUT /api/platform/accounts/{slug}/acls sync Proxmox ACLs
POST /api/platform/accounts/{slug}/repair repair Proxmox pool/ACLs
POST /api/platform/accounts/{slug}/impersonate get scoped token for account
Request body for create:
{
"slug": "acme",
"display_name": "Acme Corp",
"email": "ops@acme.com"
}
Networks
GET /api/networks list networks (operator view)
GET /api/networks/pending networks awaiting setup
GET /api/platform/accounts/{slug}/networks list account networks
POST /api/platform/accounts/{slug}/networks assign network to account
DELETE /api/platform/accounts/{slug}/networks/{network_id}
PUT /api/platform/accounts/{slug}/network update network assignment
POST /api/platform/networks/allocate allocate next free subnet
POST /api/platform/networks/prepare prepare network on cluster
POST /api/platform/networks/apply apply network config
POST /api/platform/networks/{id}/subnet add subnet
PUT /api/platform/networks/{id}/alias set alias
Instances (VMs and LXC)
GET /api/instances list all instances (across clusters)
GET /api/instances/{vmid}/domains list DNS domains for instance
POST /api/instances/{vmid}/domains add DNS domain
DELETE /api/instances/{vmid}/domains/{domain}
POST /api/instances/{vmid}/start
POST /api/instances/{vmid}/stop
POST /api/instances/{vmid}/shutdown
POST /api/instances/{vmid}/reboot
DELETE /api/instances/{vmid} destroy instance
POST /api/instances/{vmid}/adopt adopt unmanaged instance
GET /api/tasks poll Proxmox task status
POST /api/lxc create LXC container
POST /api/qemu create VM
Bunches (Resource Pools)
GET /api/bunches list bunches
GET /api/bunches/{slug}/quota quota usage for bunch
POST /api/platform/bunches create bunch
PUT /api/platform/bunches/{slug} update bunch
DELETE /api/platform/bunches/{slug} delete bunch
POST /api/platform/bunches/sync sync Proxmox pool membership
DNS
GET /api/dns/zones list DNS zones (operator)
POST /api/subdomains create CNAME record
DELETE /api/subdomains/{providerID}/{leaf} delete CNAME record
GET /api/accounts/{slug}/dns/providers list DNS providers for account
POST /api/accounts/{slug}/dns/providers register DNS provider
DELETE /api/accounts/{slug}/dns/providers/{id}
GET /api/accounts/{slug}/dns/zones list zones for account
POST /api/subdomains body:
{
"providerId": "<provider-id>",
"leaf": "myapp",
"target": "tls-10-11-0-1.a.bnna.net"
}
Or use "vmid": 12345 to derive the target automatically.
Databases — PostgreSQL
Account-scoped. All credentials are stored encrypted; the plaintext password is only returned at creation time.
GET /api/accounts/{slug}/pg-instances list PG instances
POST /api/accounts/{slug}/pg-instances link PG instance
DELETE /api/accounts/{slug}/pg-instances/{id}
GET /api/accounts/{slug}/pg-databases list databases
POST /api/accounts/{slug}/pg-databases create database
POST /api/accounts/{slug}/pg-databases/adopt adopt existing database
POST /api/accounts/{slug}/pg-databases/{name}/reset-password
DELETE /api/accounts/{slug}/pg-databases/{name}
GET /api/accounts/{slug}/pg-databases/{name}/usage usage history
GET /api/platform/pg-instances list all PG instances
POST /api/platform/pg-instances register PG instance
PATCH /api/platform/pg-instances/{id} update instance
DELETE /api/platform/pg-instances/{id}
Databases — MariaDB
GET /api/accounts/{slug}/maria-instances
POST /api/accounts/{slug}/maria-instances
DELETE /api/accounts/{slug}/maria-instances/{id}
GET /api/accounts/{slug}/maria-databases
POST /api/accounts/{slug}/maria-databases
POST /api/accounts/{slug}/maria-databases/adopt
POST /api/accounts/{slug}/maria-databases/{name}/reset-password
DELETE /api/accounts/{slug}/maria-databases/{name}
GET /api/accounts/{slug}/maria-databases/{name}/usage
GET /api/platform/maria-instances
POST /api/platform/maria-instances
PATCH /api/platform/maria-instances/{id}
DELETE /api/platform/maria-instances/{id}
Databases — SQL Server
GET /api/accounts/{slug}/mssql-databases
POST /api/accounts/{slug}/mssql-databases
POST /api/accounts/{slug}/mssql-databases/adopt
POST /api/accounts/{slug}/mssql-databases/{name}/reset-password
DELETE /api/accounts/{slug}/mssql-databases/{name}
GET /api/platform/mssql-instances
POST /api/platform/mssql-instances
PATCH /api/platform/mssql-instances/{id}
DELETE /api/platform/mssql-instances/{id}
Databases — Shared
GET /api/databases list all databases (operator, all engines)
POST /api/databases create database (auto-select instance)
POST /api/databases/{engine} create database for specific engine
GET /api/platform/databases list all (all engines)
POST /api/platform/databases/{name}/reassign move database to different instance
POST /api/platform/databases/{name}/reassign body:
{ "account_slug": "acme", "engine": "postgres" }
Engine values: postgres, mysql / mariadb, mssql / sqlserver.
Usage Metering
GET /api/accounts/{slug}/usage per-account usage summary
GET /api/platform/usage platform-wide overview
POST /api/platform/usage/collect trigger immediate collection
Storage
GET /api/templates list VM/LXC templates
GET /api/platform/storages list Proxmox storage
POST /api/platform/storages/fix repair storage config
GET /api/platform/storage-nodes list storage nodes
GET /api/platform/storage-nodes/{id}/test test storage node
DELETE /api/platform/storage-nodes/{id}
POST /api/platform/storage/{storage}/upload broadcast upload to all nodes
POST /api/platform/storage/{storage}/download-url broadcast download-URL
PBS (Proxmox Backup Server)
GET /api/platform/pbs-instances
POST /api/platform/pbs-instances
GET /api/platform/pbs-instances/{id}/test
DELETE /api/platform/pbs-instances/{id}
POST /api/platform/accounts/{slug}/assign-pbs assign PBS to account
Clusters
GET /api/platform/clusters list clusters
PUT /api/platform/clusters/{host} update cluster config
PUT /api/platform/clusters/{host}/operator-token rotate operator token
GET /api/nodes list Proxmox nodes
SSH Keys
GET /api/platform/ssh-keys list operator SSH public keys
Inventory
GET /api/platform/inventory full platform inventory
GET /api/platform/accounts/{slug}/inventory account inventory
Doctor
GET /api/platform/doctor run diagnostics (read-only)
POST /api/platform/doctor run diagnostics + auto-fix
Sheets (CSV Export)
Designed for Google Sheets IMPORTDATA. Returns TSV by default; append ?format=json or ?format=csv.
GET /api/sheets/nodes node resource usage
GET /api/sheets/instances instance list with metrics
GET /api/sheets/bunches bunch quota overview
Credentials
GET /api/platform/credentials/export export credentials bundle (dev only)
POST /api/platform/credentials/import import credentials bundle (dev only)
Requires --enable-dev-credentials-export flag. Never enable in production.
SSO — Trusted Issuers
Per-account list of OIDC issuer URLs that are allowed to exchange tokens for that account.
Platform-level issuers are configured via BNNA_SSO_ISSUERS env var and trust all accounts.
GET /api/platform/accounts/{slug}/trusted-issuers
POST /api/platform/accounts/{slug}/trusted-issuers
DELETE /api/platform/accounts/{slug}/trusted-issuers/{id}
POST body:
{ "issuer_url": "https://id.example.com" }
SSO — Roles
Named capability sets. Each role maps to a list of PVE and PBS ACL entries.
Exactly one role may be is_default: true per account; SSO users without an explicit role receive the default.
No default role = no SSO access.
GET /api/accounts/{slug}/roles
POST /api/accounts/{slug}/roles
DELETE /api/accounts/{slug}/roles/{id}
GET /api/accounts/{slug}/roles/{id}/pve-acls
POST /api/accounts/{slug}/roles/{id}/pve-acls
DELETE /api/accounts/{slug}/roles/{id}/pve-acls/{aclid}
GET /api/accounts/{slug}/roles/{id}/pbs-acls
POST /api/accounts/{slug}/roles/{id}/pbs-acls
DELETE /api/accounts/{slug}/roles/{id}/pbs-acls/{aclid}
POST /api/accounts/{slug}/roles body:
{ "name": "viewer", "is_default": true }
POST …/pve-acls body:
{ "path": "/", "pve_role": "PVEVMAdmin", "propagate": true }
POST …/pbs-acls body:
{
"pbs_instance_id": "<id>",
"datastore": "backups",
"namespace": "",
"pbs_role": "DatastoreReader",
"propagate": true
}
SSO — Token Exchange
Exchange an SSO identity JWT for a bnna bearer token scoped to an account and optional role. This endpoint is public — the SSO JWT is the credential.
POST /api/auth/sso/exchange
Request:
POST /api/auth/sso/exchange
Authorization: Bearer <sso-jwt-from-oidc-provider>
Content-Type: application/json
{
"account_slug": "acme",
"label": "my-session",
"role_id": "<optional-role-id>",
"expires_in": 86400
}
Response:
{
"result": {
"token": "bn_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"token_id": "<uuid>",
"account_slug": "acme",
"role_id": "",
"label": "my-session",
"expires_at": "2026-04-14T20:00:00Z",
"sub": "user@example.com"
}
}
The returned token value is the full bearer token — store it securely. It is not recoverable after this response.
SSO — Token Management
GET /api/accounts/{slug}/tokens list active bnna tokens
DELETE /api/accounts/{slug}/tokens/{id} revoke token
GET response does not include token values — only metadata (id, label, role_id, expires_at).
bnadm CLI Reference
Operator CLI for managing the bnna platform. Connects to bnpd using credentials from an .env file.
bnadm [--envfile <path>] <command> [args]
Global flags:
--envfile <path>— load environment from file (default:.envthen.env.secret)
Required environment variables:
BNNA_HOST=https://op.bnna.net
BNNA_TOKEN=<operator-bearer-token>
Accounts
Manage tenant accounts. Each account maps to a Proxmox resource pool.
bnadm accounts list
bnadm accounts adopt --slug <slug> [--display-name <name>] [--email <email>]
bnadm accounts health --slug <slug>
bnadm accounts repair --slug <slug>
bnadm accounts delete --slug <slug> [--confirm]
bnadm accounts assign-pbs --slug <slug> --pbs-id <id>
bnadm accounts trusted-issuers list --slug <slug>
bnadm accounts trusted-issuers add --slug <slug> --issuer-url <url>
bnadm accounts trusted-issuers delete --slug <slug> --id <id>
adopt wraps an existing Proxmox pool. repair re-creates missing Proxmox resources (group, user, pool, ACLs). assign-pbs links a PBS backup server to an account.
trusted-issuers controls which OIDC providers can exchange tokens for this account. See SSO — Trusted Issuers in the API docs.
Networks
Allocate and assign SDN networks (VLAN + subnet pairs).
bnadm networks list
bnadm networks allocate [--count <n>]
bnadm networks assign --network-id <id> --slug <slug>
allocate finds and reserves the next free VLAN/subnet. assign moves an unallocated network to an account.
Databases
Provision, adopt, and manage tenant databases (PostgreSQL and MariaDB).
bnadm databases list
bnadm databases daemons
bnadm databases create --slug <slug> --name <db> [--engine postgres|mysql]
bnadm databases copy --src <dsn> --dst <dsn>
bnadm databases empty --slug <slug> --name <db> [--engine postgres]
bnadm databases delete --slug <slug> --name <db> [--engine postgres] [--confirm]
bnadm databases adopt --slug <slug> --name <db> --dsn <dsn> [--engine postgres]
bnadm databases adopt-instance --vmid <id>
bnadm databases adopt-daemon --dsn <dsn> [--engine postgres|mysql]
bnadm databases reassign --name <db> --slug <slug> [--engine postgres]
empty drops all schema objects but keeps the database, user, and credentials. copy streams data between DSNs (useful for migrations). adopt-daemon registers an existing Postgres or MariaDB server.
Instances
Create and list LXC containers.
bnadm instances list
bnadm instances create --slug <slug> --template <name> [--cores <n>] [--mem <mb>] [--disk <gb>]
Inventory
Show a cluster-wide resource summary.
bnadm inventory
Doctor
Check cluster health: storage, nodes, databases, firewall.
bnadm doctor [--fix]
Without --fix, runs read-only diagnostics. With --fix, attempts automated repairs.
Pools
List Proxmox resource pools.
bnadm pools
Storages
Inspect storage backends, usage, and content.
bnadm storages
Templates
Manage VM and LXC templates.
bnadm templates
Impersonate
Generate a short-lived bnna bearer token scoped to a tenant account.
bnadm impersonate --slug <slug> [--ttl <duration>]
Returns a bn_ prefixed token. Useful for testing tenant-scoped operations.
Credentials
Backup and restore bnpd daemon credentials (encrypted bundles, dev only).
bnadm credentials export --out <file>
bnadm credentials import --in <file>
Requires bnpd to be running with --enable-dev-credentials-export. Never use in production.
Seed
Seed the database with cluster config and operator tokens.
bnadm seed --cluster <host> [--token <pve-token>]
Registers a Proxmox cluster and its operator API token. Run once during initial setup.
Proxmox Pass-Through
These commands proxy directly to the Proxmox API via bnpd:
bnadm users
bnadm groups
bnadm backups
bnadm replications
bnadm permissions
bnadm nodes
bnna Tenant CLI
bnna is the tenant-facing CLI, used by account holders (not platform operators).
bnna [--envfile <path>] <command>
Required environment variables:
BNNA_HOST=https://op.bnna.net
BNNA_ACCOUNT=<account-slug>
BNNA_TOKEN=bn_<token>
Tenant Commands
bnna doctor # check env, token, SSH config
bnna account # show account info
bnna inventory # list all account resources
bnna instances # list, create, manage instances
bnna databases # database operations (copy)
bnna domains # list, create, remove DNS domains
bnna roles # manage roles and ACLs
bnna tokens # manage bearer tokens
bnna roles
bnna roles list
bnna roles create --name <name> [--default]
bnna roles delete --id <id>
bnna roles pve-acls list --role-id <id>
bnna roles pve-acls add --role-id <id> --path <path> --pve-role <role> [--propagate]
bnna roles pve-acls delete --role-id <id> --acl-id <id>
bnna roles pbs-acls list --role-id <id>
bnna roles pbs-acls add --role-id <id> --pbs-id <id> --datastore <ds> --pbs-role <role> [--propagate]
bnna roles pbs-acls delete --role-id <id> --acl-id <id>
bnna tokens
bnna tokens list
bnna tokens delete --id <token-id>