Box CLIConcepts

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):

FieldTypeDescription
tierstringThe entitlement tier.
customerIdstringThe box’s customer ID.
tokenstringA JWT for the box.
packagesstring[]The entitled package names.
registryUrlstringThe package registry URL.
expiresInnumberToken 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.

See also