> For the complete documentation index, see [llms.txt](https://adatrack.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://adatrack.gitbook.io/docs/changelog/2026-04.md).

# April 2026

## v1.3.0

### Features & Enhancements

* **MQTT Broker Plugin:**
  * AdaTrack now includes a native MQTT broker as a built-in plugin. IoT devices can connect over MQTT instead of — or alongside — UDP with no external broker process, no cloud dependency, and no additional infrastructure.
  * Authentication is connection-scoped: devices use their **Device UUID** as the MQTT ClientID and username, and their **Device Secret** as the password. Once authenticated, all messages on that session are trusted — no per-packet signatures required.
  * **Topic structure:** devices publish telemetry to `data/{device_id}` and receive operator commands on `commands/{device_id}`. Strict per-device ACL enforcement prevents one device from reading or writing another device's topics.
  * **Shared pipeline:** MQTT telemetry flows through the same JavaScript decoder, database insert, geofence evaluation, alert engine, and WebSocket broadcast path as UDP. Existing device profiles and decoders work unchanged.
  * **Bi-directional:** Operators can send command payloads from the device details panel or via API to any connected device. A downlink command log tracks what was sent and when.
  * **Per-device transport selection:** Each device now has a **Transport Type** field (`UDP` or `MQTT`) set in the device form. Switching to MQTT shows a connection guide with broker address, port, topic names, and ready-to-use code snippets for Python (paho-mqtt), C, and Arduino.
  * **TLS support:** Optional — configure a certificate and private key path in the plugin settings to accept connections on port 8883 alongside plain port 1883.
  * **Broker stats page:** The plugin admin page shows live connected client count, messages in/out per minute, and a real-time session table updated over WebSocket.
  * Requires **Standard tier** license or above. Enable from **Admin > Plugins > MQTT Broker**.
  * See the [MQTT Protocol guide](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/iot-ingestion/mqtt.md) for connection instructions.
* **Plugin SDK — Public Routes & RBAC Automation:**
  * Plugin developers can now declare HTTP routes as **public** (unauthenticated) in the plugin manifest using the `Public: true` flag on a route declaration. Public routes are mounted at `/api/v1/public/plugins/{plugin_id}/...` and do not require a JWT — useful for receiving webhooks from external services.
  * Plugin-declared RBAC permissions are now **automatically seeded** into the permissions table when a plugin is enabled and removed when the plugin is uninstalled. No manual database setup is needed when deploying a plugin that declares its own permission set.
  * New `PluginHost` methods for compiled plugin authors: `SubmitPacket()` for injecting pre-authenticated payloads into the telemetry pipeline (used by transport plugins), `GetDeviceSecret()` for connection-time device authentication, and `RegisterPublicHTTPRoutes()` for registering unauthenticated route handlers.
  * See the [Plugin Development guide](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/features/plugin-development.md) for updated API reference.

### Improvements & Fixes

* **Fixed plugin initialization on page load:** Resolved a race condition in the frontend that could cause plugins to fail initializing their React Query client on first render, resulting in blank plugin pages.
* **Fixed live updates on public dashboards:** WebSocket telemetry broadcasts were not reaching public (unauthenticated) dashboard views. Connections are now correctly upgraded and subscribed to the broadcast hub.
* **Fixed Telegram notification formatting:** Markdown special characters in alert message bodies were being double-escaped, producing garbled text in Telegram notifications from the Chat Bot plugin. The escaping logic now correctly sanitizes only the characters required by Telegram's MarkdownV2 format.

## v1.2.0

### Features & Enhancements

* **ES6+ JavaScript Support for Decoders, Workflows & Plugins:**
  * Decoder scripts, workflow JS transforms, and plugin scripts can now use modern JavaScript syntax: arrow functions, `const`/`let`, template literals, destructuring, default parameters, spread/rest, classes, `for...of`, and enhanced object literals.
  * An embedded **Babel transpiler** (v7.26.x) automatically converts ES6+ to ES5 before execution. Transpilation is transparent — existing ES5 scripts continue to work without modification.
  * The transpiler runs in a fixed-size pool of pre-warmed Goja VMs (configurable via `SCRIPTING_TRANSPILER_POOL_SIZE`) to avoid serialization bottleneck under burst load.
* **Unified Compilation Cache:**
  * All JavaScript execution contexts (UDP decoders, workflow transforms, workflow conditions, plugin scripts, decoder playground) now share a single content-addressed LRU compilation cache.
  * Cache key is SHA-256 of the original source — identical scripts across different profiles share the same compiled program.
  * **Workflow JS transforms no longer recompile on every execution** — the most impactful performance improvement. Previously, every workflow execution re-parsed and re-compiled the JS transform script.
  * Concurrent compilations of the same script are deduplicated via singleflight.
  * Cache size, hit rate, miss rate, and eviction count are exposed as Prometheus metrics (`scripting_cache_hits_total`, `scripting_cache_misses_total`, `scripting_cache_size`, `scripting_cache_evictions_total`).
* **Runtime Sandboxing Improvements:**
  * **Call stack depth limit** (default: 1024 frames) prevents stack overflow from deeply recursive or infinite-recursion scripts. Configurable via `SCRIPTING_MAX_CALL_STACK`.
  * **Dangerous globals removed:** `eval()` and the `Function` constructor are disabled in all script VMs, blocking dynamic code generation as a defense-in-depth measure.
  * **Script size validation:** Scripts exceeding `SCRIPTING_MAX_SCRIPT_SIZE` (default: 64 KB) are rejected before transpilation or compilation.
* **Configurable Per-Context Timeouts:**
  * Execution timeouts are now configurable per context instead of a single hardcoded 1-second timeout:
    * UDP decoders: 500 ms (`SCRIPTING_DECODER_TIMEOUT_MS`)
    * Workflow transforms: 2 s (`SCRIPTING_TRANSFORM_TIMEOUT_MS`)
    * Workflow conditions: 500 ms (`SCRIPTING_CONDITION_TIMEOUT_MS`)
    * Decoder playground: 3 s (`SCRIPTING_PLAYGROUND_TIMEOUT_MS`)
  * Plugin scripts retain their existing per-plugin `script_timeout_ms` configuration.
* **Frontend Syntax Validation:**
  * The decoder script editor (Device Profile form) and the workflow JS transform editor now validate JavaScript syntax in real time as you type.
  * Uses the [Acorn](https://github.com/acornjs/acorn) parser (\~80 KB) targeting ECMAScript 2020, matching the ES6+ features supported by the backend transpiler.
  * Inline error messages display the line number and a clear description. Errors clear immediately when the syntax is fixed.
  * Validation is advisory only — it does not block saving or testing. The backend remains the authoritative validator.

### Improvements & Fixes

* **New Prometheus metrics:** 8 new metrics for scripting runtime observability: `scripting_transpile_total`, `scripting_transpile_duration_seconds`, `scripting_cache_hits_total`, `scripting_cache_misses_total`, `scripting_cache_size`, `scripting_cache_evictions_total`, `scripting_compile_duration_seconds`, `scripting_script_rejected_total`.
* **`adatrack-ctl init-config` updated:** The configuration generator now includes the `Scripting Runtime` section with all 9 new environment variables and documentation. The `--merge` mode adds missing scripting keys to existing `.env` files.
* **UDP processor simplified:** Removed the local per-profile decoder cache (`decoderCache`, `decoderMu`, `cachedDecoder` struct) and the `goja` import from the UDP processor. All compilation is now delegated to the shared `ScriptCache`.

## v1.1.0

### Features & Enhancements

* **Plugin System (AdaTrack Extensions):**
  * A general-purpose extension architecture that allows adding new capabilities to the platform without forking the core codebase. Plugins can extend workflows, reporting, alerting, dashboards, and more.
  * **8 extension points:** Custom workflow nodes, report blocks, alert dispatchers, dashboard widgets, HTTP routes, cron jobs, event hooks, and WebSocket channels. All registered via a clean manifest-based API.
  * **Dual runtime:** Compiled-in Go plugins for maximum performance, or JavaScript (Goja) scripted plugins for rapid development with no server recompilation. Scripted plugins are stored in the database and managed entirely from the admin UI.
  * **Admin panel:** New **Plugins** tab with full lifecycle management — install, enable, disable, configure, health check, and uninstall. Configuration forms are auto-generated from each plugin's JSON Schema (supports text, number, boolean, enum dropdown, and password fields).
  * **Platform event bus:** Five platform lifecycle events (`telemetry.received`, `alert.fired`, `geofence.transition`, `workflow.executed`, `report.generated`) are now emitted from core services, allowing plugins to react in real time via event hooks.
  * **Config encryption:** Sensitive plugin configuration values (API keys, tokens) marked `writeOnly` in the schema are encrypted at rest using AES-256-GCM. Configure with the `PLUGINS_ENCRYPTION_KEY` environment variable.
  * **Scripted plugin sandbox:** JavaScript plugins run in an isolated Goja VM with no filesystem or network access. The `adatrack.*` host API provides scoped access to devices, telemetry, events, configuration, key-value storage, and logging. Per-plugin timeout enforcement prevents runaway scripts.
  * **Frontend integration:** Plugin workflow nodes appear in the visual editor. Plugin dashboard widgets render in the dashboard builder. Plugin navigation items appear in the sidebar. Plugin pages are routed under `/plugins/{pluginID}/`.
  * See the [Plugins & Extensions guide](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/features/plugins.md) for full documentation.
* **Chat Bot Plugin (Telegram & Slack):**
  * The first compiled plugin for the AdaTrack plugin system. Provides bi-directional Telegram and Slack bot integration for monitoring and controlling the platform directly from chat.
  * **10 chat commands:** `/help`, `/link`, `/unlink`, `/whoami`, `/devices`, `/device`, `/telemetry`, `/latest`, `/alerts`, `/status` — query devices, view telemetry readings, check platform health, and manage account linking without leaving your chat app.
  * **Interactive notifications:** Receive rich alert and geofence transition notifications with inline action buttons (Acknowledge, Mute 1h, Mute 24h, View Device). Notifications respect per-user preferences including severity filtering, quiet hours, and event type toggles.
  * **Two new alert dispatchers:** `telegram_bot` and `slack_bot` channel types available in the alerting engine, alongside existing webhook-based dispatchers. Supports interactive message formatting with severity indicators.
  * **Secure account linking:** OTP-based flow — send `/link` in chat, receive a 6-digit code, enter it in the AdaTrack web UI to bind your chat identity to your platform account. Full RBAC permission enforcement on every command.
  * **Notification preferences:** Per-user settings for event types (alerts, geofence, telemetry), minimum severity threshold, quiet hours (UTC), and opt-in daily digest summaries. Configurable via the web UI.
  * **Event-driven forwarding:** Subscribes to `alert.fired`, `geofence.transition`, and `telemetry.received` platform events. Notifications are deduplicated (5-minute window per device) to prevent spam.
  * **Scheduled jobs:** Automatic session cleanup (every 6 hours), daily digest delivery (configurable hour, default 8 AM UTC), and health ping (every 5 minutes).
  * **10 RBAC permissions** for granular access control: `chatbot.devices.read`, `chatbot.telemetry.read`, `chatbot.alerts.read`, `chatbot.geofence.read`, `chatbot.workflow.read`, `chatbot.system.status`, `chatbot.alerts.manage`, `chatbot.workflow.manage`, `chatbot.report.generate`, `chatbot.admin`.
  * **License requirement:** Requires Standard tier or above with the `plugin:chatbot` feature enabled.
  * See the [Chat Bot guide](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/features/chatbot.md) for setup instructions.

### Improvements & Fixes

* **`adatrack-ctl plugin` — Plugin Management from the CLI:**
  * New `plugin` command group for managing compiled and scripted plugins from the command line: `list`, `status`, `enable`, `disable`, `config`, and `health`.
  * View all registered plugins with their state, license requirements, and type (compiled vs scripted).
  * Enable and disable plugins with automatic license validation. Update plugin configuration with `--set key=value`.
  * Run plugin health checks without the admin UI.
  * See the [adatrack-ctl reference](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/getting-started/adatrack-ctl.md) for full command documentation.
* **Event emissions wired into platform services:** The UDP processor, alerting service, geofence service, workflow engine, and reporting service now emit events to the plugin event bus, enabling event hook plugins to react to all major platform activities.
* **WebSocket global broadcast:** Plugins can now broadcast messages to all connected WebSocket clients (previously limited to per-user broadcasts).
* **Plugin HTTP subrouter:** Plugin-declared HTTP routes are now mounted under `/api/v1/plugins/{pluginID}/` with JWT authentication, plugin ID validation, and race-safe dispatch.
* **Removed dead configuration:** `PLUGINS_SCRIPT_TIMEOUT_MS` and `PLUGINS_SCRIPT_MEMORY_MB` environment variables were removed — script timeouts are configured per-plugin via the admin UI, and Goja does not support memory limits.

## v1.0.5

### Features & Enhancements

* **`adatrack-ctl license` — License Management from the CLI:**
  * New `license` command group for managing self-hosted license keys entirely from the command line — no running server or database connection required.
  * **`license status`** — Displays the current license tier, resource limits, expiry, and validation status. Reads from `ADATRACK_LICENSE_KEY`, `ADATRACK_LICENSE_FILE`, or the detected `.env` file. Supports `--json` for automation.
  * **`license install <key-or-file>`** — Installs a license key. Auto-detects whether the argument is a file path or an inline JWT. Verifies the Ed25519 signature, displays a summary, and writes the license to disk with 0600 permissions. Use `--inline` to store the JWT directly in the `.env` file instead.
  * **`license inspect <key-or-file>`** — Decodes a license JWT without verifying the signature and displays all claims (tier, limits, features, expiry). Useful for examining a license before installing it.
  * **`license remove`** — Removes the installed license file and comments out license variables in the `.env` file, reverting the instance to Community Edition.
* **`adatrack-ctl user` — User Account Management:**
  * New `user` command group for full user CRUD from the command line. All commands connect directly to PostgreSQL — the server does not need to be running.
  * **`user create`** — Creates a pre-activated user account with interactive prompts or flag-based input (`--username`, `--email`, `--password`, `--role`). Enforces the same password complexity and username validation rules as the web registration flow. Supports `--json` output.
  * **`user list`** — Lists all users in a formatted table. Filter by role with `--role`. Supports `--json`.
  * **`user get <id-or-username>`** — Displays detailed user information. Resolves by UUID or username. Supports `--json`.
  * **`user disable` / `user enable`** — Toggles the `is_active` flag on a user account.
  * **`user reset-password <id-or-username>`** — Force-sets a new password (bcrypt hashed) directly in the database without sending an email.
  * **`user delete <id-or-username>`** — Permanently deletes a user with typed confirmation. Owned resources (devices, profiles, geofences, etc.) become unowned via `ON DELETE SET NULL` foreign keys.
* **`adatrack-ctl user role` — RBAC Management:**
  * **`user role list`** — Displays all roles (ADMIN, SUPPORT, USER) and their assigned permission slugs, queried live from the `role_permissions` and `permissions` tables.
  * **`user role set <id-or-username> <role>`** — Changes a user's role with confirmation. Accepts case-insensitive input.
* **`adatrack-ctl install --create-admin` — Bootstrap First Admin:**
  * The `install` command now accepts `--create-admin` to create the first admin user immediately after schema installation, solving the bootstrapping problem where a fresh instance has no admin account and the web UI is inaccessible.
  * Additional flags: `--admin-username`, `--admin-email`, `--admin-password`. If omitted, the tool prompts interactively.
  * Admin creation is best-effort — if it fails (e.g., invalid password), the schema installation still succeeds and the user can retry with `adatrack-ctl user create --role ADMIN`.
  * See the [adatrack-ctl reference](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/getting-started/adatrack-ctl.md) for full command documentation.

### Improvements & Fixes

* **Report generation improvements:** Various fixes and enhancements to scheduled PDF report generation.

## v1.0.4

### Improvements & Fixes

* **Go 1.26.3 upgrade:** Updated all Go modules and toolchains to Go 1.26.3, including backend and adatrack-ctl. Addresses vulnerability findings.

## v1.0.3

### Features & Enhancements

* **Self-Hosted Map Tile Server:**
  * AdaTrack now includes a fully embedded OpenStreetMap tile server for air-gapped self-hosted deployments. No Mapbox account or internet connection required for map rendering.
  * Uses [PMTiles](https://github.com/protomaps/PMTiles) format with [Protomaps](https://protomaps.com/) basemap data. Tiles are extracted from the daily planet build via efficient HTTP range requests — only the tiles within your region's bounding box are downloaded.
  * Three built-in map styles: **Positron** (light), **Dark Matter** (dark), and **OSM Bright** (streets). Styles use Protomaps v4 vector tile layers and automatically resolve the correct tileset name at serve-time.
  * Globe projection support on MapLibre GL JS with smooth dark/light theme switching.
  * Font glyphs are fetched from the Protomaps font CDN on first request and cached locally for fully offline operation thereafter.
  * Dual-mode frontend: the map component automatically selects Mapbox GL JS (SaaS mode, when `VITE_MAPBOX_TOKEN` is set) or MapLibre GL JS (self-hosted mode). Mapbox is lazy-loaded to avoid initialization errors in self-hosted environments.
  * All map components (DeckGLMap, GeofenceEditor, GeoHeatmap, LiveMap widget, Heatmap widget) respect the active map provider via the new `BaseMap` and `BaseMarker` polymorphic components.
  * See the [Self-hosted Deployment guide](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/getting-started/self-hosted.md) for setup instructions.
* **`adatrack-ctl tiles` — Map Tile Management:**
  * New `tiles` command group for downloading and managing region-specific map tile data.
  * **`tiles download --region=<name>`** — Extracts tiles for a region from the Protomaps daily planet build (\~120 GB) using HTTP range requests. Only the tiles within the region's bounding box are transferred. Includes `--maxzoom` and `--threads` options.
  * **`tiles download --region=world-base`** — Downloads a low-zoom global base layer (\~100 MB) for worldwide coverage at zoom levels 0–6.
  * **`tiles download --file=<path>`** — Imports a pre-downloaded `.pmtiles` file for fully air-gapped environments.
  * **`tiles download-assets`** — Placeholder for font and sprite asset downloading (fonts are auto-cached on first request).
  * **`tiles list`** — Displays installed tilesets with name, size, and path. Use `--available` to see all downloadable regions.
  * **`tiles remove --region=<name>`** — Removes a tileset with confirmation.
  * **`tiles status`** — Shows tile data directory overview: total size, tileset count, font and sprite availability.
  * 20 pre-configured regions available: continents, major countries, and sub-regions.
  * The planet build URL is resolved dynamically (probes the latest 7 days) so tile data stays current without tool updates.
  * See the [adatrack-ctl reference](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/getting-started/adatrack-ctl.md) for full command documentation.
* **Self-Hosted Docker Compose:**
  * New `docker-compose.selfhosted.yml` for a complete air-gapped deployment stack with MailHog (email capture), local storage, and self-hosted map tiles.
  * New `.env.selfhosted.example` with all configuration pre-set for self-hosted mode.
  * Makefile targets: `make selfhosted`, `make selfhosted-tiles-us`, `make selfhosted-tiles-base`, `make selfhosted-down`.

### Improvements & Fixes

* **Fixed standalone Docker build:** The `Dockerfile.standalone` now correctly passes `-tags=standalone` to the Go build, enabling the embedded frontend. Previously the SPA handler was compiled as a no-op, causing 404 on the root page.
* **Fixed release CI:** The GitHub Actions `release.yml` workflow now passes `-tags=standalone` when building release binaries, matching the Docker build.
* **Fixed `Dockerfile.standalone` lockfile path:** Updated to use the workspace root `pnpm-lock.yaml` and `pnpm-workspace.yaml` instead of a stale frontend-specific lockfile.
* **Added `@vis.gl/react-maplibre` dependency:** Required by `react-map-gl` v8.1.0 for MapLibre GL JS support. Previously missing, causing the MapLibre `Map` component to silently fail to render.

## v1.0.2

### Features & Enhancements

* **Pluggable Email Provider System:**
  * The platform now supports multiple email backends. Operators can choose between **SMTP**, **Mailgun**, or **no-op** (console logging) via the `EMAIL_PROVIDER` environment variable.
  * New generic SMTP implementation works with any SMTP-compatible server: Gmail, AWS SES, SendGrid, Office 365, Postfix, MailHog, and more.
  * Supports four authentication modes: `plain`, `login` (for Office 365/Exchange), `crammd5`, and `none` (for local relays).
  * Automatic STARTTLS on port 587 and implicit TLS on port 465.
  * Full attachment support for scheduled report delivery over SMTP.
  * Auto-detection: if `EMAIL_PROVIDER` is not set, the system detects Mailgun or SMTP credentials automatically.
  * Zero-change backward compatibility — existing Mailgun deployments continue to work without any configuration updates.
  * See the [Email Providers guide](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/administration/email-providers.md) for setup instructions.

## v1.0.1

### Improvements & Fixes

* **`adatrack-ctl` auto-loads environment config:** The tool now automatically reads `/etc/default/adatrack` and `/opt/adatrack/config/.env` on startup, so database credentials no longer need to be passed via flags or exported in the shell. Supports `export KEY=value` syntax. New `--env-file` flag for explicit path override.
* **Fixed `adatrack-ctl` not found after installation:** The binary is now installed to `/usr/local/bin/` with correct permissions, making it available as a command for all users without requiring the full path.
* **Fixed deployment script path resolution:** Corrected project root detection in the deployment script that caused schema file copies to fail.
* **Fixed binary file permissions after deployment:** Deployed binaries now have correct ownership and execute permissions set automatically.

## v1.0.0

### Features & Enhancements

* **Self-Hosted Licensing System:**
  * Introduced a license-based resource management system for self-hosted deployments, replacing the previous unlimited-access model.
  * Three tiers available: **Community Edition** (free, no license required), **Standard**, and **Enterprise**.
  * Licenses are fully offline — no internet connection or call-home checks required.
  * License keys can be configured via the `ADATRACK_LICENSE_KEY` environment variable or a `.adatrack.lic` file.
  * New **Settings > License** page in the admin dashboard to view tier, limits, and expiry status.
  * New admin API endpoints: `GET /api/v1/admin/license` and `GET /api/v1/admin/instance`.
  * Automatic 7-day grace period after license expiry with graceful fallback to Community Edition limits.
  * Optional instance binding for licenses tied to a specific installation.
  * Background license re-verification picks up renewed licenses without server restart.
* **`adatrack-ctl` — Self-Hosted Management Tool:**
  * New standalone CLI tool for managing self-hosted AdaTrack installations.
  * **`install`** — Guided fresh database setup with pre-flight validation, extension creation, and full schema migration.
  * **`upgrade`** — Detects current schema version and applies pending migrations with confirmation.
  * **`rollback`** — Reverts migrations by step count or to a specific target version, with data-loss warnings.
  * **`status`** — Read-only view of schema version, pending migrations, extension versions, and table count.
  * **`validate`** — Pre-flight checks (PostgreSQL version, extensions, privileges, kernel tuning) without modifying anything.
  * **`uninstall`** — Full schema removal with typed confirmation safety.
  * **`backup`** / **`restore`** — Database backup and restore wrapping `pg_dump`/`pg_restore` with TimescaleDB-aware defaults.
  * **`init-config`** — Generates a starter `.env` configuration file with all supported variables, grouped by feature area.
  * **`doctor`** — Comprehensive health diagnostics across schema, TimescaleDB, storage, connections, performance, and host categories. Supports `--json` output for monitoring integration.
  * All schema migrations are embedded in the binary — no separate SQL files to manage.
  * Distributed alongside the main AdaTrack binary on every release (Linux, macOS, Windows) and included in the Docker image.
  * See the [adatrack-ctl reference](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/documentation/getting-started/adatrack-ctl.md) for full documentation.
