Capabilities

How each agent decides which features it exposes, configured entirely on the agent host.

ServerBee enforces the principle of least privilege through per-agent capabilities. Capabilities are owned by the agent host: each agent computes its own capability set from its config file (and optional CLI flags) and reports it to the server. The server can read and display these capabilities, but it cannot change them — there is no server-side toggle.

This is a deliberate trust-boundary decision. The machine that runs the agent is the only place that decides what that agent will do. A compromised or misconfigured server cannot silently turn on a remote shell, file access, or command execution on your hosts.

Capability List

ServerBee defines 11 capability bits across two risk levels. The valid mask is 2047 (bits 0..=10).

High Risk (Disabled by Default)

CapabilityKeyBit ValueDescription
Web TerminalterminalCAP_TERMINAL (1)Allow opening a remote terminal via browser
Remote ExecexecCAP_EXEC (2)Allow remote command execution
File ManagerfileCAP_FILE (64)Allow remote file browsing, editing, upload/download
Docker ManagementdockerCAP_DOCKER (128)Allow Docker container monitoring, log streaming, and container actions

These capabilities allow executing arbitrary code or accessing the filesystem on the target server, so they are disabled by default. Enable them only on trusted hosts by editing that host's agent config.

File Manager requires additional agent-side configuration (root_paths, deny_patterns) for path sandbox security. See the Agent Setup and Configuration pages for details.

Low Risk (Enabled by Default)

CapabilityKeyBit ValueDescription
Auto UpgradeupgradeCAP_UPGRADE (4)Allow remote binary upgrades
ICMP Pingping_icmpCAP_PING_ICMP (8)Allow ICMP probe tasks
TCP Probeping_tcpCAP_PING_TCP (16)Allow TCP port probe tasks
HTTP Probeping_httpCAP_PING_HTTP (32)Allow HTTP probe tasks
Security Eventssecurity_eventsCAP_SECURITY_EVENTS (256)Allow agent to report SSH login / brute-force / port-scan events (see Security Events)
Firewall Blocklistfirewall_blockCAP_FIREWALL_BLOCK (512)Allow agent to apply the server-pushed nftables blocklist. Requires root or CAP_NET_ADMIN plus the nft CLI on the host. See Firewall Blocklist
IP Qualityip_qualityCAP_IP_QUALITY (1024)Allow the Agent to run service-unlock probes and report results; optional metadata and risk enrichment runs on the Server

An agent with no [capabilities] overrides defaults to 1852 (auto upgrade + the three ping probes + security events + firewall blocklist + IP quality), which keeps the high-risk terminal, exec, file, and Docker capabilities off.

Configuring Capabilities (on the agent host)

An agent computes its capability bitmask as:

  1. Start from the built-in default set (CAP_DEFAULT = 1852).
  2. Apply the config file [capabilities] block — allow adds bits, deny removes bits. Within this layer, deny wins over allow.
  3. Apply CLI --allow-cap / --deny-cap flags on top, for ad-hoc overrides. The CLI layer wins over the config layer.
  4. Reconcile against subsystem availability: file is dropped unless [file].enabled = true with at least one root_path, and firewall_block is dropped when the startup nft probe fails (binary, kernel, or privileges missing). A reported capability always means the agent can actually serve it.

Unknown capability keys are a hard error at startup, so a typo fails fast rather than silently dropping a capability.

Config file

For installer-managed deployments, edit /opt/serverbee/etc/agent.toml on the agent host. Manual deployments may use /etc/serverbee/agent.toml or agent.toml in the working directory:

[capabilities]
# Turn on high-risk capabilities this host should expose.
allow = ["terminal", "file"]
# Strip default capabilities this host should not expose.
deny = ["ip_quality"]

Restart the agent for changes to take effect:

sudo serverbee restart agent

Environment variables

The same block can be supplied via environment variables (Figment, SERVERBEE_ prefix, __ nesting):

SERVERBEE_CAPABILITIES__ALLOW=["terminal","file"]
SERVERBEE_CAPABILITIES__DENY=["ip_quality"]

CLI flags

For one-off overrides, pass repeated flags when launching the agent:

serverbee-agent --allow-cap terminal --allow-cap file --deny-cap ip_quality

At install time

The install script accepts --caps to seed the agent's capability config during enrollment:

curl -fsSL https://raw.githubusercontent.com/ZingerLittleBee/ServerBee/main/deploy/install.sh | sudo sh -s -- agent \
  --server-url https://monitor.example.com \
  --enrollment-code YOUR_ONE_TIME_CODE \
  --caps terminal,file

Running the installer interactively also prompts for capabilities, with the defaults pre-checked.

Temporary grants

Editing the config and restarting the agent is the right way to enable a capability permanently. For short-lived, ad-hoc needs — "give me a terminal on this box for the next 30 minutes" — the agent ships a host-local CLI that temporarily turns on an off-by-default capability, then automatically turns it back off when the window ends.

Temporary grants are a host-local mechanism, fully consistent with the agent-owned trust model: only someone with a shell on the agent host can issue one. The server and the web/iOS UI remain read-only and cannot grant a capability. The server merely mirrors what the agent reports, gates control-plane requests on it, audits the change, and can fire an alert.

CLI

Run these subcommands on the agent host (typically with sudo, since they write under state_dir and the daemon usually runs as the agent user/root). They share the daemon's config (/opt/serverbee/etc/agent.toml for installer-managed deployments), so the grants file location and the maximum duration come from the same [capabilities] block.

# Temporarily enable an off-by-default capability for a bounded duration.
serverbee-agent grant terminal --for 30m --reason "debugging a stuck deploy"
serverbee-agent grant file --for 2h
serverbee-agent grant docker --for 1d

# Revoke an active grant early.
serverbee-agent revoke terminal

# List the currently active grants (cap, remaining seconds, who granted it, reason).
serverbee-agent grants
  • The duration passed to --for is <number><unit> where the unit is one of s (seconds), m (minutes), h (hours), or d (days) — e.g. 90s, 30m, 2h, 1d. --reason is optional free-form text recorded with the grant.
  • grant only turns on a capability that is currently off. Granting a capability that is already enabled in agent.toml is rejected ('terminal' is already enabled in agent.toml; nothing to grant).
  • A duration longer than temporary_max_duration (default 24h, see Configuration) is refused.
  • The CLI is one-shot: it writes the grants file and exits. The running daemon picks the change up within a few seconds — there is no need to restart the agent.

Restart and expiry semantics

Grants are persisted to <state_dir>/capability_grants.json (default /var/lib/serverbee/capability_grants.json, written 0600) with an absolute expires_at (a Unix epoch timestamp), not a relative countdown. This makes the behavior robust:

  • Survives restarts — if the agent restarts mid-window, the grant is reloaded and remains active for the rest of its original window.
  • Expires at the original deadline — restarting does not extend a grant. A grant for 30m expires 30 minutes after it was issued regardless of how many times the agent restarts in between.
  • Fails safe to OFF — if the grants file is missing, empty, corrupt, or written by an unknown schema version, the agent treats it as "no grants" and the capability stays off. A temporary grant can never strengthen the permanent set; it can only flip an otherwise-off bit on for its window.
  • Expiry bounds running work, not just new requests — when a terminal grant expires or is revoked, live terminal sessions opened under it are closed immediately (the browser sees the session end), and a security_events grant starts the agent's security pipeline the moment it becomes effective and stops it when the window closes — including grants issued while the agent was already running.

What the server sees

When a grant becomes active (or expires/is revoked), the agent re-reports its effective capability set, so the server opens or closes the gate live:

  • The change is mirrored into servers.capabilities and broadcast to browsers, so the UI updates in real time.
  • The transition is written to the audit log as capability_temporarily_granted, capability_grant_expired, or capability_grant_revoked.
  • A temporary grant of a high-risk capability (terminal, exec, file, or docker) also evaluates the event-driven capability_grant_detected alert rule. Expiry, revoke, and low-risk grants are audited but do not fire an alert.
  • Everything the agent executes for the server is re-synced against the new capability set: ping tasks are re-filtered, network probe targets and IP-quality services are re-pushed, and the firewall blocklist is reset (and re-sent only while firewall_block is still effective). Revoking a ping capability stops those probes immediately, and revoking firewall_block removes ServerBee's nftables rules from the host without waiting for a reconnect.

A grant issued while the agent is disconnected from the server is applied locally and shows up on reconnect, but it does not produce a granted audit entry or alert. The server only sees the post-reconnect capability state, not the moment of transition, so it cannot distinguish a freshly-granted capability from one that was already active. Plan around this if you rely on the alert as a tripwire.

In the web UI

While a temporary grant is active, the affected capability is shown with an amber Temporary badge and a live countdown to expiry, in both the Server Detail → Capabilities dialog and the Settings → Capabilities fleet matrix. The badge disappears automatically when the grant expires or is revoked.

Enforcement model

The agent host is the single source of truth, but enforcement happens at two boundaries for defense in depth.

Server-side gating (based on reported capabilities)

The server records what each agent reports and rejects control-plane requests for capabilities the agent has not enabled:

  • Terminal: the WebSocket upgrade is rejected with 403
  • Exec: POST /api/tasks and scheduled task runs filter out servers whose agent lacks exec and write synthetic results (exit_code = -2)
  • Auto Upgrade: POST /api/servers/{id}/upgrade returns 403 when upgrade is not reported
  • Ping and Traceroute: probe tasks are filtered by capability; traceroute requires ping_icmp
  • File Manager: file endpoints reject requests before dispatch when file is not reported
  • Docker: Docker read/action endpoints and the Docker log WebSocket routes require docker and agent runtime Docker support

Because capabilities are agent-owned, the denial reason is always agent_capability_disabled.

Agent-side enforcement

Even if a server-side request slips through, the agent re-checks capabilities locally:

  • It returns a CapabilityDenied message for unauthorized commands
  • On receiving CapabilityDenied, the server writes a synthetic result (exit_code = -1)
  • Denial events are recorded in the audit log

Reporting and display

When an agent connects (or reconnects), it sends SystemInfo carrying agent_local_capabilities. The server:

  1. Persists that value into the servers.capabilities column as a display mirror (so the dashboard can show capabilities even while the agent is offline).
  2. Broadcasts CapabilitiesChanged to all connected browsers so the UI reflects the current set in real time.

If ping-related bits change between reconnects, the server automatically re-syncs ping tasks for the agent.

Frontend behavior

The web and iOS clients display capabilities read-only:

  • Server Detail → Capabilities: a read-only summary of which capabilities the agent has enabled, with a note that they are configured in the agent config file
  • Settings → Capabilities: a read-only fleet matrix showing the enabled/disabled state per server
  • Tasks page: servers whose agent lacks exec are greyed out
  • Terminal button: hidden for servers whose agent lacks terminal
  • Files button: hidden for servers whose agent lacks file
  • Docker link: hidden for servers whose agent lacks docker

Runtime capability fields

The server response exposes three capability fields, which are all equal under the agent-owned model:

  • capabilities: the persisted mirror of the last value the agent reported
  • agent_local_capabilities: the live value reported by the connected agent
  • effective_capabilities: the value actually enforced (identical to the agent-local value)

The fields are kept distinct in the API for forward compatibility and so an offline server can still display its last-known capabilities from the mirror.