# Licensing

> What the licence switches on, the three offline ways to load it, how to check it, and how renewal works without your installation contacting anyone.

Product: AI Cost Optimizer Gateway (ACOG). Web version: https://tessarac.com/docs/acog/self-hosted/licensing. Last updated: 2026-10-16.

A self-hosted installation always passes traffic. The licence decides whether the savings run:

| Licence | State | Requests |
|---|---|---|
| None, expired, not yet valid, or invalid | **Pass-through** | Forwarded unchanged to your provider and measured. No savings. |
| Loaded and within its dates | **Active** | Savings run |

The licence covers the whole installation, every organization in it.

Licensing is entirely offline. A licence is a signed file, checked against a key built into ACOG; nothing contacts Tessarac, and there is no licence server.

## What you receive

A licence file named `<licence-id>.acog-license`, and its password, sent to you separately. Your licence agreement names a renewal contact.

## Loading it

Each method checks the file immediately and stores it, replacing any previous licence. No restart is needed: the control plane applies it at once, and the gateway within 30 seconds.

1. **Console.** **Admin → Billing**, upload the file and enter the password.
2. **API.** `POST /v1/license/load` with `{"file_b64": "<file contents>", "password": "<password>"}` (requires `admin:org`).
3. **Declarative**, for air-gapped and GitOps installations. Create a Secret and reference it in your overlay; the control plane applies it at every start.

```bash
kubectl -n aco create secret generic aco-license \
  --from-file=license=./<licence-id>.acog-license \
  --from-file=password=./licence-password.txt
```

```yaml
license:
  enabled: true
  existingSecret: aco-license
```

Without Kubernetes, set `ACOG_LICENSE_FILE` and `ACOG_LICENSE_PASSWORD_FILE` on the control plane.

## Checking it

**Admin → Billing** shows the licence's state (Active, Expired, Not yet valid, Invalid or No license), who it is issued to, and when it was issued and expires. `GET /v1/license` (requires `read:billing`) returns the same:

```json
{
  "self_host": true,
  "state": "active",
  "effective_billing": "active",
  "license": {
    "license_id": "…",
    "licensee": "…",
    "issued_at": "…",
    "expires_at": "…",
    "days_remaining": 212
  }
}
```

The gateway status pill in the console turns red with **License expired** or **License invalid** when savings stop for that reason. A licence with no expiry date is perpetual.

### Check the usage export on day one

Renewal uses a signed usage report (below). Once, after loading a new licence, confirm it can produce one:

```bash
curl -s -o /dev/null -w '%{http_code}\n' https://acog.example.com/api/v1/license/usage-export \
  -H "Authorization: Bearer $ACOG_ADMIN_KEY"
```

| Status | Meaning |
|---|---|
| `200` | The licence can produce a renewal report |
| `409` | The licence cannot produce a report and must be re-issued. Contact your renewal contact. |
| `501` | The usage store is unreachable; this says nothing about the licence |

## Renewing

Renewal is a manual exchange. Nothing leaves your installation on its own.

1. **Admin → Billing → Download usage report** produces a signed JSON file of usage totals and a per-model breakdown. It contains no prompts and no responses.
2. Send it to the renewal contact named in your licence agreement.
3. Load the new licence file you receive. It takes effect at once.

With a declarative licence, replace the Secret and restart the control plane (the chart does not restart it when only a Secret changes).

## Removing a licence

`DELETE /v1/license` (requires `admin:org`) removes it; the installation returns to pass-through. The console has no control for this.
