# Licensing

Beacon is a paid self-hosted product. Each install validates a signed **`beacon.license`** file against an embedded ECDSA P-256 public key. Validation runs offline — Beacon does not phone home.

This page covers buying, installing, rotating, and the update window for `0.3.3-beta`. It does not cover the cryptographic internals.

## Buying a license

Licenses for Beacon are sold at **[apexcode.dev/beacon](https://apexcode.dev/beacon)**. Checkout runs through Polar. On completion you get an email with a link to your license portal, where you download the signed `beacon.license` file plus Docker and Composer install commands.

Tier 1 self-hosted: $149 one-time ($99 during beta).

The license is a one-time purchase and does not expire. The version you buy keeps running whether or not you upgrade.

## Updates

Every license includes **12 months of updates from the purchase date**. Updates land through your license portal — new tags on `ghcr.io/apxcde/beacon` and new packages on `https://packages.apexcode.dev`.

After the year, keep the install you have. New releases require a renewal. Your portal shows the date updates run through.

## Installing your license

Drop the file at:

```
storage/app/beacon.license
```

The path is fixed (`config/licensing.php` → `license_path`). On Docker installs, `storage/` is a named volume, so the file persists across container restarts:

```bash
docker compose cp ./beacon.license app:/var/www/html/storage/app/beacon.license
```

On a Composer install from `packages.apexcode.dev`, copy the file into the project before first boot:

```bash
cp ./beacon.license storage/app/beacon.license
```

Restart the app container to force an immediate re-validation, or wait up to one validation cycle (default: daily).

## Test mode

You do not need a license file to develop against a running Beacon with test keys.

Append `_test` to any API key. While the request carries a `_test` key, Beacon bypasses license validation entirely (`ValidateLicenseOrTestMode`) and simulates Twilio without making real calls.

This is the right mode for:

- local development
- CI and integration tests
- evaluating Beacon before you send live SMS

It is **not** the right mode for production. A `_test` key is a permission boundary — anyone holding one can drive the system without a license.

Pulling `ghcr.io/apxcde/beacon` or installing from `packages.apexcode.dev` still uses the credentials from your license portal.

## Validation behavior

| Setting | Default | Effect |
| --- | --- | --- |
| `LICENSE_ENABLED` | `true` | Master switch. Set to `false` only in development. |
| `LICENSE_VALIDATION_FREQUENCY` | `daily` | How often the cached validation result is refreshed. `daily` / `hourly` / `always`. |
| `LICENSE_GRACE_PERIOD_DAYS` | `14` | After a license expires, Beacon keeps serving requests for this many days while logging a warning. |
| `LICENSE_HARDWARE_FINGERPRINTING` | `false` | When `true`, binds the license to one machine. Off by default for self-hosted users. |
| `LICENSE_AUDIT_LOGGING` | `true` | Logs every validation event. |
| `LICENSE_VERSION_CHECK` | `false` | Refuses to run a major version above what the license was purchased for. Used for one-time licenses. |
| `PRODUCT_VERSION` | unset | Override for the version-check comparison. |

`LICENSE_PUBLIC_KEY` — also configurable, but **do not change it** unless you are running a forked distribution. The default value is the public key used to sign every license sold at apexcode.dev/beacon.

## What happens when validation fails

| Situation | Behavior |
| --- | --- |
| No license file present, `_test` key | Allowed. |
| No license file present, live key | `402 Payment Required` from license middleware. |
| License signature invalid | `402 Payment Required`. |
| License expired, within grace period | Allowed, with a warning logged. |
| License expired, past grace period | `402 Payment Required`. |
| License version below installed Beacon version (with `LICENSE_VERSION_CHECK=true`) | `402 Payment Required`. |

The error response carries a JSON body explaining which check failed, so it is safe to surface the message in your tooling.

## Rotating a license

To replace an existing license:

1. Drop the new `beacon.license` file at `storage/app/beacon.license`, overwriting the old one — or run `php artisan license:install <file>`, which also keeps a timestamped backup of the previous license next to it (`beacon.license.backup.<timestamp>`).
2. Restart the app container, or wait for the next validation cycle.

## Renewals

Renewal extends the update window so the portal will issue newer image tags and Composer packages. The license file you already have keeps the purchased version running after the window closes.

## Disabling licensing entirely

For development only:

```env
LICENSE_ENABLED=false
```

This disables the middleware so any key (test or live) works. **Do not ship this setting to production.**

## Buying source access

If you need to modify Beacon, white-label it, or have compliance requirements that prevent signed-binary models, contact ApexCode for a source-access license. That is a separate agreement and is not covered by the standard `beacon.license` file.
