wakey
wakey is a Wake-on-LAN and LAN-observability tool for a Linux router.
It started as a small web UI running on-device. It is now being reshaped into a service-first project with:
- a reusable core model
- a Linux/OpenWrt adapter layer
- a CLI for operators
- an outbound agent plus control-plane model
What it does
wakey currently focuses on a small set of router-side jobs:
- inspect LAN neighbor state
- read DHCP lease data
- merge those facts into a higher-level device inventory
- list useful interface/broadcast information
- send Wake-on-LAN packets
In practice that means commands like:
wakey status bedroom-pc
wakey leases --include-state
wakey devs
wakey wake bedroom-pc
wakey wake --mac aa:bb:cc:dd:ee:ff
Workspace layout
wakey-core- shared types and parsing
- device, neighbor, DHCP, interface, and wake models
wakey-linux- Linux/OpenWrt adapter
- DHCP lease loading, interface summaries, neighbor lookup, WoL sending
wakey- service layer and operator CLI
wakey-agent- outbound router daemon, enrollment, websocket command execution
wakey-control-plane- enrollment endpoint, connected-agent registry, command relay API
ipjs- typed wrappers around Linux
ip -j ...data - JSON-first, with optional experimental netlink backends
- typed wrappers around Linux
Current architecture
Inside the wakey crate:
src/service- the real use-case layer
- status, leases, inventory, interfaces, wake, and query resolution
The long-term direction is:
- keep the service layer stable
- keep the local CLI stable for operators
- run remote control via outbound agent + control-plane relay
Future direction
The project now uses:
wakeyfor local operator workflows and shared service behaviorwakey-agentfor outbound enrollment and websocket executionwakey-control-planefor enrollment, registry, and command relay
Logging and troubleshooting
Both daemons emit structured tracing logs to stderr. If you are not seeing enrollment/command activity, run with increased verbosity.
Control-plane also supports a config file at
/etc/wakey-control-plane/config.toml (override with --config-file) so you
can persist telemetry settings instead of passing flags.
You can scaffold this file with:
wakey-control-plane init-config
Or bootstrap once during serve (writes only if config is missing):
wakey-control-plane serve --bootstrap-config
Inspect current persisted state:
wakey-control-plane state-stats
List/revoke enroll tokens from CLI:
wakey-control-plane list-enroll-tokens --include-expired
wakey-control-plane revoke-enroll-token --token enr-...
Machine-readable output is available:
wakey-control-plane list-enroll-tokens --include-expired --json
wakey-control-plane state-stats --json
Example:
data_dir = "/var/lib/wakey-control-plane"
bind = "0.0.0.0:8080"
public_url = "https://cp.example.com"
state_file = "state.db"
pid_file = "wakey-control-plane.pid"
command_timeout_ms = 30000
enroll_token_ttl_seconds = 86400
[telemetry]
otlp_endpoint = "http://127.0.0.1:4317"
service_name = "wakey-control-plane"
json_logs = false
If telemetry.otlp_endpoint is omitted, logs still work normally and only local
structured logs are emitted.
State is persisted in an embedded sled database (default
/var/lib/wakey-control-plane/state.db).
Relative paths in config are resolved under data_dir.
Enroll tokens are now expiring and revocable. Issuance returns expires_at_unix.
Expired tokens are rejected on enroll and can be garbage-collected periodically
or on demand.
Quick start
Control-plane:
wakey-control-plane -v serve --bind 0.0.0.0:8787 --public-url https://cp.example.com
Agent:
wakey-agent -v serve --config /etc/wakey-agent/config.toml
Fine-grained log filters
Use RUST_LOG when you want to focus on websocket/API internals.
RUST_LOG=wakey_control_plane=debug,wakey_agent=debug wakey-control-plane serve
RUST_LOG=wakey_agent=debug wakey-agent serve
What you should see
During registration/enroll:
- control-plane:
issued enroll token, thenagent enrollment accepted - agent:
starting agent enrollment, thenagent enrollment succeeded and config was written
During live connectivity:
- control-plane:
agent websocket upgraded,agent authenticated,agent disconnected - agent:
connecting agent websocket,agent websocket dns resolved,agent websocket connected,agent websocket session authenticated,heartbeat sent(debug)
During command relay:
- control-plane:
dispatching command to agent - agent:
received command from control-plane, thencommand execution completed(orcommand dispatch failed) - control-plane:
agent command completed(or timeout/error warnings)
During daemon control/state operations:
- control-plane:
wrote control-plane pid file,saved control-plane store,reloaded control-plane store from disk - agent:
wrote wakey-agent pid file,sending wakey-agent reload signal
Control-plane admin API includes token management endpoints:
POST /api/v1/control/enroll-token?ttl_seconds=<n>GET /api/v1/control/enroll-tokens?include_expired=true|falseDELETE /api/v1/control/enroll-tokens/{token}GET /api/v1/control/audit/events?agent_id=<id>&event_type=<type>&limit=<n>GET /api/v1/control/alerts?lookback_seconds=900GET /api/v1/control/alerts/history?since_unix=<ts>&limit=<n>GET /api/v1/control/alerts/ws(websocket snapshots + recent transitions)
If commands still appear silent, verify both processes are running with -v
and that RUST_LOG is not overriding to a stricter level.
If websocket connect feels delayed, compare dns_resolve_ms and ws_connect_ms
from agent logs. Slow DNS is a common source of multi-second connection stalls
when using hostnames; using a stable IP or local host mapping can avoid this.
Edge Exposure (Caddy + Cloudflare Access)
Control-plane is intended to run behind a reverse proxy with TLS termination.
Use Cloudflare Access to protect /ui/* and /api/v1/control/*, while keeping
agent enrollment and websocket endpoints reachable.
An example Caddy config is provided at:
deploy/Caddyfile.control-plane.example
Expected exposure model:
- Public:
/healthz,/api/v1/agents/enroll,/api/v1/agent/ws - Private (Cloudflare Access):
/ui/*,/api/v1/control/*
Control-plane routing is organized with the same boundary in code:
- public router: health, enroll, agent websocket
- control router: all
/api/v1/control/*admin endpoints
This keeps edge policy and app routing aligned as features grow.
UI (Initial Shell)
Control-plane now serves a minimal operator shell at /ui/ for immediate
workflow testing (agents, command runner, alerts, and recent audits).
This UI is intentionally lightweight and is the starting point for the richer Operator UI v1 plan.
CLI
wakey is usable as a local/operator CLI.
Status
Show device status rows using a free-form selector:
wakey status bedroom-pc
Or use explicit filters:
wakey status --dev br-lan --nud reachable
wakey status --mac aa:bb:cc:dd:ee:ff
wakey status --json
Leases
Show DHCP leases:
wakey leases
wakey leases --include-state
wakey leases --include-state --json
Wake
Query mode:
wakey wake bedroom-pc
Explicit/manual mode:
wakey wake --mac aa:bb:cc:dd:ee:ff
wakey wake --mac aa:bb:cc:dd:ee:ff --ip 192.168.1.255
wakey wake --mac aa:bb:cc:dd:ee:ff --json
Rules:
- query mode and explicit
--mac/--ipmode are mutually exclusive --iprequires--mac--macwithout--ipfans out to interface broadcast targets
Interfaces
Show condensed interface summaries:
wakey devs
wakey devs br-lan
wakey devs --up
wakey devs --json
Tests
This repo has two useful testing modes:
- local compile checks
- live on-device tests against the real router/runtime environment
Local
cargo check
cargo test --no-run
cargo clippy --all-targets --all-features -- -D warnings
Focused control-plane state tests:
cargo test -p wakey-control-plane state::store::tests::gc_removes_expired_tokens
cargo test -p wakey-control-plane state::store::tests::enroll_rejects_expired_token
cargo test -p wakey-control-plane state::store::tests::stats_counts_agents_and_expired_tokens
On-device
Some integration tests are intentionally #[ignore] because they use real
router state. Use the PowerShell helper:
./scripts/test_remote.ps1 -Package wakey -BinaryFilter integration_live_services -Ignored -NoCapture
./scripts/test_remote.ps1 -Package wakey -BinaryFilter integration_inventory -Ignored -NoCapture
You can further narrow execution with -Filter to select individual Rust test
functions inside a test binary.
Build target
This project is primarily aimed at an OpenWrt/Linux ARM router target. The workspace is commonly built for:
armv7-unknown-linux-musleabihf
Some crates and tests are Linux-specific by design.
Notes
ipjsis JSON-first by default; experimental netlink paths exist where they are worth keeping.- the current web client is temporary and kept alive through explicit compatibility mapping
- the service layer is the part intended to survive the migration