Concepts
This page explains the ideas the CLI commands act on: how licensing and composition work, which control plane endpoints the CLI calls, and how DNS and storefront routing fit together.
Licensing & entitlements
A box is authorized by a license key. When you run
box auth, the CLI exchanges the key with the control
plane for:
- a tier (the entitlement level),
- a customer ID (
customerId), - a JWT token,
- the entitled packages (
@withpotter/*), - a registry URL to install those packages from.
The non-secret parts of this response are recorded in the license block of
box.json; the secrets (license key and npm
token) are stored in .box/credentials.json
with mode 0600.
Composition
After activation, box auth composes the box from the entitled packages:
- The entitled engine packages are composed into the engine module
(
engine.module.ts). - The console’s page groups are projected from the entitled
@withpotter/pages-*packages, and the resolved capability set is written into the console environment.
This is what makes two boxes on different tiers expose different features from the same CLI and template.
Control plane endpoints
The CLI talks to the control plane over a small set of HTTP endpoints.
POST /license/activate
Called by box auth. CSRF-exempt.
POST /license/activate
Content-Type: application/json
{ "key": "<license-key>", "org": "<org-name>" }Response (ActivationResult):
| Field | Type | Description |
|---|---|---|
tier | string | The entitlement tier. |
customerId | string | The box’s customer ID. |
token | string | A JWT for the box. |
packages | string[] | The entitled package names. |
registryUrl | string | The package registry URL. |
expiresIn | number | Token lifetime. |
POST /boxes/deploy
Called by box deploy. Authenticated with
Authorization: Bearer <licenseKey>.
POST /boxes/deploy
Authorization: Bearer <licenseKey>
Content-Type: application/json
{
"name": "acme-bank",
"backendUrl": "https://api.acme-bank.example",
"frontendUrl": "https://shop.acme-bank.example",
"dashboardUrl": "https://admin.acme-bank.example",
"domains": [
{ "host": "acme-bank.example", "primary": true },
{ "host": "*.acme-bank.example", "wildcard": true }
]
}Response (DeployResult): registered?, nameservers?, and dnsInstructions?
describing the DNS records to create. Related record shapes include
DnsInstructionRecord { type, name, value, ttl? },
DomainDnsInstructions { host, configuredBy?, verified?, misconfigured?, records?, nameservers? },
and NameserverRecord { host, type?, value?, ttl? }.
GET /boxes/deploy/status
Called by box deploy --verify. Authenticated with the
bearer license key.
GET /boxes/deploy/status?name=acme-bank
Authorization: Bearer <licenseKey>Response (VerifyResult): domains?: DomainVerification[], where each entry is
{ host, linked, detail? }.
DELETE /boxes/deploy
Called by box deploy --remove. Authenticated with the
bearer license key.
DELETE /boxes/deploy?name=acme-bank
Authorization: Bearer <licenseKey>Detaches each registered host from the platform provider and clears its resolver
mapping, so the hosts stop routing to this box. Response (RemoveResult):
removed?: string[] — the hosts that were deregistered.
The storefront render uses a separate resolver endpoint,
GET /boxes/resolve?host=<host>, to map an incoming storefront host to the
owning bank’s backend. That endpoint is part of the render/control-plane flow
rather than the CLI; it is documented in the (upcoming) control plane section.
DNS & storefront routing
When you run box deploy, the control plane records
which backend owns each public host. The shared storefront render then resolves
incoming hosts to the right bank backend:
box deploy prints the DNS records you must create and writes them to
dns.txt. After your DNS propagates,
run box deploy --verify to confirm each domain is linked.