Clean up README structure

This commit is contained in:
Albert Armea 2025-12-29 12:38:38 -05:00
parent 95d2cbb566
commit c3f3770ea6
5 changed files with 1168 additions and 97 deletions

78
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,78 @@
# For developers
## Build for development
### tl;dr
You need a Wayland-capable Linux system, Rust, and a small set of system dependencies. Once installed, `./run-dev` will start a development instance.
### Requirements
1. **Linux with Wayland**
* Any modern Wayland compositor is sufficient. For Ubuntu, this means 25.10 or higher.
* Optional (but recommended for realistic testing): TPM-based full disk encryption and a BIOS/UEFI password to prevent local tampering.
2. **System dependencies**
* Platform-specific packages are required.
* For Ubuntu, see the [`SYSTEM_DEPS` section in the CI configuration](./.github/workflows/ci.yml).
3. **Rust toolchain**
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
### Running in development
Start a development instance:
```sh
./run-dev
```
#### Adjusting the time
To avoid having to adjust the system clock or wait for timeouts, development
builds can mock the time with `SHEPHERD_MOCK_TIME`:
```sh
SHEPHERD_MOCK_TIME="2025-12-25 15:30:00" ./run-dev
```
Time and activity history are maintained in a SQLite database, which
`./run-dev` places at `./dev-runtime/data/shepherdd.db`. Edit this database
using a tool like [DB Browser for SQLite](https://sqlitebrowser.org/) while the
service is not running to inject application usage. [TODO: Link to schema docs]
### Testing and linting
Run the test suite:
```sh
cargo test
```
Run lint checks:
```sh
cargo clippy
```
## Contribution guidelines
`shepherd-launcher` is licensed under the GPLv3 to preserve end-users' rights.
By submitting a pull request, you agree to license your contributions under the
GPLv3.
Contributions written in whole or in part by generative AI are allowed;
however, they will be reviewed as if you personally authored them. I highly
recommend adding substantial prompts and design docs provided to agents to
[docs/ai/history/](./docs/ai/history/) along with the PRs and commit hashes
associated with them.
The authors of `shepherd-launcher` do not condone software or media piracy.
Contributions that explicitly promote or facilitate piracy will be rejected.
Please support developers and creators by obtaining content legally.

162
README.md
View file

@ -10,58 +10,39 @@ not software or hardware vendors, by providing:
* access to any application that can be run, emulated, or virtualized in desktop Linux
* with granular access controls inspired by and exceeding those in iOS Screen Time
While this repository provides some recipes for existing software packages
(including non-free software), `shepherd-launcher` is *non-prescriptive*: as
the end user, you are free to use them, not use them, or write your own.
While this repository provides some examples for existing software packages
(including non-free software and abandonware), `shepherd-launcher` is
*non-prescriptive*: as the end user, you are free to use them, not use them,
or write your own.
## Screenshots
TODO:
### Home screen
* home screen at different times showing different applications
* modern proprietary application showcase (Minecraft, individual Steam games)
* emulated application showcase (ScummVM games, 90s edutainment on Win9x, Duolingo via Waydroid)
* externally managed Chrome container for access to school resources
* media showcase (local storage and individual titles from streaming services)
* time limit popup
* "token" system
TODO: home screen at different times (bedtime vs afternoon) showing different applications
## Installation
### Time limits
tl;dr:
TODO: GIF or video of GCompris a few seconds from closing, emphasizing:
* Countdown clock
* Warning messaging
* Automatic close at end of time
* Icon deliberately missing afterwards -- cooldown
1. any Linux with Wayland (optional: TPM-based FDE plus BIOS password to prevent tampering)
2. System dependencies:
- **Ubuntu/Debian**: `apt install build-essential pkg-config libglib2.0-dev libgtk-4-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf-xlib-2.0-dev libwayland-dev libx11-dev libxkbcommon-dev libgirepository1.0-dev libgtk4-layer-shell-dev librust-gtk4-layer-shell-sys-dev sway swayidle`
3. Rust (`curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`)
4. Set up cgroups for process management (one-time, requires root):
```bash
sudo ./setup-cgroups.sh
```
5. binaries (TODO: deployable package that depends on Sway and installs the config)
6. test session on login
7. configure auto-login to this session
### "access to any application that can be run, emulated, or virtualized"
### cgroups Setup
TODO: show the following running with some subset of the above features highlighted:
* Minecraft
* Steam games (World of Goo, Portal 2)
* ScummVM games (Putt Putt, Secret of Monkey Island)
* Media
The shepherd daemon uses Linux cgroups v2 to reliably terminate all processes
when a session ends. This is essential for applications like Minecraft that
spawn child processes which may escape traditional process group signals.
Run the setup script once after installation:
```bash
sudo ./setup-cgroups.sh
```
This creates `/sys/fs/cgroup/shepherd` with appropriate permissions for your
user. The directory is not persistent across reboots on most systems, so you
may want to add this to your system startup (e.g., in `/etc/rc.local` or a
systemd unit).
## Usage
TODO: open lid; play
Contributions are welcome for improvements and not yet implemented backends,
such as:
* Pre-booted Steam to improve launch time [TODO: link to issue]
* Android apps via Waydroid, including pre-booting Android if necessary [TODO: link to issue]
* Legacy Win9x via DOSBox, QEMU, or PCem, including scripts to create a boot-to-app image [TODO: link to issue]
* Chrome, including strict sandboxing and support for firewall rules [TODO: link to issue]
## Core concepts
@ -71,32 +52,87 @@ TODO: open lid; play
* **Wrappers, not patches**: existing software is sandboxed, not modified
* **Revocable access**: sessions end predictably and enforceably
## Recipes
## Non-goals
TODO
* Modifying or patching third-party applications
* Circumventing DRM or platform protections
* Replacing parental involvement with automation
* `.desktop` syntax plus rules (time allowance, time-of-day restrictions, network whitelist, etc.)
* wrappers for common applications
* how to write a custom wrapper or custom application
## Installation
`shepherd-launcher` is pre-alpha and in active development. As such, end-user
binaries and installation instructions are not yet available.
See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to run in development.
Contributions are welcome for:
* a CI step that generates production binaries [TODO: link to issue]
* an installation script [TODO: link to issue]
## Example configuration
For the Minecraft example shown above:
```toml
# Minecraft via mc-installer snap
# Ubuntu: sudo snap install mc-installer
[[entries]]
id = "minecraft"
label = "Minecraft"
icon = "minecraft"
[entries.kind]
type = "snap"
snap_name = "mc-installer"
[entries.availability]
[[entries.availability.windows]]
days = "weekdays"
start = "15:00"
end = "18:00"
[[entries.availability.windows]]
days = "weekends"
start = "10:00"
end = "20:00"
[entries.limits]
max_run_seconds = 1800 # 30 minutes (roughly 3 in-game days)
daily_quota_seconds = 3600 # 1 hour per day
cooldown_seconds = 600 # 10 minute cooldown
[[entries.warnings]]
seconds_before = 600
severity = "info"
message = "10 minutes left - start wrapping up!"
[[entries.warnings]]
seconds_before = 120
severity = "warn"
message = "2 minutes remaining - save your game!"
[[entries.warnings]]
seconds_before = 30
severity = "critical"
message = "30 seconds! Save NOW!"
```
See [config.example.toml](./config.example.toml) for more.
## Development
tl;dr:
* Run in development: `./run-dev`
* Set the `SHEPHERD_MOCK_TIME` environment variable to mock the time,
such as `SHEPHERD_MOCK_TIME="2025-12-25 15:30:00" ./run-dev`
* Run tests: `cargo test`
* Lint: `cargo clippy`
See [CONTRIBUTING.md](./CONTRIBUTING.md)
## Contributing
## Written in 2025, responsibly
`shepherd-launcher` is licensed under the GPLv3 to preserve end-users' rights.
By submitting a pull request, you agree to license your contributions under the
GPLv3.
This project stands on the shoulders of giants in systems software and
compatibility infrastructure:
Contributions written in part or in whole by generative AI are allowed;
however, they will be reviewed as if you personally authored them.
* Wayland and Sway
* Rust
* Snap
* Proton and WINE
The authors of `shepherd-launcher` do not condone software or media piracy.
Contributions that explicitly promote or facilitate piracy will be rejected.
Please support developers and creators by obtaining content legally.
This project was written with the assistance of generative AI-based coding
agents. Substantial prompts and design docs provided to agents are disclosed in
[docs/ai](./docs/ai/)

View file

@ -0,0 +1,622 @@
This is the initial prompt to create `shepherdd` and its supporting crates, provided to Claude Opus 4.5 via VSCode Agent mode with nothing other than a Rust "hello world".
It was produced by ChatGPT after some back-and-forth [in this conversation](https://chatgpt.com/share/6952b1fb-35e4-800b-abca-5f212f9d77e5).
The output was committed as ac2d2abfed39e015aed6c2400f4f4609a2823d6d.
-----
Implement `shepherdd` as described below.
### Prelude: What is `shepherdd`, and why does it exist?
`shepherdd` is the **authoritative policy and enforcement daemon** for a child-focused, parent-defined computing environment. Its job is not to be a launcher UI, a window manager, or a media player. Its job is to **decide what is allowed, when it is allowed, for how long it is allowed, and to enforce that decision reliably**—regardless of what user interface, operating system, or legacy application happens to be in use.
The problem it solves is simple to describe but hard to implement correctly:
> Parents want to give children access to *real software*—including legacy games, emulators, virtual machines, browsers, and media—without relying on opaque vendor-controlled ecosystems or fragile UI-level restrictions. Time limits must be enforced. Warnings must be accurate. Expiry must be non-negotiable. And the system must remain understandable, inspectable, and extensible over time.
`shepherdd` is the **root of trust** for this environment. Everything else—launchers, overlays, shells, admin apps—is replaceable.
---
### Core philosophy
1. **Policy, not UI, is the authority**
`shepherdd` decides *what happens*. User interfaces only request actions and display state. If a UI crashes, disconnects, or is replaced entirely, enforcement continues.
2. **Real software, unmodified**
The system must be able to supervise arbitrary third-party programs:
emulators (e.g. ScummVM), virtual machines (e.g. Windows 9x games), browsers, media players, and other legacy or closed software that cannot be instrumented or embedded. Enforcement cannot rely on cooperation from the application.
3. **Time is enforced, not suggested**
Sessions have fixed deadlines computed at launch. Warnings occur at defined thresholds. When time expires, the session is terminated—gracefully if possible, forcefully if necessary.
4. **Portability over cleverness**
Desktop operating systems have fundamentally different control models. Linux can kill process groups; Android cannot. macOS requires MDM for true kiosk mode; Windows uses job objects and shell policies. `shepherdd` must acknowledge these differences honestly through a capability-based host interface rather than pretending all platforms are equivalent.
5. **Low-level, but not reckless**
`shepherdd` is designed to run as a background service with elevated privileges where appropriate, but it avoids OS-specific assumptions in its core. Platform-specific behavior lives behind explicit adapters.
6. **Open, inspectable, and extensible**
Policies are human-readable. Decisions are explainable (“why is this unavailable?”). Actions are logged. The architecture is intended to invite future contributors—especially for additional platforms—without requiring them to understand the entire system.
---
### What `shepherdd` is *not*
* It is **not** a graphical launcher.
* It is **not** a window manager or compositor.
* It is **not** a parental surveillance tool.
* It does **not** depend on cloud services.
* It does **not** assume a particular desktop environment or vendor ecosystem.
---
### How the system is expected to be used
* On **Linux/Wayland**, `shepherdd` runs as a service.
It starts or coordinates with a compositor (e.g. Sway), launches supervised applications, and communicates with a launcher UI and an always-on HUD overlay via local IPC.
* On **Windows**, a future adapter may integrate with a custom shell or kiosk configuration while preserving the same policy and enforcement logic.
* On **macOS**, `shepherdd` may operate in “soft kiosk” mode or integrate with MDM / Autonomous Single App Mode for hard lockdowns, using the same core.
* On **Android**, the same policy engine could back a managed launcher and device-owner workflow, even though enforcement primitives differ.
In all cases, **the daemon is the entry point**: shells connect to it; admin tools manage it; enforcement flows from it.
---
### What you, the coding agent, are building
You are not building a UI.
You are building a **policy engine and enforcement service** that:
* loads and validates a parent-defined policy,
* evaluates availability and time limits,
* tracks and supervises running sessions,
* emits warnings and state changes,
* terminates sessions when required,
* exposes a stable IPC API for multiple frontends,
* and does so in a way that can survive OS differences and future expansion.
If this daemon is correct, the rest of the system can evolve freely. If it is wrong, no amount of UI polish will fix it.
That is why `shepherdd` exists.
-----
# `shepherdd` library requirements document
This document specifies the **libraries (crates/modules)** that a coding agent must implement to build `shepherdd` from scratch. It assumes **zero prior context** and defines the architecture, responsibilities, APIs, and non-functional requirements needed to support a portable “policy + enforcement” daemon with replaceable frontends and host adapters.
The implementation language is assumed to be **Rust** (recommended for portability + service-style reliability), but the requirements are written so the design could be ported.
---
## 0. Glossary
* **Daemon (`shepherdd`)**: The authoritative service that loads policy, decides whats allowed, tracks time, issues warnings, and enforces expiry.
* **Shell / Frontend**: UI applications (e.g., Wayland launcher grid, HUD overlay, Windows shell, macOS kiosk UI) that display state and send commands. Shells do not enforce policy.
* **Host Adapter**: Platform-specific integration that can spawn/stop apps and (optionally) manage focus/fullscreen/lockdown.
* **Entry**: A whitelisted launchable unit (command, VM recipe, media collection).
* **Session**: A running instance of an Entry with start time, deadline, warnings, and enforcement actions.
---
## 1. Top-level library set
Implement the following libraries (Rust crates/modules), each testable in isolation:
1. **`shepherd-core`**
Pure, platform-agnostic policy engine and session state machine.
2. **`shepherd-host-api`**
Capability-based trait interfaces for platform adapters (Linux/Windows/macOS/Android). No platform code.
3. **`shepherd-host-linux`** (initial implementation target)
Linux host adapter: spawn/kill process trees, optional cgroups, optional Sway IPC integration hooks.
4. **`shepherd-config`**
Config schema, parsing, validation, and hot-reload support.
5. **`shepherd-store`**
Persistence abstraction + at least one concrete backend for:
* audit log
* usage/accounting
* current state recovery
6. **`shepherd-ipc`**
Local privileged IPC transport and protocol implementation (Unix domain socket for Linux). Authentication/authorization for local clients.
7. **`shepherd-api`**
Protocol types (request/response/event structures) shared by daemon and clients. Must be stable and versioned.
8. **`shepherd-remote`** (optional but designed-in; may be stubbed initially)
Remote management plane over HTTPS with pairing/auth. Must not be required for local operation.
9. **`shepherd-util`**
Shared utilities (time, IDs, error types, rate limiting helpers, etc.).
The `shepherdd` binary will mostly wire these together. This document focuses on the libraries.
---
## 2. System-level goals (must be supported by libraries)
### 2.1 Core capabilities
* Maintain a **whitelist** of allowed entries.
* Restrict entries by:
* **time windows** (day-of-week + time-of-day windows)
* **max run duration per launch**
* optional **daily quotas** and **cooldowns**
* Provide system status needed by shells:
* current session, time remaining, upcoming warnings
* “why is this entry unavailable?”
* Emit warnings at configured thresholds.
* Enforce expiry by terminating the running session via host adapter.
* Support “media playback entries” using the same policy primitives.
### 2.2 Portability and extensibility
* Core logic must not assume Wayland, process-killing, or fullscreen exist.
* Enforcement must be expressed in terms of **capabilities**.
* Protocol must support multiple shells and admin tools simultaneously.
* The daemon must run as a service; shells connect over local IPC.
### 2.3 Safety and determinism
* The daemon is the **sole authority** for timing and enforcement.
* Must be resilient to UI crashes/disconnects.
* Timing must use **monotonic time** for countdowns (wall-clock changes must not break enforcement).
* All commands must be auditable.
---
## 3. `shepherd-core` requirements
### 3.1 Responsibilities
* Parse validated config objects (from `shepherd-config`) into internal policy structures.
* Evaluate policy at a given time (“what entries are visible/launchable now?”).
* Implement session state machine:
* `Idle`
* `Launching`
* `Running`
* `Warned` (can be multiple warning levels)
* `Expiring` (termination initiated)
* `Ended` (with reason)
* Compute:
* allowed duration if started “now” (min of entry max duration, time-window end, quota remaining)
* warning schedule times
* Produce a deterministic **Enforcement Plan**:
* what to do on launch
* when to warn
* when/how to expire
### 3.2 Data structures
#### 3.2.1 Entry model
Minimum fields:
* `entry_id: EntryId` (stable)
* `label: String`
* `icon_ref: Option<String>` (opaque reference; shell interprets)
* `kind: EntryKind`:
* `Process { argv: Vec<String>, env: Map, cwd: Option<Path> }`
* `Vm { driver: String, args: Map }` (generic “VM recipe”)
* `Media { library_id: String, args: Map }`
* `Custom { type_name: String, payload: Value }`
* `availability: AvailabilityPolicy`
* `limits: LimitsPolicy`
* `warnings: WarningPolicy`
#### 3.2.2 AvailabilityPolicy
* `time_windows`: list of windows:
* days-of-week mask
* start time-of-day (local time)
* end time-of-day (local time)
* semantics: entry is allowed if “now” is in at least one window.
#### 3.2.3 LimitsPolicy
* `max_run: Duration`
* optional:
* `daily_quota: Duration`
* `cooldown_after_run: Duration`
* `min_gap_between_runs: Duration` (alias of cooldown)
* Must support future extension.
#### 3.2.4 WarningPolicy
* list of thresholds in seconds before expiry (e.g., 300, 60, 10)
* each threshold has:
* `message_template` (optional)
* `severity` (info/warn/critical)
* warnings must not be emitted twice for the same session.
### 3.3 Public API
* `CoreEngine::new(policy: Policy, store: StoreHandle) -> CoreEngine`
* `CoreEngine::list_entries(now) -> Vec<EntryView>`
* `EntryView` includes:
* enabled/disabled
* reasons list (structured codes)
* if enabled: `max_run_if_started_now`
* `CoreEngine::request_launch(entry_id, now) -> LaunchDecision`
* Returns either `Denied { reasons }` or `Approved { session_plan }`
* `CoreEngine::start_session(approved_plan, host_session_handle, now)`
* `CoreEngine::tick(now_monotonic) -> Vec<CoreEvent>`
* Emits scheduled warnings and expiry events.
* `CoreEngine::notify_session_exited(exit_info, now) -> CoreEvent`
* `CoreEngine::stop_current(request, now) -> StopDecision`
* request may be user stop vs admin stop vs policy stop.
* `CoreEngine::get_state() -> DaemonStateSnapshot`
### 3.4 Event model
Core emits events for IPC and host enforcement:
* `SessionStarted`
* `Warning { threshold, remaining }`
* `ExpireDue`
* `SessionEnded { reason }`
* `EntryAvailabilityChanged` (optional; used for UI updates)
* `PolicyReloaded` (optional)
### 3.5 Time rules
* All “countdown” logic uses monotonic time.
* Availability windows use wall-clock local time.
* If wall-clock jumps, session expiry timing remains based on monotonic time, but **no extension** should be granted by clock rollback.
### 3.6 Testing requirements
* Unit tests for:
* time window evaluation (DST boundaries included)
* quota accounting
* warning schedule correctness
* state machine transitions
* Property tests recommended for “no double warning / no negative remaining / no orphan state”.
---
## 4. `shepherd-host-api` requirements
### 4.1 Responsibilities
Define the interface between core daemon logic and platform integration.
### 4.2 Capability model
`HostCapabilities` must include (at minimum):
* `spawn_kind_supported: Set<EntryKindTag>` (Process/Vm/Media/Custom)
* `can_kill_forcefully: bool`
* `can_graceful_stop: bool`
* `can_group_process_tree: bool` (process group / job object)
* `can_observe_exit: bool`
* `can_observe_window_ready: bool` (optional)
* `can_force_foreground: bool` (optional)
* `can_force_fullscreen: bool` (optional)
* `can_lock_to_single_app: bool` (MDM/Assigned Access style, optional)
### 4.3 SessionHandle abstraction
`HostSessionHandle` must be opaque to core:
* contains platform-specific identifiers (pid/pgid/job object, bundle ID, etc.)
* must be serializable if you plan crash recovery; otherwise explicitly “not recoverable”.
### 4.4 Host API trait
Minimum methods:
* `capabilities() -> HostCapabilities`
* `spawn(entry: Entry, spawn_opts: SpawnOptions) -> Result<HostSessionHandle>`
* `stop(handle, mode: StopMode) -> Result<()>`
* `StopMode`: `Graceful(Duration)` then `Force`
* `subscribe() -> HostEventStream` (async)
* `HostEvent`: `Exited(handle, exit_status)`, optionally `WindowReady(handle)`, `SpawnFailed`, etc.
* Optional (feature-gated):
* `set_foreground(handle)`
* `set_fullscreen(handle)`
* `ensure_shell_visible()` (return to launcher)
* `start_shell_process()` / `start_compositor()` (Linux-specific convenience, but keep generic naming)
---
## 5. `shepherd-host-linux` requirements (initial adapter)
### 5.1 Spawn/kill semantics
* Must spawn `Process` entries without invoking a shell:
* `execve(argv[0], argv, env)` behavior
* Must place spawned process in its own **process group**.
* Must be able to terminate entire process group:
* `SIGTERM` then `SIGKILL` after grace timeout.
* Must handle “VM recipes” as process spawns (e.g., launching qemu) using same group semantics.
### 5.2 Optional containment (extensible)
Design hooks for future:
* cgroups v2 creation per session (cpu/mem/io limits optional)
* namespacing (optional)
* environment sanitization and controlled PATH
### 5.3 Observability
* Must provide exit notifications for sessions.
* Should capture stdout/stderr to logs (file per session or journald).
### 5.4 Linux compositor management (optional module)
Provide an optional module that:
* can start Sway (or connect to existing)
* can issue minimal compositor commands (focus/fullscreen)
* can subscribe to compositor events (window created/destroyed)
This module must be optional so the daemon can run headless or under another compositor.
---
## 6. `shepherd-config` requirements
### 6.1 Config format
* Must support TOML (recommended). YAML acceptable but TOML preferred for strictness.
* Provide versioned schema with explicit `config_version`.
### 6.2 Validation
Must reject invalid config with clear errors:
* duplicate entry IDs
* empty argv
* invalid time windows
* warning thresholds >= max_run
* negative durations
* unknown kinds/drivers (unless `Custom` allows unknown)
### 6.3 Hot reload
* Must support reload on SIGHUP (Linux) or an API call.
* Reload must be atomic:
* either new policy fully applied, or old remains.
* On reload, current session continues with old plan unless explicitly configured otherwise.
---
## 7. `shepherd-store` requirements
### 7.1 Responsibilities
Persist:
* audit events (append-only)
* usage accounting (per entry/day)
* cooldown tracking
* last-known daemon snapshot (optional)
### 7.2 Backends
Minimum viable:
* SQLite backend (recommended)
OR
* append-only JSON lines log + periodic compacted summary
### 7.3 API
* `Store::append_audit(event)`
* `Store::get_usage(entry_id, day) -> Duration`
* `Store::add_usage(entry_id, day, duration)`
* `Store::get_cooldown_until(entry_id) -> Option<Time>`
* `Store::set_cooldown_until(entry_id, time)`
* `Store::load_snapshot() -> Option<Snapshot>`
* `Store::save_snapshot(snapshot)`
### 7.4 Crash tolerance
* audit log must not corrupt easily
* writes must be durable enough for “time accounting correctness” (use transactions if SQLite)
---
## 8. `shepherd-api` requirements (protocol types)
### 8.1 Versioning
* `api_version: u32`
* Backward compatibility policy:
* minor additions are allowed
* breaking changes require version bump
### 8.2 Core types
* `EntryId`, `SessionId`, `ClientId`
* `EntryView` (for UI listing)
* `DaemonStateSnapshot`
* `ReasonCode` enum (structured unavailability reasons)
* `Command` enum + request/response wrappers
* `Event` enum for streaming
### 8.3 Commands (minimum)
Local privileged plane must support:
* `GetState`
* `ListEntries { at_time? }`
* `Launch { entry_id }`
* `StopCurrent { mode }`
* `ReloadConfig`
* `SubscribeEvents`
Optional admin:
* `ExtendCurrent { by }` (must be capability/role gated)
* `SetPolicy { policy_blob }` (remote plane only or locked down)
Events (minimum):
* `StateChanged(snapshot)`
* `SessionStarted`
* `WarningIssued`
* `SessionExpired`
* `SessionEnded`
* `PolicyReloaded`
* `AuditAppended` (optional)
---
## 9. `shepherd-ipc` requirements (local plane)
### 9.1 Transport
Linux MVP:
* Unix domain socket at a configurable path.
* newline-delimited JSON (NDJSON) **or** length-prefixed binary frames (protobuf). Either is acceptable, but must be explicitly framed.
### 9.2 Multiplexing
Must support:
* multiple concurrent clients
* at least one client subscribed to events
* request/response on the same connection (duplex) or separate connections; either is fine.
### 9.3 AuthN/AuthZ (local)
Minimum:
* rely on filesystem permissions of socket path
* identify peer UID (where supported) and attach to `ClientInfo`
Roles:
* `shell` (UI/HUD)
* `admin` (local management tool)
* `observer` (read-only)
If peer UID is unavailable on future platforms, design the interface so other mechanisms can be plugged in.
### 9.4 Rate limiting
* per-client command rate limits to avoid UI bugs DOSing the daemon.
---
## 10. `shepherd-remote` requirements (remote plane; optional but designed)
### 10.1 Transport
* HTTPS server (TLS required)
* REST and/or gRPC acceptable
* Must be optional/off by default.
### 10.2 Pairing
Must support a pairing flow that does not require cloud:
* device generates a one-time code displayed on the local shell
* remote client uses code to obtain a long-lived credential
* credential stored securely on device
### 10.3 Authorization
Remote actions must be scoped:
* `read-only`
* `policy edit`
* `approve/deny/extend`
### 10.4 Security basics
* TLS
* brute-force protection on pairing
* audit log of remote actions
---
## 11. Cross-cutting requirements
### 11.1 Logging
* Structured logs
* Per-session logs linkable to `session_id`
* Separate audit log (non-repudiation style) from debug logs
### 11.2 Metrics/health
Expose health endpoints via local IPC:
* liveness (“daemon loop running”)
* readiness (“policy loaded”, “host adapter OK”)
### 11.3 Configuration of privilege
Libraries must not assume root/admin.
* Host adapter exposes what it can do.
* Daemon must degrade gracefully if running unprivileged (e.g., no cgroups).
### 11.4 Deterministic enforcement
* If an entry is launched, daemon must compute a fixed deadline and enforce it regardless of UI state.
* Warn/expire must be emitted at-most-once.
---
## 12. Deliverables checklist for a coding agent
A coding agent implementing these libraries must produce:
1. `shepherd-core` with full unit tests for time windows, warnings, quotas.
2. `shepherd-host-api` trait + capabilities, with mock host for tests.
3. `shepherd-host-linux` minimal:
* spawn process group
* graceful+force kill
* exit observation
4. `shepherd-config` parse+validate TOML.
5. `shepherd-store` (SQLite or append-only log) with basic usage accounting.
6. `shepherd-api` command/event types + versioning.
7. `shepherd-ipc` local UDS server:
* handle commands
* event subscription
* peer UID auth (where available)
8. Integration “smoke test” that:
* loads a sample config
* launches `sleep 999`
* warns at thresholds
* kills at expiry
* emits events to a test client

View file

@ -0,0 +1,369 @@
This prompt created the UI. It was run in the same conversation as the one that created shepherdd, and was also provided to Claude Opus 4.5 via VSCode Agent mode.
It was produced by ChatGPT after some back-and-forth [in this conversation](https://chatgpt.com/share/6952b1fb-35e4-800b-abca-5f212f9d77e5).
The output was committed as e2013eb6949298ce1f1c32e24f7a2a1785718154.
-----
Now implement the UI that integrates with `shepherdd` as described below.
---
# Linux / Wayland Launcher UI Specification
## 1. Purpose and scope
The Linux/Wayland launcher UI is the **primary user-facing shell** for a kiosk-style, child-friendly computing environment backed by `shepherdd`.
Its responsibilities are **presentation and input only**:
* display what `shepherdd` allows *right now*
* request launches and stops
* display authoritative state (time remaining, warnings)
* never enforce policy on its own
The launcher **must not**:
* track time limits independently
* decide availability rules
* attempt to supervise or kill applications
If the launcher crashes or disconnects, `shepherdd` continues enforcement.
---
## 2. High-level architecture
The Linux UI is split into **two cooperating Wayland clients**:
1. **Launcher UI** (main grid)
2. **HUD / Overlay UI** (always visible)
Both are ordinary Wayland clients and communicate **only** with `shepherdd` over local IPC.
```
┌───────────────────────────────────────────┐
│ Sway (kiosk-configured compositor) │
│ │
│ ┌───────────────┐ ┌──────────────────┐ │
│ │ HUD Overlay │ │ Running App │ │
│ │ (layer-shell) │ │ (fullscreen) │ │
│ └───────────────┘ └──────────────────┘ │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ Launcher UI (grid, home screen) │ │
│ └───────────────────────────────────────┘ │
└───────────────────────────────────────────┘
```
---
## 3. Compositor assumptions (Sway)
### 3.1 Required compositor features
The launcher assumes a Wayland compositor that supports:
* **wlr-layer-shell** (for always-on HUD)
* fullscreen surfaces
* IPC or rules to:
* force fullscreen
* prevent workspace switching
* disable escape keybindings
Sway is the reference implementation.
### 3.2 Kiosk constraints to enforce in Sway config
The system must:
* expose **no user-visible workspaces**
* disable:
* Alt+Tab
* Mod+key bindings
* exit / reload bindings
* force launched applications fullscreen
* ensure layer-shell overlay is always topmost
The launcher UI **must not rely on Sway configuration details**, but it must assume these constraints hold.
---
## 4. Process model
### 4.1 Launcher UI process
* GTK4 Wayland application
* normal Wayland surface
* visible only when:
* no session is running
* or user explicitly returns to “home”
### 4.2 HUD / Overlay process
* GTK4 Wayland application using **layer-shell**
* always visible, regardless of running app
* anchored to a screen edge (typically top)
### 4.3 Privilege
* runs as the **same user** as the compositor
* does **not** require elevated privileges
* all privileged actions go through `shepherdd`
---
## 5. IPC contract with `shepherdd`
### 5.1 Connection
* Unix domain socket (path configurable)
* persistent connection
* reconnect logic required
### 5.2 Commands issued by the launcher UI
* `ListEntries`
* `Launch { entry_id }`
* `StopCurrent`
* `GetState`
* `SubscribeEvents`
### 5.3 Events consumed by launcher & HUD
* `StateChanged`
* `SessionStarted`
* `WarningIssued`
* `SessionExpired`
* `SessionEnded`
* `PolicyReloaded`
The UI **must treat daemon state as authoritative**.
---
## 6. Launcher UI (main grid)
### 6.1 Visual layout
* Fullscreen window
* Grid of large, touch-friendly icons
* Each tile represents one `EntryView` from `shepherdd`
Each tile must show:
* icon
* label
* enabled / disabled state
* disabled reason (optional text or icon)
### 6.2 Availability handling
For each entry:
* If disabled:
* tile is visually muted
* tapping does nothing
* optional “why” tooltip (e.g. “Not available until 3pm”)
* If enabled:
* tapping sends `Launch(entry_id)`
* UI transitions to “launching” state
### 6.3 Launch feedback
When a launch is requested:
* grid input is disabled
* show:
* “Starting…” indicator
* wait for:
* `SessionStarted` → transition away
* or error → restore grid
### 6.4 Return to home
The launcher UI must be able to re-appear when:
* session ends
* user presses “close” in HUD (if allowed)
* daemon requests shell visibility
---
## 7. HUD / Overlay UI (always visible)
### 7.1 Placement and layer
* Wayland **layer-shell**
* layer: `overlay`
* exclusive zone: minimal (or none)
* must not be obscured by fullscreen apps
### 7.2 Required elements
The HUD must display, at minimum:
1. **Time remaining**
* authoritative countdown from daemon
* format: `MM:SS` or `H:MM:SS`
* visually emphasized when below warning thresholds
2. **Battery level**
* via UPower (local system API)
* percent + icon
* daemon does not own battery state
3. **Volume**
* current output volume
* mute state
* controls may:
* send `SetVolume` via daemon
* or adjust locally (implementation choice)
4. **Close / End session button**
* sends `StopCurrent`
* availability depends on daemon state
* may be hidden or disabled for certain entries
5. **Power button**
* opens a small modal:
* Suspend
* Shutdown
* Restart
* actions are forwarded to daemon (daemon decides if allowed)
### 7.3 Warning presentation
On `WarningIssued`:
* show banner or modal in HUD
* severity reflected visually
* optional sound cue
* must not block rendering of underlying app
On `SessionExpired`:
* show “Times up” overlay
* remain visible until session ends and home returns
### 7.4 Failure handling
If daemon disconnects:
* HUD must:
* show “Disconnected” indicator
* attempt reconnect
* **not** fabricate time remaining
---
## 8. Interaction between launcher and HUD
* Launcher and HUD **do not communicate directly**
* Both reflect the same daemon state
* HUD remains visible while launcher is hidden
* Launcher must ignore input while a session is running
---
## 9. Input and accessibility considerations
### 9.1 Input
* Touch-friendly sizing
* Mouse and keyboard supported
* No reliance on keyboard shortcuts for core actions
### 9.2 Accessibility
* High contrast mode recommended
* Large fonts
* Minimal text; icon-first design
* Avoid hover-only affordances
---
## 10. Error states and recovery
### 10.1 Daemon unavailable at startup
* Show blocking error screen:
* “System not ready”
* retry button
* Do not allow launching without daemon
### 10.2 App fails to launch
* Daemon emits failure
* Launcher returns to grid
* Optional error message
### 10.3 Session ends unexpectedly
* HUD updates immediately
* Launcher reappears automatically
---
## 11. Non-functional requirements
### 11.1 Robustness
* UI must tolerate daemon restarts
* UI must tolerate compositor restarts (best effort)
### 11.2 Determinism
* Countdown is driven only by daemon events/state
* UI must never infer remaining time independently
### 11.3 Maintainability
* No daemon logic embedded in UI
* All policy logic treated as opaque
---
## 12. Implementation constraints and recommendations
* Toolkit: **GTK4**
* Language: **Rust (gtk-rs)** preferred
* Layer-shell: `gtk4-layer-shell` (or equivalent)
* IPC client: reuse `shepherd-api` and `shepherd-ipc` types
---
## 13. Explicit non-goals
* Embedding third-party apps inside the launcher
* Replacing the compositor
* Managing system accounts or users
* Performing enforcement locally in UI
---
## 14. Success criteria
The launcher UI is considered correct if:
* It can fully drive the system using only daemon IPC.
* It remains usable while arbitrary fullscreen apps (VMs, emulators) run underneath.
* Time warnings and expiry are always visible and accurate.
* Replacing the launcher UI with another client would not weaken enforcement.

View file

@ -1,34 +0,0 @@
#!/bin/bash
# Setup cgroups v2 for shepherd-launcher
# This script must be run as root (or with sudo)
set -e
CGROUP_BASE="/sys/fs/cgroup/shepherd"
# Check if cgroups v2 is available
if [ ! -f /sys/fs/cgroup/cgroup.controllers ]; then
echo "Error: cgroups v2 is not available on this system"
echo "Make sure your kernel supports cgroups v2 and it's mounted"
exit 1
fi
# Get the user who will run shepherd (default to SUDO_USER or current user)
SHEPHERD_USER="${1:-${SUDO_USER:-$(whoami)}}"
echo "Setting up cgroups for shepherd-launcher..."
echo "User: $SHEPHERD_USER"
# Create the shepherd cgroup directory
mkdir -p "$CGROUP_BASE"
# Set ownership so the shepherd daemon can create session cgroups
chown "$SHEPHERD_USER:$SHEPHERD_USER" "$CGROUP_BASE"
# Set permissions (owner can read/write/execute, others can read/execute)
chmod 755 "$CGROUP_BASE"
echo "Created $CGROUP_BASE with ownership $SHEPHERD_USER"
echo ""
echo "cgroups v2 setup complete!"
echo "The shepherd daemon can now create session cgroups for reliable process management."