Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

112 changes: 112 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,115 @@
# 2.4.0 (2026-05-06)

### ⚠️ BREAKING CHANGE — License activation is now required

Starting with v2.4.0, every Evolution API instance must be activated against the
Evolution Foundation licensing server before serving API traffic. Until activation,
all business endpoints return:

```
HTTP 503 Service Unavailable
{
"error": "service not activated",
"code": "LICENSE_REQUIRED",
"register_url": "https://<your-host>/manager/login",
"instance_id": "<uuid>",
"docs_url": "https://docs.evolutionfoundation.com.br/licensing",
"message": "..."
}
```

The following routes always remain public so the operator can recover:
`/license/status`, `/license/register`, `/license/activate`, `/manager/**`,
`/health`, `/server/ok`, `/ws`, static assets.

### Migration guide

1. Pull the new version and install dependencies:
```bash
git pull
npm install
```

2. Apply the new migration (creates the `RuntimeConfig` table). Required:
```bash
npm run db:deploy
```
If you skip this step, the server now fails fast with a clear error
asking you to run `db:deploy`.

3. Start the service. There are three activation paths:

- **Already have a valid licensing key?** Set it as `AUTHENTICATION_API_KEY`
in your `.env` and restart. The bootstrap step will validate the key with
the licensing server, persist it, and activate the instance automatically.

- **First-time activation via the manager UI?** Open
`https://<your-host>/manager/login`. The manager detects that the
instance is unlicensed and redirects you to the registration page on
the licensing server. After you complete the form, you are sent back
to `/manager/license/callback?code=...`, the manager exchanges the code,
and the dashboard becomes accessible.

- **Calling the API from code (n8n, Make, custom scripts) without a valid
license?** Every request will receive `503 LICENSE_REQUIRED` with the
`register_url` field pointing to the manager. Open it in a browser to
activate.

### Added

- **Licensing module** under `src/licensing/` — RuntimeContext, gate middleware,
signed/unsigned HTTP transport, hardware-based instance ID, fire-and-forget
heartbeat (every 30 min), graceful shutdown deactivation.
- **Public license endpoints**:
- `GET /license/status` — current activation state and (masked) api_key
- `GET /license/register?redirect_uri=` — initiates registration on the
licensing server, returns `register_url`
- `GET /license/activate?code=` — exchanges the authorization code received
on the callback for a real api_key, persists it, marks the runtime active.
- **New Prisma model** `RuntimeConfig` (key/value rows in `RuntimeConfig` table)
for both PostgreSQL and MySQL schemas.
- **Auto-detect missing migration**: if the `RuntimeConfig` table is absent,
the server prints a clear banner explaining `npm run db:deploy` and exits 1
instead of throwing a stack trace from the Prisma client.
- **Manager v2** ships with the new license-aware login flow that recognises
HTTP 503 / `LICENSE_REQUIRED`, calls `/license/register`, and redirects to
the registration server. After the callback, it lands on
`/manager/license/callback?code=...` and finalises activation. The new
manager bundle is included under `manager/dist/`.

### Notes

- `AUTHENTICATION_API_KEY` keeps its original meaning (global API key for
business endpoints) **and** gains a second use as the bootstrap license
key. If the value you have is a real licensing key, activation is silent.
If it is not, the service starts unlicensed and waits for activation via
the manager.
- Activation is a one-time operation. The `api_key` is stored in the database
and reused across restarts. The licensing server is only consulted again
for periodic heartbeats (telemetry — non-blocking) and on graceful shutdown
(`/v1/deactivate`).
- If the licensing server is unreachable but the instance has been activated
before, the service continues to serve traffic normally — local DB is the
source of truth for activation state after the first successful call.
- Release builds bake the licensing endpoint into the bundle as an XOR-encoded
string via tsup `define`, so the URL never appears as a plain literal in
`dist/main.js`. Generate the pair with `node tools/encode-url.js <url>` and
pass `LICENSE_ENDPOINT_ENCODED` / `LICENSE_ENDPOINT_XOR_KEY` as Docker
build-args (NOT runtime env vars). Local dev builds use a parts-array
fallback that still avoids a single string literal but is not obfuscated.

### Troubleshooting

- **`HTTP 503 LICENSE_REQUIRED`** — expected before activation. Follow the
migration guide.
- **`The table evolution_api.RuntimeConfig does not exist`** (legacy stack
trace if you somehow bypass the new auto-detect) — run `npm run db:deploy`.
- **`Global API key not accepted by licensing server: invalid signature`** —
your existing `AUTHENTICATION_API_KEY` is not a valid licensing key. Use
the manager UI flow to obtain a new one.

---

# 2.3.7 (2025-12-05)

### Features
Expand Down
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ RUN chmod +x ./Docker/scripts/* && dos2unix ./Docker/scripts/*

RUN ./Docker/scripts/generate_database.sh

# Licensing endpoint is XOR-encoded into the bundle by tsup `define`. Pass the
# pair via build-args (NEVER as runtime env vars) to keep the URL out of the
# compiled JavaScript as a plain literal. Generate them with
# `node tools/encode-url.js <url>`. Leaving them empty is OK for non-release
# builds — the dev fallback in src/licensing/endpoint.ts kicks in.
ARG LICENSE_ENDPOINT_ENCODED
ARG LICENSE_ENDPOINT_XOR_KEY
ENV LICENSE_ENDPOINT_ENCODED=${LICENSE_ENDPOINT_ENCODED}
ENV LICENSE_ENDPOINT_XOR_KEY=${LICENSE_ENDPOINT_XOR_KEY}

RUN NODE_OPTIONS="--max-old-space-size=2048" npm run build

FROM node:24-alpine AS final
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,33 @@ docker run -p 8080:8080 --env-file .env evoapicloud/evolution-api:latest

---

## License Activation

Starting from **v2.4.0**, every Evolution API instance must be activated
against the Evolution Foundation licensing server before serving API
traffic. While unactivated, business endpoints return
`HTTP 503 LICENSE_REQUIRED` with a `register_url` pointing to the manager
UI.

There are two ways to activate:

1. **Set a known licensing key** as `AUTHENTICATION_API_KEY` in your
`.env`. The server validates it on boot, persists it locally and
activates the instance silently.
2. **Activate via the manager UI** at `https://<your-host>/manager/login`.
On first login the manager detects an unlicensed backend and redirects
you to the registration server; once you complete the form you are
redirected back and the dashboard becomes accessible.

Activation is a one-time operation — the key is persisted in the
`RuntimeConfig` table and reused across restarts. The licensing server is
only consulted again for fire-and-forget heartbeats (telemetry) and a
best-effort `/v1/deactivate` notice on graceful shutdown.

Full activation guide: <https://docs.evolutionfoundation.com.br/licensing>

---

## Architecture

Evolution API is built with a multi-provider, event-driven architecture:
Expand Down
1 change: 0 additions & 1 deletion evolution-manager-v2
Submodule evolution-manager-v2 deleted from 3137df
1 change: 1 addition & 0 deletions manager/dist/assets/index-C-JyjMiq.css

Large diffs are not rendered by default.

485 changes: 0 additions & 485 deletions manager/dist/assets/index-CO3NSIFj.js

This file was deleted.

1 change: 0 additions & 1 deletion manager/dist/assets/index-DsIrum0U.css

This file was deleted.

584 changes: 584 additions & 0 deletions manager/dist/assets/index-pLdnG_0T.js

Large diffs are not rendered by default.

Loading
Loading