agent-info
GitHubBreeze RMM Go Agent 的快速参考文档,涵盖架构、CLI命令、配置及通信协议。用于开发、调试Agent代码或理解其与API的交互机制。
Trigger Scenarios
Install
npx skills add LanternOps/breeze --skill agent-info -g -y
SKILL.md
Frontmatter
{
"name": "agent-info",
"description": "Quick reference for the Breeze RMM Go agent architecture, commands, configuration, build process, and data flows. Use when working on agent code, debugging agent issues, or understanding how the agent communicates with the API."
}
Breeze RMM Agent Reference
The Go agent runs on managed devices (Windows, macOS, Linux) and communicates with the Hono API server.
Architecture Overview
agent/
cmd/breeze-agent/main.go # Entry point (cobra CLI)
agent.yaml # Config file (auto-generated on enroll)
internal/
config/config.go # Config loading/saving (viper)
heartbeat/heartbeat.go # Main run loop, command dispatch, inventory
websocket/client.go # Real-time WebSocket connection
collectors/ # System data collectors (per-platform)
remote/
tools/ # Command handlers (processes, services, files, etc.)
desktop/ # WebRTC remote desktop
terminal/ # PTY management (per-platform)
updater/ # Self-update mechanism
scripts/ # Script execution runner
pkg/api/client.go # HTTP client for enrollment
CLI Commands
breeze-agent run # Start the agent
breeze-agent enroll <key> --server URL # Enroll with server
breeze-agent version # Print version
breeze-agent status # Check enrollment status
breeze-agent --config /path/to/file # Use custom config
Config File (agent.yaml)
agent_id: <sha256-hash>
auth_token: brz_<hex-token>
org_id: <uuid>
site_id: <uuid>
server_url: http://localhost:3001
heartbeat_interval_seconds: 60
metrics_interval_seconds: 30
enabled_collectors:
- hardware
- software
- metrics
- network
- Location:
/etc/breeze/agent.yaml(Linux),/Library/Application Support/Breeze/agent.yaml(macOS),%ProgramData%\Breeze\agent.yaml(Windows) - Permissions: Directory 0700, file 0600 (contains auth token)
- Auth token:
brz_prefix, stored as SHA-256 hash in DB (agentTokenHashcolumn)
Communication Channels
1. Heartbeat (HTTP Polling)
POST /api/v1/agents/:id/heartbeatevery 60s- Sends: CPU, RAM, disk metrics + status + agent version
- Receives: pending commands, config updates, upgrade instructions
- Auth:
Authorization: Bearer brz_<token>
2. WebSocket (Real-time)
ws(s)://server/api/v1/agent-ws/:agentId/ws?token=brz_<token>- Receives commands instantly (no polling delay)
- Sends command results + terminal output
- Auto-reconnect with exponential backoff (1s initial, 60s max, 0.3 jitter)
- Ping/pong keepalive: ping every 54s, pong timeout 60s
- Max message size: 512KB
3. Inventory (HTTP Push)
- Sent on startup + every 15 minutes
PUT /api/v1/agents/:id/software- software inventoryPUT /api/v1/agents/:id/disks- disk inventoryPUT /api/v1/agents/:id/network- network adaptersPUT /api/v1/agents/:id/connections- active connectionsPUT /api/v1/agents/:id/patches- pending + installed patchesPUT /api/v1/agents/:id/eventlogs- event logs (every 5 min)
Command Types
All commands use: {id: string, type: string, payload: map[string]any}
Results use: {type: "command_result", commandId: string, status: string, result: any}
Process Management
| Type | Handler | Payload |
|---|---|---|
list_processes |
ListProcesses |
{search, sortBy, sortDir, page, limit} |
get_process |
GetProcess |
{pid} |
kill_process |
KillProcess |
{pid} |
Service Management
| Type | Handler | Payload |
|---|---|---|
list_services |
ListServices |
{search, page, limit} |
get_service |
GetService |
{name} |
start_service |
StartService |
{name} |
stop_service |
StopService |
{name} |
restart_service |
RestartService |
{name} |
File Operations
| Type | Handler | Payload |
|---|---|---|
file_list |
ListFiles |
{path} |
file_read |
ReadFile |
{path} (max 1MB) |
file_write |
WriteFile |
{path, content, encoding} (text or base64) |
file_delete |
DeleteFile |
{path, recursive} |
file_mkdir |
MakeDirectory |
{path} |
file_rename |
RenameFile |
{oldPath, newPath} |
Terminal (PTY)
| Type | Handler | Payload |
|---|---|---|
terminal_start |
StartTerminal |
{sessionId, cols, rows} |
terminal_data |
WriteTerminal |
{sessionId, data} |
terminal_resize |
ResizeTerminal |
{sessionId, cols, rows} |
terminal_stop |
StopTerminal |
{sessionId} |
Terminal output streams via WebSocket: {type: "terminal_output", sessionId, data}
Terminal commands use term- prefix IDs and skip DB persistence.
Windows-Specific
| Type | Handler | Payload |
|---|---|---|
event_logs_list |
ListEventLogs |
{} |
event_logs_query |
QueryEventLogs |
{logName, level, source, eventId, query, page, limit} (query = XPath, exclusive with level/source/eventId) |
event_log_get |
GetEventLogEntry |
{logName, recordId} |
tasks_list |
ListTasks |
{folder, page, limit} |
task_get |
GetTask |
{name, path} |
task_run |
RunTask |
{name, path} |
task_enable |
EnableTask |
{name, path} |
task_disable |
DisableTask |
{name, path} |
registry_keys |
ListRegistryKeys |
{hive, path} |
registry_values |
ListRegistryValues |
{hive, path} |
registry_get |
GetRegistryValue |
{hive, path, name} |
registry_set |
SetRegistryValue |
{hive, path, name, type, data} |
registry_delete |
DeleteRegistryValue |
{hive, path, name} |
System Commands
| Type | Handler | Payload |
|---|---|---|
reboot |
Reboot |
{delay} |
shutdown |
Shutdown |
{delay} |
lock |
Lock |
{} |
collect_software |
inline | {} |
start_desktop |
desktop.SessionManager |
{sessionId, offer} |
stop_desktop |
desktop.SessionManager |
{sessionId} |
Enrollment Flow
- User runs:
breeze-agent enroll <key> --server <url> - Agent collects hardware info via
collectors.HardwareCollector POST /api/v1/agents/enrollwith enrollment key + device info- Server returns:
{agentId, authToken, orgId, siteId, config} - Agent saves config to
agent.yamlwith 0600 permissions - Auth token stored as SHA-256 hash in
devices.agentTokenHash
Command Execution Flow
API creates command (DB) → dispatches via WebSocket (or heartbeat response)
→ Agent receives {id, type, payload}
→ heartbeat.executeCommand() dispatches to handler
→ Handler returns tools.CommandResult {status, stdout, stderr, error}
→ Result sent via WebSocket AND HTTP POST to /agents/:id/commands/:cmdId/result
Build & Development
cd agent
make run # Build and run locally
make build-all # Cross-compile (windows/linux/darwin, amd64/arm64)
go build -o /tmp/breeze-agent-bin ./cmd/breeze-agent/ # Quick build
/tmp/breeze-agent-bin run # Run built binary (needs 'run' subcommand!)
Key Dependencies
github.com/gorilla/websocket- WebSocket clientgithub.com/spf13/cobra- CLI frameworkgithub.com/spf13/viper- Configuration managementgithub.com/shirou/gopsutil- System metrics collectiongithub.com/pion/webrtc- Remote desktop (WebRTC)
Platform-Specific Files
Many packages have platform-specific implementations:
*_darwin.go- macOS*_linux.go- Linux*_windows.go- Windows*_other.go- Stub for unsupported platforms
Notable: Terminal PTY uses cgo on macOS (pty_darwin.go) for posix_openpt/grantpt/unlockpt/ptsname.
Dev Push (Fast Binary Update)
Bypasses the full release cycle for rapid agent iteration: build → upload → restart in seconds.
Flow
make dev-push (reads .env.dev for defaults)
1. Cross-compile binary (queries device OS/arch from API)
2. Upload binary via POST /api/v1/dev/push (multipart, JWT or API key auth)
3. API saves to temp dir, computes SHA256, creates ephemeral download URL (5-min TTL)
4. API sends `dev_update` command to agent via WebSocket
5. Agent disables auto_update (persisted to config), downloads binary, verifies checksum
6. Agent backs up current binary, replaces, restarts
Authentication
Dev-push accepts two auth methods (JWT or API key). Prefer the API key — it doesn't expire hourly.
Option 1: API key (recommended) — Set in .env.dev:
# .env.dev (gitignored, at repo root)
BREEZE_API_KEY=brz_XXXX # from web UI → Settings → API Keys
BREEZE_DEV_DEVICE=<device-uuid>
BREEZE_API_URL=http://localhost:3001
Then just cd agent && make dev-push — no extra args needed.
The dev-push route sends X-API-Key header when the token starts with brz_, Authorization: Bearer otherwise.
Option 2: JWT — For endpoints that don't accept API keys (e.g. /devices/:id/diagnostic-logs):
# Generate a 1-hour JWT token:
./agent/scripts/gen-jwt.sh # uses first user in DB
./agent/scripts/gen-jwt.sh todd@olivetech.co # specific user
# Use it:
export TOKEN=$(./agent/scripts/gen-jwt.sh)
curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/api/v1/devices/<id>/diagnostic-logs
JWT requirements (if generating manually):
- Library:
jose(NOTjsonwebtoken) - Algorithm:
HS256 - Secret:
JWT_SECRETfrom.env(at repo root) - Required claims:
iss: "breeze",aud: "breeze-api" - Required fields:
sub(real user UUID fromuserstable),email,scope: "system",type: "access" - User lookup:
docker exec breeze-postgres-dev psql -U breeze -d breeze -t -c "SELECT id, email FROM users LIMIT 3;"
Usage
# Easiest — uses .env.dev defaults (API key + device ID):
cd agent && make dev-push
# Override device or token:
make dev-push DEVICE=<deviceId>
make dev-push AUTH_TOKEN=<jwt-or-api-key>
# Manual — build + push separately:
cd agent
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-X main.version=dev-$(date +%s)" \
-o bin/breeze-agent-dev ./cmd/breeze-agent
curl -X POST http://localhost:3001/api/v1/dev/push \
-H "X-API-Key: brz_XXXX" \
-F "agentId=DEVICE_ID" \
-F "binary=@bin/breeze-agent-dev"
Command Reference
| Type | Payload | Notes |
|---|---|---|
dev_update |
{downloadUrl, checksum, version} |
Disables auto_update, triggers UpdateFromURL |
Key Files
| File | Purpose |
|---|---|
apps/api/src/routes/devPush.ts |
Upload endpoint + ephemeral download route |
agent/internal/heartbeat/handlers_devupdate.go |
handleDevUpdate — disables auto-update, triggers updater |
agent/internal/updater/updater.go → UpdateFromURL() |
Direct URL download (skips version-lookup API) |
agent/internal/config/config.go → SaveTo() |
Persists auto_update flag across restarts |
Guard Rails
- Production disabled: only works when
NODE_ENV !== 'production'orDEV_PUSH_ENABLED=true - Auto-update disabled:
dev_updatesetsauto_update: falsein config to prevent heartbeat from overwriting the dev binary - Re-enable: set
auto_update: truein agent.yaml or re-enroll the device - Ephemeral: download tokens expire after 5 minutes and files auto-cleanup
Dev Iteration Loop
The intended workflow for debugging/developing agent code: fetch logs → fix code → build & push → check logs.
┌─────────────────────────────────────────────────────────┐
│ 1. FETCH LOGS — see what's happening on the agent │
│ │
│ # Get recent agent logs (shipped from agent → DB) │
│ GET /api/v1/devices/:id/diagnostic-logs │
│ ?level=warn,error │
│ ?component=updater │
│ ?search=keyword │
│ ?since=2026-02-15T00:00:00Z │
│ │
│ # Or bump log level for more detail (auto-reverts) │
│ Send `set_log_level` command: │
│ {level: "debug", durationMinutes: 30} │
│ │
│ # Direct DB query for fastest access: │
│ psql: SELECT * FROM agent_logs │
│ WHERE device_id = '<id>' │
│ ORDER BY timestamp DESC LIMIT 50; │
├─────────────────────────────────────────────────────────┤
│ 2. FIX CODE — edit Go source in agent/internal/... │
├─────────────────────────────────────────────────────────┤
│ 3. BUILD & DEPLOY — push new binary to live agent │
│ │
│ cd agent && make dev-push │
│ # reads .env.dev for API key + device ID │
│ │
│ Agent restarts with new binary in ~5 seconds. │
├─────────────────────────────────────────────────────────┤
│ 4. CHECK LOGS — verify the fix │
│ │
│ GET /api/v1/devices/:id/diagnostic-logs │
│ ?since=<deploy-time> │
│ │
│ Look for: │
│ - New agent version in logs (dev-<timestamp>) │
│ - Error/warn messages resolved │
│ - Expected behavior in component logs │
│ │
│ If not fixed → loop back to step 1 │
└─────────────────────────────────────────────────────────┘
API endpoints used in the loop:
| Step | Endpoint | Auth | Purpose |
|---|---|---|---|
| Fetch logs | GET /api/v1/devices/:id/diagnostic-logs |
JWT (gen-jwt.sh) |
Query shipped agent logs with filters |
| Bump log level | set_log_level command via WS |
Agent | Temporarily increase verbosity |
| Build & push | make dev-push |
API key (.env.dev) |
Upload new binary, trigger agent restart |
| Check logs | GET /api/v1/devices/:id/diagnostic-logs?since=... |
JWT (gen-jwt.sh) |
Verify fix after deploy |
Log shipping pipeline: Agent logging package → buffer → POST /api/v1/agents/:id/logs → agent_logs table → queryable via /devices/:id/diagnostic-logs
Filters: ?level=, ?component=, ?search=, ?since=, ?until=, ?page=, ?limit= (max 1000)
Agent Version Management & Upgrades
Release Pipeline
Tag commit (v*) → GitHub Actions builds all platforms → Assets uploaded to GitHub Releases
→ API syncs versions via POST /api/v1/agent-versions/sync-github
→ Registered in `agent_versions` table with download URLs + SHA256 checksums
→ Agents auto-upgrade via heartbeat response `upgradeTo` field
Version Registry (Database)
The agent_versions table tracks each binary per platform/arch/component.
| Endpoint | Auth | Purpose |
|---|---|---|
GET /agent-versions/latest?platform=X&arch=Y |
None | Get latest version info + download URL |
GET /agent-versions/:version/download?platform=X&arch=Y |
None | Get download URL for specific version |
POST /agent-versions |
System | Manually register a version |
POST /agent-versions/sync-github?version=vX.Y.Z |
System | Sync from GitHub releases |
Auto-Upgrade via Heartbeat
On each heartbeat, the API compares the agent's reported version against the latest registered version:
- Semver builds (e.g.
0.12.0): upgraded when a newer version exists (compareAgentVersions) - Dev builds (
dev-*): always offered the latest release version (dev versions can't be semver-compared) - The heartbeat response includes
upgradeTo: "X.Y.Z"which the agent acts on
Key code: apps/api/src/routes/agents/heartbeat.ts lines ~188-198
Self-Update Mechanism (Agent Side)
- Agent receives
upgradeToin heartbeat response - Calls
GET /api/v1/agent-versions/{version}/download?platform=X&arch=Yto get URL + checksum - Downloads binary from URL (GitHub CDN, S3, or local API)
- Verifies SHA256 checksum
- Backs up current binary to
.backup - Platform-specific restart:
- Windows: Spawns detached PowerShell script (stop service → copy binary → start service)
- Linux:
systemctl restart breeze-agent - macOS:
launchctl kickstart -k system/com.breeze.agent - Fallback:
syscall.Exec()to replace process
- On failure, restores from
.backup
Key files: agent/internal/updater/updater.go, agent/internal/heartbeat/heartbeat.go
Upgrading from Dev Build to Release
Dev-push sets auto_update: false in the agent config to prevent heartbeat from overwriting dev binaries. To return a device to the release track:
Option 1 — Edit agent config (on the device):
Set auto_update: true in agent.yaml, then restart the agent. The next heartbeat will trigger upgrade to latest release.
Option 2 — Re-enroll the device (clean slate).
Note: The compareAgentVersions function returns 0 for dev versions (can't parse dev-* as semver), so the heartbeat has special handling: if agentVersion.startsWith('dev-'), it always sets upgradeTo to the latest release regardless of comparison.
Binary Serving Modes
Controlled by BINARY_SOURCE env var:
| Mode | Behavior |
|---|---|
github (default) |
Redirects agent to GitHub Releases CDN |
local + S3 |
Generates presigned S3 URLs |
local (disk) |
Serves from ./agent/bin/ (or AGENT_BINARY_DIR) |
Install Scripts
| Platform | Script | Binary Location | Service |
|---|---|---|---|
| Linux | agent/scripts/install/install-linux.sh |
/usr/local/bin/breeze-agent |
systemd (breeze-agent.service) |
| macOS | agent/scripts/install/install-darwin.sh |
/usr/local/bin/breeze-agent |
launchd (com.breeze.agent.plist) |
| Windows | agent/scripts/install/install-windows.ps1 |
C:\Program Files\Breeze\breeze-agent.exe |
Windows Service (BreezeAgent) |
Dynamic installer endpoint: GET /api/v1/agents/install.sh — detects OS/arch, downloads binary, enrolls, installs service.
Security
- Agent auth:
brz_token inAuthorization: Bearerheader - Server validates via SHA-256 hash comparison (same pattern as API keys)
- Rate limited: 120 req/60s per agent via Redis sliding window
- WebSocket token passed as query parameter, validated on connect
- Config file restricted to owner-only (0600)
- Mutating commands are audit-logged with
actorType: 'agent'
Version History
- 10bb1af Current 2026-08-20 08:49


