Enterprise Phase 3: RBAC, Policies, Audit Trail, and Fleet Enrollment

Enterprise Phase 3: RBAC, Policies, Audit Trail, and Fleet Enrollment

Pilot Protocol v1.5 ships the enterprise stack. Three phases of development, merged to main, covering role-based access control, network policies, structured audit logging, consent-based invites, fleet enrollment, webhook reliability, key lifecycle management, and a health endpoint. This post covers everything that shipped and why it matters for production agent deployments.

Role-Based Access Control

Every network now has three roles: owner, admin, and member. The node that creates a network is automatically the owner. Nodes that join via token or accept an invite start as members.

Authorization is checked on every mutation. The registry evaluates a three-step chain: global admin token, per-network admin token, then the node’s RBAC role. This means infrastructure operators can always override, but day-to-day management works through roles without sharing tokens.

# Promote a member to admin
pilotctl network promote 1 --node 686

# Demote an admin back to member
pilotctl network demote 1 --node 686

# Kick a node from the network
pilotctl network kick 1 --node 687

Owners cannot be kicked. Admins cannot promote or demote — only owners can change roles. This prevents privilege escalation within the network.

Network Policies

Networks can now enforce policies that restrict membership and communication:

Policies use merge-on-update semantics. Setting MaxMembers does not reset AllowedPorts. Partial updates are safe.

# Set a membership cap
pilotctl network set-policy 1 --max-members 50

# Restrict ports
pilotctl network set-policy 1 --allowed-ports 80,443,7

Consent-Based Invites

Invite-only networks no longer auto-add nodes. When you invite a node to a network, it lands in an inbox. The target node must explicitly accept or reject. This is a privacy requirement: joining a network changes your trust surface, since any member can connect to you. Nodes should not be silently enrolled.

The flow:

  1. An admin invites a node: pilotctl network invite 1 --node 686
  2. The target polls their inbox: pilotctl network invites
  3. The target accepts or rejects: pilotctl network accept 1 or pilotctl network reject 1
  4. On acceptance, the node joins the network with member role.

Invites are deduplicated (one per network per target), persisted across registry restarts, and capped at 100 per node. The old direct-join path for invite-only networks is blocked — attempting JoinNetwork on an invite-only network returns an error directing you to the consent flow.

Structured Audit Trail

Every registry mutation now emits a structured audit event via slog. When the registry runs with --log-format=json, the output is SIEM-ingestible. Filter with jq:

# Stream audit events from the registry log
tail -f /var/log/pilot-registry.log | jq 'select(.msg=="audit")'

# Example output
{
  "time": "2026-03-27T10:15:03Z",
  "level": "INFO",
  "msg": "audit",
  "audit_action": "network.created",
  "network_id": 3,
  "name": "prod-fleet",
  "join_rule": "token",
  "creator_node_id": 685
}

18 mutation handlers are instrumented:

CategoryActions
Nodesnode.registered, node.deregistered
Networksnetwork.created, network.deleted, network.renamed
Membershipnetwork.joined, network.left, member.promoted, member.demoted
Trusttrust.created, trust.revoked
Settingsvisibility.changed, task_exec.changed, hostname.changed, tags.changed
Handshakehandshake.relayed, handshake.responded
Keyskey.rotated

Every event includes the relevant IDs (node, network, peer) as structured attributes. No string parsing required.

Fleet Enrollment

Deploying N agents to the same network is now a config change, not N manual commands. The daemon accepts two new flags:

# Start a daemon that auto-joins networks 1 and 3
pilot-daemon -registry host:9000 -beacon host:9001 \
  -admin-token $TOKEN -networks 1,3 \
  -listen :4000 -encrypt

On startup, the daemon calls JoinNetwork for each configured network ID. Already-a-member errors are silently ignored (idempotent). Failed joins log a warning but do not prevent the daemon from starting. Each successful auto-join emits a network.auto_joined webhook event.

This means you can deploy a fleet with identical config files. Push the config, restart the daemons, and they all converge to the same network membership.

Webhook Reliability

The webhook system got three upgrades for enterprise event delivery:

On shutdown, the webhook client drains its queue with a 5-second timeout. Pending events are delivered if the endpoint is reachable; abandoned otherwise.

Key Lifecycle Management

Ed25519 identity keys now carry metadata:

Nodes can set their own key expiry via a signature-verified command. This enables compliance policies like “rotate keys every 90 days” without requiring infrastructure-level enforcement.

Health Endpoint and Metrics

The registry now exposes a /healthz HTTP endpoint:

curl http://registry:9000/healthz
{
  "status": "ok",
  "version": "1.5.0",
  "uptime_seconds": 86412,
  "nodes_online": 247,
  "networks_count": 12,
  "requests_total": 1482903,
  "errors_total": 37
}

Internal Prometheus-style metrics track request counts, duration, and errors per message type. The pilotctl health command queries daemon health via IPC for local monitoring.

Registry Hardening

Four hardening measures to protect the registry under adversarial conditions:

Secure Channel Authentication

Encrypted tunnels now verify identity during the ECDH handshake. Both sides present Ed25519 signatures, and the tunnel records the authenticated PeerNodeID. This binds the ephemeral X25519 session key to a persistent identity — you know who you are talking to, not just that the channel is encrypted.

Backward compatibility is preserved: peers that do not support authentication fall back to unauthenticated encryption. No flag day required.

Network Management via pilotctl

All network operations are now available through pilotctl, routed through the daemon’s IPC socket. Every command is signed by the daemon’s Ed25519 identity.

# List your networks
pilotctl network list

# Join a token-gated network
pilotctl network join 1 --token my-secret

# Leave a network
pilotctl network leave 1

# List members
pilotctl network members 1

# Invite a node (admin/owner only)
pilotctl network invite 1 --node 686

# Check pending invites
pilotctl network invites

# Accept or reject
pilotctl network accept 1
pilotctl network reject 1

Test Coverage

The enterprise features ship with 43 new tests across 8 test files, all passing with -parallel 4. Coverage includes:

Release

All enterprise features are in v1.5.0-rc1, available now via the install script:

# Install release candidate
PILOT_RC=1 curl -fsSL https://pilotprotocol.network/install.sh | sh

# Or wait for the stable release
curl -fsSL https://pilotprotocol.network/install.sh | sh

The release pipeline is fully automated: push a v* tag, and GitHub Actions runs the full test suite, builds binaries for 4 platforms (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64), executes an integration harness with a live registry + beacon + 2 daemons, and publishes the release with checksums.

What Is Next

The enterprise stack is the foundation. What follows:

Try the Enterprise Features

Install the release candidate and create your first RBAC-governed network.

Get Started  ·  Network Docs  ·  Open Console