No description
  • Python 94.8%
  • Just 5.2%
Find a file
2026-03-02 17:47:33 +00:00
src justfile and stuff 2026-03-02 17:47:33 +00:00
.gitignore initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00
.python-version initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00
beowulfd.service confirmed working with gui applications, systemd unit 2025-12-05 17:30:10 +00:00
example_beowulfd.service initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00
example_beowulfdrc initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00
example_firefox.desktop initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00
example_hosts initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00
justfile justfile and stuff 2026-03-02 17:47:33 +00:00
main.py initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00
pyproject.toml initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00
README.md update readme 2025-12-11 21:25:48 +00:00
uv.lock initial commit: functioning daemon and cli 2025-11-14 23:18:05 +00:00

Beowulf

A tool for delegating GUI applications to multiple backend hosts using waypipe.

Overview

Beowulf consists of two components:

  • beowulf - Client tool that launches GUI applications on remote hosts
  • beowulfd - Daemon that manages host allocation based on resource usage

How It Works

  1. You run beowulf "firefox https://en.wikipedia.org/"
  2. The client asks the daemon which host should run the application
  3. The daemon analyzes historical resource usage and current host metrics to make an intelligent allocation decision
  4. The client launches the application via waypipe on the chosen host
  5. The application runs remotely but appears as a local window
  6. The daemon tracks resource usage for future allocation decisions

Installation

Prerequisites

  • Python 3.11 or higher
  • waypipe installed and configured
  • SSH access to remote hosts with key-based authentication
  • Remote hosts need a working /proc filesystem along with python3, ps, and pgrep

Install

# Clone or navigate to the beowulf directory
cd beowulfd

# Install with uv
uv pip install -e .

Configuration

1. Create hosts file

Create ~/.beowulf/hosts with one hostname per line:

# Format: hostname or hostname:port
localhost
workstation.local
server1.example.com
server2.example.com:2222

Important: All hosts must already have their SSH host keys in your ~/.ssh/known_hosts file.

2. (Optional) Create daemon configuration

Create ~/.beowulf/beowulfdrc to customize behavior:

# Polling interval in seconds (default: 10)
polling_interval = 10

# Sticky assignments (always run on specific host)
sticky.firefox = workstation.local
sticky.gimp = server1.example.com

Usage

Start the daemon

beowulfd

# Inspect daemon options
beowulfd --help

The daemon will:

  • Create the ~/.beowulf directory structure
  • Create a Unix socket at ~/.beowulf/run/daemon
  • Connect to all configured hosts via SSH
  • Begin monitoring resource usage
  • Log activity to stdout
  • Expose a Plotly-based metrics dashboard on http://127.0.0.1:8080 by default

Visit http://127.0.0.1:8080 (or your configured host/port) to inspect per-host CPU, memory, and iowait graphs collected over time.

Tip: Run under a supervisor like systemd or in a tmux/screen session.

Launch applications

# Basic usage
beowulf "firefox https://en.wikipedia.org/"

# Any command works
beowulf "gimp"
beowulf "inkscape drawing.svg"

# Suppress client diagnostics (only show app stdout/stderr)
beowulf --silent "firefox https://en.wikipedia.org/"

# Run CLI applications over SSH (no waypipe)
beowulf --cli "htop"

# Check allocation without launching (dry-run)
beowulf --dry-run "firefox"

# View client help
beowulf --help

The beowulf command will:

  • Block for the entire lifetime of the application
  • Forward signals (SIGINT, SIGTERM) to the remote process
  • Exit with the same exit code as the remote application
  • Fail gracefully if the daemon is not running
  • Use waypipe for GUI applications, or --cli for TUI/CLI programs

Use in Desktop Entry files

Beowulf can be used in .desktop files for GNOME, KDE, etc:

[Desktop Entry]
Name=Firefox (Beowulf)
Exec=beowulf "firefox %u"
Type=Application
Terminal=false

Metrics dashboard

Beowulfd now exposes a read-only Plotly dashboard showing CPU, memory, and I/O wait trends for every connected host. By default it listens on 127.0.0.1:8080, but you can change this with --http-host and --http-port, or disable it entirely with --disable-http.

The dashboard renders static graphs for the most recent samples collected while the daemon runs, so leave the service online to accumulate history. Plotly's JavaScript runtime is bundled directly with the daemon, so the page works without internet access.

Allocation Strategy

The daemon makes intelligent allocation decisions based on:

  1. Historical data: Apps that historically use more memory are allocated to hosts with sufficient free memory
  2. Current resources: CPU usage, memory availability, and I/O wait times
  3. I/O wait penalty: Hosts with high I/O wait are deprioritized even if resources are available
  4. Sticky configuration: Specific apps can be pinned to specific hosts
  5. Fallback: Unknown apps go to the host with the most free resources

Protocol

Communication between client and daemon uses JSON over Unix socket:

// Client - Daemon: Request allocation
{"cmd": "allocate", "app": "firefox"}

// Daemon - Client: Respond with host
{"host": "host1.local", "reason": "lowest iowait"}

// Client - Daemon: Report launched process
{"cmd": "report", "app": "firefox", "pid": 12345, "host": "host1.local"}

Architecture

beowulfd (Daemon)

  • Maintains persistent SSH connections to all configured hosts
  • Polls resource metrics every N seconds (configurable)
  • Stores metrics in TinyFlux time series database
  • Tracks running processes and their resource usage
  • Provides allocation API via Unix socket
  • Process names are normalized to first token (e.g., "firefox-esr" - "firefox")

beowulf (Client)

  • Connects to daemon via Unix socket
  • Requests host allocation for an application
  • Launches application via waypipe ssh HOST COMMAND
  • Attempts to determine remote PID and reports back to daemon
  • Blocks until application exits
  • Returns same exit code as remote application

Data Storage

Resource metrics are stored in ~/.beowulf/metrics.db (TinyFlux database):

  • Host metrics: host.HOSTNAME - CPU, memory, I/O wait
  • App metrics: app.APPNAME - Per-process CPU and memory usage

Process data is only counted when the process is running (no zero values).

Troubleshooting

Daemon won't connect to hosts

Check:

  • SSH keys are set up correctly: ssh host command should work without password
  • Host keys are in ~/.ssh/known_hosts: Run ssh host once manually first
  • Firewall allows SSH connections

Client says "daemon is not running"

  • Start the daemon: beowulfd
  • Check socket exists: ls ~/.beowulf/run/daemon
  • Check daemon logs for errors

Application doesn't launch

  • Verify waypipe works: waypipe ssh host firefox should work manually
  • Check daemon logs for allocation errors
  • Try dry-run mode: beowulf --dry-run "firefox"

Remote PID not detected

The client uses pgrep to find the remote PID. If this fails:

  • Process tracking won't work, but the app will still run
  • This is a warning, not a fatal error
  • Check that pgrep is available on remote host

Security

  • Unix socket permissions are set to owner-only (600)
  • Only the user running beowulfd can use beowulf
  • All communication happens locally via Unix socket
  • SSH connections use your existing SSH configuration and keys

Example Session

# Terminal 1: Start daemon
$ beowulfd
2025-11-11 17:53:00 - beowulfd - INFO - Loaded 2 hosts
2025-11-11 17:53:00 - beowulfd - INFO - Connected to localhost:22
2025-11-11 17:53:00 - beowulfd - INFO - Connected to workstation.local:22
2025-11-11 17:53:00 - beowulfd - INFO - Connected to 2/2 hosts
2025-11-11 17:53:00 - beowulfd - INFO - Socket server listening on /home/user/.beowulf/run/daemon
2025-11-11 17:53:00 - beowulfd - INFO - Starting monitoring loop (interval: 10.0s)

# Terminal 2: Launch application
$ beowulf "firefox https://en.wikipedia.org/"
Allocated to workstation.local: low CPU usage, high available memory
Launching: waypipe ssh workstation.local firefox https://en.wikipedia.org/
Reported PID 12345 to daemon

# Firefox opens as a window, beowulf blocks until you close it

License

MIT