WebSocket API & CLI

NeuroSkill™ exposes a local JSON WebSocket server on every launch. All 60+ EEG metrics, device events, and control commands are available over a single persistent connection. No authentication required — the server binds to 0.0.0.0 (all interfaces) so LAN clients can connect.

A companion CLI (cli.ts) provides instant command-line access with auto-discovery, session-aware defaults, colorized output, and --json mode for piping to jq.

9 commands 3 event streams JSON over WebSocket LAN accessible No auth mDNS discovery GPU UMAP CLI included

Connection

mDNS Discovery

NeuroSkill™ registers a Bonjour / Avahi service on startup. The service type is _skill._tcp.local. with TXT records version=1 and format=json. Discover the port automatically:

dns-sd -B _skill._tcp macOS
avahi-browse _skill._tcp Linux

Direct Connection

The port is OS-assigned (random). If mDNS is unavailable, use lsof to find it:

pgrep -if skill | xargs -I{} lsof -iTCP -sTCP:LISTEN -nP -p {}

Then connect:

ws://<host>:<port>

Notes

  • Multiple clients can connect simultaneously
  • Broadcast capacity: 512 messages — slow consumers will lag
  • The server binds to 0.0.0.0 — accessible from LAN, not just localhost

Wire Format

Outbound (server → client)

Broadcast events are UTF-8 JSON with an "event" field:

{ "event": "eeg-bands", "payload": { ... } }

Inbound (client → server)

Send commands as JSON with a "command" field:

{ "command": "status" }

Responses

Command responses echo the command name and include an "ok" boolean:

Success

{ "command": "status", "ok": true, ... }

Error

{ "command": "label", "ok": false,
  "error": "missing required field: \"text\"" }

Not Broadcast

Raw EEG samples (256 Hz), PPG (64 Hz), IMU (50 Hz), and spectrogram slices are not broadcast over the WebSocket — their high frequency would overwhelm the connection. Those streams are only available via the internal Tauri IPC event bus for the native UI.

Events server → client

eeg-bands ~4 Hz

Fired every epoch (~250 ms). The main data stream — contains 60+ computed metrics: per-channel absolute and relative band powers (δ θ α β γ γ+), cross-band ratios (TBR, TAR, BAR, DTR, FAA), spectral shape (PSE, APF, BPS, SNR, SEF95, spectral centroid), complexity (Hjorth activity/mobility/complexity, permutation entropy, Higuchi FD, DFA exponent, sample entropy, PAC θ–γ), composite scores (focus, relaxation, engagement, mood, meditation, cognitive load, drowsiness), laterality index, coherence, mu suppression, headache & migraine research correlates, consciousness metrics (LZC, wakefulness, integration), PPG (HR, RMSSD, SDNN, pNN50, LF/HF, respiratory rate, SpO₂, perfusion index, Baevsky stress index), IMU (head pitch, roll, stillness, nod/shake counts), and artifact events (blink count/rate, jaw clench count/rate).

Example payload
{
  "event": "eeg-bands",
  "payload": {
    "timestamp": 1708800000.25,
    "channels": [
      {
        "channel": "TP9",
        "delta": 12.3, "theta": 8.1, "alpha": 15.2,
        "beta": 6.7, "gamma": 2.1, "high_gamma": 0.4,
        "rel_delta": 0.274, "rel_theta": 0.180, "rel_alpha": 0.338,
        "rel_beta": 0.149, "rel_gamma": 0.047, "rel_high_gamma": 0.009,
        "dominant": "alpha", "dominant_symbol": "α", "dominant_color": "#22c55e"
      },
      { "channel": "AF7", "..." : "..." },
      { "channel": "AF8", "..." : "..." },
      { "channel": "TP10", "..." : "..." }
    ],
    "faa": -0.08,
    "tar": 0.53, "bar": 0.82, "dtr": 1.52, "tbr": 1.21,
    "pse": 0.72, "apf": 10.2, "bps": -1.45, "snr": 18.3,
    "sef95": 28.4, "spectral_centroid": 12.1,
    "coherence": 0.65, "mu_suppression": 0.89, "mood": 62.0,
    "laterality_index": -0.03, "pac_theta_gamma": 0.12,
    "hjorth_activity": 245.1, "hjorth_mobility": 0.31, "hjorth_complexity": 1.42,
    "permutation_entropy": 0.91, "higuchi_fd": 1.42,
    "dfa_exponent": 0.85, "sample_entropy": 1.63,
    "focus_score": 55.0, "relaxation_score": 42.0, "engagement_score": 68.0,
    "hr": 72.1, "rmssd": 42.1, "sdnn": 58.3, "pnn50": 18.2,
    "lf_hf_ratio": 1.8, "respiratory_rate": 14.5,
    "spo2_estimate": 97.8, "perfusion_index": 2.1, "stress_index": 120.5,
    "blink_count": 34, "blink_rate": 12.3,
    "jaw_clench_count": 2, "jaw_clench_rate": 0.7,
    "head_pitch": -5.2, "head_roll": 1.3, "stillness": 95.0,
    "nod_count": 0, "shake_count": 0,
    "meditation": 52.0, "cognitive_load": 38.0, "drowsiness": 12.0,
    "headache_index": 18.3, "migraine_index": 11.7,
    "consciousness_lzc": 62.4, "consciousness_wakefulness": 71.0, "consciousness_integration": 45.8
  }
}
muse-status ~1 Hz

Device heartbeat. Connection state, battery %, sample counts, per-channel signal quality, firmware/hardware version, serial number, MAC address, headset preset, retry state, and raw sensor readings (accelerometer, gyroscope, fuel gauge, temperature).

Example payload
{
  "event": "muse-status",
  "payload": {
    "state": "connected",
    "connected": true,
    "streaming": true,
    "name": "Muse-A1B2",
    "id": "00:55:DA:B7:...",
    "serial_number": "MUSE2-A1B2C3D4",
    "mac_address": "00:55:DA:B7:...",
    "firmware_version": "1.4.2",
    "hardware_version": "6",
    "bootloader_version": "2.1",
    "preset": "p21",
    "battery": 85,
    "sample_count": 128000,
    "ppg_sample_count": 32000,
    "channel_quality": { "TP9": 0.95, "AF7": 0.88, "AF8": 0.91, "TP10": 0.97 },
    "retry_attempt": 0,
    "retry_countdown_secs": null,
    "accel": [0.01, -9.81, 0.03],
    "gyro": [0.1, -0.2, 0.05],
    "fuel_gauge_mv": 3850,
    "temperature_raw": 2950
  }
}
label-created on event

Emitted when any client creates a label via the Label window or the label command. Broadcast to all connected clients so dashboards can show markers on the EEG chart.

Example payload
{
  "event": "label-created",
  "payload": {
    "text": "eyes closed meditation",
    "label_id": 42
  }
}

Commands client → server

status

Returns a comprehensive snapshot: device state (connection, battery, firmware, serial, MAC, preset, sample counts, retry state, raw sensors), current session (start time, duration), embedding counts (today, total, recording days, encoder loaded, overlap config), label count, last calibration timestamp, per-channel signal quality, 48-hour sleep staging summary, latest epoch scores (all 60+ metrics), and recording history (total sessions/days, streak, today vs 7-day averages).

Request
{ "command": "status" }
Response
{
  "command": "status", "ok": true,
  "device": {
    "state": "connected", "connected": true, "streaming": true,
    "name": "Muse-A1B2", "battery": 85, "sample_count": 128000,
    "firmware_version": "1.4.2", "serial_number": "MUSE2-...",
    "mac_address": "00:55:DA:B7:...", "preset": "p21"
  },
  "session": { "start_utc": 1708790000, "duration_secs": 2847 },
  "embeddings": {
    "today": 342, "total": 14820, "recording_days": 31,
    "encoder_loaded": true, "overlap_secs": 2.5
  },
  "labels": { "total": 12 },
  "calibration": { "last_calibration_utc": 1708750000 },
  "signal_quality": { "TP9": 0.95, "AF7": 0.88, "AF8": 0.91, "TP10": 0.97 },
  "sleep": {
    "window_hours": 48, "total_epochs": 420, "epoch_secs": 5,
    "wake_epochs": 38, "n1_epochs": 35, "n2_epochs": 210,
    "n3_epochs": 95, "rem_epochs": 42
  },
  "scores": {
    "focus": 55.0, "relaxation": 42.0, "engagement": 68.0,
    "faa": -0.080, "tar": 0.530, "bar": 0.820, "tbr": 1.210,
    "hr": 72.1, "meditation": 52.0, "drowsiness": 12.0,
    "bands": { "rel_delta": 0.274, "rel_theta": 0.180, "rel_alpha": 0.338, "..." : "..." }
  },
  "history": {
    "total_sessions": 45, "recording_days": 31,
    "current_streak_days": 5, "total_recording_hours": 82.3,
    "longest_session_min": 95, "avg_session_min": 42,
    "today_vs_avg": {
      "focus": { "today": 0.62, "avg_7d": 0.55, "delta_pct": 12.7, "direction": "up" }
    }
  }
}
label

Inserts a timestamped annotation. Returns the created label_id. Also broadcasts a label-created event to all connected clients (including the desktop app's dashboard chart).

Parameters

NameTypeRequiredDescription
textstringrequiredLabel text (free-form, must not be empty)
label_start_utcu64optionalOverride start timestamp in unix seconds (default: now)
Request
{ "command": "label", "text": "eyes closed resting state" }
Response
{ "command": "label", "ok": true, "label_id": 42 }
sessions

Lists all recording sessions across all daily folders, newest first. Sessions are contiguous ranges of embedding epochs (gap threshold: 120 seconds). Each session includes start/end timestamps, epoch count, and the day folder name.

Request
{ "command": "sessions" }
Response
{
  "command": "sessions", "ok": true,
  "sessions": [
    { "start_utc": 1708790000, "end_utc": 1708792847, "n_epochs": 541, "day": "20260224" },
    { "start_utc": 1708700000, "end_utc": 1708702565, "n_epochs": 513, "day": "20260223" },
    { "start_utc": 1708681500, "end_utc": 1708684353, "n_epochs": 570, "day": "20260223" }
  ]
}
compare

Computes aggregate metrics for two time ranges and returns them side-by-side. Includes per-range session metrics (all bands + derived scores + PPG), sleep staging for both ranges, compare insights (timeseries stats, per-metric deltas with direction and percentage change, lists of improved/declined metrics), and auto-enqueues a Brain Nebula™ projection job for visual comparison.

Parameters

NameTypeRequiredDescription
a_start_utcu64requiredSession A start (unix seconds)
a_end_utcu64requiredSession A end
b_start_utcu64requiredSession B start
b_end_utcu64requiredSession B end
Request
{ "command": "compare", "a_start_utc": 1708700000, "a_end_utc": 1708702565, "b_start_utc": 1708790000, "b_end_utc": 1708792847 }
Response
{
  "command": "compare", "ok": true,
  "a": { "focus": 0.62, "relaxation": 0.45, "hr": 72.1, "..." : "..." },
  "b": { "focus": 0.71, "relaxation": 0.38, "hr": 68.4, "..." : "..." },
  "sleep_a": { "summary": { "total_epochs": 513, "..." : "..." }, "epochs": [...] },
  "sleep_b": { "summary": { "total_epochs": 541, "..." : "..." }, "epochs": [...] },
  "insights": {
    "n_epochs_a": 513, "n_epochs_b": 541,
    "deltas": {
      "focus": { "a": 0.62, "b": 0.71, "abs": 0.09, "pct": 14.5, "direction": "up" },
      "hr": { "a": 72.1, "b": 68.4, "abs": -3.7, "pct": -5.1, "direction": "down" }
    },
    "improved": ["focus", "engagement", "snr"],
    "declined": ["relaxation", "hr"]
  },
  "umap": {
    "queued": true, "job_id": 3,
    "estimated_ready_utc": 1708800012,
    "queue_position": 0, "estimated_secs": 12,
    "n_a": 513, "n_b": 541
  }
}
sleep

Classifies EEG epochs into sleep stages (Wake/N1/N2/N3/REM) using relative band-power ratios with simplified AASM heuristics. Returns a per-epoch hypnogram and a summary, plus a sleep analysis with efficiency, onset latency, REM latency, transition count, awakenings, stage durations, and bout analysis.

Parameters

NameTypeRequiredDescription
start_utcu64requiredAnalysis window start (unix seconds)
end_utcu64requiredAnalysis window end (must be ≥ start_utc)
Request
{ "command": "sleep", "start_utc": 1708750000, "end_utc": 1708780000 }
Response
{
  "command": "sleep", "ok": true,
  "epochs": [
    { "utc": 1708750000, "stage": "Wake", "rel_delta": 0.22, "rel_theta": 0.25, "rel_alpha": 0.30, "rel_beta": 0.18 },
    { "utc": 1708750005, "stage": "N1", "..." : "..." }
  ],
  "summary": {
    "total_epochs": 1054, "epoch_secs": 5,
    "wake_epochs": 134, "n1_epochs": 89, "n2_epochs": 421,
    "n3_epochs": 298, "rem_epochs": 112
  },
  "analysis": {
    "efficiency_pct": 87.3, "onset_latency_min": 8.5,
    "rem_latency_min": 62.0, "transitions": 34, "awakenings": 6,
    "stage_minutes": { "wake": 11.2, "n1": 7.4, "n2": 35.1, "n3": 24.8, "rem": 9.3 },
    "bouts": {
      "N2": { "count": 8, "mean_min": 4.4, "max_min": 12.1 },
      "N3": { "count": 5, "mean_min": 5.0, "max_min": 9.2 },
      "REM": { "count": 3, "mean_min": 3.1, "max_min": 5.8 }
    }
  }
}
calibrate

Opens the calibration window in the app. Requires a connected Muse device. The calibration protocol runs guided alternating actions (default: Eyes Open / Eyes Closed, 10s each, 5s breaks, 3 loops) to establish baseline metrics.

Request
{ "command": "calibrate" }
Response
{ "command": "calibrate", "ok": true }
umap

Enqueues a Brain Nebula™ projection job (GPU-accelerated UMAP via fast-umap + wgpu/CubeCL) on the selected embeddings. Returns a job_id for polling. Results are cached in ~/.skill/umap_cache/ — repeated queries for the same session pair return instantly. User labels are attached to points when available.

Parameters

NameTypeRequiredDescription
a_start_utcu64requiredSession A start (unix seconds)
a_end_utcu64requiredSession A end
b_start_utcu64requiredSession B start
b_end_utcu64requiredSession B end
Request
{ "command": "umap", "a_start_utc": 1708700000, "a_end_utc": 1708702565, "b_start_utc": 1708790000, "b_end_utc": 1708792847 }
Response
{
  "command": "umap", "ok": true,
  "queued": true, "job_id": 5,
  "estimated_ready_utc": 1708800014,
  "queue_position": 0, "estimated_secs": 14,
  "n_a": 513, "n_b": 541
}
umap_poll

Polls an async Brain Nebula™ projection job. Returns pending with epoch progress (epoch, total_epochs, loss, best_loss, elapsed_secs, epoch_ms), or complete with the 3D point cloud and cluster analysis (separation score, inter-cluster distance, intra-spread per session, centroids, outlier counts).

Parameters

NameTypeRequiredDescription
job_idu64requiredJob ID from the umap command
Request
{ "command": "umap_poll", "job_id": 5 }
Response
{
  "command": "umap_poll", "ok": true,
  "status": "complete",
  "result": {
    "points": [
      { "x": 1.23, "y": -0.45, "z": 2.01, "session": 0, "utc": 1708700005 },
      { "x": 1.31, "y": -0.52, "z": 1.98, "session": 0, "utc": 1708700010, "label": "focused" },
      { "x": -2.10, "y": 1.82, "z": 0.33, "session": 1, "utc": 1708790005 }
    ],
    "n_a": 513, "n_b": 541, "dim": 32, "elapsed_ms": 8432,
    "analysis": {
      "separation_score": 3.42,
      "inter_cluster_distance": 4.15,
      "intra_spread_a": 1.21, "intra_spread_b": 1.08,
      "centroid_a": [0.82, -0.31, 1.55],
      "centroid_b": [-1.95, 1.42, 0.22],
      "n_outliers_a": 3, "n_outliers_b": 2
    }
  }
}

Command-Line Interface

A TypeScript CLI wraps the WebSocket API with mDNS auto-discovery, session-aware defaults, colorized output, and --json mode for piping to jq. 9 commands including search, compare, sleep staging, and UMAP with live progress bars.

CLI Docs
npx neuroskill status --json | jq '.scores.focus'

WebSocket Examples

Python Stream metrics + submit labels

import asyncio, json, websockets, time

async def main():
    async with websockets.connect("ws://localhost:PORT") as ws:
        # Open calibration window
        await ws.send(json.dumps({"command": "calibrate"}))
        print(await ws.recv())

        # Submit a label
        await ws.send(json.dumps({"command": "label",
            "text": "eyes closed resting state"}))
        print(await ws.recv())

        # Search embeddings from the last 5 minutes
        now = int(time.time())
        await ws.send(json.dumps({"command": "search",
            "start_utc": now - 300, "end_utc": now, "k": 5}))
        result = json.loads(await ws.recv())
        print(json.dumps(result, indent=2))

        # Listen for live events
        async for msg in ws:
            data = json.loads(msg)
            if data.get("event") == "eeg-bands":
                p = data["payload"]
                print(f"α={p['channels'][1]['rel_alpha']:.3f}  focus={p['focus_score']:.0f}  hr={p.get('hr','--')}")

asyncio.run(main())

Node.js Search + compare

const WebSocket = require("ws");
const ws = new WebSocket("ws://localhost:PORT");

ws.on("open", () => {
  // Search last 5 minutes
  const now = Math.floor(Date.now() / 1000);
  ws.send(JSON.stringify({
    command: "search",
    start_utc: now - 300, end_utc: now, k: 10
  }));
});

ws.on("message", (data) => {
  const msg = JSON.parse(data);
  if (msg.command) {
    // Response to a command
    console.log("command response:", msg.command, msg.ok);
  } else if (msg.event) {
    // Broadcast event
    console.log("event:", msg.event);
  }
});

websocat Quick terminal test

# Install: brew install websocat  (or: cargo install websocat)

# Open calibration window
echo '{  "command":"calibrate" } ' | websocat ws://localhost:PORT

# Submit a label
echo '{  "command":"label","text":"meditation session" } ' | websocat ws://localhost:PORT

# Search last 10 minutes
echo '{  "command":"search","start_utc":'$(($(date +%s)-600))',"end_utc":'$(date +%s)' } ' \
  | websocat ws://localhost:PORT

# Stream all events (Ctrl+C to stop)
websocat ws://localhost:PORT

Notes

Embedding Model

Embeddings are 32-dimensional vectors produced by the ZUNA EEG foundation model. Each epoch is 5 seconds of 4-channel EEG (1,280 samples @ 256 Hz), z-scored and divided by 10. The HNSW index uses M=16, ef_construction=200 for fast approximate nearest-neighbor search.

UMAP Backend

Brain Nebula™ uses fast-umap with a wgpu/CubeCL GPU backend (parametric UMAP). Configurable via ~/.skill/umap_config.json: n_neighbors, n_epochs (50–2000), repulsion_strength, neg_sample_rate, timeout_secs. Results cached in ~/.skill/umap_cache/.

Data Storage

Each recording day gets a folder (~/.skill/YYYYMMDD/) containing eeg.sqlite (embeddings table + timestamps) and eeg_embeddings.hnsw (HNSW index). Labels are stored separately in ~/.skill/labels.sqlite. Settings live in ~/.skill/settings.json.

Overlap & Epoch Rate

Default overlap is 2.5 s (50%), meaning a new embedding epoch every 2.5 s. Configurable between 0 s (no overlap) and 4.5 s (90% overlap). The band analysis hop is 64 samples (250 ms), producing ~4 snapshots/second.

Sleep Staging

Sleep classification uses relative band-power ratios (delta/theta/alpha/beta) with simplified AASM heuristics applied to each 5-second embedding epoch. The analysis includes efficiency, onset/REM latency, transition count, awakenings, per-stage durations, and bout statistics.