Skip to content

Releases: TheTractorHacker/RivetMSP

RivetMSP 26.10.23: the Remote button opens the remote screen in a new tab

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 14 Oct 17:42

Deploy notes: back up the database, pull this tag, run the database update (Administration > Updates or php scripts/update_cli.php --update_db). RivetCore is now 1.0.0-rc.17 (the tracked vendor/rivet/rivet-core is the tag's git archive, so no composer install is needed). DB 2.6.94: Core migration 0026_rmm_mesh_hide adds two columns to endpoint_agent_mesh_settings (mesh_hide default 31, mesh_hide_tools empty); nothing else is touched. No cron change.

  • No MeshCentral screens around the session. The Remote button used to land on MeshCentral's own interface (header, device list, tabs). The URL now carries MeshCentral's hide parameter, as Tactical RMM's "Take control" does: the default hide=31 hides the masthead, top bar, footer, tab titles and the left bar with the device list, so only the device view is left. From MeshCentral's page source: bit 1 masthead, 2 top bar, 4 footer, 8 titles, 16 left bar, 32 back button.
  • It opens in a NEW tab, reliably. Browsers block a tab opened after an asynchronous request, so the tab could silently fail to open. The click now opens a blank tab immediately and sends it to the session when the launch call answers. If the launch fails the blank tab is closed and the reason is shown; if the browser refuses the tab an Open remote session link appears instead. A tab from an earlier launch is never reused (each launch has its own login token). Same for the Remote tab (Desktop, Terminal, Files), the legacy device page and the Tactical asset page.
  • Settings. Administration > Endpoint agent > MeshCentral: Hide MeshCentral interface elements (0 to 255, default 31; 31 = only the remote screen, 0 = the full MeshCentral and the old URL) and an optional value for Terminal and Files (empty = same; 0 allowed). Only shown once the database update has run.
  • MeshCentral does not connect by itself. From its source, nothing in the URL starts the connection: the new tab shows the Desktop view with MeshCentral's Connect button; click it once. The card says so.
  • Not verified on a live MeshCentral. The hide bits come from MeshCentral's page source (default.handlebars); the smoke runs against a mock Mesh server. See RivetCore's docs/rmm/REMOTE_AND_LOGS.md, "The Remote button", for the test to run on yours.
  • Tests: tests/rmm_ui_p68_remote.php (default hide on every view, the form, per-view values, 0 equals the old URL, refusals, a technician cannot change it, the helper is linked before the page scripts and nothing opens a tab after the async answer), new browser smoke tests/browser/rmm_rc17_remote_smoke.mjs (10 checks: a tab exists before the launch answers, the final URL has login, gotonode, viewmode=11 and hide=31, the tab cannot reach its opener, every click opens its own tab, the popup-blocked link, a failed launch closes the tab, hide 0, Terminal and Files, the admin card).

RivetMSP 26.10.22

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 14 Oct 16:04

[26.10.22] RivetMSP — the Run script dialog offers the script library, and one Scripts area

The bug. On an asset page the top Run script button opened a dialog whose "What to run" list held only "PowerShell text" (and "A saved script" when the older table had rows). The RivetCore script library (the Starter pack and your own scripts) was not in it, and Linux devices had no Run script button at all.

Deploy notes: back up the database, pull this tag. No database change (DB stays 2.6.93), no new cron line, no setting. The older addresses keep working.

  • One dialog, every source. "What to run" is now: Script library (N) (when the Scripts switch is on, you may run saved scripts and the library has scripts for the device's platform), A saved script (the older list, Windows, only when it has rows), Synced from (N) (older scripts that can run through the vendor RMM the asset is linked to, only with the older scripts grant) and PowerShell (type or paste) on Windows or Shell (type or paste) on Linux, only where Core supports that free-form job for your role. A single choice is shown as text, not as a one-option dropdown. Linux devices get the button.
  • Script library source. A search box (name, tag, description), the scripts grouped by risk class (Read only, Changes the system, Disruptive, Not classified), the chosen script's description, tags and risk badge, and its parameter form built from the script's schema (typed inputs, ranges and length hints, required fields, secrets as password fields, choices). Dry run (nothing is changed) is a tick box that starts ticked for a script that changes things (and follows the script's own default for a read-only one); unticking it really sends false. A script that needs approval says so and the button reads Request approval; a destructive script needs its confirmation box. It submits through the same handler as the Run a library script card of the Jobs tab (CSRF, client scoping, Core's checks), and the answer is a message with a link to the Jobs tab (or to Approvals). Empty states explain and link the Starter pack (administrators) or Scripts.
  • One Scripts area. The Endpoints menu had both Script library and Scripts. There is now ONE Scripts entry and one page with tabs: Library (the RivetCore library, the default) and Synced from (the older list, labelled with the vendor RMM's real name; shown when older rows exist or a vendor RMM that provides scripts is on). rmm_script_library.php redirects to the Library tab with its query string, rmm_scripts.php?category= opens the synced tab, rmm_script_run.php is unchanged. The older list is responsive now.
  • Import into the library. A one-time, idempotent action for the hand-made older scripts (no vendor script id): creates library scripts through Core's API as you (platform from the stored type, the old category as a tag, the risk class you pick, default Changes the system, approval only for Disruptive, note "imported from legacy Scripts"), skips what is already there with the same name and text, copies a name clash to " (legacy)", reports CMD and empty scripts as skipped, audits it, and leaves the older rows alone ("Imported" mark). Needs write access to scripts; Core checks you again for each script.
  • Wording. "Scripts" in the menu, titles and help text.

Tests: new tests/rmm_ui_p23_d_scripts_hub.php (dialog sources by platform and role, grouping, forms, dry-run default, approvals, destructive confirmation, client scoping, switch and module off at zero queries, synced scripts, one menu entry, tabs, older addresses, import and its idempotence) and tests/browser/rmm_run_dialog_smoke.mjs (desktop and 390 px, light and dark, keyboard); the existing RMM suites and smokes are updated for the single menu entry.

RivetMSP 26.10.21 — multi-select ticket filters and a default Service Desk view

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 14 Oct 11:39

The Service Desk ticket filters took one value each (one priority, one agent, one board). Every filter is now a group of checkboxes, so a saved view can be "High + Medium priority, assigned to Alex or Bo, on boards Network and Email", and an agent or an administrator can choose the view Tickets opens with.

Deploy notes: back up the database, pull this tag, php scripts/update_cli.php --update_db (2.6.92 to 2.6.93: ONE new table, ticket_view_defaults, CREATE TABLE IF NOT EXISTS, nothing existing altered; the code works before it runs, only choosing a default needs the table). No new cron line, no nginx change, no new composer package. Existing saved views and bookmarked URLs keep working unchanged.

  • Checkbox filters, OR inside a group and AND across groups. Assigned to (me / unassigned / any agents, any combination), Priority (Low / Medium / High), Board, Category, On-site / Remote (both ticked = no filter), Due (Overdue and Due today are independent; both ticked matches either) and Status (All open / All closed / Everything, or specific statuses). Each group is a fieldset with a legend, a count badge ("Any" when nothing is ticked) and Select all / Clear; every box has a label and works from the keyboard. The Save View form and the live quick-filter panel on the Tickets page use the same groups (the toolbar buttons open a checkbox menu; closing it or pressing Apply runs the filter; "More Filters" holds Assigned to, On-site / Remote and Due).
  • Old and new URLs. assigned=me, priority=High, board=5, onsite=1, status=Closed, status=7, overdue=1 and the dashboard's assigned=0 (nobody) still work and mean the same as the one-item lists. The canonical form is assigned[]=me&assigned[]=7&priority[]=High&priority[]=Medium. A stored view of either form opens as the canonical URL, and the highlighted view in the sidebar is decided by what the filters mean, not how the URL spells them. The viewer's own id and me are the same choice. status=All (the "All Tickets" box) now really shows every ticket.
  • One validator. ticketFiltersNormalize() turns any input into integers, enum members and fixed tokens (hostile values, nested arrays and oversized values are dropped; a group keeps at most 50 values), ticketFiltersSql() builds the conditions from that, and the list, the pagination and sort links, the kanban board, the client ticket lists and the API all read it. Kanban now honours Priority, Board, On-site, Due and Tags too (it used to ignore them). ticketViewDescribe() reads naturally ("Open tickets · Assigned to Alex, Bo or me · Medium or High priority · Board: Network or Email · Overdue or Due today").
  • Default view. Add / Edit view: Make this my default view. A star beside every view in the Ticket Views list sets or clears it in one click. Opening Tickets with no filter in the URL loads the default (as a redirect to the same page with the view's filters in the address bar, keeping the list / kanban display mode). Any filter in the URL, or a view link, wins. The list offers Reset to my default (?default=1) and Show all open (?status=Open). One personal default per agent and one shared default (set by an agent with support level 3, Make shared default in the view's menu, or the extra checkbox in the form) for agents who have no personal default; the order is personal, then shared, then the old behaviour. A default that was deleted, un-shared or can no longer be used is skipped gracefully; deleting a view forgets the defaults that point at it. Setting, replacing and clearing a default is audit-logged (type "Ticket View"). A client's own ticket list never gets the default.
  • Permissions tightened on the way. The Save View and Edit View modals now require write access to the support module, and the edit modal only opens for a view the agent owns (or a shared view for support level 3); /api/v1/ticket-views requires support read access and refuses a key locked to one client. Client portal logins never reach any of it (agent pages sign them out; API tokens are agents only).
  • API. GET /api/v1/tickets takes several values for priority, onsite, assigned (me / unassigned / ids), status_id, category_id, board_id and tag_id (comma list or repeated name[]; OR inside a filter, AND between filters; mine=1 and priority=high work as before; overdue + due_today together match either). GET /api/v1/ticket-views translates a multi-value view into those comma lists (all values still strings) and marks the agent's default with is_default. api/v1/openapi.yaml and docs/API.md updated.
  • Not changed. The CSV export (an unfiltered export of all tickets, as before), the sidebar counters and the stat boxes (whole-desk numbers), and the per-ticket live stream (sse_ticket_stream.php, which follows one ticket, not a list) do not take list filters.

DB and tests: DB 2.6.93; db.sql gains ticket_view_defaults (a fresh import and the 2.6.92-to-2.6.93 update produce the same schema, 301 tables compared, no uca1400; the step is idempotent). Tests: new tests/ticket_views.php (171 checks: old / new forms and the canonical round trip, hostile and capped values, every filter combination against an independent PHP filter on 24 seeded tickets, the form validator, descriptions, default resolution / set / clear / replace / archived / un-shared / deleted fallbacks, the real Tickets page, pagination and sort links, kanban, the redirect and its escapes, handler permissions, audit lines, CSRF, the modals, the API, client portal and client-locked key refusals) and a browser smoke tests/browser/ticket_views_smoke.mjs (14 checks: two priorities and two agents by checkbox, save as default, Tickets opens with it, an explicit filter overrides, Reset to my default, Show all open, kanban, the star toggle, Select all / Clear, keyboard, phone width, dark theme). tests/wave1_integration.php and tests/endpoint_agent_module.php follow the new step.

RivetMSP 26.10.20 - the integration sync watcher stops crying wolf

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 12 Oct 17:41

Fixes the false-positive and repeating "sync stopped / sync failing" emails the Wave 1 recovery watcher produced on a live install (about twelve a day): UniFi sync has stopped: no run for 94 days (expected every 17 min) (UniFi now syncs from an external daily script that does not write unifi_sync_log; the old in-app scheduled runs were still used to infer the interval) and Sophos Firewalls: the last 3 runs all ended in an error (an external per-minute script looped over every enabled integration, including the built-in rivetit_agent type, and wrote that type's refusal, "Endpoint agent devices are managed from the device page, not through an RMM client.", against the previous integration's row). No RivetCore change: the pin stays at 1.0.0-rc.16.

Deploy notes: back up the database, pull this tag, php scripts/update_cli.php --update_db (2.6.91 to 2.6.92: ONE new table, recovery_watch_overrides, CREATE TABLE IF NOT EXISTS, nothing existing altered; the code works before it runs, only saving a mute or an expected interval needs the table). No new cron line, no nginx change, no new composer package.

  • Dormant integrations. An integration whose newest run is older than Dormant after (recovery setting sync_dormant_days, default 14 days, 0 = never) is dormant: no email, shown as "dormant since " with Mute and Forget history actions. At most ONE "no longer running" notice per 30 days, and only when its last run was good and it has just gone quiet (within a week of crossing the line): an integration that went quiet long ago, such as the 94-day UniFi case, produces nothing. A never-run integration older than the setting is dormant too.
  • Per-integration overrides (table recovery_watch_overrides: source, integration id, muted, expected interval in seconds, note, updated by/at). Admin > Backup > Integration sync watcher: Mute / Unmute, Edit (expected to run every N hours, a note, muted), Forget history. An external daily script can be declared "expected every 24 h"; a declared interval is judged at 3x and opts out of dormancy (a declared script that stops is stale). Every change is CSRF-protected and written to the audit log with who, which integration and the new values; Forget history needs its confirmation box and removes only that integration's sync-log rows and alert state.
  • Repeat cadence. The same problem is mailed at most once per 24 hours (new setting alert_repeat_hours, default 24, was a fixed 6; applies to every recovery alert, the e-mail footer shows the live value), the first occurrence is immediate, and a recovery ("sync is working again" / "running again") is announced once, only when the problem had actually been mailed. Stale and failing no longer both keep state for one integration (so no double recovery notice).
  • Failure rule. "The last N runs failed" counts only that integration's runs with a real error: a failed status with a message that is not the "Endpoint agent devices are managed from the device page" sentinel. Sentinel rows are not the integration's runs at all (they neither fail it nor keep it looking fresh). Integration types without a vendor API (rivetit_agent, SyncWatch::UNWATCHED_RMM_TYPES) are not watched.
  • Panel. A per-integration table: state (healthy / failing / stale / dormant / muted), last run (UTC), expected interval (and whether declared), the reason, actions. Names and notes are escaped; no inline handlers.
  • Docs. docs/RECOVERY_RUNBOOK.md section 2a: the watcher, and the rule for external sync scripts (write rmm_sync_log / unifi_sync_log, or declare the interval or mute; any script that copies the "sync all enabled integrations" loop must skip type rivetit_agent).
  • Other. SyncWatch::snapshot() (pure read, used by the panel) and SyncWatch::evaluate() (pure verdict); RecoveryAlerts::recovered(); RecoveryAlerts::raise() takes an optional repeat window (the dormant notice uses 30 days). New: src/Recovery/WatchOverrides.php, tests/recovery_watch.php, tests/browser/recovery_watch_smoke.mjs.

DB and tests: DB 2.6.92; db.sql gains recovery_watch_overrides (a fresh import and the 2.6.91-to-2.6.92 update produce the same schema, no uca1400; the step is idempotent). Tests: new tests/recovery_watch.php (80 checks: the exact live cases, UniFi with 94-day-old runs, Sophos with sentinel rows mixed in, a genuinely failing integration mailed once then not for 24 h, the 23 h / 24 h / 48 h boundaries, recovered notice once, a declared 24 h interval, mute, the 30-day dormant notice, forget history, the panel's rendering and escaping, the handlers with their audit lines and clamps, the session-time-zone guard) and a browser smoke tests/browser/recovery_watch_smoke.mjs (6 checks: real login, every state rendered, Edit / Mute / Unmute / Forget history through the real forms, phone width). tests/recovery_drill.php 125 (the 6 h window became 24 h, failed runs carry a message), tests/wave1_integration.php 87 and tests/endpoint_agent_module.php 87 (the 2.6.92 step and its idempotence), tests/rmm_ui.php 1755, tests/endpoint_agent_smoke.php 47, tests/rmm_installer_ui.php 141, golden replay 10, RivetCore PHPUnit 229 tests, and the security / crypto / platform / compliance / mail-intake suites all pass. Known failures, identical on 26.10.19 (verified on a clean checkout): sla_pause 2, recovery_integration 3, the strict golden replay.

RivetMSP 26.10.19 (RivetCore 1.0.0-rc.16: pushed Mesh agent install, MSI installer)

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 12 Oct 04:35

Adopts RivetCore 1.0.0-rc.16. Two owner requests. (A) The Mesh options live only in a stamped installer, so an agent that enrolled without them, or one that updated itself, never got a Mesh agent: a technician can now push the install (and the removal) from the device page, and install it on many devices from the fleet page. (B) The server could hand out only the stamped exe: it can now also hand out an .msi, built from an uploaded MSI template.

Deploy notes: back up the database, pull this tag, composer install --no-dev --no-plugins --no-scripts, php scripts/update_cli.php --update_db (2.6.90 to 2.6.91: Core migration 0025, one new table endpoint_agent_msi_templates; safe before or right after the code, and until it runs everything works, only publishing an MSI template needs the table). No new cron line, no nginx change. Agents: a pushed Mesh install needs the rc.16 agent (an older agent shows a disabled button, "update the agent first", and the API answers 409 unsupported); the MSI needs the rc.16 setup --from-msi inside the template. Nothing changes for the exe installer or for older agents. Build the template with RivetCore's make msi (release tag agent-v* attaches it with SHA256SUMS.msi) and upload it under Administration > Endpoint agent > MSI templates (or php scripts/endpoint_agent_publish.php <file>.msi --msi --version X --arch amd64 --activate).

(A) Push the MeshCentral agent

  • Device page, Remote tab (Remote access card): Install MeshCentral agent while MeshCentral is on with an https address and a device group and the device does not report the agent (disabled, with the reason, for an agent that did not announce job:mesh_install); Remove MeshCentral agent (page confirmation dialog with a box, a destructive job offered once, never retried; also offered after MeshCentral was switched off) and Run the install again once it reports the agent. The page follows the job result. The caller sends nothing but force: address, device group and pin come from the saved settings when Core creates the job, and the group id is never in the job row, the page or any result. agent/post/rmm_remote_tools.php actions mesh_install / mesh_uninstall.
  • Agent fleet page, bulk install: a collapsed card (every device I may manage, a client, a tag or a group; Count the devices, tick the confirmation, Install) that shows what Core queued and what it skipped (installed, queued, agent too old, no permission, error). Capped by bulk_run_max; refused above approval_bulk_threshold (the approval queue only knows script runs). agent/post/rmm_mesh_bulk.php, js/rmm_mesh_bulk.js. Never reaches a client the user may not manage.
  • Rules unchanged from Core: rmm.remote.manage and the remote-tools switch, idempotent (already_installed, already_queued), audited (Remote Tool Queued, Mesh Agent Bulk Install, Mesh Agent Installed / Install Failed).

(B) MSI installer

  • Format selector exe | msi in the Add device dialog (Agent fleet page and client page) and in Administration > Deployment; the msi option is disabled, with the reason, when no template is current for the architecture (Core builds x64 only; an ARM64 PC uses the exe). The download is the template plus the payload, never a change to the stored template, named RivetIT-Agent-<client>-x64.msi; the Also install the MeshCentral agent box works the same.
  • Deployment commands gain an msi block (msiexec, property form, silent downloader, uninstall, detection) for format msi.
  • MSI templates card: upload, list (version, size, SHA-256, where the platform was read from), make current, deactivate, reactivate; CLI --msi. Unsigned and UNVERIFIED on Windows: the card, the dialog and the commands say so (SmartScreen; whether msiexec accepts a package with the payload appended, repair, upgrade and the custom actions have never run on Windows; RivetCore docs/rmm/MSI_VERIFY.md is the checklist).
  • CSP safe (no inline handlers, no native dialogs), CSRF on every handler, labelled controls and live regions, tables scroll inside their card on a phone, nothing is drawn and no RMM query runs while the module is off.

Pin and DB: rivet/rivet-core ^1.0.0-rc.16 (lock reference cb27c541b6459b3be032bd410812328d0bbdb8bc), vendored copy identical to git archive of the tag (and installed.json / installed.php). DB 2.6.91 runs Core migration 0025 through the Core runner; db.sql gains endpoint_agent_msi_templates and the ledger row (a fresh import and the 2.6.90-to-2.6.91 update produce the same schema, no uca1400).

Tests: tests/rmm_ui.php 1755 checks (88 new, tests/rmm_ui_p16_mesh_push.php), tests/rmm_installer_ui.php 144 (59 new: the Format selector, upload and its refusals, the download with the real wixl package and the full package from make msi: OLE header, decodable trailer, stored template untouched; the Administration cards; the CLI), tests/endpoint_agent_module.php 86 (the 2.6.91 step, twenty endpoint_agent_* tables), tests/wave1_integration.php 85, a tenth browser smoke tests/browser/rmm_p16_smoke.mjs (13 checks: Remote tab install and remove with the in-page dialog, bulk, Format selector, template upload through the real file input, dark mode, phone width), and the real agent proof (98 checks, 27 new: a real rc.16 Linux agent under systemd enrolled WITHOUT Mesh is pushed mesh_install through the technician API against the mock Mesh server: wrong pin, unknown group, oversize body, the good case, node mapping, idempotence, bulk, force, removal). The agent's own Go reader decodes a PHP-stamped copy of the real make msi package. Known failures, identical on 26.10.18 (rc.15): sla_pause 2, recovery_integration 3, the strict golden replay, and check 14 of the rmm_p68_admin_smoke browser smoke.

  • Not verified: a real MeshCentral (the pushed install ran against Core's mock behaviour only; the Linux agent ids, install arguments and node-id locations are as unverified as in rc.12), and anything on Windows: the pushed install on Windows, the MSI (unsigned; msiexec with an appended payload, repair, upgrade, custom actions), the Windows agent.

RivetMSP 26.10.18 (RivetCore 1.0.0-rc.15: Mesh device group id in MeshCentral's URL form)

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 12 Oct 00:50

Adopts RivetCore 1.0.0-rc.15. A real MeshCentral (an owner's server) showed that its device group id (meshid) is not hex: it is 64 characters of base64 in MeshCentral's URL spelling (@ for +, $ for /; 48 raw bytes), with no mesh// prefix, and the download address is https://<mesh>/meshagents?id=4&meshid=<64 characters>&installflags=0. Core now accepts that, and RivetMSP's Mesh card follows. DB 2.6.90: one idempotent step gated on 2.6.89 that runs Core migration 0024_rmm_mesh_group_url through the Core runner (one added column, endpoint_agent_mesh_settings.group_id_url varchar(64) NOT NULL DEFAULT '', guarded by information_schema; nothing else altered; db.sql carries it and a fresh import equals a migrated database). Nothing is switched on by this update. Rows saved with rc.12 to rc.14 keep working (the URL form is derived from their stored hex). Ported from RivetIT 26.10.41 (same Core pin and the same behaviours; RivetMSP's own card and page structure). The warning is shown inside the Mesh card, once, instead of in the toast.

Deploy notes: back up the database, pull this tag, composer install --no-dev --no-plugins --no-scripts, php scripts/update_cli.php --update_db (2.6.89 to 2.6.90; safe before or right after the code, and until it runs everything works: only the verbatim URL form is not remembered). No new cron line, no nginx change. Roll out an agent built from RivetCore rc.15 with this server if you use the optional Mesh install: the installer payload's mesh.meshid is now the URL form (same member, payload version 1), which agents from rc.12 to rc.14 cannot use (they record a Mesh error; the RivetMSP install itself is unaffected). After the update, re-save the group id once if you want the verbatim form MeshCentral printed kept; reissue installers from the updated server.

  • Device group id field (Administration > Endpoint agent > MeshCentral). Help text: paste the download address from MeshCentral (device group, Add Agent, Windows 64-bit installer) or just its meshid; it is 64 characters, not hex; treat it like an invitation key. The field is no longer limited to hex: it takes the whole pasted address, the bare 64-character meshid, mesh//<64> and the old hex form, trims only whitespace and sends the raw value to Core (mesh_group_id) with room for a pasted address (2000 characters). Core refuses whitespace inside, quotes, &, #, a wrong length and another alphabet with one sentence and writes nothing; what was typed is put back escaped. The field stays a masked password input with its show toggle and now reads back mesh_group_id_url (the canonical hex stays in mesh_group_id). Warnings from the save response (today: a pasted address for a host other than the saved MeshCentral address) are shown in the card after Save ("Saved, with a warning: ..."), once, role="alert".
  • Certificate pin. Help text: leave empty when the server certificate is issued by a public CA such as Let's Encrypt (a certificate that renews every 60 to 90 days would break a pin); use a pin only for a private CA or a self-signed certificate. Read it from the server shows Core's pin_probe.note and, when pin_probe.publicly_trusted is true (a second, verified handshake succeeded), shows a "publicly trusted: no pin needed" panel and offers no way to put the hash in the pin field; for a self-signed certificate the proposal, the note and the button are as before.
  • Core's note under Advanced now says what was checked against a real MeshCentral (the download address shape, agent id 4) and what was not (Linux agent ids, install arguments, node-id locations).
  • Installer payload and Linux command. mesh.meshid and the Linux --mesh-group-id carry the URL form (verbatim as pasted, else derived from the hex); install-linux.sh quoting ('...') is Core's, so the $ in an id is safe.
  • Pin: rivet/rivet-core ^1.0.0-rc.15 (lock reference b930c50063823d1ee324216b7612e518bbb87998), vendored copy identical to git archive of the tag; tests/wave1_integration.php follows the pin and the new step (82 checks, was 79).
  • Tests: tests/rmm_ui.php 1667 checks (was 1617; the Mesh fragment now covers the bare meshid, the whole pasted address for the saved host and for a different host (warning), hex, mesh//, whitespace trimming, 19 hostile or malformed values refused with nothing written, escaping of a hostile value put back, clearing, a row from before migration 0024, the new help texts, the self-signed probe note and the publicly-trusted panel, end to end through Core's verified handshake against a LOCAL CA (a second php -S started with curl.cainfo = that CA, plus the control that the same server is not trusted by the first web server; this proves the wiring, not Let's Encrypt itself; tests/support/tls_stub_server.py answers the HEAD that handshake sends)), tests/rmm_installer_ui.php 85 (payload decode shows the URL-form meshid, verbatim for a pasted address with @ and $), tests/endpoint_agent_module.php 84 (the 2.6.89 to 2.6.90 step, idempotent, equals a fresh db.sql import), the Mesh browser smoke 19 checks (was 15: the same forms through the real page, the warning, hostile values, and the payload decoded from a real installer download), the other eight browser smokes, golden replay, real agent (12) and real P2/P3 agent (30) suites with an agent built from the rc.15 tag, phpunit (229 tests) and the regression list. Known results, identical on 26.10.17 (rc.14, checked by running the same suites on that tree in the same environment): sla_pause 2, recovery_integration 3 (the 600000-iteration drill archive), browser rmm_p68_admin_smoke check 14 (queue-worker guidance text, environment dependent), and endpoint_agent_golden.php strict reporting only the four intentional 01-disabled differences (a disabled module answers 503 module_disabled; golden replay, which allows exactly those, is clean); rmm_smoke check 25 and a HQ substring in a CSRF token on the fields page of rmm_ui_p23_a_policies.php are intermittent. The real-agent proof (tests/real_agent_proof/run.sh, docker, a real systemd agent built from the rc.15 tag, a MOCK Mesh server) passes with 71 checks. The real-agent proof harness (tests/real_agent_proof) now uses a fresh random URL-form group id. Every group id in the tests is generated at run time or a fixed fake; no real MeshCentral address or id is in the repository.
  • Not verified: only the Mesh download address shape and agent id 4 were checked against a real MeshCentral (by Core, not here); the Linux agent ids, the install and uninstall arguments, where the node id can be read, and everything on a Windows host are not. Nothing was installed from a real Mesh server and nothing was deployed.

RivetMSP 26.10.17: RivetCore 1.0.0-rc.14 (Alerting thresholds fix)

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 11 Oct 23:13

Small patch release. Adopts RivetCore 1.0.0-rc.14, which fixes Cannot use object of type stdClass as array from AlertingActions::getThresholds: on an install that never edited its global checks (the built-in list, where pending_reboot has empty params) the thresholds part of the Alerting tab of an agent-linked device failed, was logged, and showed the "declares no thresholds" note for every check. Inside Core a check definition's params is now always an array; \stdClass exists only on the wire (ChecksValidator::toWire()), so the stored checks_json, the signed check messages and the policy version hashes are byte for byte unchanged (the golden transcripts still replay identically, 10 files). No migration: DB stays 2.6.89.

Deploy notes: back up the database, pull this tag, composer install --no-dev --no-plugins --no-scripts. No update_db step, no new cron line, no nginx change, no agent change (an agent built from rc.13 keeps working; the fix is server side).

  • Edition code audited, no change needed. Nothing in includes/rmm_*.php, agent/rmm_*.php, agent/post/rmm_*.php, js/ or src/Core/Adapter/Endpoint/ checks instanceof \stdClass, json-encodes a check list or reads params as an object; the thresholds editor and the policy viewer already treat params as an array, and the administration checks editor prints Core's wire-shaped settings summary ({}, asserted below).
  • Pin: rivet/rivet-core ^1.0.0-rc.14 (lock reference fae6d98e63669294ed1bbf6719d082b1bedeb600), vendored copy identical to git archive of the tag. tests/wave1_integration.php follows the pin (79 checks).
  • Regression test in tests/rmm_ui_p23_c_alerting.php: with no checks_json stored, a device that reports disk_c, pending_reboot, svc_eventlog and agent_update loads the asset page Alerting tab; the technician thresholds route answers for each defined check, every default check lists in the thresholds part, the server log holds no stdClass / Cannot use object / fatal line, and the administration checks editor still shows "params": {}. Run against the rc.13 vendored Core the same checks fail (pending_reboot params is an object, thresholds answer 500, the error is logged), so the test is not vacuous.
  • Tests: tests/rmm_ui.php 1617 checks (18 new), nine browser smokes, the RMM, golden (replay identical, strict identical), real agent and phpunit (229 tests) suites and the regression list all as on 26.10.16. Known failures, identical on 26.10.16 (rc.13 Core): sla_pause 2, recovery_integration 3 (the 600000-iteration drill archive), browser rmm_p68_admin_smoke check 14 (queue-worker guidance text, environment dependent); rmm_smoke check 25 (tag removal after reload) is intermittent. Nothing was deployed.

RivetMSP 26.10.16: RivetCore 1.0.0-rc.13 (Mesh agent install, starter pack)

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 11 Oct 22:29

Adopts RivetCore 1.0.0-rc.13 (rc.12: the optional MeshCentral agent install with automatic node-id mapping, migration 0023; rc.13: the RMM starter pack). Aligned with RivetIT 26.10.39 (same page name agent/rmm_starter_pack.php, same handler, same Mesh box rule: offered only once MeshCentral is on with an address). DB 2.6.89: one idempotent step gated on 2.6.88 that runs Core migration 0023_rmm_mesh_agent through the Core runner (three new tables, nothing existing altered; db.sql carries it and a fresh import equals a migrated database). The starter pack has no migration. Nothing is switched on by this update: no installer asks for the Mesh agent until an administrator saves the Mesh install settings and ticks the box, and nothing from the starter pack is installed until an administrator installs it. Older agents are answered exactly as before (golden transcripts replay identically, strict mode included).

Deploy notes: back up the database, pull this tag, composer install --no-dev --no-plugins --no-scripts, php scripts/update_cli.php --update_db (2.6.88 to 2.6.89; safe before or right after the code). No new cron line, no nginx change. To use the Mesh install, roll out an agent built from RivetCore rc.13 (an older agent never reports Mesh and ignores the installer members).

  • Optional MeshCentral agent (off by default). The Add device dialog and the Administration deployment form get Also install the MeshCentral agent (unticked every time, disabled with Core's own refusal sentence until Mesh is switched on with an https address and a device group id). Administration > MeshCentral gets the install settings: device group id (masked, it is a secret), certificate pin with Read it from the server (shows the SHA-384 and SHA-256 of the server's certificate and never saves; a button puts it in the field and you press Save), the overwrite switch, and JSON corrections of the agent-id and install-argument tables behind a warning that they are unverified. Core validates everything and writes nothing when any field is invalid. The device's Remote access card (and the Remote tab) shows the Mesh agent state: installed with its version or the install error, and what the server did with the node id (mapped; conflict, a visible alert that names the other device only when it belongs to the same client; kept_manual, a visible hint). The node id itself is shown to administrators only. Events rmm.mesh.agent_installed and rmm.mesh.node_conflict are in the shared picker.
  • RMM starter pack. Endpoints > Starter pack (administrators, scripts sub-switch): browse and filter 73 scripts (41 Windows, 32 Linux; 57 read only, 10 change the system, 6 disruptive), 3 check sets and 3 policy drafts (created switched off and unassigned), tick what you want, Preview, Install selected, Uninstall selected (preview first, then an in-page confirmation), a settings form per draft (time server, SSH unit, certificate paths, backup service...), Core's signing-key blocker shown first. An empty selection is never sent to Core (Core reads it as the whole pack). The script library marks starter scripts (Starter badge, risk chip, Update waiting for a parked newer version, with a link that compares it with yours); policies show a Starter badge and never print their marker, and saving a draft keeps it. Event rmm.starter.installed.
  • Tests: tests/rmm_ui.php 1599 checks (new fragments rmm_ui_p1213_mesh.php, rmm_ui_p1213_starter.php), tests/rmm_installer_ui.php 81 (payload decode: the Mesh members only when ticked, token-gated download, Linux flags), the pin probe against a local TLS server, auto-mapping from real check-ins (conflict, kept manual, overwrite), two browser smokes (Mesh 15 checks, Starter 16; desktop and phone, light and dark), the real Linux agent proof (71 checks, was 39) extended with a MOCK Mesh server and the starter pack through the API.
  • Not verified: anything that talks to a real MeshCentral server (download address and flags, agent ids, install and uninstall arguments, where the node id can be read), the Windows code on a Windows host, and all 41 Windows starter scripts (parse-checked by Core, never run). Nothing was deployed.

RivetMSP 26.10.15

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 11 Oct 18:45

[26.10.15] RivetMSP — RivetCore 1.0.0-rc.11: remote tools, file transfer, log collection, RMM scale

Ports RivetIT's released adoption (26.10.38) of RivetCore 1.0.0-rc.11 (RMM Phase 6 remote tools, Phase 8 log collection, scale items S3 to S8). DB 2.6.88 (one idempotent step gated on 2.6.87 that runs Core migrations 0021_rmm_scale_storage and 0022_rmm_remote_and_logs through the Core runner: ten new tables, nothing existing altered; plus settings.config_rmm_remote_manage_lvl3 and the four config_rmm_extstore_* columns, all off; db.sql carries it and a fresh import equals a migrated database). Nothing is switched on by this update: the remote_tools, filetransfer and logs sub-switches, partitioning, herd control and the external metric store are all off until an administrator turns them on, and older agents are answered exactly as before (golden transcripts replay identically).

Deploy notes: back up the database, pull this tag, composer install --no-dev --no-plugins --no-scripts, php scripts/update_cli.php --update_db (2.6.87 to 2.6.88; safe before or right after the code). No new cron line. The live nginx rules are not changed by this release (the shipped templates already deny backups/, where file transfers are stored). To use remote tools or logs, roll out an agent built from RivetCore rc.11 first.

  • Device bridges: api/v1/agent_transfer.php and api/v1/agent_logs.php, routed above the bearer parsing and documented in openapi.yaml; api/v1/rmm_gate.php is Core's rc.11 template (answers both while the module is off and enforces the warm start before any database work). File transfers live in backups/endpoint-transfer (EA_TRANSFER_DIR overrides).
  • Remote tab on the asset page (services, processes, files, MeshCentral desktop/terminal/files) in its own handler agent/post/rmm_remote_tools.php; agent/post/rmm_remote.php (Remote Connect) is untouched and now pinned by a test.
  • Logs and Log rules (Endpoints menu, shown only to users with the ability; asset Logs tab), client scoped by Core.
  • Administration: cards for Remote tools, File transfer, Log collection, Scale settings, Metric storage and partitioning and the External metric store. RivetMSP writes metrics through Core's DatabaseMetricSink, so Core's daily rollup and partitions apply to RivetMSP's own tables: the storage card lists rmm_metric_hourly, rmm_metric_daily and endpoint_agent_check_history, shows the rollup status, and Enable converts every table the probe allows (naming any it leaves plain); the Performance and capacity panel shows the autoscaling hint, cache, herd control and the three tiers.
  • Permissions: new ability rmm.remote.manage (administrators; a role with scripts level 3 and remote access only while the new Administration setting is on); sending a file to a device and the remote-tools settings stay administrator-only. Events rmm.file.transferred and rmm.log.rule_matched appear in the picker (read from Core's catalog).
  • Operations: cron/integration_worker.php --worker=N runs several queue workers; with Redis the RMM read cache and token buckets use it from every entry point; housekeeping (already called by cron/cron.php) now also builds the daily rollup, prunes logs, expires transfers and flushes the external buffer. The external store's three settings are registered for key rotation and a schema scan fails when a secret-looking column is missing from the registry.
  • Tests: tests/rmm_ui.php with new fragments (remote, logs, admin incl. the rollup and partitioning of all three tables on MariaDB 11.8), three browser smokes (17, 15 and 18 checks, desktop and phone, light and dark), tests/real_agent_proof/run.sh (39 checks with the real Linux agent built from rc.11 under systemd), policy conformance cases, registry scan. Not verified: any Windows code path, a real MeshCentral server, Core's S9 24-hour 10,000-device run, and live systems (nothing was deployed).

RivetMSP 26.10.14

Choose a tag to compare

@TheTractorHacker TheTractorHacker released this 11 Oct 05:22

Ports RivetIT's released adoption (26.10.35 plus the 26.10.37 secret-box fix) of RivetCore 1.0.0-rc.10 RivetCore\Crypto (ADR-011) for stored secrets. DB 2.6.87 (one idempotent step gated on 2.6.86: widens the encrypted
columns so the new envelope fits, adds settings.config_vault_v3_enabled (0), config_vault_dek_wrap, config_vault_v3_prepared_at and api_tokens.token_enc_dek; no stored value changes; db.sql carries it and a fresh
import equals a migrated database). Nothing changes on an existing install until an administrator creates the key file; the credential vault stays on the legacy format until its flag is switched on.
Read docs/KEY_MANAGEMENT.md before deploying.

Deploy notes (in this order; take a database dump first):

  1. Back up the database, pull this tag and run composer install --no-dev --no-plugins --no-scripts (nothing new is installed; the lock is unchanged).
  2. php scripts/update_cli.php --update_db (2.6.86 to 2.6.87). Safe to run before anything else; the app keeps working on the config.php key exactly as before.
  3. As root, create the key file: sudo php scripts/keys_cli.php generate. It creates /etc/rivetmsp 0755 (the web user must be able to traverse it) and keys.json root:www-data 0640, with the config.php key as kid k1. Check with php scripts/keys_cli.php status: it must not say "NOT REACHABLE".
  4. Take an offline copy of /etc/rivetmsp/keys.json now, apart from the dump and the backup passphrase.
  5. Dry run, then move everything: php scripts/rewrap_cli.php --dry-run, php scripts/rewrap_cli.php (repeats until a pass changes nothing; resumable). php scripts/keys_cli.php status --inventory shows the result.
  6. Leave the credential vault flag off (config_vault_v3_enabled stays 0; scripts/vault_v3_cli.php is for a later, deliberate step).
  7. Rollback: set $config_crypto_v3_settings = false; in config.php (new writes are ENC2: again; v3 values already written stay readable by this release). Downgrading the code needs the values moved back first, or a restore of the pre-upgrade dump.
  • Settings and two-factor seeds. encryptSetting() / decryptSetting() (same signatures, an optional context) go through RivetMSP\Crypto\SettingsCrypto / RivetCore's SettingsVault. With a key file, new values are v3:<kid>:...; ENC2:, ENC: and cleartext keep reading and move to v3 lazily (the settings row) and with the tool. users.user_token seeds are v3 of the totp purpose bound to the user. A value that cannot be opened reads as empty (logged once, never the value); an unknown or damaged envelope (v4:, ENC3:, a truncated v3:) now fails closed instead of being handed back as if it were cleartext; true legacy cleartext still reads. Flags $config_crypto_v3_settings, $config_crypto_v3_totp.
  • Tooling. scripts/keys_cli.php (status, generate, fingerprint, add-key, set-active, retire), scripts/rewrap_cli.php (one RewrapSource per encrypted column; Core's Rewrapper: dry run, resumable, compare-and-set, never truncates a too-narrow column, one audit row per column and run) and Administration > Security > Encryption keys. The registry (src/Crypto/ColumnRegistry.php) is derived from secStragglerColumns() and also covers the RMM module's secrets (signing key, Mesh login key, queued job secrets, secret custom fields), including a composite-key source.
  • The key file directory. generate as root creates the directory 0755 and the file root:www-data 0640; the installer (deploy/install.sh) creates it as root after setup; status, the CLI and the panel say KEY FILE NOT REACHABLE (with the command that fixes it) when the web user cannot traverse the directory or read the file, instead of showing a plain legacy install.
  • Credential vault v3 (flag config_vault_v3_enabled, default off, scripts/vault_v3_cli.php): fields sealed with the vault data key (AAD credential:<id>:<field>), per-user vw3: wraps at the next password login, mobile tokens wrap the data key, writes (including a restore from an uploaded backup) finalised to v3. Not enabled by this release.
  • Recovery. The backup manifests (in-app zip and deploy/backup.sh) keep the fingerprint formula and add the key ring by kid and fingerprint (no key material); deploy/restore.sh and deploy/install.sh --restore-from accept --key-file / --restore-key-file; the setup wizard restore names the missing keys; the restore drill reports "Key file for this backup" and opens v3 samples. The key file is never in an archive: escrow it separately.
  • Ciphertext prefix checks accept v3: everywhere (a v3 value would otherwise be mistaken for cleartext): secIsWrapped(), the cleartext-row SQL, the webhook URL masking on the list and edit pages, the drill's secret check, EndpointSecretBox (already since 26.10.13). A cleartext vault key or TOTP seed is wrapped in its own context. decryptSetting() loads Composer's autoloader itself, so a cron script that never did cannot silently read v3 values as empty.
  • Tests: tests/crypto_settings.php, crypto_rewrap.php, crypto_vault.php, crypto_panel.php, tests/crypto_msp_integration.php (prefix sites, fail-closed, key file access, TOTP login over HTTP, cron/mail_queue.php against a fake SMTP server with the password and an XOAUTH2 token, webhook queue delivery with the HMAC, every settings secret, provider credentials, all after a real rewrap), tests/crypto_rmm_after_rewrap.php (the RMM signing key signs a job over HTTP after a real rewrap; Mesh key, job and custom field secrets, Tactical / Level / Action1 / Sophos credentials; an unreadable key file), and RivetCore's RewrapSourceConformanceTestCase for the integer and string key sources plus a composite-key test.