linux observer
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 132 lines 8.2 kB view raw view rendered
1# AGENTS.md 2 3Development guidelines for solstone-linux, a standalone Linux desktop observer. 4 5## Project Overview 6 7solstone-linux is a companion app that runs alongside the main [solstone](https://solstone.app) journal. It is one of the owner's observers — it experiences screen and audio along with the owner on a Linux desktop using PipeWire and GStreamer, stores segments locally, and syncs them to a solstone server. It runs as a systemd user service on GNOME Wayland sessions. 8 9This is **not** part of the solstone monorepo. It is a standalone package with its own release lifecycle, installed via pipx alongside system-provided PyGObject/GStreamer bindings. 10 11## Source Layout 12 13``` 14src/solstone_linux/ 15 __init__.py Package version 16 cli.py CLI entry point (run, setup, install-service, status) 17 solstone-linux.service.in Systemd unit template (rendered by install-service) 18 config.py Config loading/persistence (~/.local/share/solstone-linux/) 19 observer.py Main capture loop — state machine (idle/screencast), audio + video 20 screencast.py Portal-based multi-monitor recording (xdg-desktop-portal + GStreamer) 21 audio_recorder.py Stereo audio recording (mic + system via soundcard) 22 audio_detect.py Audio device detection via ultrasonic tone 23 audio_mute.py PulseAudio mute state detection 24 activity.py Cross-desktop activity detection (screen lock, power save) via DBus 25 monitor_positions.py Monitor position assignment from geometry 26 session_env.py Desktop session environment checks and recovery 27 streams.py Stream name derivation (hostname-based) 28 sync.py Background sync service — uploads completed segments to server 29 upload.py HTTP upload client for solstone ingest server 30 recovery.py Crash recovery for orphaned .incomplete segments 31 32tests/ pytest test suite 33contrib/ Reference icons for development fallback 34``` 35 36## Architecture 37 38The observer runs a single asyncio event loop with two concurrent concerns: 39 401. **Capture loop** (`observer.py`) — Checks activity status every 5 seconds, records audio continuously, manages screencast recording via GStreamer. Creates 5-minute segments in `~/.local/share/solstone-linux/captures/YYYYMMDD/stream/HHMMSS_DDD/`. Segment directories start as `.incomplete` and are renamed on finalization. 41 422. **Sync service** (`sync.py`) — Background asyncio task that walks the captures directory, queries the server for existing segments, and uploads missing ones. Circuit breaker pattern with error-type-aware thresholds. 43 44State machine has two modes: `screencast` (screen active, recording video) and `idle` (screen inactive). Mode transitions, mute state changes, and 5-minute intervals all trigger segment boundaries. 45 46The capture loop never makes network calls. It writes locally; sync handles all uploads. 47 48## Commands 49 50```bash 51make install # Create venv, install package + dev tools (pytest, ruff) via uv 52make test # Run all tests 53make test-only TEST=tests/test_config.py # Run specific test 54make format # Auto-format with ruff 55make ci # Lint + format check + tests 56make install-service # Smart install-or-upgrade: guards against cross-repo contamination; runs CI in upgrade mode 57make service-restart # systemctl restart wrapper 58make service-status # systemctl status wrapper 59make service-logs # systemctl log tail wrapper 60make uninstall-service # Disable + remove unit + pipx uninstall 61make clean # Remove build artifacts and caches 62make versions # Show installed package versions 63``` 64 65## Development Principles 66 67- **Simple code.** Prefer plain functions over classes. Use dataclasses for structured data. Only use classes when managing stateful lifecycle (Observer, Screencaster, SyncService, AudioRecorder). 68- **Async by default.** The main loop is asyncio. DBus calls, subprocess management, and sync all use async. Audio recording uses a dedicated thread because soundcard is blocking. 69- **No network in the capture loop.** The observer writes segments locally. The sync service uploads asynchronously. This keeps capture reliable even when the server is down. 70- **Atomic directory operations.** Segments start as `HHMMSS.incomplete/`, are renamed to `HHMMSS_DDD/` on completion, or `HHMMSS.failed/` on recovery failure. 71- **System site-packages required.** PyGObject and GStreamer bindings come from system packages. The venv (and pipx) must use `--system-site-packages`. 72 73## File Headers 74 75All `.py` source files must include this header as the first two lines: 76 77```python 78# SPDX-License-Identifier: AGPL-3.0-only 79# Copyright (c) 2026 sol pbc 80``` 81 82Add this header to new `.py` files in `src/solstone_linux/` and `tests/`. Do not add headers to markdown, TOML, or config files. 83 84## Runtime Dependencies 85 86System packages (not pip-installable): 87- `python3-gobject` / `python3-gi` — PyGObject for GTK4 and GDK 88- GStreamer with PipeWire plugin (`gst-launch-1.0 pipewiresrc`) 89- PipeWire running 90- `pactl` (PulseAudio utils) for mute detection 91- xdg-desktop-portal with ScreenCast support 92 93Python packages (in pyproject.toml): 94- `requests` — HTTP upload client 95- `numpy` — Audio buffer manipulation and RMS computation 96- `soundfile` — FLAC encoding 97- `soundcard` — Audio device enumeration and recording 98- `dbus-next` — Async DBus client for portal and activity detection 99- `PyGObject` — GDK monitor geometry (installed from system) 100 101## Data Paths 102 103- Config: `~/.local/share/solstone-linux/config/config.json` 104- Captures: `~/.local/share/solstone-linux/captures/` 105- State: `~/.local/share/solstone-linux/state/` 106- Restore token: `~/.local/share/solstone-linux/config/restore_token` 107- Install source marker: `~/.config/solstone-linux/.install-source` (tracks which repo clone owns the pipx install) 108 109## Key Patterns 110 111- **Activity detection is cross-desktop.** Uses ordered DBus fallback chains for screen lock (freedesktop.org ScreenSaver → GNOME ScreenSaver) and power save (Mutter DisplayConfig → KDE Solid PowerManagement). All backends degrade gracefully to safe defaults. 112- **Audio is stereo-interleaved.** Left channel = microphone, right channel = system audio. When muted, channels are split into separate mono FLAC files. 113- **Screencast uses xdg-desktop-portal.** Session persistence via restore tokens avoids re-prompting the user. GStreamer subprocess (`gst-launch-1.0`) handles the actual PipeWire recording. 114- **Crash recovery runs on startup.** `recovery.py` scans for orphaned `.incomplete` directories older than 2 minutes and finalizes or marks them as failed. 115 116## Testing 117 118Tests use pytest with standard mocking. No system dependencies required for tests — audio devices, DBus, and GStreamer are mocked. Run `make test` to execute the full suite. 119 120## Brand canon 121 122- **solstone-linux is an observer.** In the system anatomy, `solstone = observers + sol agent + journal`. This repo implements one of those observers. 123- **The canon lives elsewhere.** Owner-facing terminology comes from sol pbc's internal brand canon (system anatomy + voice terminology guides). This repo's branded prose follows it; the canon itself is not vendored here. 124- **Use co-experience language in branded prose.** In README, INSTALL, onboarding text, settings copy, and error messages, describe solstone-linux as something that experiences screen and audio along with the owner. Never describe it as watching, recording, monitoring, or tracking the owner. 125- **Keep code language in code-only contexts.** Internal architecture terms such as `Capture loop`, the capture pipeline, module names, and data-path names are canon-permitted here and must not be renamed just to match branded prose. 126- **Edit with the surface in mind.** If the owner sees the string, follow the canon. If the text is naming code, pipelines, modules, or storage artifacts for engineers, the existing internal vocabulary stays. 127 128Canon source of truth: sol pbc's internal brand canon (system-anatomy guide). 129 130## License 131 132AGPL-3.0-only -- Copyright (c) 2026 sol pbc