Skip to main content

Changelog

All notable changes to this project. The format follows Keep a Changelog, and the project uses Semantic Versioning: agents and connectors of a major version work with every server of that major version.

Unreleased​

Added​

  • Hand-drawn charts, illustrations and date pickers: charts in Axis and Edge are drawn in the Ink & Strata hand (ink-outlined bars and slices, wobbly lines, pencil gridlines; the palette starts with violet). Empty lists, no results, errors and 404 pages show small hand-drawn illustrations. Every date and date-and-time field opens a hand-drawn calendar instead of the browser's. @entrosity/ui 0.7.0: SketchIllustration, EmptyState, DatePicker ([Conventions]).

  • Faster pages: the hand-drawn look no longer runs an SVG filter on every outline, chart and list row, and sticky headers no longer blur what scrolls under them, so busy pages (Axis devices and screen wall, Matrix rooms, Sphere live view) scroll and update smoothly again (@entrosity/ui 0.7.2).

  • Responsive on phones and tablets: Hub, Axis, Edge, Matrix, Sphere and the website work from 320px wide: no sideways scrolling, tables scroll in their own box, filters and forms stack, grids reflow and dialogs fit the screen (@entrosity/ui 0.7.1).

  • Entrosity Sphere, optimise for live view checks the streams: after tuning, the connector watches each camera's sub stream until two keyframes arrive and measures the interval (measured_ms on every ChannelTune; the job now has 5 minutes). Cameras apply a value the NVR passes on only after some minutes, and some keep their own (it syncs back to the NVR), so a camera whose stream keeps the old interval is listed as not applied yet (optimise again later) or, when the NVR already held the shorter interval, as keeping its own (set it on the camera); the dialog also says how many cameras were checked. Needs the updated connector ([NVRs]).

  • Entrosity Sphere, optimise for live view: tenant admins can choose Optimise for live view on a connected NVR's page: every camera's sub stream (the one grids show) is set to send a keyframe at least every 1, 2, 3 or 4 seconds (1 recommended; frame rate × seconds frames), so a viewer opening a camera waits less for the first picture (Dahua cameras default to one every 2 seconds). Intervals already as short are kept, main streams, which the NVR records, are never changed, and sub streams use a little more bandwidth. The NVR's settings are read back, since an NVR may accept a value an IP camera does not take; the dialog sums up N changed, N already fine, N not changed and lists the failures by camera. Audited as nvr.tune_live. API: POST /tenants/{tenantID}/nvrs/{nvrID}/tune-live {keyframe_seconds} (1–4, default 1; permission nvrs:manage), a job whose result is an NvrLiveTuneResult; 409 nvr_offline when the NVR is disconnected, 409 connector_offline. Connector job sphere.nvr.tune_live (LiveTuneJob, LiveTuneResult; lane probe, 3 minutes), Dahua Encode[i].ExtraFormat[0].Video.GOP through configManager setConfig; the simulator implements it. Needs the updated connector ([NVRs], [Sphere connector protocol]).

  • Entrosity Vertex (beta): Active Directory management: a new product at /vertex on the Hub's host, with sign-in on Entrosity Hub (product vertex, roles Tenant admin and Helpdesk; Hub platform admins are global admins). Users with every attribute (binary values as b64:, a deny list for identity, security and password attributes), password resets, unlock, enable/disable, move, rename, delete, group membership and actions on up to 500 users; groups and members; OUs; Group Policy (create, rename, status, delete, links with enforce and order, registry-based settings, security filtering, backups on the domain controller before every change, the XML report; the default domain policies are read-only); fine-grained password policies; and CSV bulk imports of up to 10,000 users (; or ,, create/update/upsert, attr:<ldapName> columns, groups by DN or name, a preview of every row, commit with step-up, a result file without passwords). Vertex has no connector of its own: every request is an operation run as a job on the tenant's Axis connector on a domain controller, as the AD account configured in Vertex (Windows PowerShell 5.1, ActiveDirectory and GroupPolicy modules, a loopback PowerShell session; the account needs Remote Management Users). Safety layers: write switches per kind and for deletes (all off by default), managed OUs, protected objects (adminCount=1, critical system objects, built-in RIDs), change limits (600 per user, 2,000 per tenant per minute), step-up for settings, deletes and import commits, the author's rights checked again when the connector fetches the job's secrets (author_revoked), the connector's local guard (rmm-connector vertex guard accept), and the audit log. The AD password and new passwords are stored encrypted (VERTEX_CREDENTIALS_KEY), handed to the connector once and never returned. The API is ready; the web interface is in development, and Vertex is not deployed yet: the Hub's migration 0013_vertex_product registers it disabled and in beta ([Entrosity Vertex], [Setting up the domain controller], [Bulk import], [Running Entrosity Vertex], [Vertex API], [Vertex protocol]).

  • Axis for Entrosity Vertex: an internal listener for Vertex (RMM_INTERNAL_ADDR, :8089, on only with RMM_VERTEX_INTERNAL_URL and RMM_VERTEX_AXIS_TOKEN): Vertex lists a tenant's connectors and queues, reads and cancels vertex.op/vertex.read jobs there. The connector API gains POST /api/connector/v1/vertex/jobs/{jobID}/secrets and …/chunks, relayed to Vertex only for live Vertex jobs of the asking connector; Axis stores no Vertex secrets and tells Vertex when a Vertex job finishes ([Configuration]).

  • Site connector, Vertex capability: Windows builds announce vertex and run Vertex jobs in two new lanes (vertex, one change at a time; vertex-read, two reads). Changes need the new local guard vertex-guard.json, changed only from an elevated prompt with rmm-connector vertex guard show|accept|set|reset: accept the managed OUs, switch on GPO (--gpo), password policy (--pso) and delete (--deletes) changes, and set the cap of writes per minute (--max-writes-per-minute, default 120; raise it to at least 200 for bulk imports). GPO backups go to %ProgramData%\Entrosity\Vertex GPO Backups ([Site connector], [The connector's local guard]).

  • Hub, reset a user's authentication: platform admins can choose Reset authentication on a user's page to turn off the user's two-factor authentication (lost phone and recovery codes), reset their password (the old one stops working at once and a link to choose a new one, valid for 24 hours, is e-mailed), or both. The user is signed out everywhere and the action is audited as user.reset_auth; you cannot reset your own. API: POST /admin/users/{userID}/reset-auth, codes self_reset, user_not_active, nothing_to_reset ([Reset authentication]).

  • Axis screen wall, 30 screens, 720p and lock: the wall shows up to 30 computers of a room; an enlarged tile streams 720p (1280×720, about 5 pictures a second) and goes back to the small picture when you return to the grid. Each live tile has a lock button that locks the computer's own keyboard and mouse, and Lock all / Unlock all lock the whole room; the lock ends with the session at the latest (Ctrl+Alt+Del lifts it until that screen is gone). Remote desktop gets Lock keyboard and mouse in control mode: the user cannot interfere, the technician's input still works. Stream messages lock / locked (control sessions and wall tiles) and large (wall tiles), relayed by the server; older agents ignore them. Needs the new agent (Windows BlockInput on the helper's input thread) ([Screen wall], [Remote desktop]).

  • Axis deployments, several packages at once: the deployment wizard picks one or more packages (at most 20); several become one deployment per package with the same targets and options, in the order chosen, created in one transaction. A computer installs them one after another; a job waiting in the agent behind another no longer times out (a job acknowledged but not started is timed from when the jobs before it finished; agents now report when a job starts), and a restart a reboot policy asks for waits until the last queued package. API: POST /tenants/{tenantID}/deployments/batch (package_ids), permission deployments:create ([Deployments]).

  • Axis screen wall: Screen wall watches up to 26 screens of a computer room at once, view only: pick an Entrosity Matrix room and every computer's live picture (about once a second, at most 640×400) shows in a grid; click one to enlarge it. Rooms and their computers come from Matrix (the room's FortiGate address group, matched to Axis devices by name). Technicians and administrators see every room; technicians always show the session bar on the computers, administrators choose. The users are never asked. A tile that cannot show a picture says why (not in Axis, offline, in a remote session, agent update needed); a wall never interrupts a remote desktop session. New Axis role Teacher (Hub product role teacher): only the screen wall, for the rooms granted to them on Matrix's Room rights page, without the session bar; a right withdrawn in Matrix ends their screens within about 15 seconds. Needs agents that announce remote_wall (they update themselves) and MATRIX_AXIS_TOKEN (compose: RMM_MATRIX_INTERNAL_URL, RMM_MATRIX_AXIS_TOKEN, Matrix's MATRIX_INTERNAL_ADDR :8086). API: GET /tenants/{tenantID}/screen-wall/rooms, POST /tenants/{tenantID}/screen-wall/open, permission screens:view, codes remote_busy, remote_wall_unsupported, screen_wall_unavailable; remote_desktop job field profile; Axis migration 0017_screen_wall, Hub 0012_axis_teacher ([Screen wall], [Roles]).

  • Changing your e-mail: the Hub's Account page has an E-mail card: enter the new address and your current password; the change applies when the link sent to the new address is opened (24 hours, newest link only), the old address is told, and you are signed out everywhere. API: POST /me/email, POST /auth/email/confirm; migration 0010_email_changes ([Your account]).

  • Entrosity Matrix, allowed sites: the new Allowed sites page lists the domains a room's computers can still reach while its internet is off: a list for all rooms of a firewall and, optionally, each room's own list. Domains only (example.com or *.example.com, at most 200 per list; a pasted link is saved as its host, international names in punycode), no paths and no SSL inspection. Tenant admins edit every list, operators the lists of the rooms granted to them, viewers only look. Save sends the whole list to the connector, which checks the list's version, asks Matrix to authorize each write, creates FQDN address objects matrix-site:<domain>, sets the members of the list's FortiGate address group (Matrix allowed sites, Matrix allowed sites <room>) and confirms by reading it back; an unconfirmed save is never repeated automatically (Check / retry). Entries Matrix did not create stay read-only. Matrix never creates policies: each list's group and ACCEPT policy are set up once from the CLI that FortiGate setup shows (tenant admins). Needs the firewall's new Allow editing allowed sites (off by default), matrix-connector guard set <id> --site-writes on on the connector computer and an updated connector (older ones keep working without allowed sites: connector_outdated). Room cards show N allowed sites; changes are in the history as Allowed sites. API: GET/PUT /tenants/{tenantID}/firewalls/{firewallID}/allowed-sites[/{list}], GET …/allowed-sites/setup-cli, permission allowed_sites:manage, event matrix.sites, job matrix.sites.set, codes connector_outdated, sites_not_set_up, invalid_domain; migration 0003_allowed_sites ([Allowed sites], [Setting up the FortiGate], [Matrix API]).

  • Axis deployment targets: Choose computers in the targets step of a deployment (and of Run script) opens a picker: search computers by name, user, IP address or serial number, tick them or Select all shown, or tick one or more Active Directory OUs to add all their computers and then untick single computers, and remove chosen ones; before, specific devices could only be chosen from the device list ([Deployments]).

  • Back button: every app (Hub, Axis, Edge, Sphere) has a Back button in the header that returns to the previous screen; it is hidden on the first screen opened in the app.

  • Coloured statuses everywhere: statuses in every app use one colour scheme from @entrosity/ui (green online, connected, active or done; blue in progress; amber pending or unknown; red offline, refused or failed; grey idle). In Sphere, an NVR's Status and Wanted are green when online or connected; an offline Axis device's dot is red ([Conventions]).

  • Entrosity Sphere administration: the global admin Overview now matches Axis: NVRs online and failing, cameras, connectors online, unacknowledged alarms of the last 24 hours, live streams and users, and a table of the same counters per tenant. The new Connector releases page lists every stored connector installer with a Download button (a fresh one-hour link per click), marks the release connectors are updated to, counts the connectors by version and shows the release public key ([Connectors → Connector releases]).

  • Axis release downloads: Download on Agent releases saves a release's MSI as rmm-<component>-<version>.msi (global admins; drafts once uploaded; audited as release.download) ([Agent releases]).

  • Entrosity Edge, disabling controllers: Disable controller on a controller's page (tenant admins, after a confirmation) makes Edge and its connector leave the controller alone, for example while the vendor's old software manages it or while it is serviced: its open configuration jobs are cancelled, the connector stops polling, configuring and setting its clock (edge.controller.remove), card and access changes are not sent to it, its events are not collected, and whatever the connector still reports about it is ignored. It shows Disabled in the list and in the page header, is not counted on the dashboard, and Open door, Test connection and Send configuration again are hidden (the API refuses them with controller_disabled, 409); its settings can still be edited. Enable controller sends its complete configuration again. Audited as controller.update. API: enabled on controllers and in PATCH /tenants/{tenantID}/controllers/{controllerID}, sync status disabled; migration 0005_controller_enabled ([Disable a controller]).

  • Entrosity Edge, editing controllers: Edit controller on a controller's page (tenant admins) changes its name, site, connection (TCP/IP or RS-485), address, bus address and doors layout; only what changed is sent, and the PIN keeps its own dialog. Changing Doors rebuilds the doors and readers as adding the controller with that mode does: door 1 stays with its id, name and access groups, door 2 is added (with the Wiegand 2 reader as its entry reader) or removed (the reader becomes door 1's exit reader), and the new layout is sent to the controller right away. Door 2 cannot be removed while access groups use it: door_in_use (409) names the groups. Readers can now be named on the controller's page. API: door_mode in PATCH /tenants/{tenantID}/controllers/{controllerID} ([Edit a controller], [Change the doors layout]).

  • Entrosity Edge, card numbers as printed: Wiegand 26 cards are entered and shown by the 10-digit number printed on them, e.g. 0015592682 (facility code 237, card number 60650): Register card takes Card number by default, with Facility code + number as the alternative, and cards, the cardholder's page, the live monitor, the history and the dashboard show the printed number with facility code:card number beside it. Card search matches the printed number with or without its leading zeros. The API is unchanged (facility_code and card_number) ([Wiegand 26 card numbers]).

  • Entrosity Edge connector development builds and self-update: every change to the connector publishes its MSI and EXE as the rolling pre-release dev of entrosity-edge-connector (versions 0.0.<build>-dev.<commit>, each upgrading the previous; the first tagged release upgrades them). CI also publishes every build to Edge (tagged releases to the stable channel, pre-releases and dev builds to beta), which signs it and offers it to the connectors: connectors update themselves, verify the release signature, install the new MSI from a scheduled task and roll back to the previous version if it does not start. Each connector follows the Stable channel by default; tenant admins switch it to Beta (development builds) in the new Updates column of Connectors, which also shows Updating to X while an update is on its way. The MSI now also installs over a newer version (for the rollback). Needs EDGE_MASTER_KEY, EDGE_RELEASE_SIGNING_KEY (edge-server release-keygen) and EDGE_RELEASE_TOKEN on the server, and RELEASE_PUBLIC_KEY, EDGE_RELEASE_TOKEN and EDGE_RELEASE_API in the connector's repository. Connectors installed before self-update must be upgraded by hand once ([Connectors → Updates], [Running Entrosity Edge]).

  • Products in beta: a platform admin can put a product in beta (Administration → Overview → Products): only platform admins see and open it, grayed out with a Beta badge in the launcher; organizations and their users neither see it nor get its sign-in tokens until it is released. Entrosity Edge starts in beta ([Products in beta]).

  • Entrosity Edge: a new product for cloud-managed physical access control with TRAcK ACCESS TrackBase002 controllers, at /edge on the Hub's host, with sign-in on Entrosity Hub (product edge, roles Tenant admin, Operator and Viewer). Cardholders with validity periods, Wiegand 26-bit cards and iButton keys, weekly schedules with holidays, and access groups; each controller receives its complete configuration (up to 2,000 cards) and shows whether it is in sync; a live monitor and searchable history of every door event; remote door opening. An on-site Windows connector (edge-connector.msi) links the controllers to Edge, keeps working through outages and delivers every event exactly once. The connector's trackbase002 driver speaks the Track Access controller protocol over TCP (see below); a built-in simulator runs complete trials without hardware. English and Bulgarian ([Entrosity Edge], [Edge API reference]).

  • Entrosity Edge, TrackBase002 driver: the connector configures TrackBase002 controllers over TCP (cards, up to 16 time zones, applied as differences; Send configuration again rewrites everything), reads their event log, keeps their clock within 2 seconds and opens doors remotely. Each controller's PIN is set in Edge (Controller PIN when adding it, or Set PIN/Change PIN/Clear PIN on its page), stored encrypted with EDGE_MASTER_KEY, never shown again, and sent to the connector only with its jobs; the connector no longer reads trackbase.json (a file left on disk is ignored, and uninstall still removes it). Controller discovery has no PINs: found controllers are listed without their firmware until they are added with a PIN. Limits: TCP only, no holidays on the controller, one set of schedules per card, 24-bit card numbers, door states not reported yet. The protocol has not been verified against a live controller yet: verify on one controller before moving a site over ([Controllers], [Edge connector]).

  • Entrosity Sphere: a new product for video management of Dahua NVRs (and OEM NVRs on the same firmware) in the browser, at /sphere on the Hub's host, with sign-in on Entrosity Hub (product sphere, roles Tenant admin, Operator and Viewer). Live camera grids of 1, 4, 9 or 16 tiles and a single camera view over WebRTC with snapshots and full screen, PTZ with presets, recording search and playback on a timeline, the NVRs' alarms live with acknowledgement, saved views (personal or shared), and connecting to and disconnecting from NVRs. An on-site Windows connector (sphere-connector.msi) reaches NVRs that are not on the Internet: it connects out to Sphere (TCP 443 to the Hub's host), publishes a camera's stream to Sphere's media server (TCP 8322 to media.entrosity.com, a DNS-only host name, since the Hub's host is behind Cloudflare) only while someone watches it (once, whatever the number of viewers), and delivers every alarm exactly once. NVR passwords are stored encrypted and never shown again. Sphere starts in beta (platform admins only) and needs the DNS-only media host (SPHERE_MEDIA_DOMAIN) and ports 8322/tcp and 8189/udp+tcp on the server; a built-in simulator runs trials without an NVR. English and Bulgarian ([Entrosity Sphere], [Live view], [Playback], [Alarms], [NVRs], [Running Entrosity Sphere], [Sphere API reference]).

  • Entrosity Sphere, grid splits and PTZ in the grid: Layout in live view opens a picker with a drawing of each SmartPSS split: 1, 2×2, one big + 5, one big + 7, 3×3, 4×4, 5×5, 6×6 and 8×8 (up to 64 tiles). Saved views store any of them (API layout 1, 4, 6, 8, 9, 16, 25, 36 or 64, up to 64 cells; migration 0003_view_splits). Tiles of 5×5 and larger splits drop the NVR name and keep only Show only this camera and Close; empty ones show a +. Operators and tenant admins get a collapsible PTZ panel under the camera list that moves the camera of the selected (or expanded) tile, with Show PTZ controls for cameras the NVR does not report as PTZ ([Live view], [PTZ cameras]).

  • Entrosity Sphere, playback of several cameras: tick up to 64 cameras in the searchable camera tree; they play side by side, in sync, from the moment clicked on a shared timeline (a row per camera, six visible, the rest scroll; 24 h or 1 h zoom). A camera that did not record at that moment says so; when none did, playback starts at the next recording of any of them. Playback uses the same splits, growing to hold the selection ([Playback]).

  • Entrosity Sphere connector download and self-update: Download connector on Connectors (tenant admins) downloads the newest connector installer through a link valid one hour, and new enrollment tokens link it too. CI uploads every connector build to Sphere (tags to stable, -suffix tags and every push to main to dev), which signs it and keeps the newest ten (migration 0004_connector_releases); connectors built with the release key update themselves to the newest release of SPHERE_CONNECTOR_UPDATE_CHANNEL, verify its signature, install it from the scheduled task SphereConnectorUpdate and roll back if the new version does not start (the MSI now installs over a newer version). Version shows updating to X, or update to X in amber for connectors that must be reinstalled by hand once. Needs SPHERE_RELEASE_SIGNING_KEY and SPHERE_RELEASE_TOKEN on the server and RELEASE_PUBLIC_KEY, SPHERE_RELEASE_TOKEN and RELEASE_PUBLISH_ENABLED in entrosity-sphere-connector ([Connectors → Updates], [Running Entrosity Sphere]).

  • Entrosity Matrix: a new product for switching the internet access of computer rooms on and off through FortiGate firewall policies, at /matrix on the Hub's host, with sign-in on Entrosity Hub (product matrix, roles Tenant admin, Operator and Viewer). A room is a FortiGate IPv4 policy whose name matches a per-firewall pattern (RE2 with a (?P<room>…) group) and whose direction is exactly the configured source → destination; Matrix only ever changes such a policy's status and the /32 address objects of its address groups. Rooms as cards by building with a confirmed enable/disable, disable until… (in 45 minutes, at the end of the school day or at a chosen time; switched back on automatically, with Schedules listing upcoming and failed re-enables), bulk changes by selection or building, addresses and groups (change a computer's IP, add a computer, Check / retry of interrupted operations), per-operator room rights, and a history of every change and refusal with its steps. An on-site Windows connector (matrix-connector.msi) reaches the FortiGate's REST API on the LAN: before every write it re-reads the policy, needs a local guard accepted on its computer (matrix-connector guard accept), asks Matrix to authorize the write and confirms it by reading it back; an unconfirmed write is never repeated automatically. FortiGate API tokens are stored encrypted (MATRIX_CREDENTIALS_KEY) and never shown again; both write switches are off by default; a read-only check (also offline, matrix-connector check) warns when a later ACCEPT policy would still let a disabled room out, since a disabled rule alone does not prove that the internet is off. matrix-server import-stop-internet brings over the room rights and history of the single-school panel it replaces. Matrix starts in beta (platform admins only), is optional per host (MATRIX_ENABLED, compose profile matrix), and has a built-in simulator for trials. English and Bulgarian ([Entrosity Matrix], [Setting up the FortiGate], [Moving from the old panel], [Running Entrosity Matrix], [Matrix API reference]).

  • Bulgarian interface: Entrosity Hub and Axis are translated into Bulgarian. English stays the default; the language button in the header (and on the Hub's sign-in pages) switches between English and Български, and the choice is shared by the Hub and Axis in the browser. Dates, numbers and known server errors follow the language ([Your account]).

  • Documentation: the documentation is available in Bulgarian at /docs/bg/. English stays the default; the language menu in the navbar switches between the two ([Writing docs]).

Changed​

  • Axis agent icon: rmm-agent.exe, its windows (taskbar and Alt+Tab) and the installer's entry in Settings › Apps show a hand-drawn Entrosity icon instead of Windows' default one. Needs agent 0.1.18-dev or later.
  • Axis agent, session bar, consent prompt and restart notice: the bar a user sees during a remote session, the prompt asking whether a technician may connect, and the scheduled restart or shutdown notice are drawn in the Ink & Strata look (paper panel with a hand-drawn ink outline, violet strata, hatch marks, ink-outlined buttons); behaviour and texts are unchanged. Needs agent 0.1.17-dev or later (0.1.15-dev for the bar and the prompt).
  • Axis screen wall, session bar off by default: for administrators, Show the session bar on the computers now starts unticked; tick it to show the bar. Technicians still always show it, teachers never do ([Screen wall]).
  • Entrosity Sphere, faster camera switching: measured in production on 2026-10-09, switching to a camera took 4.1–5.3 seconds when its stream was not running (the connector opening it on the NVR, then the wait for the camera's next keyframe, then WebRTC) and 1.6–2.8 seconds when it was. A live stream nobody watches now keeps running for 5 minutes (sub streams) or 2 minutes (main streams) instead of 30 seconds, so switching back skips the start on the NVR (SPHERE_LIVE_IDLE_GRACE, SPHERE_LIVE_IDLE_GRACE_MAIN, Go durations, at least 10s); idle streams use the site's upload while they run. At a stream limit, the idle stream unwatched the longest stops to make room instead of the viewer getting stream_limit; streams with viewers are never stopped, and playbacks still stop when their viewer leaves. A stream is live as soon as the connector's start job succeeds, the browser prepares the WebRTC connection while the camera starts, and the connector connects to the media server while the NVR answers instead of after it. A wanted stream a connector's report leaves out for 45 seconds is started again even if it was live (a connector restart) ([Live view], [Running Entrosity Sphere]).
  • Entrosity Matrix, teachers see only their rooms: teachers no longer see the rooms not granted to them (they were shown View only): the rooms, address groups, allowed-sites lists (the list for all rooms stays visible, read only), re-enable schedules, history and live updates hold only their rooms, and firewalls without any of their rooms are left out; the server never sends the others. A teacher without rooms sees No rooms are assigned to you yet. A teacher's change of a room not granted to them now answers 404 unknown_room (was 403 room_not_granted), like a room that does not exist, and is still recorded as refused. Viewers, tenant admins and global admins are unchanged ([Roles and permissions], [Room rights]).
  • New look, "Ink & Strata": the website, Entrosity Hub, Axis, Edge, Matrix, Sphere and this documentation take on the hand-drawn look of the brand artwork: grey paper, wobbly ink outlines with irregular corners and hard ink shadows, violet wave strata sweeping in from the corners, # and //// doodles and an underlined wordmark; the sign-in pages are framed like the artwork, and the dark theme becomes an ink-on-violet night. The font is Geist. Nothing moves or changes behaviour. Shipped in @entrosity/ui 0.6.0 (tokens --ink, --shadow-ink, --wave-1 … --wave-6, --glare; classes sketch, sketch-field, sketch-edge-*, sketch-underline, hatch-*, paper; components SketchWaves, HatchMark, SketchFrame, SketchDefs) ([Conventions]).
  • Axis, teachers get no e-mail: alert e-mails (immediate, roll-ups, digests, quiet-hours summaries and resolutions) are never sent to members with the Teacher role, whatever their saved preferences; teachers no longer reach My notifications ([Alerts]).
  • Entrosity Matrix: the role Operator is now called Teacher (Hub product role teacher), with the same rights: it switches the rooms granted to it on Room rights. Existing operators become teachers (Hub migration 0011_matrix_teacher, Matrix 0005_teacher_role); Matrix still accepts the old id while it catches up. Error code not_operator is now not_teacher ([Roles and permissions]).
  • Repositories: the entrosity/RMM monorepo is split into separate repositories with their history: entrosity-shared-go, entrosity-ui, entrosity-axis.backend, entrosity-axis.frontend, entrosity-axis-agent, entrosity-axis-connector, entrosity-hub.backend, entrosity-hub.frontend, entrosity-infra and entrosity-docs ([Repositories]). @entrosity/ui is published to GitHub Packages and the apps are bumped automatically on each release.
  • Production: since 2026-09-26 production is deployed from the split repositories: every new main image of a backend, a frontend or the documentation triggers the deploy workflow of entrosity-infra, which ships the images axis-backend, hub-backend and entrosity-web (formerly rmm-backend, platform-backend and rmm-web) to the host, now in /opt/entrosity/deploy ([Continuous deployment]). The monorepo's deploy and release workflows are disabled; agent and connector builds are published to production by entrosity-axis-agent and entrosity-axis-connector (a dev build on every push to main), and dev builds are now versioned 0.1.<run>-dev.<sha> so they sort above the monorepo's 0.0.x dev builds.
  • Production host: production moved to a new server (93.123.16.96) on 2026-09-27, which it shares with the website, the helpdesk and Vaultwarden: nginx keeps ports 80/443 and routes the Entrosity host names to Caddy by SNI; the new WEB_HTTP_PORT and WEB_HTTPS_PORT publish Caddy on loopback ([TLS → Behind an existing nginx]). DNS-only records (files.manage, media) now point at the new address.
  • Database: production runs PostgreSQL 18 (was 16), moved with a full dump and restore; the PostgreSQL 16 data is kept for rollback.
  • Axis device list: long site names in the Site column are shortened with an ellipsis so the table stays narrow; hover over one to see the full name ([Devices]).
  • Entrosity Sphere limits: each NVR serves 4 playbacks at once by default (was 2, SPHERE_MAX_PLAYBACKS_PER_NVR), and a connector publishes up to 64 streams, enough for an 8×8 grid (was 32: SPHERE_MAX_STREAMS_PER_CONNECTOR on the server, SPHERE_CONNECTOR_MAX_STREAMS on the connector). A playback stops as soon as its viewer leaves instead of after 30 seconds, and closing or reloading the tab ends the viewer's streams at once ([Limits]).

Removed​

  • Entrosity Matrix, Entrosity services: Matrix no longer keeps a built-in Matrix Entrosity services group: while a room's internet is off, only the sites teachers and admins list are reachable. The Entrosity Axis agents of such a room lose their connection until its internet is on again, unless an admin adds the needed domains to a list (advised against for the Hub and Axis management, which are behind Cloudflare: allowing them opens Cloudflare's shared addresses). The Allowed sites page no longer shows the locked Entrosity services list, the firewall settings no longer have Keep Entrosity services reachable, the setup CLI no longer adds the group to the shared policy, and MATRIX_SERVICE_DOMAINS is no longer read. The connector refuses matrix.services.sync as an unsupported job, no longer reports the group and drops guard set --service-writes (guard files with allow_service_writes keep working); the server refuses a leftover sync (services_removed). Old Entrosity services entries stay in the history, shown as Entrosity services (removed). Migration 0004_remove_entrosity_services drops services_enabled, deletes open sync jobs and cancels their pending changes. On FortiGates set up earlier an admin can remove the empty group ([Allowed sites], [Removing the old Entrosity services group], [Running Entrosity Matrix]).

Fixed​

  • Axis dropdowns: every dropdown now looks like the text fields (same border, background and focus ring); the device filter's Site, Source and Active Directory OU were black boxes whose focus ring spilled out of the panel. Shared as @entrosity/ui/native-select.
  • Entrosity Hub launcher: the product cards in a row line up again: the role, organization and Open rows sit at the bottom of every card instead of moving down with a longer description or the beta note.
  • Entrosity Edge connector, TrackBase002 clocks: a controller whose clock came back invalid was left with a meaningless clock: the connector read the clock back after setting it with a reply that carries no clock (0x27, all zeros on live controllers), so it took every layout for rejected, wrote the last one tried (BCD) and gave up. It now reads the clock back with a status read, leaves the protocol's layout on the controller when none reads back valid, and tries again every 10 minutes instead of giving up ([Edge connector]).
  • Entrosity Hub: the browser tab showed an old icon (the portal's early lightning-bolt logo, kept in browsers' favicon cache) instead of the Entrosity mark that Axis shows. The Hub's icon is now served under a new address, so every browser picks up the Entrosity mark.
  • Entrosity Hub: in Administration → Users, a user's Access card showed the product role dropdowns without saying which product each one was for. Each role now has its product's name next to it ([Platform administration]).

[1.0.0] – unreleased​

This is the first production release. It adds production hardening on top of the feature set of phases 0–5.

Features (phases 0–5)​

  • Identity: multi-tenant portal with global admins, tenant admins, technicians and viewers. Sign-in with argon2id passwords, TOTP and recovery codes, rotating refresh cookies, invitations, password reset, and an audit log.
  • Devices: Windows agent (Go service) with enrollment tokens, WebSocket connection with an HTTPS polling fallback, and full inventory (hardware, software, updates, services, users, network). Live device list, filters, metrics and device actions: inventory, reboot, shutdown, uninstall from inventory, wake-on-LAN, merge.
  • Active Directory: a site connector per site does LDAP(S) sync of AD computers, pushes the agent over WinRM or SMB, and sends wake-on-LAN packets.
  • Packages and deployments: MSI, EXE, winget and PowerShell packages with detection rules and browser uploads to object storage. Deployments go to devices, filters or all devices, with schedules, maintenance windows, concurrency, retries, reboot policies and live progress.
  • Scripts: a versioned script library (PowerShell, PowerShell 7, cmd) with typed parameters and live output. Run-as-logged-on-user.
  • Alerts: rules for offline devices, disk, CPU/memory, outdated agents, failed deployments, AD sync and connectors. Global rules with tenant overrides, e-mail notifications and digests.
  • Self-update: signed (Ed25519) agent and connector releases with channels, rollout percentages and watchdog rollback.

Added in phase 6 (hardening)​

  • Row-level security (migration 0007):
    • The server runs as rmm_app, and every tenant request is scoped in PostgreSQL.
    • Composite tenant foreign keys, and an append-only audit log.
  • An authorization fuzz test with 82 cross-tenant cases.
  • Retention:
    • A daily cleanup with defaults per category, configurable via RMM_RETENTION_*.
    • Per-tenant job and audit windows under Tenant settings.
    • Management of the metrics partitions.
  • Performance:
    • Migration 0008 adds indexes and a trigram device search.
    • Software filters use a SECURITY DEFINER function.
    • List and dashboard queries run in parallel.
    • A seed tool (cmd/seed) and an API benchmark (cmd/apibench), with baselines in docs/perf/.
  • Load handling, tested with 5,000 simulated agents and a 1,000-device deployment:
    • batched heartbeats;
    • inventory section fingerprints;
    • ingestion back-pressure;
    • enrollment token reads without row locks;
    • deadlock-safe deployment result handling.
  • Multiple replicas:
    • Sign-in limits are stored in PostgreSQL (migration 0009), so they hold across replicas.
    • Revocation and tenant suspension take effect on every replica.
  • Production deployment:
    • deploy/docker-compose.prod.yml with Caddy (automatic TLS, security headers, CSP, WebSocket/SSE-aware proxy, SPA).
    • A distroless non-root backend image, and a portal image running Caddy as uid 10001.
    • A tuned PostgreSQL with a WAL archiving hook, MinIO, a systemd unit, and .env.prod.example.
    • A healthcheck subcommand. The backend refuses to start on a mismatched schema, and migrate force fixes a dirty migration.
  • Monitoring:
    • Prometheus metrics on an internal listener (RMM_METRICS_ADDR, plus pprof).
    • A Grafana dashboard, 7 alert rules, and deploy/docker-compose.monitoring.yml.
  • Operations:
    • deploy/scripts/backup.sh, restore.sh and upgrade.sh (rolling restart), with a restore drill.
    • rmm-server rotate-master-key, and RMM_JWT_SECRET_OLD for JWT secret rotation.
    • RMM_MIN_AGENT_VERSION forces updates regardless of rollout.
  • Security:
    • POST /auth/sse-token issues short-lived, tenant-bound event stream tokens.
    • Log redaction, and a WebSocket origin check test.
    • A security.yml workflow (govulncheck, gosec, pnpm audit, gitleaks, Trivy, SBOM) and Dependabot.
    • SECURITY.md and docs/security-review.md.
    • Release images are scanned, pushed to ghcr.io and published with SPDX SBOMs.
  • Documentation:
    • A documentation site (Docusaurus, website/), served at /docs/ by the portal's Caddy: user guide, administration, operations, reference, development, and the API reference rendered from backend/api/openapi.yaml.
    • docs/operations.md and docs/user-guide.md.
    • Rendered API reference in docs/api/index.html (make api-docs).
    • Refreshed architecture, data model, protocol and development guides.

Added after phase 6​

  • Configurable Hub sign-in limits. PLATFORM_AUTH_RATE_PER_MINUTE (default 10 per client IP) and PLATFORM_EMAIL_RATE_PER_MINUTE (default 5 per e-mail address) set how many sign-in and other public auth attempts the Hub accepts per minute.
  • Grouped offline e-mails and more notification controls. Devices going offline are no longer mailed one by one: one e-mail per batch (5, 15, 30 or 60 minutes) lists the devices that went offline and every device currently offline, and digests include the same sections. New settings under My notifications: digest frequency (15 minutes, hourly, daily at an hour), per-type e-mail switches, e-mails when alerts resolve, and quiet hours with an option to still send critical alerts (held e-mails arrive as one summary afterwards). Migration 0014.
  • Dark theme. Axis and Entrosity Hub follow the light or dark setting of the device they are opened on; a theme button (System, Light, Dark) in the header overrides it for this browser.
  • Device sign-ins. The agent reports every Windows sign-in (console and Remote Desktop, with the client) and sign-out; the device's Sign-ins tab lists them. Entries are deleted 7 days after the sign-out. Migration 0015.
  • CPU temperature. Heartbeats carry the hottest thermal zone (where the hardware reports one), the Metrics tab charts it, and a new alert type CPU temperature comes with a global rule (above 90 °C for 10 minutes). Migration 0016.
  • Remote desktop. A Remote desktop button on an online Windows device opens its screen in a new browser tab, to watch (View only) or to use its mouse and keyboard (Control), optionally after the signed-in user accepts a prompt. Built in, with nothing to install on the technician's side: the agent captures the screen with a helper in the console session (sign-in screen, lock screen and UAC prompts included) and sends changed tiles as JPEG, and the backend relays the stream over HTTPS, across replicas too. Multiple monitors, picture quality, Ctrl+Alt+Del and Type text; one session per device, at most 8 hours. While a session runs, the user sees a small bar naming the technician with an End session button. Sessions end when access changes (device decommissioned, agent revoked, tenant suspended, technician disabled or demoted), and a viewer link opened outside the portal's dialog asks before connecting. Only tenant and global admins (devices:remote_unattended) can start a session without the consent prompt or without the session bar (migration 0011); for technicians both are always on.
  • Branded restart and shutdown notices. Reboots and shutdowns (device actions and package reboot policies) show every signed-in user a notice in the Entrosity Axis design with the logo, the administrator's message, a countdown and Dismiss, instead of the plain msg.exe box (which was missing on Home editions). The consent prompt, session bar and notices share one UI kit in the agent. New permission devices:remote (technicians and admins), audit entries remote.session_request, remote.session_start and remote.session_end, migration 0010 (remote_sessions) and the RMM_NODE_URL setting. This replaces the RustDesk integration of the development builds; the RMM_RUSTDESK_* settings are gone.
  • Devices by OU: the device list has an Active Directory OU tree beside it (domain, OUs and containers with computer counts). Selecting an OU lists the devices in it and below it; Not in Active Directory lists agent-only devices. The Filters OU picker shows the same tree. The tree can be hidden and shown again (remembered per browser).
  • Delete enrollment tokens: global admins can delete any enrollment token permanently (POST /tenants/{id}/enrollment-tokens/{tokenId}/delete), confirming with their own password (422 on a wrong one, rate-limited like sign-in). Tenant admins can still only revoke. The deletion is audited; enrolled agents and connectors are not affected. A deleted AD push token is replaced before the next push is sent.
  • AD computers: the page is in everyone's sidebar and shows agent coverage for the selected OU: how many computers run the agent, how many are being pushed, failed or never pushed, and why pushes failed (error codes with counts, causes and fixes). Every figure is a filter. It has the same OU tree as Devices. The list shows the agent and push columns first, with the reason for a failure. New: GET /tenants/{id}/ad-computers/summary, and the push and push_error_code filters on GET /ad-computers.
  • The ou filter of GET /ad-computers now includes sub-OUs and ignores case, like the device list's.
  • Sidebar: global admins always see a tenant's menu (the tenant they are in, else the one they opened last), and the sidebar stays in place while the page scrolls, scrolling on its own when it is taller than the window.
  • Agent push over SMB first: the Windows connector now installs over \\host\ADMIN$ and a temporary service (as PsExec and PDQ Deploy do) and uses WinRM only when port 445 is closed or the share is not usable. It follows the installer log, so a failed install reports its exit code, what it means and the last log lines (tokens redacted) within seconds, and it cleans up the MSI, the log and leftover push services.
  • A failed push now names its channel and step (for example open the service manager: access denied), and the AD computers list shows that message under the error, above the suggested fix.

Fixed after phase 6​

  • Live pages (devices, alerts, deployments, dashboard) could miss a change that happened while their live connection was opening, e.g. devices that enrolled right after the page loaded stayed hidden until the next change; the portal now refetches its live data whenever the connection opens.
  • The Windows agent's inventory came back mostly empty (no last boot, logged-on user, IP or MAC addresses, disks, network, services, updates or local users): its PowerShell scripts were fed through stdin, where statements spanning several lines are silently skipped. They are now passed with -EncodedCommand, and a script without output is reported as a collector error.
  • SMB pushes failed with open the service manager: access denied although the push account was an administrator: the service manager was opened as the connector's own account (the domain controller's computer account). The SMB steps now run as the push account.
  • The site connector MSI never created its Windows service (two service entries with the same name), so every install rolled back after enrolling. It now installs one service, run as LocalSystem or the given domain account, and sets restart-on-failure with sc.exe.
  • Job results containing a NUL byte (for example an LDAP error from the connector) were rejected by PostgreSQL and lost, so Test connection reported "the connector did not answer in time". NUL bytes are now removed before the result is stored.
  • Pushing the agent over WinRM failed on most computers with copy_failed ("copy exited 0", "checksum does not match") or a bogus auth_failed ("invalid content type"): the MSI was sent on stdin while the WinRM client polled for output, which corrupted the encrypted HTTP stream. Commands now run one at a time in one shell, and the MSI is sent in chunks and decoded with certutil.
  • A push downloaded the "latest" agent MSI but checked it against a hash computed earlier, so publishing a release during a push failed it with a checksum mismatch. Pushes now use the newest stable release's versioned MSI and recorded hash. Downloads are retried (4 attempts), and a DNS failure on the connector server names the host it cannot resolve.
  • The agent MSI's verbose log showed the enrollment token in the enroll command line; the property is now hidden.

Security fixes (found in the phase 6 review)​

  • High: a technician's silent_args for uninstall from inventory ran through cmd.exe as SYSTEM, which allowed command injection. Only plain switches are accepted now.
  • High: a package uninstall by name could pick a per-user (HKCU) uninstall entry, which any Windows user can write, and run it as SYSTEM. Only machine-wide entries are used now.
  • Medium: string parameters of cmd scripts could inject commands through %RMM_PARAM_*% expansion. cmd metacharacters are refused now.
  • Medium: a revoked agent key stayed usable for up to 60 s on other replicas, and suspended tenants kept their open agent and connector connections.
  • Medium: the agent MSI pushed by the connector was not hash-verified.
  • Low:
    • Uploads are always stored as application/octet-stream.
    • Rejected uploads are deleted.
    • The files host sends a sandbox CSP.
    • CORS origins are validated.
    • Presigned URL signatures are kept out of agent and connector errors.
    • Undeliverable job payloads are no longer logged.
    • The development example secrets are refused in production.
  • The EventSource access token no longer appears in URLs (stream tokens).
  • Dependencies:
    • Go toolchain pinned to go1.26.8 (standard library fixes).
    • moby/go-archive upgraded to 0.3.0.
    • Caddy rebuilt with current dependencies.

Changed​

  • Axis no longer has pages for what Entrosity Hub manages: Tenants and Global admins (Admin), Users (tenant settings) and Account are gone; the menu under your name links to Manage account on Entrosity Hub.
  • Entrosity Hub's product launcher greets you, shows your organization at the top and one card per product: the Entrosity mark, the product's name (product part in gray) and what it does, Access granted (or Suspended), your role and the organization. The whole card opens the product.
  • Entrosity Axis has an Entrosity Hub button in its header that takes you back to the Hub's product launcher.
  • Charts use the shadcn/ui chart component: the dashboard's operating systems donut, a device's CPU, memory and disk chart and its CPU temperature chart, and the agent and connector adoption charts. Their tooltips and legends follow the theme (light and dark) and show units.
  • Everyone signs in on Entrosity Hub; Axis has no local sign-in any more (migration 0013). Axis's passwords, sessions, two-factor secrets, invitations, password resets and sign-in counters are gone: its users table is a copy of the Hub's users, and roles are in tenant_memberships. Users, invitations, tenants (the Hub's organizations) and roles are managed on the Hub; Axis's Users, Global admins and Tenants pages are read-only lists with links to the Hub, and Account links to the Hub's account page. Old sign-in, password reset and invitation links forward to the Hub. Axis accepts only the Hub's product tokens; RMM_PLATFORM_URL, RMM_PLATFORM_INTERNAL_URL and RMM_PLATFORM_TOKEN are required and RMM_AUTH_MODE is gone. Removed API: /auth/config, /auth/login, /auth/refresh, /auth/logout, /auth/password/*, /auth/totp*, /invitations/*, creating and deleting tenants, and creating, changing and deleting users and invitations; tenant PATCH takes only settings. Deleting an enrollment token always takes a Hub step_up_token. User/Me lost totp_enabled and last_login_at, and the tenant dashboard pending_invitations. The error code auth_moved and Axis's sign-in error codes no longer exist. rmm-server bootstrap-admin and make bootstrap-admin are removed: the first admin is created on the Hub (platform-server bootstrap-admin, make bootstrap-platform-admin). Rolling back past migration 0013 means restoring a backup taken before it.
  • Axis moved from /manage to /axis: the portal is at https://<PLATFORM_DOMAIN>/axis/ and its API at /axis/api/v1; new agents and connectors enroll with https://<PLATFORM_DOMAIN>/axis (the Hub's migration 0002 points the launcher there). Old /manage links redirect; agents and connectors enrolled under /manage keep working.
  • Entrosity Hub moved to hub.entrosity.com, with Axis at https://hub.entrosity.com/axis and the documentation at https://hub.entrosity.com/docs/. portal.entrosity.com and manage.entrosity.com redirect browsers and keep serving the API for agents and connectors enrolled against them. Everyone signs in once more after the move.
  • The Hub always runs in the production stack. PLATFORM_DOMAIN and the PLATFORM_* secrets are required; COMPOSE_PROFILES=platform and RMM_EDGE are no longer used, and RMM_PUBLIC_URL, RMM_PORTAL_URL and RMM_CORS_ORIGINS default to the Hub's host. Caddy always serves the Hub's host, the old Axis host (RMM_DOMAIN) and the new optional PLATFORM_OLD_DOMAINS (earlier Hub host names). The cutover script and workflow have one stage left, move <domain>, which moves the Hub to another host name (and status).
  • make e2e runs the Axis and Hub Playwright suites against one stack (make e2e-hub is gone); the connector smoke test, the Windows nightly test, cmd/seed, cmd/apibench and the k6 script sign in on the Hub.
  • The product is now called Entrosity Axis (formerly Entrosity RMM). The new name appears in the portal (header, page title, sign-in and invitation pages), e-mails (subjects, body and footer), the documentation site, the API reference, the site connector's Windows service display name and Add/Remove Programs entry, and the Grafana dashboard. New two-factor enrollments show Entrosity Axis as the issuer in authenticator apps; existing entries still say RMM and keep working because the secrets do not change. Technical names are intentionally unchanged so existing installations, agents and scripts keep working: RMM_* settings, rmm-agent.exe / rmm-connector.exe, the RMMAgent / RMMConnector services, %ProgramData%\RMM paths, X-RMM-* headers, cookie names, metric names, the rmm database and roles, image and compose service names, MSI upgrade codes, Go module paths and the /docs/ URL.
  • A user can belong to several tenants, with a different role in each (migration 0012, tenant_memberships). Permissions follow the role in the tenant being used; users of several tenants choose one after signing in and switch in the sidebar. GET /me returns is_global_admin and memberships instead of role and tenant_id, and sign-in responses carry the same user. Alert e-mail preferences are per tenant: /me/notification-prefs moved to /tenants/{id}/me/notification-prefs. The tenant setting "require two-factor authentication" was removed (it was never enforced; Entrosity Hub enforces it per organization). New GET /auth/config reports the sign-in mode.
  • Sign-in on Entrosity Hub (prepared, off by default): with RMM_AUTH_MODE=platform Axis accepts the platform's product tokens (RMM_PLATFORM_URL, RMM_PLATFORM_INTERNAL_URL, RMM_PLATFORM_TOKEN), keeps a copy of the platform's users, organizations and roles (pulled every RMM_PLATFORM_SYNC_INTERVAL; metric rmm_platform_sync_age_seconds, alert RMMPlatformSyncStale), refuses sessions the platform ended, and answers the local sign-in and user-management endpoints with 410 auth_moved. Deleting an enrollment token then takes a platform step-up token (step_up_token) instead of the password. The portal detects the mode (GET /auth/config): it then signs in and out on the Hub, renews its product token from the Hub's session, follows sign-outs in other tabs, and shows users, global admins, tenant names and account security read-only with links to the Hub. Local sign-in stays the default until the cutover.
  • Entrosity Hub in the production stack (off until enabled): COMPOSE_PROFILES=platform runs the Hub's database, migration and service; backups, restores and upgrades include it; the deploy and release workflows build the platform-backend image. Caddy's sites are chosen with RMM_EDGE (rmm, hub, hub-only), which makes the move to portal.entrosity.com a settings change ([runbook]). New deploy/.env.prod.example; RMM_PUBLIC_URL, RMM_PORTAL_URL and RMM_CORS_ORIGINS can now be set in deploy/.env.
  • The portal moved to /manage (step one of moving to the Entrosity Platform at portal.entrosity.com): the portal is served at https://<domain>/manage/ and its API, for the browser, at /manage/api/v1. The site root and old portal addresses (bookmarks, e-mailed invitation and reset links) redirect there. /api/* keeps working for enrolled agents, connectors and scripts. RMM_PORTAL_URL now includes the path (https://<domain>/manage); it also scopes the refresh cookie, so everyone signs in once more after the upgrade.
  • GET /tenants/{id}/events accepts ?sse_token= from POST /auth/sse-token. ?access_token= is no longer accepted. The portal handles this itself; custom clients must switch.
  • The public /metrics route was removed. Metrics are served on RMM_METRICS_ADDR only.
  • Presigned upload targets always ask for Content-Type: application/octet-stream.