Real-time keyboard and mouse click statistics with a visual UI. Backend records input events via Windows hooks or rdev, stores counts in SQLite or MongoDB, and serves a live-updating HTML frontend.
cargo run --releaseOpen http://localhost:5000 in a browser.
| Argument | Platform | Description |
|---|---|---|
--console, -c |
Windows | Attach a console window (hidden by default) |
--help, -h |
all | Print usage and exit (all binaries support this) |
Create a config.json next to the executable (optional — all fields have defaults):
{
"database": {
"backend": "sqlite",
"sqlite": {
"path": "monitor.sqlite",
"table": "daily_stats"
},
"mongodb": {
"protocol": "mongodb",
"database": "keymouse_monitor",
"hosts": ["localhost:27017"],
"ssl": true,
"replicaSet": "atlas-abc-shard-0",
"appName": "MyApp",
"username": "user",
"password": "pass",
"collection": "daily_stats"
}
},
"port": 5000,
"listener": "rawinput",
"save_interval_secs": 60,
"log": {
"level": "info",
"file": "logs/monitor.log",
"rotation": "daily",
"console": true
}
}| Key | Type | Default | Description |
|---|---|---|---|
backend |
string | "sqlite" |
"sqlite" or "mongodb" |
sqlite |
object | {path: "monitor.sqlite", table: "daily_stats"} |
SQLite settings |
mongodb |
object | (see below) | MongoDB connection settings |
| Key | Type | Default | Description |
|---|---|---|---|
path |
string | "monitor.sqlite" |
Database file path |
table |
string | "daily_stats" |
Table name |
| Key | Type | Default | Description |
|---|---|---|---|
protocol |
string | "mongodb" |
URI protocol (mongodb / mongodb+srv) |
database |
string | "keymouse_monitor" |
Database name |
username |
string (nullable) | null |
Auth username |
password |
string (nullable) | null |
Auth password |
authSource |
string | "admin" |
Auth source database |
ssl |
bool | true |
Use TLS |
replicaSet |
string (nullable) | null |
Replica set name |
appName |
string (nullable) | null |
Application name |
hosts |
string array (nullable) | null |
Host list, e.g. ["host:27017"] |
connectTimeoutMs |
number | 15000 |
Connection timeout |
serverSelectionTimeoutMs |
number | 30000 |
Server selection timeout |
collection |
string | "daily_stats" |
Collection name |
| Key | Type | Default | Description |
|---|---|---|---|
port |
number | 5000 |
HTTP server port |
listener |
string | "rawinput" (Windows)"rdev" (other) |
Input event backend |
update_mode |
string | "diff" |
DB save mode: "diff" (only changed keys) or "full" (snapshot) |
save_interval_secs |
number | 60 |
Periodic DB save interval |
log |
object | (see below) | Logging configuration |
| Key | Type | Default | Description |
|---|---|---|---|
level |
string | "info" |
Log level (trace, debug, info, warn, error) |
file |
string | "logs/monitor.log" |
Log file path (relative to exe) |
rotation |
string | "daily" |
Log rotation: "daily", "hourly", "never" |
console |
bool | true |
Whether to also log to terminal when --console is passed |
| Value | Platform | Keyboard | Mouse |
|---|---|---|---|
"rawinput" |
Windows | WH_KEYBOARD_LL hook |
Raw Input (WM_INPUT) via hidden message-only window + RIDEV_NOLEGACY |
"native" |
Windows | WH_KEYBOARD_LL hook |
WH_MOUSE_LL hook |
"rdev" |
All | rdev listen() |
rdev listen() |
On non-Windows only "rdev" is available and selected automatically. Unknown values fall back to "rdev".
| Method | Path | Description |
|---|---|---|
GET |
/keycounts |
Current in-memory counts as JSON {"key": count, ...} |
GET |
/history?start=YYYY-MM-DD&end=YYYY-MM-DD |
Aggregated stats for a date range |
GET |
/events |
SSE stream — pushes delta JSON on each key/button press; first event per connection is a full snapshot |
GET |
/api/export?format=nested|flat&start=YYYY-MM-DD&end=YYYY-MM-DD&session=<id> |
Full database export as JSON (streams records with reactive SSE progress) |
GET |
/api/export/progress?session=<id> |
Polling endpoint: {"current":N,"total":M,"done":bool} |
GET |
/api/export/progress/stream?session=<id> |
SSE stream of export progress for the given session |
POST |
/api/import?mode=overwrite|merge |
Import JSON data from export format |
GET |
/api/version |
{"version": "2.3.0", "name": "keymouse-monitor"} |
data: {"a": 42, "enter": 7, "mouse_left": 3, ...}\n\n
The first event after a (re)connect is a full snapshot of all keys; subsequent events carry only the keys whose count changed since the last push (delta). Fires on every key/button/wheel event (only when at least one SSE client is connected).
Two output formats available via the UI export modal:
{
"backend": "sqlite",
"exported_at": "2026-06-23T12:34:56",
"records": {
"2026-06-22": {"a": 100, "enter": 7},
"2026-06-23": {"a": 42, "mouse_left": 3}
}
}Flat format (same structure, records is an array of {date, key, count} objects):
{
"backend": "sqlite",
"exported_at": "2026-06-23T12:34:56",
"records": [
{"date": "2026-06-22", "key": "a", "count": 100},
{"date": "2026-06-22", "key": "enter", "count": 7}
]
}Import accepts both formats. Import modes: "overwrite" (replace existing records) or "merge" (add counts).
curl http://localhost:5000/keycounts
curl "http://localhost:5000/history?start=2026-06-01&end=2026-06-22"
curl http://localhost:5000/api/export > backup.json
curl -X POST "http://localhost:5000/api/import?mode=merge" -d @backup.json -H "Content-Type: application/json"- Counts saved to SQLite/MongoDB every
save_interval_secs(default 60). - Graceful shutdown via
Ctrl+C: saves remaining in-memory data before exit. - DB schema (SQLite): table
daily_statswith columnsdate(TEXT),key(TEXT),count(INTEGER), primary key(date, key). - MongoDB: collection
daily_statswith documents{date, key, count}.
cargo build --releaseSingle binary at target/release/keymouse-monitor (.exe on Windows) with embedded icon. No runtime dependencies.
Statically linked C runtime (via .cargo/config.toml).
CI (GitHub Actions) builds on push to main that modifies the version file; assets include binary + index.html.
├── src/
│ ├── main.rs # Entry point, timer, graceful shutdown
│ ├── config.rs # Config loading + all serde structs
│ ├── api.rs # Axum router, SSE, all endpoints
│ ├── data.rs # In-memory MonitorData, save logic
│ ├── log.rs # Tracing-based logging (tinfo/terror/twarn/...)
│ ├── database/
│ │ ├── mod.rs # Database enum, DatabaseBackend trait, ImportMode
│ │ ├── sqlite.rs # SQLite backend (rusqlite, flat schema)
│ │ └── mongodb.rs # MongoDB backend (dedicated tokio runtime)
│ ├── maps.rs # Key/Button → display string mapping (common/src/maps.rs)
│ └── listener/
│ ├── mod.rs # Dispatch by kind (native/rawinput/rdev)
│ ├── common.rs # CallbackData, process_event
│ ├── keyboard.rs # VK → rdev::Key mapping
│ ├── native.rs # WH_KEYBOARD_LL + WH_MOUSE_LL
│ ├── rawinput.rs # WH_KEYBOARD_LL + Raw Input (stack buffer)
│ └── rdev.rs # Cross-platform rdev backend
├── tools/
│ ├── key_viewer/ # Live VK code inspector (--rawinput, -h)
│ ├── mouse_bench/ # CPU benchmark of 3 mouse backends (--auto, -j, -h)
│ └── db_check/ # Database connectivity checker [CONFIG_PATH] (-h)
├── index.html # SPA frontend (dark theme, grid layout)
├── static/
│ ├── svg/ # SVG assets (logo)
│ └── icon/ # Auto-generated icon (app.ico, app.rc)
├── scripts/build.rs # Auto-generates app.ico from SVG
├── CHANGELOG.md # Version history
├── config.json # Optional config file
├── version # Version string for CI (2.3.0)
└── Cargo.toml