SEC-001: Registration tokens stored as SHA-256 hashes. Migration 046 adds token_hash column, backfills from plaintext, drops token column. All queries use hash. Token plaintext shown once at creation (reveal panel in UI), never retrievable again. Follows the refresh-token pattern. SEC-005: README "no sanitization" claims corrected — code correctly sanitizes against log injection (ANSI stripping, control char replacement, truncation). Wording updated to match reality. SEC-008: Command creation with idempotency_key uses ON CONFLICT DO NOTHING instead of blind insert. Prevents duplicate command execution. Trust model: Ed25519 key rotation documented — signing_keys table supports multiple concurrent active keys with a sliding window for zero-downtime rotation. OSV.dev ecosystem coverage updated (apt, dnf added).
13 KiB
RedFlag
Self-hosted update management for operators who own their stack.
v0.2.1.1 — May 2026 · MIT License
NOT YET ANNOUNCED — Tags do not imply stability. Massive flux. Use at your own risk.
The update manager is part of your attack surface. Most homelab tooling ignores this. RedFlag doesn't.
Every command the server issues is Ed25519-signed. Agents verify the signature, check the nonce, validate the timestamp, and reject anything they've seen before. The signing key never leaves your server. Hardware binding means a stolen agent config doesn't work on a different machine. You can read the security model in the code, not in marketing copy.
It also just manages your updates — across Linux and Windows, including Docker containers running on those hosts — from a single dashboard, with a human approval step before anything gets installed.
Next up is the supply-chain gate: the server mints a signed capability token over the exact resolved package closure, and a network-less privileged executor verifies the signature and artifact hashes before anything installs. Wired up and in live testing now — soon.
ConnectWise charges $50/agent/month. RedFlag doesn't.
![]() |
![]() |
![]() |
|---|
Quick Start
Server
git clone https://github.com/Fimeg/RedFlag.git
cd RedFlag
cp config/.env.bootstrap.example config/.env
docker-compose build && docker-compose up -d
Open http://localhost:31336, complete the setup wizard, then restart:
docker-compose down && docker-compose up -d
Agent
Get a registration token from Settings → Token Management, then:
Linux / macOS:
curl -sfL https://your-server.com/install | sudo bash -s -- your-registration-token
Windows:
iwr https://your-server.com/install.ps1 | iex your-registration-token
What It Manages
| Platform | Package Managers / Scanners |
|---|---|
| Linux | APT, DNF, Docker (socket) |
| Windows | Winget, Windows Update (COM), Docker (socket) |
Agents run at the OS level and query the Docker socket directly — there's no separate container agent. Agents are pull-based: they check in every 5 minutes and execute what the server has approved. The server never initiates a connection.
Trust Model
Agents register with a one-time token plus a hardware fingerprint. The server stores the fingerprint; future check-ins that don't match the registered machine are rejected. This prevents config copying between hosts.
On first connect, the agent fetches and caches the server's Ed25519 public key (TOFU). Every subsequent command is verified against it. The signing_keys table supports multiple concurrent active keys with a sliding window — a new key is promoted to primary while the old key remains active, so agents that cached the previous key continue verifying successfully until the operator deactivates it. Rotation is zero-downtime; no coordinated restart required.
Every command includes a signed nonce with a 10-minute validity window. The agent tracks executed nonces and rejects replays, including from an attacker who intercepted a valid command.
Agent-server communication runs over HTTPS. The Ed25519 signing model is a defense-in-depth layer on top of that — commands can't be forged or replayed even if traffic is somehow intercepted or TLS is terminated at a proxy. The signing model doesn't assume the transport is trustworthy. Cert pinning and enforced TLS verification are on the roadmap.
Before a package is installed: the agent fetches the expected SHA-256 from the server, downloads the artifact, verifies the hash. Mismatch blocks the install. OSV.dev is queried for known vulnerabilities at discovery time (async, deduped) for npm, PyPI, apt, and dnf packages — results are visible in the dashboard before approval.
Refresh-token rotation. Each renewal mints a new refresh token and marks the old one consumed. Replaying a consumed token whose successor was also consumed means theft — the server revokes the entire token family and logs a security event. Agent crash-before-save is covered by accept-previous-once grace: a consumed token whose successor is still unconsumed gets a fresh one, not a revocation.
Machine-bound renewal. The renewal endpoint now checks X-Machine-ID against the registered host, exactly as command endpoints do. A stolen config.json cannot mint access tokens from an unregistered machine — a mismatch returns 403 with a logged machine_id_mismatch security event. The agent surfaces this as a critical event, not a quiet backoff.
Security Health is surfaced as a dashboard panel on each agent — signing status, nonce protection, machine binding violations, command validation — so the posture is visible without digging through logs.
Architecture
┌─────────────────┐
│ Web Dashboard │ React + TypeScript
│ Port: 31336 │
└────────┬────────┘
│ HTTPS + JWT + Machine Binding
┌────────▼────────┐
│ Server (Go) │ PostgreSQL · Ed25519 Signing Service
│ Port: 31337 │
└────────┬────────┘
│ Pull-based (agents check in, not the reverse)
├──────────────────┐
┌────────▼────────┐ ┌──────▼──────────┐
│ Linux Agent │ │ Windows Agent │
│ │ │ │
│ APT / DNF │ │ Winget / WUA │
│ Docker socket │ │ Docker socket │
└─────────────────┘ └─────────────────┘
Features
- Approval workflow — updates queue for human review before anything runs
- Maintenance windows — day/time gates on when installs can proceed
- Upstream tracking — polls GitHub, Gitea, GitLab, Bitbucket for new releases; flags EOL drift
- Drift detection — knows what should be installed vs. what is, bridges the gap into update packages
- Agent self-update — SHA-256 → signature → atomic binary swap → service restart, reconciled server-side
- Dependency dry-run — checks before installing, not after
- Idempotent installer — re-running won't create duplicate agents
- Proxy support — HTTP/HTTPS/SOCKS5 for restricted networks
- Native services — systemd on Linux, Windows Services on Windows
- Full audit trail — all operations logged with context, sanitized against log injection
Status
Working in production:
- Linux and Windows agent registration and update management
- APT, DNF, Winget, Windows Update, Docker image scanning
- Dry-run dependency checking
- Real-time heartbeat and rapid polling
- Ed25519 key rotation and replay protection
- Supply chain hash verification and OSV.dev checks
- Maintenance windows
- Upstream version tracking (GitHub, Gitea, GitLab, Bitbucket, Repology, endoflife.date)
- Agent self-update with rollback
Known issues:
- Winget detection occasionally misses packages (Windows API limitation)
- Some Windows Updates reappear after installation (Windows Update quirk, not ours)
- No AUR, Snap, Flatpak, or Homebrew support yet
- macOS agent binaries not yet signed
- Mobile dashboard is usable, not optimized
Updating
git pull && docker-compose down && docker-compose build --no-cache && docker-compose up -d
Agent self-update runs from the dashboard. Requires a real service manager (systemd on Linux, SCM on Windows). Container-only agent deployments can't self-update through this path — redeploy with the new image instead.
If a self-update times out, the previous binary is preserved at <binary>.bak on the agent host. Restore manually and restart the service.
Nuclear option (full reset)
docker-compose down -v --remove-orphans && \
rm config/.env && \
docker-compose build --no-cache && \
cp config/.env.bootstrap.example config/.env && \
docker-compose up -d
This wipes all data including the database. Re-register agents afterward with new tokens.
Upgrading from pre-v0.1.20
Old installations used different paths. Clean reinstall is the supported migration path.
Remove old artifacts if present:
sudo rm -rf /etc/aggregator/ /usr/local/bin/aggregator-agent /var/lib/aggregator/
Then install fresh with the standard one-liner.
Philosophy
RedFlag follows ETHOS:
- Honest — what you see is what you get
- Transparent — errors logged with full context, sanitized against injection
- Secure — hardware binding, cryptographic verification, local-only logging
- Open standards — no vendor lock-in, no cloud dependency, no telemetry
The maintainer runs this on their own infrastructure. Releases are versioned, migrations are idempotent. If something breaks, the error shows up in full — not swallowed into a generic failure message. Log output is sanitized against injection (ANSI stripping, control character replacement, field truncation) but the content is preserved.
Built for operators who'd rather own the problem than outsource it.
Changelog
v0.2.1.0 (May 2026)
- Helper privilege split:
redflag-helpernow invoked viasudo systemd-run --pipeas its own transient service, escaping the agent'sProtectSystem=strictsandbox. The helper and its dnf/apt children see the real root filesystem. - Token-is-the-command: all package-manager operations now routed through one of two paths — discovery (scan, dry-run, hash-resolve) through a unified
DiscoveryRunner, mutation (install, upgrade) exclusively throughconsumer.go → systemd-run --pipe → redflag-helper. The agent no longer has code to rundnf install -yorapt install -ydirectly. SecureCommandExecutordeleted — replaced byDiscoveryRunnerfor discovery and the helper for mutation.- Sudoers narrowed: agent user can no longer run mutation commands (
dnf install *,apt install -y *, etc.) — only discovery + the helper invocation. EcosystemConfigregistry: adding a new package ecosystem (AUR, Snap, Flatpak, Homebrew) means one config entry and implementing the discovery interface. Mutation is automatic via the token path.- Hash verification fail-closed: empty expected hash now returns an error instead of silently passing.
Installerinterface shrunk from 7 methods to 4:IsAvailable,GetPackageType,DryRun,VerifyHash.apt-get→aptthroughout (ecosystem config, sudoers, discovery commands).
v0.2.0.7 (May 2026)
- Refresh-token rotation with accept-previous-once crash-recovery grace
- Machine-bound renewal path closes stolen-config.json replay attack
- Typed sentinel errors for auth failures in agent polling loop
- Loud terminal-state detection: revoked refresh tokens and machine-ID mismatches surface as critical events, not silent retries
- No-more-unsigned-binaries: signing-disabled path removed from orchestrator; unsigned fallback removed from download handler
v0.2.0.6 (May 2026)
- Agent cold-start trust root: signed release manifest verified before first binary execution
- Supply-chain hash registry (Layer 1): expected SHA-256 stored server-side, verified by agents before install
- Drift → UpdatePackage bridge: drifted bindings automatically create pending update packages
- Upstream tracking UI with release-source adapters (GitHub, Gitea, GitLab, Bitbucket)
- Attention panel: surfaces offline agents, failed updates, EOL drift, upstream movement
v0.2.0.0 (May 2026)
- Maintenance windows for scheduling/gating installs
- OSV.dev supply chain checks at approval time (npm/PyPI)
- Ed25519 key rotation with TTL-based auto-refresh
- Command signing v3: agent_id binding prevents relay attacks
- Replay protection via signed nonces (10-minute window)
- Semver-aware version comparison
- Binary path traversal protection
- Machine ID rebind endpoint for hardware migration
- 170+ tests across 18 packages
- Command lifecycle v2:
receivedstate, disk-persisted dedup, authenticated binary download
v0.1.27 (December 2025)
- Hardware binding with machine fingerprinting
- Ed25519 signing for all commands and updates
- Error transparency system (ETHOS #1)
- Circuit breakers and retry logic
- Agent auto-update fully implemented
- Rate limiting (60 req/min, configurable)
- Command deduplication and idempotency
License
MIT — see LICENSE.
Third-party: Windows Update integration based on windowsupdate (Apache 2.0).








