shepherd-launcher/crates/shepherdd
Albert Armea 60d5d9f1f1 lint
2026-01-01 22:55:53 -05:00
..
src lint 2026-01-01 22:55:53 -05:00
tests Fix CI 2025-12-31 00:59:21 -05:00
Cargo.toml :%s/daemon/service/g 2025-12-29 12:52:03 -05:00
README.md shepherdd shouldn't require root to run 2025-12-31 22:33:44 -05:00

shepherdd

The Shepherd background service.

Overview

shepherdd is the authoritative policy and enforcement service for the Shepherd ecosystem. It is the central coordinator that:

  • Loads and validates configuration
  • Evaluates policy to determine availability
  • Manages session lifecycles
  • Enforces time limits
  • Emits warnings and events
  • Serves multiple clients via IPC

Key principle: shepherdd is the single source of truth. User interfaces only request actions and display state—they never enforce policy independently.

Architecture

┌──────────────────────────────────────────────────────────────┐
│                         shepherdd                            │
│                                                              │
│  ┌─────────────┐   ┌─────────────┐   ┌────────────────────┐  │
│  │   Config    │   │    Store    │   │    Core Engine     │  │
│  │   Loader    │──▶│  (SQLite)   │──▶│ (Policy + Session) │  │
│  └─────────────┘   └─────────────┘   └──────────┬─────────┘  │
│                                                 │            │
│  ┌─────────────┐  ┌─────────────┐               │            │
│  │    Host     │  │     IPC     │◀──────────────┘            │
│  │   Adapter   │◀─│   Server    │                            │
│  │  (Linux)    │  │             │                            │
│  └──────┬──────┘  └──────┬──────┘                            │
│         │                │                                   │
│         │      Unix Domain Socket                            │
│         │                │                                   │
└─────────┼────────────────┼───────────────────────────────────┘
          │                │
          ▼                ▼
    Supervised        ┌─────────┐  ┌─────────┐  ┌─────────┐
    Applications      │Launcher │  │   HUD   │  │  Admin  │
                      │   UI    │  │ Overlay │  │  Tools  │
                      └─────────┘  └─────────┘  └─────────┘

Usage

Running

# With default config location
shepherdd

# With custom config
shepherdd --config /path/to/config.toml

# Override socket and data paths
shepherdd --socket /tmp/shepherdd.sock --data-dir /tmp/shepherdd-data

# Debug logging
shepherdd --log-level debug

Command-Line Options

Option Default Description
-c, --config /etc/shepherdd/config.toml Configuration file path
-s, --socket From config IPC socket path
-d, --data-dir From config Data directory
-l, --log-level info Log verbosity

Environment Variables

Variable Description
SHEPHERD_SOCKET Override socket path (default: $XDG_RUNTIME_DIR/shepherdd/shepherdd.sock)
SHEPHERD_DATA_DIR Override data directory (default: $XDG_DATA_HOME/shepherdd)
RUST_LOG Tracing filter (e.g., shepherdd=debug)

Main Loop

The service runs an async event loop that processes:

  1. IPC messages - Commands from clients
  2. Host events - Process exits, window events
  3. Timer ticks - Check for warnings and expiry
  4. Signals - SIGHUP for config reload, SIGTERM for shutdown
┌────────────────────────────────────────────────────┐
│                    Main Loop                       │
│                                                    │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐   │
│  │   IPC   │ │  Host   │ │  Timer  │ │ Signal  │   │
│  │ Channel │ │ Events  │ │  Tick   │ │ Handler │   │
│  └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘   │
│       │           │           │           │        │
│       └───────────┴─────┬─────┴───────────┘        │
│                         │                          │
│                         ▼                          │
│              ┌──────────────────┐                  │
│              │  Process Event   │                  │
│              └──────────────────┘                  │
│                         │                          │
│                         ▼                          │
│              ┌──────────────────┐                  │
│              │ Broadcast Events │                  │
│              └──────────────────┘                  │
└────────────────────────────────────────────────────┘

Command Handling

Client Commands

Command Description Role Required
GetState Get full state snapshot Any
ListEntries Get available entries Any
Launch Start a session Shell/Admin
StopCurrent End current session Shell/Admin
ReloadConfig Hot-reload configuration Admin
SubscribeEvents Subscribe to event stream Any
GetHealth Health check Any
SetVolume Set system volume Shell/Admin
GetVolume Get volume info Any

Response Flow

Client Request
      │
      ▼
Role Check ──────▶ Denied Response
      │
      ▼
Command Handler
      │
      ▼
Core Engine
      │
      ▼
Response + Events ──────▶ Broadcast to Subscribers

Session Lifecycle

Launch

  1. Client sends Launch { entry_id }
  2. Core engine evaluates policy
  3. If denied: respond with reasons
  4. If approved: create session plan
  5. Host adapter spawns process
  6. Session transitions to Running
  7. SessionStarted event broadcast

Enforcement

  1. Timer ticks every 100ms
  2. Core engine checks warnings and expiry
  3. At warning thresholds: WarningIssued event
  4. At deadline: initiate graceful stop
  5. After grace period: force kill
  6. SessionEnded event broadcast

Termination

  1. Stop triggered (expiry, user, admin, process exit)
  2. Host adapter signals process (SIGTERM)
  3. Wait for grace period
  4. Force kill if needed (SIGKILL)
  5. Record usage in store
  6. Set cooldown if configured
  7. Clear session state

Configuration Reload

On SIGHUP or ReloadConfig command:

  1. Parse new configuration file
  2. Validate completely
  3. If invalid: keep old config, log error
  4. If valid: atomic swap to new policy
  5. Emit PolicyReloaded event
  6. Current session continues with original plan

Health Monitoring

The service exposes health status via GetHealth:

{
  "status": "healthy",
  "policy_loaded": true,
  "store_healthy": true,
  "host_healthy": true,
  "uptime_seconds": 3600,
  "current_session": null
}

Logging

Uses structured logging via tracing:

2025-01-15T14:30:00.000Z INFO  shepherdd: Starting shepherd service
2025-01-15T14:30:00.050Z INFO  shepherd_config: Configuration loaded entries=5
2025-01-15T14:30:00.100Z INFO  shepherd_ipc: IPC server listening path=/run/shepherdd/shepherdd.sock
2025-01-15T14:30:15.000Z INFO  shepherd_core: Session started session_id=abc123 entry_id=minecraft
2025-01-15T14:59:45.000Z WARN  shepherd_core: Warning issued session_id=abc123 threshold=60
2025-01-15T15:00:45.000Z INFO  shepherd_core: Session expired session_id=abc123

Persistence

State is persisted to SQLite:

/var/lib/shepherdd/
├── shepherdd.db       # SQLite database
└── logs/
    └── sessions/      # Session stdout/stderr

Signals

Signal Action
SIGHUP Reload configuration
SIGTERM Graceful shutdown
SIGINT Graceful shutdown

Dependencies

This binary wires together all the library crates:

  • shepherd-config - Configuration loading
  • shepherd-core - Policy engine
  • shepherd-host-api - Host adapter trait
  • shepherd-host-linux - Linux implementation
  • shepherd-ipc - IPC server
  • shepherd-store - Persistence
  • shepherd-api - Protocol types
  • shepherd-util - Utilities
  • tokio - Async runtime
  • clap - CLI parsing
  • tracing - Logging
  • anyhow - Error handling

Building

cargo build --release -p shepherdd

Installation

The service is typically started by the compositor:

sway.conf

# Start shepherdd FIRST - it needs to create the socket before HUD/launcher connect
# Running inside sway ensures all spawned processes use the nested compositor
exec ./target/debug/shepherdd -c ./config.example.toml

See CONTRIBUTING.md for development setup.