Synheart Sensor Agent
A privacy-first PC background sensor that captures keyboard and mouse interaction timing (not content) for behavioral research.
Overview
Synheart Sensor Agent is a pure collector for behavioral signals. It ingests how you interact — never what you type or read — from two sources:
- Native OS input — keystroke timing and category, mouse movement
magnitude, and click/scroll timing, captured via OS-level event APIs
(
start). - Browser behavior — anonymized interaction metrics streamed from the
Synheart Behavior Chrome extension
to a local HTTP endpoint (
serve, behind theserverfeature).
The agent does not extract behavioral features, build sessions, or compute
derived metrics. Raw events are emitted via an in-process channel (or accepted and
acknowledged over the local HTTP endpoint) and discarded immediately. Feature
extraction, windowing, session management, and any Human State Interface (HSI)
output are the responsibility of downstream consumers (e.g. synheart-core-rust /
synheart-session-runtime).
Key Features
- Privacy by Design: Never captures key content, passwords, or screen coordinates
- Pure Collector: Emits raw timing/magnitude events; no feature extraction or storage of raw data
- Transparency: Cumulative collection statistics viewable via the
statuscommand - App Context (library API):
get_frontmost_app_id()returns the foreground application's bundle ID on demand — never window titles, URLs, or content. Thestartcommand does not record it. - macOS Support: Native integration using Core Graphics event taps
- Windows Support: Native integration using the Windows Hooks API
- Optional Local Server: Feature-gated HTTP endpoint for receiving behavioral data from a companion (e.g. a browser extension)
Privacy Guarantees
╔══════════════════════════════════════════════════════════════════╗
║ ✓ WHAT WE CAPTURE: ║
║ • When keys are pressed (timing only) ║
║ • Key categories (typing, navigation, backspace, enter, …) ║
║ • Common shortcut patterns (copy, paste, … — timing only) ║
║ • How fast the mouse moves (magnitude only) ║
║ • When clicks and scrolls occur (timing only) ║
║ • Foreground app bundle ID (for task-switch detection) ║
║ ║
║ ✗ WHAT WE NEVER CAPTURE: ║
║ • Which keys you press (no passwords, messages, etc.) ║
║ • Where your cursor is (no screen position tracking) ║
║ • Window titles, file names, or application content ║
║ • Any screen content ║
╚══════════════════════════════════════════════════════════════════╝
Run synheart-sensor privacy at any time to print the full privacy declaration.
Installation
From crates.io
From Source
# Clone the repository
# Build in release mode
# The binary will be at target/release/synheart-sensor
Requirements
- Rust: 1.82 or later (MSRV)
- macOS: 10.15 (Catalina) or later
- Windows: 10 or later
- Permissions: Input Monitoring (macOS) or Run as Administrator (Windows)
Usage
Grant Input Capture Permission
macOS
Before the agent can capture events, you need to grant Input Monitoring permission:
- Open System Preferences > Security & Privacy > Privacy
- Select Input Monitoring in the left sidebar
- Click the lock icon and authenticate
- Add the
synheart-sensorapplication - Restart the application
Windows
Run the sensor agent as Administrator to capture input events:
- Right-click the
synheart-sensor.exebinary - Select Run as administrator
- Or launch your terminal (Command Prompt / PowerShell) as Administrator before running the binary
Commands
# Start capturing (all sources by default)
# Start with specific sources
# Pause collection (signals a running instance via the config file)
# Resume collection
# Show permission state, configuration, and cumulative statistics
# Print the privacy declaration
# Show the active configuration and its file path
Example Output
While running, the agent prints a periodic heartbeat with the cumulative event count, then a session summary on exit:
Synheart Sensor Agent v0.4.0
Starting collection...
Keyboard: enabled
Mouse: enabled
Press Ctrl+C to stop
[sensor] 312 events captured
[sensor] 689 events captured
^C
Stopping collection...
Session Statistics:
- Keyboard events processed: 421
- Mouse events processed: 268
- Shortcut events processed: 12
- Session duration: 34 seconds
Privacy Guarantee:
- No key content captured
- No cursor coordinates captured
- Only timing, categories, and magnitude data retained
Library Usage
The crate can also be used as a library to consume the raw event stream directly:
use ;
let mut collector = new;
collector.start.expect;
// Receive privacy-preserving events as they are captured.
while let Ok = collector.receiver.recv
Each SensorEvent carries only timing, a category, and (for mouse movement/scroll)
a magnitude — never key codes, characters, or coordinates.
Browser Behavior Ingestion (HTTP Server)
The Synheart Behavior Chrome extension
captures anonymized browser interaction metrics (keystroke timing categories,
scroll/cursor patterns, tab-focus changes — never URLs, titles, or content) and
streams them to the sensor agent over a local HTTP endpoint behind the server
feature flag. The agent validates the payload and acknowledges it; it performs no
feature extraction or storage — orchestration is the caller's responsibility.
# Build with the server feature
# Start the server (binds to 127.0.0.1 only)
# Require a bearer token on /collect (recommended)
# or via the environment:
SYNHEART_SENSOR_TOKEN=""
Endpoints:
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/health |
none | Liveness check; returns status and version |
POST |
/collect |
Authorization: Bearer …* |
Accepts a { "session": { … } } JSON payload |
* When a token is configured (via --token or SYNHEART_SENSOR_TOKEN), /collect
requires a matching Authorization: Bearer <token> header and returns 401
otherwise. Configure the same token in the extension popup. If no token is set, the
endpoint is open and intended for trusted loopback-only use.
The extension defaults to http://127.0.0.1:8081/collect (port configurable in the
extension popup). A browser extension with host permissions is not subject to
browser CORS for loopback requests, so no chrome-extension:// origin needs to be
allow-listed; CORS here only governs page-origin callers (http://localhost,
http://127.0.0.1).
Configuration
Configuration is stored at:
- macOS:
~/Library/Application Support/synheart-sensor-agent/config.json - Windows:
%APPDATA%\synheart-sensor-agent\config.json
Default configuration:
Cumulative collection statistics are persisted alongside the config as
transparency.json in data_path.
Architecture
Native OS input ─┐
(keyboard/mouse) │
▼
┌─────────────────────────────────────────────────────────────┐
│ Synheart Sensor Agent │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Collector │────────────────────▶│ Raw Events │ │
│ │ (platform) │ │ (channel) │ │
│ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌──────────────────────────────┐ │
│ │Transparency │ │ HTTP server (--features │ │
│ │ Log │ │ server): POST /collect │◀──┼── Chrome
│ └─────────────┘ └──────────────────────────────┘ │ extension
└─────────────────────────────────────────────────────────────┘ (127.0.0.1:8081)
Pure sensor — windowing, features, sessions, and HSI are handled
downstream (e.g. synheart-core-rust / synheart-session-runtime).
Development
Building
# Debug build
# Release build
# Build with the optional HTTP server
# Run tests
# Run with logging
RUST_LOG=debug
A Makefile provides convenience targets (make fmt, make lint, make test,
make build, make ci, …).
Running the Demo
Project Structure
synheart-sensor-agent/
├── Cargo.toml # Project manifest
├── src/
│ ├── main.rs # CLI entry point
│ ├── lib.rs # Library exports
│ ├── config.rs # Configuration management
│ ├── server.rs # Optional HTTP server (--features server)
│ ├── collector/
│ │ ├── mod.rs # Platform selection + re-exports
│ │ ├── types.rs # Privacy-preserving event types
│ │ ├── macos.rs # macOS implementation (Core Graphics)
│ │ ├── windows.rs # Windows implementation (Hooks API)
│ │ └── noop.rs # No-op collector for other platforms
│ └── transparency/
│ ├── mod.rs # Transparency module
│ └── log.rs # Privacy-preserving collection statistics
└── examples/
└── capture_demo.rs # Demo application
Troubleshooting
macOS: "Insufficient permissions to capture input events"
- Open System Preferences > Security & Privacy > Privacy
- Select Input Monitoring
- Ensure the application is in the list and checked
- If already checked, remove and re-add the application
- Restart the application
Windows: input events not captured
- Right-click the terminal or binary and select "Run as administrator"
- If using PowerShell, launch it with elevated privileges
- Check that antivirus / Group Policy is not blocking low-level input hooks
- Restart the application
No events being captured
- Ensure you're actively typing or moving the mouse
- Check that the correct sources are enabled (
--sources all) - Verify permission is granted with
synheart-sensor status - macOS: Confirm Input Monitoring permission in System Preferences
- Windows: Confirm the process is running as Administrator
High CPU usage
- Some CPU use is expected during heavy, continuous input
- Usage should be minimal when idle
Contributing
This is a source-available repository. Issues and feature requests are welcome; pull requests are not accepted at this time. See CONTRIBUTING.md for the rationale and the supported contribution path.
Security
To report a vulnerability, see SECURITY.md.
License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Acknowledgments
- Built with Rust
- macOS event capture via core-graphics
- Windows event capture via windows
- CLI powered by clap