- JavaScript 62.3%
- Python 18.2%
- Go 15.1%
- Shell 3.5%
- CSS 0.6%
- Other 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Une machine n'acceptait qu'une commande à la fois : toute nouvelle demande était refusée (409) tant que la précédente n'était pas terminée, et les actions en masse ignoraient purement les machines occupées. Chaque machine a désormais sa file : jusqu'à 10 commandes, exécutées l'une après l'autre dans l'ordre d'envoi. L'essentiel existait déjà — le poll remet la plus ancienne commande QUEUED une fois que rien n'est IN_PROGRESS, et l'en-tête X-Skip-Command empêche de remettre une commande à un agent occupé. La limite tenait à un seul contrôle, au moment de la création. Ce contrôle est remplacé par trois règles, dans un module unique (utils/command-queue.js) partagé par les routes, les actions de groupe et le CLI, pour que les trois ne puissent plus diverger : - une commande identique déjà en attente n'est pas ajoutée une seconde fois : l'appelant récupère l'existante et la suit comme la sienne ; - plus rien n'est accepté derrière une désinstallation, la machine ne reviendra pas le chercher ; - la file est plafonnée à 10 commandes. La mise à jour des agents en masse, la vérification des mises à jour, l'inventaire des paquets et les actions de groupe rejoignent la file des machines occupées au lieu de les sauter. Côté agent (0.13.0), la boucle est réveillée dès qu'une commande se termine, au lieu d'attendre son intervalle : une file de cinq commandes à 15 s ne met plus plus d'une minute rien qu'à démarrer. Le réveil est émis après la libération du verrou — réveillé avant, le poll suivant montrerait encore l'agent occupé et le reste de la file attendrait un intervalle entier. Un agent plus ancien fonctionne avec la file, à raison d'une commande par intervalle. L'interface affiche « +N en file » dans la colonne des commandes, et une commande qui attend son tour n'est plus annoncée comme un délai dépassé. Dashboard 0.16.0 / agent 0.13.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
| agent | ||
| dashboard | ||
| dev-plugin | ||
| docs | ||
| .gitignore | ||
| AAUL | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| install.sh | ||
| LICENSE | ||
| README.md | ||
| RELEASE.md | ||
| release.sh | ||
| SECURITY.md | ||
Agent Auto Update Linux
Ce projet est Québecois et donc généralement programmer en Francais mais pour el rendre plus universel ici c'est en Anglais mais en bas vous aurez des lien vers le projet Auxinux et sont Wiki est en Francais
Agent Auto Update Linux, or AAUL, is a small fleet management project built around two parts:
dashboard/: the server-side dashboard and API used by administratorsagent/: the client-side agent installed on managed machines
The original project name says "Linux", but the current codebase also supports macOS and Windows for agent installation and update flows.
The dashboard is responsive: it is usable from a phone or tablet, not only from a desktop browser.
What the project does
AAUL lets you register a machine in the dashboard, generate a one-time installer URL, and let the agent poll the dashboard for work.
The dashboard can:
- register agents and generate installation commands
- queue commands such as
RUN_NOW,SET_SCHEDULE,UPDATE_AGENT,UNINSTALL(up to ten per machine, run one after another in the order they were sent) - display machine state, package information, logs, and last-seen activity
- show live CPU / memory / disk usage per host, with 24-hour history
- aggregate pending updates across the whole fleet and flag the security ones
- push a log-retention policy that every agent enforces locally
- report antivirus health per host, check it hourly, start a remote scan, turn real-time protection back on (never off: that stays on the machine), and offer to install ClamAV on Linux hosts that have no antivirus
- build and serve the latest agent bundle used for agent self-update
The agent can:
- install on Debian/Ubuntu, Fedora/RHEL, Arch Linux, macOS, and Windows 11
- report local system information back to the dashboard
- report lightweight vital signs (CPU, memory, disk, load) on every check-in
- apply operating system updates using the native package manager
- report which pending updates come from a security channel
- report antivirus health (product, real-time protection, signature freshness, last scan, detected threats), run a scan on demand, turn real-time protection on, and install ClamAV — each only when asked
- prune its own log directory according to the dashboard's retention policy
- update itself from the bundle exposed by the dashboard
- run as a background service on each supported platform
Repository layout
agent/
app/ Python agent runtime (Linux, macOS)
app/lib/metrics.py Vital signs collected on every check-in
app/lib/antivirus.py Antivirus health and on-demand scans (ClamAV)
app/lib/logs.py Log listing, reading and retention
systemd/ Linux service templates
windows/ Go agent for Windows — one static binary, no runtime
dashboard/
client/ React + Vite frontend
server/ Express API and server-side logic
public/agent/ Built agent bundle served to clients
config/config.json Static dashboard configuration
docs/
ARCHITECTURE.md High-level codebase tour
SECURITY_AUDIT.md Security notes and historical findings
install.sh Production installer / updater for the dashboard
AAUL Local wrapper for the dashboard CLI during development
How it works
- An administrator creates an agent entry from the dashboard.
- The dashboard stores an install token and an agent API token.
- The generated
/install?token=...URL returns a platform-specific installer script. - The installer downloads the latest agent bundle from
/agent/latest.tar.gz. - The installed agent polls
/api/agent/polland executes queued commands. - The agent reports results to
/api/agent/command-result.
The agent never needs inbound access from the dashboard. The communication model is poll-based.
Supported platforms
Agent package updates are implemented for:
- Debian and derivatives through
apt - Fedora / RHEL families through
dnforyum - Arch Linux and derivatives through
pacman - macOS through
softwareupdate, plus Homebrew when present - Windows 11 through
wingetand Windows Update APIs
Windows agent
The Windows agent is a separate program: a single static Go binary that lives in
agent/windows/. Linux and macOS keep the Python agent.
The split exists because the runtime, not the agent, was the problem. Getting a
usable Python interpreter onto a fresh Windows machine took roughly 260 lines of
PowerShell — working around the Microsoft Store alias stub under WindowsApps,
installing Python through winget, locating it again outside the session PATH,
creating a virtualenv, verifying pip really existed — and it kept breaking. A Go
binary has nothing to bootstrap, so the installer is down to ~130 lines that
place a file and register a service.
What that changes on a managed machine:
- Prerequisites: administrator rights, and nothing else. No Python, no
virtualenv, no pip, no
tar. - Service: a real Windows service (
AgentAutoUpdate), visible inservices.msc, started at boot and restarted by the SCM on failure — instead of a SYSTEM scheduled task. - Daily updates: run by the service itself, and caught up if the machine was
off when the window passed. There is no second scheduled task to drift out of
sync with
config.json. - CLI: the service binary is the CLI (
aaul-agent status), added to the machinePATHat install. No wrapper.cmd. - Upgrading from the Python agent: the installer removes the old scheduled
tasks and virtualenv, and keeps
config.jsonandlogs\. The machine stays the same agent on the dashboard, with its history.
winget is optional. It is resolved from the App Installer package under
C:\Program Files\WindowsApps as well as from PATH, because the winget
alias on PATH belongs to a user profile and does not exist for the LocalSystem
account the service runs under.
Building the Windows agent
It is cross-compiled from the development machine — no Windows build host and no CI runner are involved:
cd dashboard && bash scripts/build-agent-bundle.sh
That produces both public/agent/latest.tar.gz (Python agent) and
public/agent/windows/aaul-agent.exe (Go agent), stamped with the version from
agent/app/VERSION. It needs the Go toolchain; set AAUL_SKIP_WINDOWS_AGENT=1
to skip it while working on the Python agent alone. release.sh refuses to
publish without the binary.
Tests for the Go agent:
cd agent/windows
go test ./... # signing, config, logs, winget parsing
GOOS=windows go vet ./... && GOOS=windows go build ./...
Testing the Windows installer
The PowerShell installer is generated, so it is checked rather than trusted.
npm run check:install always enforces the static rules (pure ASCII, values
interpolated through psAsciiQuote, no uncaptured external call inside a
function, well-formed here-strings), and additionally — when pwsh is
present — parses the generated script with PowerShell's own parser and runs
scripts/test-windows-installer.ps1, which checks that the installer's
integrity check agrees byte for byte with the dashboard's.
PowerShell is not required to install or run AAUL; the extra checks are simply skipped when it is missing. To enable them on a development machine:
curl -sL https://github.com/PowerShell/PowerShell/releases/latest/download/powershell-7.5.0-linux-x64.tar.gz \
| tar -xz -C ~/.local/pwsh # any recent 7.x release works
One limitation worth knowing: pwsh on Linux is PowerShell 7 and reads files as
UTF-8. It cannot reproduce Windows PowerShell 5.1's ANSI decoding, which is why
the ASCII rule is enforced statically instead of relying on the parser.
Service management is implemented through:
systemdon Linuxlaunchdon macOS- the Service Control Manager on Windows
Versioning
Two files hold a version, and they are the only two a human edits:
| File | Component |
|---|---|
dashboard/VERSION |
dashboard |
agent/app/VERSION |
agent — Python bundle and Go Windows binary |
Everything else derives from them:
install.shreadsdashboard/VERSIONfor its install report;client/vite.config.jsreads it at build time and injects__APP_VERSION__, which is what the footer shows;scripts/generate-release-md.mjsreads both to produceRELEASE.md;scripts/build-agent-bundle.shstampsagent/app/VERSIONinto the Go binary.
npm insists on carrying its own copy in package.json and the lockfiles.
Nothing user-facing reads those, and scripts/sync-versions.mjs writes them:
echo 0.14.0 > dashboard/VERSION
cd dashboard && npm run sync:versions
release.sh does this for you. npm run check:versions (part of
npm run check:client) fails if anything has drifted.
This is deliberate scar tissue. The version used to be duplicated across eight
files; three releases in a row shipped with one of them missed — an install
reporting v0.12.4 -> v0.12.4 while the release notes announced 0.13.0, then a
footer a full version behind because a stale client/.env overrode the build.
The rule is now: one file per component, everything else reads it.
Development
Install dashboard dependencies:
cd dashboard
npm install
npm --prefix client install
Run the server:
cd dashboard
npm run dev:server
Run the frontend:
cd dashboard
npm run dev:client
Or run both:
cd dashboard
npm run dev:all
Build the agent bundle used by /install and agent self-update:
bash dashboard/scripts/build-agent-bundle.sh
Production install / update
The intended production entrypoint is install.sh.
Fresh install or upgrade:
sudo ./install.sh
The installer:
- copies the project to
/opt/AAUL - verifies that the native modules (
sqlite3,bcrypt) actually load, and rebuilds them if npm blocked their install scripts - preserves runtime data during upgrades
- installs Node.js dependencies
- builds the frontend
- builds the agent bundle
- installs the
agentautoupdate-dashboardsystemd service - installs the local
AAULCLI on the host
Using an installed agent
Once an agent is installed on a machine it is managed locally by a background service and by the dashboard. The quickest way to see the agent state from the host is the local CLI.
Agent CLI commands
On Linux and macOS the CLI is agentautoupdate, installed under
/usr/local/bin/agentautoupdate. On Windows it is aaul-agent, the agent
binary itself, installed under C:\ProgramData\AgentAutoUpdate\bin and added
to the machine PATH. The subcommands below are the same on both, except where
noted; run aaul-agent help for the Windows-specific ones
(install-service, run --console).
# Show installed version and local config
agentautoupdate version
agentautoupdate status
# Send a heartbeat to the dashboard without running a command
agentautoupdate check-in
# Run operating-system updates immediately
sudo agentautoupdate update
# Update the agent itself from the dashboard bundle
sudo agentautoupdate update-agent
# Read the most recent agent log
agentautoupdate logs
agentautoupdate logs --tail 50
# Point the agent at a different dashboard address or host
sudo agentautoupdate config set-dashboard 192.168.1.10
sudo agentautoupdate config set-dashboard https://dashboard.example.com:3002
# Remove the agent from this machine
sudo agentautoupdate uninstall
The dashboard URL stored in
/opt/agentautoupdate/config.json(Linux) or/Library/Application Support/AgentAutoUpdate/config.json(macOS) must be reachable by the agent. If the dashboard moves to a new host or port, useconfig set-dashboardon the agent.
Service control
The background poller is what keeps the agent talking to the dashboard.
| Platform | Service / task | Useful commands |
|---|---|---|
| Linux (systemd) | agentautoupdate.service |
sudo systemctl status agentautoupdate.service, sudo systemctl restart agentautoupdate.service |
| macOS (launchd) | com.agentautoupdate.poller |
sudo launchctl print system/com.agentautoupdate.poller, sudo launchctl kickstart -k system/com.agentautoupdate.poller |
| Windows (SCM) | service AgentAutoUpdate |
Get-Service AgentAutoUpdate, Restart-Service AgentAutoUpdate |
When an agent stops talking to the dashboard
The dashboard shows an agent as offline when it has not received a poll for more than a few minutes. The machine can be powered on and healthy while the agent itself is unable to reach the dashboard. Here is the usual diagnostic order:
-
Check the agent service on the machine
- Linux:
sudo systemctl status agentautoupdate.service --no-pager - macOS:
sudo launchctl print system/com.agentautoupdate.poller - Windows:
Get-Service AgentAutoUpdate, oraaul-agent status
If the service is stopped, start or restart it. A freshly installed agent that has never connected will usually show an error here first.
- Linux:
-
Read the agent log
agentautoupdate logs --tail 100Common log errors:
Connection Error: ... Is the dashboard running?— the agent cannot reach the dashboard URL.SSL Error: ... Check certificate configuration.— TLS mismatch, expired certificate, or self-signed certificate not allowed.HTTP 401: Invalid tokenorInvalid request signature— the agent API token is wrong, rotated, or the agent clock is off.Missing response signature— the agent expects signed dashboard responses but the dashboard did not sign them.
-
Verify network reachability from the agent machine
# Linux / macOS curl -v https://<dashboard-host>:<port>/health # or, if the dashboard is HTTP curl -v http://<dashboard-host>:<port>/healthIf this fails, the problem is network or firewall related, not AAUL related. Check DNS, routing, firewalls, and reverse-proxy rules.
-
Check the dashboard URL configured on the agent
agentautoupdate status | grep dashboardUrl sudo agentautoupdate config showIf the dashboard was moved behind HTTPS, a new public IP, or a different port, update it:
sudo agentautoupdate config set-dashboard https://<new-host>:3002Then restart the poller service.
-
Check TLS / HTTPS settings
The agent stores TLS policy in
security.jsonnext toconfig.json:{ "verifyTls": true, "allowSelfSigned": false, "signRequests": true }- If the dashboard uses a self-signed certificate, set
allowSelfSigned: true(development only) or, better, install the CA on the agent machine. - If HTTPS was disabled on the dashboard but the agent still points at
https://..., the connection will fail. - The dashboard's
dashboard/config/config.jsondefinesdefaultApiProtocol. Make sure the install URL matches the protocol the agent actually uses.
- If the dashboard uses a self-signed certificate, set
-
Verify the agent API token
The token lives in
config.jsonasagentApiToken. If you regenerated or reinstalled the agent entry in the dashboard, the old token becomes invalid. The fastest fix is to reinstall the agent with a fresh install token, or to updateagentApiTokenin the agent'sconfig.jsonto match the value shown in the dashboard for that agent. -
Check clock skew
Request signatures have a five-minute validity window. Run
date -uon the agent and on the dashboard host. If they differ by more than a couple of minutes, signing will fail and the dashboard will return 401. -
Force a manual check-in
agentautoupdate check-inIf this succeeds, the service restart above likely fixed it. If it fails, the error printed here is the real cause.
What the dashboard "online" status really means
The agent is poll-based: it calls /api/agent/poll at pollIntervalSeconds (default every 15–60 seconds). The dashboard marks an agent online when last_seen_at is within the last ~5 minutes. There is no persistent connection, so if an agent stops polling, check the agent side first — the dashboard cannot initiate contact.
Agent file locations
| Platform | Runtime directory | Config file | Logs |
|---|---|---|---|
| Linux | /opt/agentautoupdate |
config.json |
logs/agent-YYYY-MM-DD.log |
| macOS | /Library/Application Support/AgentAutoUpdate |
config.json |
logs/agent-YYYY-MM-DD.log |
| Windows | C:\ProgramData\AgentAutoUpdate |
config.json |
logs\agent-YYYY-MM-DD.log |
Dashboard CLI
After a production install, the host gets an AAUL command for local administration.
Examples:
AAUL -status
AAUL -version
AAUL -reset-admin
AAUL -agent -list
AAUL -agent -update <AGENT_ID>
AAUL -agent -exec <AGENT_ID>
AAUL -agent -remove <AGENT_ID>
Fleet packages and security updates
Packages -> See the whole fleet aggregates every machine's pending updates into
one list. For each package you get the number of hosts affected and whether the
update comes from a security channel, so the page answers the two questions that
actually drive patching: what must I fix and how much of my fleet does it
touch.
Security detection is per-platform and best effort:
| Platform | Source of the security flag |
|---|---|
| Debian / Ubuntu | the suite in apt list --upgradable (<codename>-security), cross-checked against apt-get -s dist-upgrade |
| Fedora / RHEL | dnf updateinfo list security |
| openSUSE | zypper list-patches --category security |
| Arch, macOS, Windows | not available — these publish no per-package security metadata, so updates are reported as regular ones rather than guessed |
A package is flagged as security-relevant as soon as one host reports it from
a security channel: the same version can sit in -security on one distro release
and in plain -updates on another, and under-reporting a security fix is the
costlier mistake.
Use Security only to reduce the list, or Patch security updates to queue just
those updates on every affected machine. Bulk updates still require the per-agent
package-management opt-in; agents without it are skipped.
Host metrics
Every poll carries a small set of vital signs (CPU, load average, memory, swap,
root filesystem and the fullest mounted filesystem, process and session
counts). This is deliberately separate from the full inventory
(collect_system_info()), which is expensive and still runs only on demand.
- Fleet cards and the fleet-health panel show current usage at a glance.
- The agent page adds 24-hour sparklines.
- A host is listed as "under pressure" above 90% CPU, 90% memory or 85% disk.
Disk pressure uses the fullest volume, not
/: a machine can look healthy on/while/varis the one about to fill up.
History is sampled once per host every 5 minutes (not once per poll, which would
add thousands of rows a day without adding visible detail) and pruned according
to Admin -> Metric history retention, 30 days by default.
Agents older than 0.9.0 simply report no metrics; the dashboard shows "no metrics reported yet" rather than a misleading 0%.
Log collection and retention
Agents write one log file per day. Nothing used to delete them, so a long-lived machine accumulated hundreds of files.
Retention is configured centrally in Logs -> Log retention and pushed to every
agent on its next check-in. Each agent then enforces it locally, at most once an
hour:
- delete logs older than the age limit;
- delete the oldest remaining logs until the total fits the size limit.
Defaults are 30 days or 200 MB, whichever comes first. Two safety rules are
hardcoded in the agent and cannot be overridden from the dashboard: the current
day's log is never deleted, and only files the agent itself produced
(agent-YYYY-MM-DD.log) are ever eligible for removal.
Clean up now applies the policy to one host immediately, which is what you want
when a disk is filling up. The log viewer supports search, level highlighting
(errors, warnings, success), line wrapping and download.
Version checking
RELEASE.md at the repository root publishes the versions that are currently
released and the release notes in both French and English. It is
generated, never hand-edited — release.sh rebuilds it from
dashboard/client/src/lib/changelog.js and the VERSION files, so the published
reference cannot lag behind a release.
The dashboard reads its release notes from that same file rather than from a copy baked into the bundle, and shows the section matching the interface language. Three sources are tried, in order:
- the cached copy of the last successful fetch — this is what lets you read what a newer version contains before installing it;
- the
RELEASE.mdshipped with the installation, whichgit pullalways puts on disk, so an isolated install still has its notes; - nothing, in which case the window says so instead of showing a blank list.
Each dashboard reads that file periodically and compares it with what it runs. When a newer version exists, a small amber badge appears next to the version in the footer, and the release-notes window shows what to run:
cd ~/AAUL && git pull && ./install.sh
Nothing is ever downloaded or installed automatically — the check only reports.
- Interval and on/off switch live in
Admin, default every 30 minutes. - Turn it off on an isolated network. Otherwise the dashboard attempts one outbound request per interval that can never succeed.
Version sourceaccepts a custom URL if you mirrorRELEASE.mdinternally.- The release-notes window has a Check now button that works even when the periodic check is disabled.
To regenerate the file by hand:
cd dashboard
npm run gen:release # rewrite RELEASE.md
npm run check:release # fail if it is out of date (useful in CI)
Beta status & known limitations
AAUL is currently released as a public beta (pre-1.0). It is usable and has been through a security review, but a few things are worth knowing before you deploy it on a fleet you care about:
-
Versioning. For as long as the project is in beta, both the dashboard and the agent stay on
0.x.x— pre-1.0 in the semver sense, meaning the API and behavior may still change between releases.1.0.0is reserved for the first stable release. Note that the agent auto-update compares version strings, not version order: any change to the agent version (in either direction) triggers an update on the next poll. -
Metrics and log retention need agent 0.9.0+. Older agents keep working normally, but report no vital signs and do not prune their own logs. Push
UPDATE_AGENTto the fleet to enable both. -
Security flags depend on the package manager. Arch, macOS and Windows expose no per-package security metadata, so their updates are never marked as security updates. Absence of the flag on those platforms means "unknown", not "safe".
-
Run it over HTTPS. Agent tokens and admin sessions travel over the wire. Enable HTTPS (or put the dashboard behind a TLS-terminating reverse proxy) before exposing it beyond localhost. Plain-HTTP install is disabled by default.
-
The dashboard runs privileged commands on agents. Treat admin accounts as highly sensitive. The default admin password is generated at first launch and written to
.initial-admin-password(mode0600) next to the database — change it immediately and delete that file. -
Agent services run as root. This is inherent to OS package management. Only install the agent on machines you administer.
-
The optional agent local web UI binds to all interfaces and is reachable on the LAN when enabled. It is off by default, requires PAM/system-user authentication, and is rate-limited — enable it only on trusted networks, and prefer HTTPS for it too.
-
Plugins run in the dashboard process. Install only plugins you trust.
-
Native modules must build. The dashboard depends on
sqlite3andbcrypt, which ship compiled binaries. Recent npm versions block dependency install scripts by default (install scripts blocked because they are not covered by allowScripts), in which casenpm cisucceeds but the binary is never fetched and the server dies at boot with Could not locate the bindings file. Note thatnpm rebuildis blocked by the same policy and still reports success.install.shdetects this, runs each package's own install script directly (bypassing npm), and aborts with instructions rather than restarting a service that cannot start. If you install dependencies by hand, verify with:cd /opt/AAUL/dashboard node -e "require('sqlite3'); require('bcrypt')" # must print nothing -
A few remaining
npm auditadvisories are build-time-only transitive dependencies of native modules (tar/cacachevianode-gyp/node-pre-gyp) and are not reachable at runtime. Seedocs/SECURITY_AUDIT.md.
Found a security issue? See SECURITY.md — please report privately.
Documentation
Current entry points:
- docs/ARCHITECTURE.md
- docs/SECURITY_AUDIT.md
- SECURITY.md — vulnerability reporting policy
- CONTRIBUTING.md
- CODE_OF_CONDUCT.md
The older docs/wiki/ pages were removed from this public tree because they no longer matched the current codebase.
Notes for contributors
This repository is meant to stay readable by contributors who are new to the project.
The code should prefer:
- explicit names over short names
- small helpers over repeated inline logic
- comments that explain intent, not syntax
- platform-specific code that is isolated instead of hidden in conditionals everywhere
See CONTRIBUTING.md for the maintainer-facing conventions used in this repository.
Links
- Git: git.auxinux.ca/Auxinux/AAUL
- Project page: aaul.auxinux.ca
- Auxinux wiki: auxinux.ca/projets/projet-aaul
License
MIT — Copyright (c) 2026 André Porlier — Projet Auxinux