Command Palette
Search for a command to run...

power-manage-agent

Overview

The agent runs on every managed Linux device. Architecture:

┌──────────────────────────────────────────────┐
│                agent process                  │
│                                               │
│  handler ──→ executor (25 action types)       │
│    │            │                             │
│    ├────────────┼──→ store (SQLite, 3 migs)   │
│    │            │                             │
│  scheduler ────→│   (offline dispatch)        │
│                                               │
│  credentials (mTLS cert + key)                │
│  deviceauth (enrollment socket)               │
│  luksd (LUKS passphrase daemon)              │
│                                               │
│  All executors use sdk/ (capability library)  │
└──────────────────────────────────────────────┘

Internal packages

handler/ (3 files)

Gateway stream RPC handling:

FilePurpose
handler.goMain stream handler — receives ServerMessage, dispatches to executor, streams back AgentMessage
interfaces.goInterface definitions for executor, store, scheduler
terminal.goTerminal session setup, PTY management, WebSocket relay

executor/ (25 files)

Action execution — one file per action domain. Each executor validates input, constructs argv via the SDK capability library, executes via the injected Runner, and reports results.

FileAction(s)SDK package used
executor.goDispatch routing, executor registry
cmd.goShell, ScriptRunsys/exec
action_package.goPackage install/removepkg
action_update.goSystem updatepkg
action_repository.goRepository add/removesys/repo
action_deb.goStandalone .deb installpkg
action_rpm.goStandalone .rpm installpkg
action_appimage.goAppImage installpkg
action_flatpak.goFlatpak install/removepkg
action_service.goSystemd service enable/disable/start/stopsys/service
action_file.goFile create/write/deletesys/fs
action_directory.goDirectory create/deletesys/fs
action_reboot.goSystem rebootsys/reboot
action_user.goUser create/modify/deletesys/user
group.goGroup create/modify/deletesys/user
action_ssh.goSSH key + config managementSDK SSH helpers
sudo.goSudo/doas policy (AdminPolicy)sys/user (sudoers/doas)
wifi.goWiFi network configurationsys/network
luks.goLUKS encryption + key managementsys/encryption
lps.goLPS password managementInternal proxy to control
agent_update.goSelf-updateInternal
per_user.goPer-user action execution (user scope)sys/desktop
fs.goFilesystem helperssys/fs
helpers.goShared executor utilities
verify_stream_rpc.goStream RPC HMAC verificationverify

scheduler/ (1 file)

Offline scheduling when disconnected from gateway:

  • Persistent queue — actions in SQLite, not memory.
  • Crash recovery — replays incomplete actions on restart.
  • Clock awareness — monotonic clock surviving reboots.
  • Change detection — re-executes only when parameters changed.
  • Maintenance window respect — defers actions outside allowed windows.

store/ (2 files + 3 migrations)

SQLite persistence via database/sql with WAL mode:

FilePurpose
store.goDatabase open, migration apply, action CRUD, result storage, dispatch queue, clock state, certificates
(queries)sqlc-annotated queries

Migrations:

#FileContent
001initial_schema.sqlActions table, results table, dispatch queue, certificates, clock state
002settings.sqlAgent settings (sync interval, labels)
003action_groups.sqlAction group membership for atomic dispatch

credentials/ (1 file)

mTLS certificate and private key management:

  • Load certificate + key from SQLite store
  • Renew certificate via RenewCertificate RPC at 80% of lifetime
  • Pin certificate fingerprint to prevent substitution

deviceauth/ (2 files)

Enrollment via Unix socket at /run/pm-agent/enroll.sock:

FilePurpose
enroll.goEnrollment client — connects to socket, sends registration token, receives signed certificate
enroll_server.go(In control server) Socket server — validates token, signs CSR, returns certificate

Rate-limited to 5 attempts per minute.

luksd/ (4 files)

LUKS passphrase daemon — serves disk encryption keys to initramfs during boot:

FilePurpose
server.goUnix socket server — listens during boot window, serves keys
client.goClient for initramfs to request keys
protocol.goWire protocol — request/response format
enroller.goKey enrollment — registers new passphrases with LUKS slots

Communicates over local Unix socket only. Keys encrypted at rest with AES-GCM. Daemon shuts down after root filesystem is mounted.

archtest/ (2 files)

Architecture fitness functions:

  • No dynamic SQL (only sqlc-generated queries)
  • Protobuf JSON consistency
  • Constant-time secret comparison
  • time.Now usage patterns

Execution model

Action lifecycle

  1. Receive: Gateway streams SyncActions response with pending dispatches. Each dispatch carries action ID, type, params, and HMAC signature.
  2. Verify HMAC: Agent verifies signature against shared secret. Drops unsigned dispatches, raises SecurityAlert.
  3. Persist: Action committed to SQLite before execution — crash-safe.
  4. Execute: executor constructs argv via SDK, executes via injected Runner (sudo/doas/direct), captures stdout/stderr/exit code.
  5. Report: Streams ActionResult back to gateway with status, exit code, output (secrets redacted).
  6. Mark complete: SQLite row updated with result.

Command output streaming

Long-running actions (ScriptRun, Package update) stream output chunks (OutputChunk messages) to the gateway in real time, before completion. The gateway relays these to the web UI for live terminal-like output.

Security properties

Fail-closed design

BoundaryFailure mode
Stream connectionDisconnect + exponential backoff retry
Certificate verificationRefuse connection, no actions
HMAC signatureDrop dispatch, raise SecurityAlert
Enrollment rate limit5 attempts/minute, then reject
SQLite corruptionRefuse to start, flag for operator
LUKS daemonShut down after boot window, no persistent listen

Secret handling

  • Never log secrets: passwords, LUKS keys, tokens, private keys redacted from stdout/stderr before reporting.
  • Certificate private key: stored in SQLite, loaded into memory, never serialized to logs or results.
  • LUKS passphrases: served over local Unix socket only. Zeroed after use (secureZero()).
  • HMAC shared secret: never persisted to disk; derived from certificate handshake.

mTLS

  • Agent presents client certificate signed by control server CA.
  • Gateway verifies chain + CRL before accepting stream.
  • Certificate renewal at 80% of lifetime.
  • Agent proves private key possession during every TLS handshake.

Invariants

  1. Never execute unsigned actions — HMAC verification before every dispatch.
  2. Fail-closed on all security boundaries — stream, certificate, signature, enrollment, LUKS.
  3. Persist before execute — actions committed to SQLite before dispatching.
  4. No secrets in logs or results — redacted before reporting.
  5. ULIDs for all internal identifiers.
  6. SQLite with WAL mode — enforced by pragma test.
  7. All crypto calls carry domain-separation info tags.
  8. Credential material zeroed after usesecureZero().
  9. Generated queries only — no dynamic SQL (archtest-enforced).
  10. Constant-time comparison for all secret material (archtest-enforced).

Configuration

  • CLI flags: --gateway-url, --enroll-socket, --data-dir
  • Environment: PM_GATEWAY_URL, PM_DATA_DIR
  • Systemd: power-manage-agent.serviceAfter=network-online.target, Restart=always, RestartSec=5

ADR index

Agent-specific decisions in server/docs/adr/:

ADRDecision
0003Action signing — full envelope HMAC
0010LUKS passphrase daemon socket
0011Agent update authenticity
0012Package argv hardening
0013Enrollment trust model
0017Agent stream loop fail-closed