Skip to content

Commit 78330e8

Browse files
committed
release: 2.4.0 - license activation required
Polishes the licensing rollout for public release: - Better error UX: HTTP 503 now carries instance_id, docs_url and an actionable message instructing the operator to open the manager UI or set AUTHENTICATION_API_KEY in .env. - Better boot banner: lists the activation paths (manager UI, env var) with the docs URL and the instance_id. - Auto-detect missing migration: if the RuntimeConfig table is absent, the server prints a clear banner asking the operator to run npm run db:deploy and exits 1, instead of throwing a Prisma stack trace from inside the bootstrap. - Version bump 2.3.7 -> 2.4.0. - CHANGELOG entry with BREAKING CHANGE notice and migration guide. - README section 'License Activation' linking to docs.evolutionfoundation.com.br/licensing.
1 parent efa3d62 commit 78330e8

4 files changed

Lines changed: 169 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,109 @@
1+
# 2.4.0 (2026-05-06)
2+
3+
### ⚠️ BREAKING CHANGE — License activation is now required
4+
5+
Starting with v2.4.0, every Evolution API instance must be activated against the
6+
Evolution Foundation licensing server before serving API traffic. Until activation,
7+
all business endpoints return:
8+
9+
```
10+
HTTP 503 Service Unavailable
11+
{
12+
"error": "service not activated",
13+
"code": "LICENSE_REQUIRED",
14+
"register_url": "https://<your-host>/manager/login",
15+
"instance_id": "<uuid>",
16+
"docs_url": "https://docs.evolutionfoundation.com.br/licensing",
17+
"message": "..."
18+
}
19+
```
20+
21+
The following routes always remain public so the operator can recover:
22+
`/license/status`, `/license/register`, `/license/activate`, `/manager/**`,
23+
`/health`, `/server/ok`, `/ws`, static assets.
24+
25+
### Migration guide
26+
27+
1. Pull the new version and install dependencies:
28+
```bash
29+
git pull
30+
npm install
31+
```
32+
33+
2. Apply the new migration (creates the `RuntimeConfig` table). Required:
34+
```bash
35+
npm run db:deploy
36+
```
37+
If you skip this step, the server now fails fast with a clear error
38+
asking you to run `db:deploy`.
39+
40+
3. Start the service. There are three activation paths:
41+
42+
- **Already have a valid licensing key?** Set it as `AUTHENTICATION_API_KEY`
43+
in your `.env` and restart. The bootstrap step will validate the key with
44+
the licensing server, persist it, and activate the instance automatically.
45+
46+
- **First-time activation via the manager UI?** Open
47+
`https://<your-host>/manager/login`. The manager detects that the
48+
instance is unlicensed and redirects you to the registration page on
49+
the licensing server. After you complete the form, you are sent back
50+
to `/manager/license/callback?code=...`, the manager exchanges the code,
51+
and the dashboard becomes accessible.
52+
53+
- **Calling the API from code (n8n, Make, custom scripts) without a valid
54+
license?** Every request will receive `503 LICENSE_REQUIRED` with the
55+
`register_url` field pointing to the manager. Open it in a browser to
56+
activate.
57+
58+
### Added
59+
60+
- **Licensing module** under `src/licensing/` — RuntimeContext, gate middleware,
61+
signed/unsigned HTTP transport, hardware-based instance ID, fire-and-forget
62+
heartbeat (every 30 min), graceful shutdown deactivation.
63+
- **Public license endpoints**:
64+
- `GET /license/status` — current activation state and (masked) api_key
65+
- `GET /license/register?redirect_uri=` — initiates registration on the
66+
licensing server, returns `register_url`
67+
- `GET /license/activate?code=` — exchanges the authorization code received
68+
on the callback for a real api_key, persists it, marks the runtime active.
69+
- **New Prisma model** `RuntimeConfig` (key/value rows in `RuntimeConfig` table)
70+
for both PostgreSQL and MySQL schemas.
71+
- **Auto-detect missing migration**: if the `RuntimeConfig` table is absent,
72+
the server prints a clear banner explaining `npm run db:deploy` and exits 1
73+
instead of throwing a stack trace from the Prisma client.
74+
- **Manager v2** ships with the new license-aware login flow that recognises
75+
HTTP 503 / `LICENSE_REQUIRED`, calls `/license/register`, and redirects to
76+
the registration server. After the callback, it lands on
77+
`/manager/license/callback?code=...` and finalises activation. The new
78+
manager bundle is included under `manager/dist/`.
79+
80+
### Notes
81+
82+
- `AUTHENTICATION_API_KEY` keeps its original meaning (global API key for
83+
business endpoints) **and** gains a second use as the bootstrap license
84+
key. If the value you have is a real licensing key, activation is silent.
85+
If it is not, the service starts unlicensed and waits for activation via
86+
the manager.
87+
- Activation is a one-time operation. The `api_key` is stored in the database
88+
and reused across restarts. The licensing server is only consulted again
89+
for periodic heartbeats (telemetry — non-blocking) and on graceful shutdown
90+
(`/v1/deactivate`).
91+
- If the licensing server is unreachable but the instance has been activated
92+
before, the service continues to serve traffic normally — local DB is the
93+
source of truth for activation state after the first successful call.
94+
95+
### Troubleshooting
96+
97+
- **`HTTP 503 LICENSE_REQUIRED`** — expected before activation. Follow the
98+
migration guide.
99+
- **`The table evolution_api.RuntimeConfig does not exist`** (legacy stack
100+
trace if you somehow bypass the new auto-detect) — run `npm run db:deploy`.
101+
- **`Global API key not accepted by licensing server: invalid signature`**
102+
your existing `AUTHENTICATION_API_KEY` is not a valid licensing key. Use
103+
the manager UI flow to obtain a new one.
104+
105+
---
106+
1107
# 2.3.7 (2025-12-05)
2108

3109
### Features

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,33 @@ docker run -p 8080:8080 --env-file .env evoapicloud/evolution-api:latest
124124

125125
---
126126

127+
## License Activation
128+
129+
Starting from **v2.4.0**, every Evolution API instance must be activated
130+
against the Evolution Foundation licensing server before serving API
131+
traffic. While unactivated, business endpoints return
132+
`HTTP 503 LICENSE_REQUIRED` with a `register_url` pointing to the manager
133+
UI.
134+
135+
There are two ways to activate:
136+
137+
1. **Set a known licensing key** as `AUTHENTICATION_API_KEY` in your
138+
`.env`. The server validates it on boot, persists it locally and
139+
activates the instance silently.
140+
2. **Activate via the manager UI** at `https://<your-host>/manager/login`.
141+
On first login the manager detects an unlicensed backend and redirects
142+
you to the registration server; once you complete the form you are
143+
redirected back and the dashboard becomes accessible.
144+
145+
Activation is a one-time operation — the key is persisted in the
146+
`RuntimeConfig` table and reused across restarts. The licensing server is
147+
only consulted again for fire-and-forget heartbeats (telemetry) and a
148+
best-effort `/v1/deactivate` notice on graceful shutdown.
149+
150+
Full activation guide: <https://docs.evolutionfoundation.com.br/licensing>
151+
152+
---
153+
127154
## Architecture
128155

129156
Evolution API is built with a multi-provider, event-driven architecture:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "evolution-api",
3-
"version": "2.3.7",
3+
"version": "2.4.0",
44
"description": "Rest api for communication with WhatsApp",
55
"main": "./dist/main.js",
66
"type": "commonjs",

src/licensing/runtime.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,23 @@ export async function initializeRuntime(opts: InitializeOptions = {}): Promise<R
122122
const rc = new RuntimeContext(globalApiKey, tier, version);
123123

124124
// Step 1: Instance ID (hardware-based, persistent across restarts).
125-
rc.instanceId = await loadOrCreateInstanceID();
125+
try {
126+
rc.instanceId = await loadOrCreateInstanceID();
127+
} catch (err) {
128+
if ((err as { code?: string })?.code === 'P2021') {
129+
// Prisma error P2021 = "table does not exist" — almost always means
130+
// the operator skipped `npm run db:deploy` after upgrading.
131+
logger.error('╔══════════════════════════════════════════════════════════╗');
132+
logger.error('║ Database is missing the licensing table ║');
133+
logger.error('╚══════════════════════════════════════════════════════════╝');
134+
logger.error('The RuntimeConfig table was not found in the database.');
135+
logger.error('Run the migration and restart:');
136+
logger.error(' npm run db:deploy');
137+
logger.error(`Docs: ${DOCS_URL}`);
138+
process.exit(1);
139+
}
140+
throw err;
141+
}
126142

127143
// Step 2: Try loading existing license from DB.
128144
const stored = await loadRuntimeData();
@@ -160,21 +176,31 @@ export async function initializeRuntime(opts: InitializeOptions = {}): Promise<R
160176
logger.debug(`Global API key not accepted by licensing server: ${readErrorMessage(err)}`);
161177
}
162178
} else {
163-
printRegistrationBanner();
179+
printRegistrationBanner(rc);
164180
rc.setActive(false);
165181
}
166182

167183
globalRC = rc;
168184
return rc;
169185
}
170186

171-
function printRegistrationBanner(): void {
187+
const DOCS_URL = 'https://docs.evolutionfoundation.com.br/licensing';
188+
189+
function printRegistrationBanner(rc?: RuntimeContext): void {
172190
logger.warn('╔══════════════════════════════════════════════════════════╗');
173191
logger.warn('║ License Registration Required ║');
174192
logger.warn('╚══════════════════════════════════════════════════════════╝');
175-
logger.warn('Server starting without license.');
176-
logger.warn('API endpoints will return 503 until license is activated.');
177-
logger.warn('Use GET /license/register to get the registration URL.');
193+
logger.warn('This Evolution API instance is not activated yet.');
194+
logger.warn('API endpoints will return HTTP 503 until activation.');
195+
logger.warn('');
196+
logger.warn('To activate:');
197+
logger.warn(' 1. Open the manager at /manager/login on this host');
198+
logger.warn(' 2. Or set AUTHENTICATION_API_KEY in your .env with a valid licensing key');
199+
logger.warn(` 3. Docs: ${DOCS_URL}`);
200+
if (rc?.instanceId) {
201+
logger.warn('');
202+
logger.warn(`Instance ID: ${rc.instanceId}`);
203+
}
178204
}
179205

180206
function maskKey(key: string): string {
@@ -229,7 +255,9 @@ export function gateMiddleware(rc: RuntimeContext) {
229255
error: 'service not activated',
230256
code: 'LICENSE_REQUIRED',
231257
register_url: managerUrl,
232-
message: 'License required. Open the manager to activate your license.',
258+
instance_id: rc.instanceId,
259+
docs_url: DOCS_URL,
260+
message: `This Evolution API instance is not activated. Open ${managerUrl} to activate, or set AUTHENTICATION_API_KEY in your .env with a valid licensing key. Docs: ${DOCS_URL}`,
233261
});
234262
}
235263

0 commit comments

Comments
 (0)