Skip to content

Commit 71bf563

Browse files
v2.4.0: license activation required (BREAKING CHANGE) (#2530)
* feat(licensing): mirror evolution-go license activation system Phase 1 of the licensing rollout — backend only. Brings the same activation lifecycle that already exists in evolution-go (pkg/core) into evolution-api as src/licensing/, plus a public /license/* router and a gate middleware that 503s API traffic until activation. Module layout (mirrors pkg/core/*.go): - src/licensing/model.ts (config keys, type contracts) - src/licensing/store.ts (Prisma RuntimeConfig CRUD + hardware-based instance ID) - src/licensing/endpoint.ts (XOR-decoded URL with parts-array dev fallback) - src/licensing/transport.ts (axios + HMAC-SHA256 signing) - src/licensing/integrity.ts (paridade-only stubs - Baileys does not consume) - src/licensing/runtime.ts (RuntimeContext, initializeRuntime, gateMiddleware, heartbeat, shutdown, completeActivation) Public routes (no auth) - same contract as evolution-go: - GET /license/status -> {status, instance_id, api_key (masked)} - GET /license/register?redirect_uri -> POST /v1/register/init upstream - GET /license/activate?code= -> POST /v1/register/exchange + activate Bootstrap order in src/main.ts: 1. setDB(prisma) 2. initializeRuntime({tier: 'evolution-api', version, globalApiKey}) 3. /license router (always public) 4. gateMiddleware (503 LICENSE_REQUIRED before business routers) 5. business routers 6. startHeartbeat (30 min, fire-and-forget) 7. SIGTERM/SIGINT -> POST /v1/deactivate (best-effort) Behaviour notes: - AUTHENTICATION_API_KEY is reused as bootstrap key (mirrors GLOBAL_API_KEY in Go). If a license already exists in the DB, the service runs locally even if the licensing server is unreachable. - Gate middleware allowlist: /license/*, /manager/**, /assets/**, /store/**, /health, /server/ok, /favicon.ico, /ws, common static extensions. - Heartbeat carries optional telemetry_bundle with messages_sent / messages_recv that callers can feed via trackMessageSent() / trackMessageRecv(). Schema: - New Prisma model RuntimeConfig (key/value) on both postgresql and mysql schemas. Run npm run db:migrate:dev per provider before starting the service. Endpoint URL ofuscation: - Set LICENSE_ENDPOINT_ENCODED + LICENSE_ENDPOINT_XOR_KEY (hex) in release builds to avoid the licensing URL appearing as a plain string in the bundle. - Dev fallback assembles license.evolutionfoundation.com.br from a parts array, same technique as evolution-go. Phase 2 (manager-v2 UI for the activation flow) lands in a separate PR under evolution-foundation/evolution-manager-v2. * feat(licensing): add Prisma migration for RuntimeConfig table Adds the database migration that creates the licensing storage table (postgres + mysql). This was missing from the previous licensing commit. Without this migration, npm run db:deploy is a no-op and the server will fail to find the table at boot. * 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. * build(manager): refresh manager/dist with v2.4.0 bundle - Bumps the embedded manager UI to the version published on evolution-foundation/evolution-manager-v2 main, which now includes the license-aware login flow that mirrors evolution-go-manager. - Removes the legacy manager/dist/assets/test-interactive.js stand-alone script — its functionality is now a proper React component (TestInteractiveModal) inside the bundle, accessed from the instance card on the dashboard. - Updates the manager-v2 submodule pointer to track main. * style(licensing): apply prettier/eslint autofix and hoist DOCS_URL The autofix from the pre-push hook reorders imports, normalizes line breaks and reformats the constructor signature. Also moves DOCS_URL to the top of the module so the auto-detect error path can reference it without hitting the temporal dead zone. * feat(licensing): bake licensing endpoint into bundle at build time Mirrors evolution-go/tools/build-dist/obfuscate.go: the URL of the licensing server is now XOR-encoded into the JS bundle by tsup `define`, so it never appears as a plain literal in dist/main.js. The Dockerfile accepts the pair as build-args (NOT runtime env vars) so an operator cannot point the running service at a different licensing server. - src/licensing/endpoint.ts: read from compile-time `__LICENSE_ENDPOINT_*__` identifiers replaced by tsup; keep parts-array fallback for dev builds. - tsup.config.ts: `define` reads LICENSE_ENDPOINT_ENCODED / _XOR_KEY from build env at the moment npm run build is invoked. - tools/encode-url.js: helper to generate the hex pair for a given URL. Usage: eval "$(node tools/encode-url.js <url>)". - Dockerfile: ARG + ENV plumbing for the build stage only. - CHANGELOG: notes about the build-time obfuscation. * chore: drop evolution-manager-v2 submodule The manager-v2 source repository is now private, so the CI checkout step fails when trying to fetch the submodule (no PAT configured, GITHUB_TOKEN has no cross-repo read scope). Drop the submodule entirely — the runtime artefact already lives under manager/dist/ in this repo, which is what the Express server serves. Source for the manager continues to be maintained at evolution-foundation/evolution-manager-v2 (private). * style(licensing): prettier autofix on endpoint.ts * docs(changelog): expand 2.4.0 entry with all features since 2.3.7 Previous entry only covered the licensing rollout. The release actually includes 50 commits worth of work: - Manager v2 completely redesigned (Tailwind v4 + @evoapi/design-system, dual-provider support, advanced sessions panels, license flow, Test Interactive modal, full i18n). - Carousel message endpoint (POST /message/sendCarousel). - Cross-client fix for buttons and list rendering on WhatsApp Web/Desktop/iOS via the <biz> stanza node and the legacy listMessage payload. - Interactive buttons via deviceSentMessage with corrected CTA limits and PIX payment_info support. - Catalog orderMessage and quoted productMessage support. - New messaging-history.set event with cumulative counts. - markMessageAsPlayed audio receipt endpoint. - SQS custom base_url. - LID -> phone-number mapping with cache. - Multiple bug fixes (mentionsEveryOne, getLastMessage, markMessageAsRead, list-message JSON cloning, Cloud API race conditions, instance logout idempotency, zombie-instance cleanup, network family timeout, etc.).
1 parent 9a4ae2b commit 71bf563

27 files changed

Lines changed: 1802 additions & 942 deletions

.gitmodules

Lines changed: 0 additions & 3 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,250 @@
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+
#### Manager v2 — completely redesigned dashboard
61+
62+
The embedded manager (served at `/manager`) was rebuilt from the ground up
63+
on **Tailwind v4** + the new **`@evoapi/design-system`**, using the same
64+
visual language as the rest of the Evolution Foundation product line.
65+
Every screen was refactored — no surface remains untouched.
66+
67+
Highlights:
68+
69+
- **Modern dashboard** with skeleton loading, illustrated empty state,
70+
and a typed-name confirmation modal for instance deletion (no more
71+
accidental clicks).
72+
- **Dual-provider support**: the manager now talks to either
73+
`evolution-api` or `evolution-go` (selected at login, persisted in
74+
localStorage). When connected to a GO backend, the sidebar/router
75+
automatically hide the modules GO does not implement.
76+
- **Sessions panels** for the seven chatbot integrations (OpenAI, Dify,
77+
N8N, EvoAI, EvolutionBot, Flowise, Typebot) gained advanced filters
78+
(name / number / status / time presets + custom),
79+
bulk-status-change actions, client-side pagination, and a real
80+
send-message modal calling `/message/sendText`.
81+
- **License-aware login** — see the *Licensing* section below for the
82+
details.
83+
- **🧪 Test Interactive** modal on each instance card — a 5-tab
84+
payload editor (Reply / CTA / PIX / List / Carousel) for
85+
smoke-testing the new interactive-message endpoints from the
86+
dashboard. Replaces the legacy stand-alone `test-interactive.js`
87+
vanilla script that used to be injected into `index.html`.
88+
- **Full i18n coverage** in **pt-BR / en-US / es-ES / fr-FR** — every
89+
screen, every toast, every modal.
90+
- **Branding refresh** — sidebar/footer/login point to
91+
`docs.evolutionfoundation.com.br`, GitHub links to
92+
`evolution-foundation/evolution-manager-v2`, contact to
93+
`suporte@evofoundation.com.br`.
94+
95+
The new bundle is shipped pre-built under `manager/dist/`. The manager
96+
source repository moved to `evolution-foundation/evolution-manager-v2`
97+
(private) — the previous in-repo submodule was dropped.
98+
99+
#### Licensing
100+
- **Licensing module** under `src/licensing/` — RuntimeContext, gate middleware,
101+
signed/unsigned HTTP transport, hardware-based instance ID, fire-and-forget
102+
heartbeat (every 30 min), graceful shutdown deactivation. Mirrors the
103+
evolution-go `pkg/core/` reference implementation.
104+
- **Public license endpoints**:
105+
- `GET /license/status` — current activation state and (masked) api_key
106+
- `GET /license/register?redirect_uri=` — initiates registration on the
107+
licensing server, returns `register_url`
108+
- `GET /license/activate?code=` — exchanges the authorization code received
109+
on the callback for a real api_key, persists it, marks the runtime active.
110+
- **New Prisma model** `RuntimeConfig` (key/value rows in `RuntimeConfig` table)
111+
for both PostgreSQL and MySQL schemas.
112+
- **Auto-detect missing migration**: if the `RuntimeConfig` table is absent,
113+
the server prints a clear banner explaining `npm run db:deploy` and exits 1
114+
instead of throwing a stack trace from the Prisma client.
115+
- **Manager v2** ships with the new license-aware login flow that recognises
116+
HTTP 503 / `LICENSE_REQUIRED`, calls `/license/register`, and redirects to
117+
the registration server. After the callback, it lands on
118+
`/manager/license/callback?code=...` and finalises activation. The new
119+
manager bundle is included under `manager/dist/`.
120+
121+
#### Interactive Messages (Buttons / List / CTA / PIX / Carousel)
122+
- **New endpoint `POST /message/sendCarousel/{instance}`** — multi-card
123+
product carousel built on top of `interactiveMessage` + `carouselMessage`.
124+
Single-card-without-image falls back to `nativeFlowMessage` for iOS
125+
compatibility. New DTO `SendCarouselDto`, schema `carouselMessageSchema`.
126+
- **Button rendering fixed on WhatsApp Web/Desktop/iOS/Android** — removed the
127+
`viewOnceMessage` wrapper that prevented buttons from rendering and started
128+
injecting the required `<biz><interactive type=native_flow v=1>
129+
<native_flow v=9 name=mixed/></interactive></biz>` node into the
130+
`relayMessage` stanza via Baileys' official `additionalNodes` option.
131+
- **List messages fixed on WhatsApp Web/Desktop** — switched to legacy
132+
`listMessage` with `SINGLE_SELECT` listType (the modern
133+
`interactiveMessage + single_select` format does not render on Web/Desktop)
134+
and added `<biz><list type=product_list v=2/></biz>`.
135+
- **Interactive buttons via `deviceSentMessage`** + corrected CTA limits
136+
(max 2 CTA buttons, no mixing with reply or PIX), aligning with the
137+
WhatsApp Business message contract.
138+
- **PIX support** for interactive button messages (`payment_info` button
139+
type — exactly 1 button, isolated).
140+
- **Quoted product / Catalog `orderMessage`** support — handles
141+
`quotedMessage.productMessage` and the catalog `orderMessage` shape,
142+
including `getTypeMessage` enrichment, deduplication cache for
143+
processed order IDs, and propagation through Chatwoot integration.
144+
- Manager UI: a `🧪 Test Interactive` button on each instance card opens
145+
a modal with five tabs (Reply / CTA / PIX / List / Carousel) and an
146+
editable JSON payload — useful for smoke-testing every kind of
147+
interactive message without leaving the dashboard.
148+
149+
#### History Sync
150+
- **New event `messaging-history.set`** emitted on sync completion, with
151+
cumulative counts (chats, contacts, messages, isLatest, progress).
152+
Allows downstream consumers to know exactly when a history sync has
153+
finished and how much was imported.
154+
- Cumulative counters reset on a new sync start to avoid carry-over
155+
between consecutive syncs.
156+
157+
#### Other
158+
- **New endpoint `POST /chat/markMessageAsPlayed/{instance}`** — emits the
159+
audio "played" receipt (PTT/VOICE), completing the read/delivered/played
160+
triplet for voice messages.
161+
- **SQS integration** now accepts a custom `base_url` (useful for
162+
LocalStack and corporate VPC endpoints).
163+
- **LID → phone-number mapping and caching** — translates the new
164+
`@lid` identifiers WhatsApp uses for hidden-phone profiles into the
165+
real `@s.whatsapp.net` JID for downstream processing, with a cache
166+
to avoid redundant lookups.
167+
168+
#### Branding / Documentation
169+
- README, LICENSE, NOTICE, TRADEMARKS standardised under the
170+
**Evolution Foundation 2026** identity.
171+
- All GitHub URLs migrated from `EvolutionAPI` to `evolution-foundation`.
172+
- New README section "License Activation" linking to
173+
<https://docs.evolutionfoundation.com.br/licensing>.
174+
175+
### Fixed
176+
177+
- **`mentionsEveryOne` honours `false`** — earlier the flag was always
178+
applied regardless of value (#2470).
179+
- **`getLastMessage`**: corrected the Prisma JSON path filter so the
180+
query returns the actual last message (#2495 / #2515).
181+
- **`markMessageAsRead`**: corrected JID filter to cover all user types
182+
(regular, business, broadcast, group).
183+
- **List messages**: removed destructive JSON cloning that triggered
184+
`this.isZero` when the message contained `Long`-typed fields (#2461).
185+
- **History sync race condition**: completion event is now emitted
186+
*before* the contact upsert, so consumers don't observe the sync as
187+
finished while contacts are still being written (#2510).
188+
- **Business API (Cloud)**: race condition in sender identification
189+
resolved (#2493); execution order normalised; `chatwootIds`
190+
correctly propagated.
191+
- **`/instance/logout/{instance}`** — idempotent: returns SUCCESS instead
192+
of 400 when the instance is already closed, so the manager UI delete
193+
flow (logout-then-delete) does not surface a misleading error.
194+
- **`remove.instance` event** — emitted even when logout itself fails,
195+
preventing zombie instances after a partially failed delete (#2520).
196+
- **Chatbot session**: a closed session no longer blocks bot
197+
re-activation.
198+
- **Docker compose**: fresh-install startup failures resolved.
199+
- **WhatsApp chats**: `accountLid` handling, `remoteJid` normalisation
200+
and `chatsRaw` mapping cleaned up to avoid mismatched contact data
201+
on first connection.
202+
- **Facebook ads**: `externalAdReply` context readability and fallback
203+
path for missing fields.
204+
- **Networking**: added the `--network-family-autoselection-attempt-timeout`
205+
flag in `start:prod` so IPv4/IPv6 races no longer hang the boot on
206+
hosts with broken IPv6.
207+
- **Trailing slashes** on configuration URLs are now tolerated in all
208+
HTTP clients.
209+
- Verbose-log fix: undefined `maxRetries` reference inside the
210+
`messages.update` handler.
211+
212+
### Notes
213+
214+
- `AUTHENTICATION_API_KEY` keeps its original meaning (global API key for
215+
business endpoints) **and** gains a second use as the bootstrap license
216+
key. If the value you have is a real licensing key, activation is silent.
217+
If it is not, the service starts unlicensed and waits for activation via
218+
the manager.
219+
- Activation is a one-time operation. The `api_key` is stored in the database
220+
and reused across restarts. The licensing server is only consulted again
221+
for periodic heartbeats (telemetry — non-blocking) and on graceful shutdown
222+
(`/v1/deactivate`).
223+
- If the licensing server is unreachable but the instance has been activated
224+
before, the service continues to serve traffic normally — local DB is the
225+
source of truth for activation state after the first successful call.
226+
- Release builds bake the licensing endpoint into the bundle as an XOR-encoded
227+
string via tsup `define`, so the URL never appears as a plain literal in
228+
`dist/main.js`. Generate the pair with `node tools/encode-url.js <url>` and
229+
pass `LICENSE_ENDPOINT_ENCODED` / `LICENSE_ENDPOINT_XOR_KEY` as Docker
230+
build-args (NOT runtime env vars). Local dev builds use a parts-array
231+
fallback that still avoids a single string literal but is not obfuscated.
232+
233+
### Troubleshooting
234+
235+
- **`HTTP 503 LICENSE_REQUIRED`** — expected before activation. Follow the
236+
migration guide.
237+
- **`The table evolution_api.RuntimeConfig does not exist`** (legacy stack
238+
trace if you somehow bypass the new auto-detect) — run `npm run db:deploy`.
239+
- **`Global API key not accepted by licensing server: invalid signature`**
240+
your existing `AUTHENTICATION_API_KEY` is not a valid licensing key. Use
241+
the manager UI flow to obtain a new one.
242+
- **Buttons/list not rendering on WhatsApp Web** — make sure you are on
243+
v2.4.0+; the `<biz>` stanza node and the legacy `listMessage` payload
244+
shipped with this release are required for cross-client rendering.
245+
246+
---
247+
1248
# 2.3.7 (2025-12-05)
2249

3250
### Features

Dockerfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ RUN chmod +x ./Docker/scripts/* && dos2unix ./Docker/scripts/*
3131

3232
RUN ./Docker/scripts/generate_database.sh
3333

34+
# Licensing endpoint is XOR-encoded into the bundle by tsup `define`. Pass the
35+
# pair via build-args (NEVER as runtime env vars) to keep the URL out of the
36+
# compiled JavaScript as a plain literal. Generate them with
37+
# `node tools/encode-url.js <url>`. Leaving them empty is OK for non-release
38+
# builds — the dev fallback in src/licensing/endpoint.ts kicks in.
39+
ARG LICENSE_ENDPOINT_ENCODED
40+
ARG LICENSE_ENDPOINT_XOR_KEY
41+
ENV LICENSE_ENDPOINT_ENCODED=${LICENSE_ENDPOINT_ENCODED}
42+
ENV LICENSE_ENDPOINT_XOR_KEY=${LICENSE_ENDPOINT_XOR_KEY}
43+
3444
RUN NODE_OPTIONS="--max-old-space-size=2048" npm run build
3545

3646
FROM node:24-alpine AS final

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:

evolution-manager-v2

Lines changed: 0 additions & 1 deletion
This file was deleted.

manager/dist/assets/index-C-JyjMiq.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)