License and entitlement

Signed lattice.entitlement.json, not a serial. Meter is leased + reserved + quarantined + assigned.

Lattice does not use a serial you type into a box like Windows Server. You download a signed file (lattice.entitlement.json) from the customer portal and put it on the cluster. Community (no file) is 1,000 managed IPs, one Ubuntu VM. Paid files raise the cap and allow three-node HA — they do not require it.

Same binaries for community and paid. Do not edit the JSON to raise the cap; that breaks the signature and you fall back to community.

Portal (latticeddi.com)

A paid license_id is one install. After you install Lattice (Community is fine until this step), copy the cluster License page activation_bundle (lattice-install-v1 plus install_id and public_key) into Activate in the portal. The portal issues a file with bound_install_id.

You wantDo this
Relocate the same clusterBackup/restore Postgres. Same install_id. Not a portal step.
Transfer the seat to a new installRun Transfer on the old cluster, paste the voucher + new install_id + new public key in the portal. Two transfers per rolling 365 days.
Dead cluster / lost voucherContact support. Do not invent a second activation.
Renew this seatLicense page in the portal. Same license_id. Unused term is kept.
Upgrade this seat (Starter → Professional → Team → Org → Scale)Same page. Same license_id and bind. Pay the new list. Unused days stay on this seat at the new tier. Upload the new file on that cluster.
Another clusterBilling. A second purchase is a second seat, not a silent renew.

Community is replaced when you buy the first paid seat. That is not an upgrade of the Community license_id.

Cluster License: Relocate this cluster, Transfer license, Upload / replace entitlement

Cluster License: Relocate this cluster, Transfer license, Upload / replace entitlement

Windows/SSH copy of the file: windows-admin §4 and install-prerequisites §6. Console upload: License.

File

Default: /etc/lattice/entitlement.json. Override with LATTICE_ENTITLEMENT_PATH. Download from the customer portal after signup (community) or a paid issue.

{
  "kid": "v1",
  "payload": {
    "spec": "lattice.entitlement/v1",
    "tier": "team",
    "max_managed_ips": 25000
  },
  "sig": "<base64 Ed25519>"
}

Signature is over canonical UTF-8 JSON of payload (object keys sorted). Pretty-printing the file does not break verify. The signing public key is in the Lattice binaries. LatticeDDI holds the private key; it is not in the entitlement file or this archive.

What is counted

Managed IPs = distinct addresses that are leased, DHCP-reserved, quarantined, or IPAM-assigned. Prefix size, free inventory, offered, and excluded are not the meter. A tracking-only server subnet still counts each assignment.

Evaluated every 5 minutes and on reservation create.

Enforcement

  1. Verify signature and time window (not_beforeexpires_at + grace_days, default 14).
  2. Missing or invalid file (including a tampered max_managed_ips): community, 1000 IPs. The unsigned payload cap is never trusted.
  3. Over max_managed_ips: do not crash DHCP. Renew existing leases. Refuse new grants (counted when a lease is committed, not when an Offer is sketched), new reservations, and new assignments. API returns 402 / license_limit.
  4. After expires_at + grace_days: same as over-limit (renew only).
  5. A reservation that already counts still gets DHCP when over the cap.
  6. No per-node license. Any number of dhcpd processes.
  7. No required phone-home.

GET /api/v1/license (readonly) and the Overview tile show status, used, and cap. The snapshot always includes install_id and install_public_key (base64), even on community, so the portal can bind. The install private key is never returned.

Identity, relocate, transfer license

Two paths only:

  1. Relocate (normal): backup → restore on new VMs → same Postgres install_id and install keypair → same entitlement. Shut down old nodes. The portal is not involved. New hardware, same database = backup and restore. Do not call this “move.”
  2. Transfer license (rare): clean install (new identity). This cluster is abandoning its seat. Data is not copied (that is restore). Self-serve 1–2 transfers per year; more only if support raises the cap on that license in the portal.

install_id is public (UI, tickets). It must not sign transfers. Transfer proof is an Ed25519 install keypair created at first boot. The private key never appears in the entitlement JSON (that file is copied).

Optional payload field bound_install_id: empty/omitted is an unbound bearer file (today’s files keep working). If set and it does not match local install_id, the cluster falls back to community (wrong_install). PUT /license refuses to store a mismatched bound file (400). Community (missing/invalid file, or authentic community tier) stays unlimited installs, 1000 IPs, no HA.

Copying lattice.entitlement.json onto a fresh database is stopped when the file has bound_install_id that does not match. After transfer, shut down the old cluster. Restoring a VM snapshot of the old cluster while the new one is live can dual-run until expires_at.

Transfer (License page, admin): type the cluster name. Atomically writes retire tombstones (Postgres or /var/lib/lattice/license-retire — if any marker is present, retired), drops to renew-only immediately, community after 7 days, burns the install private key, then shows a one-time transfer voucher signed by the old key (not_after ~72h). The API node writes the file immediately; every other node (api, dhcpd, worker) copies the Postgres tombstone onto its local license-retire file the next time it reloads identity. No local undo that leaves the voucher valid. Accidental transfer: support rebinds to the same install_id if the voucher is unused. A dead cluster cannot mint a voucher → portal rebind.

A valid signed entitlement does not override a retire tombstone. DHCP never crashes.

Fleet

Fleet is a secondary lock, not the seat counter. The agent reports license_id + install_id. One non-empty paid license_id per active Fleet cluster. Empty/community: many clusters OK (still gated by Fleet max_clusters). Relocate to the same Fleet row is allowed. Transfer: new Fleet registration; do not copy FLEET_AGENT_KEY. Deleting/disabling the old Fleet row frees the unique license. Optional admin steal_license on PATCH. No required LatticeDDI.com phone-home.

Replace in the console

Admins open License (or the Overview tile) and upload or paste lattice.entitlement.json. PUT /api/v1/license verifies the signature, then stores the file in Postgres so every api/dhcpd/worker process uses it. The on-disk path is updated when that directory is writable.

Operator and readonly can see status; they cannot replace the file. A hand-edited cap fails verify and is refused.

Tiers

tiermax_managed_ips
community1000
starter5000
professional10000
team25000
org50000
scale100000
custom (Enterprise)explicit

Community includes DHCPv4, IPAM, API, BIND TSIG, and AD GSS-TSIG. Cap is 1000 managed IPs. No HA (single node only).

Every paid tier includes HA ability; the installer asks whether this node should be single-node or three-node HA. Cluster join, bundled Patroni, Cluster UI, and DHCP-only scale-out require a paid file and --mode ha-*.

The cap is how many addresses you may catalog and lease, not a packets-per-second rating. Plan DHCP load from lease time (RFC T1, Lattice 1-day default): DHCP lease time. Tiers and list prices: pricing.

Install

Host sizing, Ubuntu, netplan, NTP, and how to place this file: install-prerequisites.

# /etc/lattice/lattice.env
LATTICE_ENTITLEMENT_PATH=/etc/lattice/entitlement.json

File mode 0640, owned by the lattice user. A valid file with max_managed_ips: 0 is a revoke (renew only). Deleting the file falls back to community. There is no phone-home revocation list.

Fleet (separate product)

Fleet manages many Lattice clusters from one pane of glass. It uses a different entitlement spec — not a feature on the cluster file:

  • Spec: lattice.fleet.entitlement/v1
  • Meter: max_clusters (registered clusters), not managed IPs
  • Face names match cluster Lattice (Starter / Team / Org / Scale). An unknown or missing file is Community (2 clusters).
FaceClusters
Community (missing file)2
Starter10
Team50
Org200
Scale500
Quoted (custom)unlimited, or the number on the file

Each cluster still needs its own lattice.entitlement.json for DHCP/IPAM caps. Fleet license gates the Fleet control plane only.

Release tarballs are GPG-signed (SHA256SUMS.asc). That authenticates the download. A missing or invalid entitlement is Community. Hand-editing the cap in the JSON fails verify.