Files
Veskslop-t/docs/daemon.md
T

11 KiB

Daemon

The daemon runs outside Vesktop. It holds all logic. It talks to the core bridge and exposes MCP.

Files

File Role Status
poc/src/control-bridge.ts MCP server, tool list, health endpoint, core reconnect Implemented
poc/src/core-process.ts Start, stop, and restart the Vesktop core process Implemented
poc/src/qr-session.ts Remote-auth QR login session (shared with the web UI) Implemented
poc/src/auth-service.ts Auth state for the web UI Implemented
poc/src/puppet.ts Trust list, command handling, trigger log, response policy Implemented
poc/src/communication.ts Pure log, parser, and context logic Implemented
poc/src/message-cache.ts Channel message cache in a tmp folder Implemented
poc/src/action-log.ts Persistent JSONL action log with rotation Implemented
poc/src/pacing.ts Paced send queue and read queries Implemented
poc/src/webui.ts Web UI listener and JSON API First draft
poc/public/ Static web UI (HTML, CSS, JS) First draft
poc/src/core-client.ts Client actions over the core bridge Implemented (primary transport)
poc/src/cdp-client.ts Chrome DevTools Protocol client Development fallback
poc/src/qr-login.ts Terminal QR login Implemented and verified
poc/src/test-communication.ts Unit tests for the pure logic Implemented
poc/src/test-cache.ts Unit tests for the message cache Implemented
poc/src/mock-core.ts Mock core bridge for daemon tests Implemented
poc/src/test-trigger.ts Inject a trigger event into the mock core Implemented
poc/src/cdp-probe.ts CDP feasibility probe Development tool
poc/src/cdp-dispatch.ts Run one CDP expression in the client Development tool

Transport

The primary transport is the validated core bridge. The daemon connects to vesktop-puppet.sock. All actions go through the validated capability layer. See core-bridge.md.

The CDP transport is a development fallback. It needs no Vesktop patch. It evaluates raw code in the page, so it has no core-side guardrails. Use it for debugging only.

Pacing

The daemon paces all outbound sends. A send is message.send or dm.send.

  • The first send of a burst waits 30 to 200 ms.
  • Later sends in the same burst wait 500 to 2800 ms.
  • A burst ends after a random 5 to 20 seconds of quiet. The next send is a fast first action.

Read queries are serialized with a small 100 to 300 ms gap. Reads do not reset the send burst. Identical concurrent reads are coalesced. The daemon prefers cached state over queries.

The core adds a second, hard rate limit. See core-bridge.md.

MCP tools

The daemon exposes MCP over Streamable HTTP. The default address is http://127.0.0.1:8788/mcp.

Tool Purpose
get_state List the user, the guilds, and the channels.
send_message Send a message to a channel.
read_messages Read cached messages from a channel.
send_dm Send a DM to a user.
get_voice_state Which voice channel a user is in and who else is there.
get_voice_overview Occupied voice channels in a guild and their members.
get_user_volume Per-user playback volume and the client output volume.
set_user_volume Set a user's playback volume (0 to 200).
voice_join Join a voice channel, muted by default.
voice_leave Leave the current voice channel.
voice_move Move to another voice channel.
get_audio_status Audio engine, TTS, queue, ducking, and capture state.
speak Queue text to speech into the virtual microphone (text, provider?, voice?, speed?, pitch?).
start_tts_engine Warm up the TTS engine so the first line is fast (engine?).
stop_audio Stop speech and clear the queue.
start_capture / stop_capture Record or stop recording the client output sink.
list_audio_devices The client's input and output audio devices.
set_audio_devices Set the client's input and output devices.
get_transcript Read captured call segments with speaker, WAV path, and text.
search_users Search the client user cache by username, global name, or nickname.
list_dms List cached DM and group DM channels with recipients.
get_message_log Read the trigger log.
get_action_log Read the persistent action log.
run_commands Run several read-only tools in one call, in order.
get_context Assemble conversation context for a channel. Fetches new messages by default.
get_puppet_config Read the response policy and context settings.
set_puppet_config Change the response policy and context settings.
whitelist_list List the owner and the trusted users.
whitelist_add Add users to the trust list.
whitelist_remove Remove users from the trust list.

Run commands

# Start the daemon. It connects to the core bridge and starts the web UI.
pnpm control:bridge

# Start the daemon with a different web UI port.
WEBUI_PORT=18889 pnpm control:bridge

# Start a mock core for tests without Electron.
VESKSLOP_SOCKET=/tmp/vesktop-puppet-mock.sock pnpm control:mock

# Inject a trigger event into the mock core.
VESKSLOP_SOCKET=/tmp/vesktop-puppet-mock.sock pnpm test:trigger

# Send one test DM. The only permitted live target is zzib.
pnpm poc:dm

# Run the pure-logic unit tests.
pnpm test:comm
pnpm test:cache

# Development tools for the CDP fallback.
pnpm cdp:probe
CDP_EXPRESSION='...' pnpm cdp:dispatch

Core process

The daemon can start, stop, and restart the Vesktop client (poc/src/core-process.ts). The web UI Core tab and the /api/core/* routes use it.

  • The spawn sets VESKSLOP_HEADLESS=1 and VESKSLOP_TOKEN_FILE (the daemon token file).
  • The default command is node_modules/.bin/electron . from the repo root. VESKSLOP_CORE_CMD replaces it. VESKSLOP_CORE_ARGS replaces the default flags.
  • On Linux without a DISPLAY, the daemon starts Xvfb on the first free display from :99 and points the client at it. An existing DISPLAY wins. VESKSLOP_XVFB=0 disables auto-start.
  • The status holds the last exit code and signal, and the last 20 output lines. The Core tab shows both, so a failed start explains itself.
  • The daemon stops a client that it started, and any Xvfb it started, when the daemon exits. An externally started client is reported as external and is not managed.
  • When the core bridge drops, the daemon retries every 2 s. A restart moves the bridge connection to the new client. The in-memory trigger log resets; the state file and the action log survive.
  • QR login lives in poc/src/qr-session.ts and is shared by the terminal tool and the web UI. See auth.md.

Verified live on 2026-09-13: the web UI started the client under auto-Xvfb, the bridge connected, state.get returned the real account and guilds, and stop exited with code 0.

Health endpoint

GET /health returns the core connection state, the socket path, the owner ID, and the query count.

{ "ok": true, "coreConnected": true, "socketPath": "/run/user/1000/vesktop-puppet.sock", "ownerId": "177117296140091393", "queries": 4 }

Settings and paths

Setting Default Purpose
VESKSLOP_SOCKET $XDG_RUNTIME_DIR/vesktop-puppet.sock Core bridge socket
VESKSLOP_REQUEST_TIMEOUT_MS 30000 How long a core request waits before it fails.
VESKSLOP_STATE_FILE poc/.veskslop-state.json Owner, trust list, and config
VESKSLOP_ACTION_LOG <state dir>/actions.jsonl Persistent action log
VESKSLOP_ACTION_LOG_MAX_BYTES 5 MiB Action log rotation limit
VESKSLOP_CACHE_DIR <tmp>/veskslop-cache Context cache folder
VESKSLOP_CACHE_MAX_BYTES 50 MiB Context cache size limit
MCP_HOST / MCP_PORT 127.0.0.1 / 8788 MCP listen address
WEBUI_HOST / WEBUI_PORT 0.0.0.0 / 8789 Web UI listen address. No authentication.
WEBUI_TLS / WEBUI_TLS_KEY / WEBUI_TLS_CERT 1 / unset Self-signed HTTPS, or an existing certificate.
VESKSLOP_CORE_CMD unset Full command that starts the client. Overrides the default.
VESKSLOP_CORE_ARGS --no-sandbox --disable-gpu --disable-dev-shm-usage Flags for the default Electron command.
VESKSLOP_XVFB unset Set to 0 to disable automatic Xvfb start on Linux.
VESKSLOP_AUDIO_AUTOCONNECT 1 Point the client at the virtual devices on connect.
VESKSLOP_AUDIO_MANAGE_SERVER 1 Start pulseaudio when pactl cannot reach a server.
VESKSLOP_AUDIO_SINK / _SOURCE / _CAPTURE_SINK veskslop_tts / veskslop_mic / veskslop_capture Virtual device names.
VESKSLOP_AUDIO_CLIENT_MATCH vesktop,electron,chromium,discord Stream name patterns moved into the capture sink.

The daemon warns at startup when dist/ is older than the core sources. Rebuild with pnpm build and restart the client, or new actions fail with UNKNOWN_ACTION. | VESKSLOP_AUDIO_DUCK | 30 | Sink volume percent while others speak. | | VESKSLOP_TTS_LEAD_MS | 150 | Lead-in silence before each TTS line (0 disables, max 2000). | | VESKSLOP_FFPLAY / VESKSLOP_FFMPEG | ffplay / ffmpeg | Playback and capture binaries. | | VESKSLOP_TTS_CMD | unset | Local TTS command with {out} and {text_file} (also {voice}, {speed}, {pitch}). | | VESKSLOP_TTS_NAME | custom engine | Display name for the command engine in the web UI. | | VESKSLOP_TTS_API_URL / _API_KEY / _PROVIDER | unset | HTTP TTS provider and bearer token. | | VESKSLOP_TTS_VOICES | unset | Voice list for a custom command: id[|Name[|gender[|language]]], comma separated. | | VESKSLOP_TTS_SPEED / _PITCH | unset | Exported to the command only when a value is set. | | VESKSLOP_TTS_EXPRESSION | unset | Flextalk Expression profile value (0-100), exported when set. | | VESKSLOP_TRANSCRIPT | <state dir>/transcripts.jsonl | Transcript store path. | | VESKSLOP_PREFERENCES | <state dir>/preferences.json | Web UI preferences (visible guilds, TTS). | | VESKSLOP_CAPTURE_AUTO | 0 | Start capture when the bot joins a call. | | VESKSLOP_VAD_THRESHOLD_DB / _MIN_SPEECH_MS / _SILENCE_MS / _PRE_ROLL_MS / _MAX_SEGMENT_MS | -35 / 120 / 600 / 300 / 30000 | VAD tuning. | | VESKSLOP_STT_CMD / _API_URL / _API_KEY | unset | Speech-to-text provider. | | VESKSLOP_VOSK_MODEL / _VOSK_DIR | poc/models/vosk-model* | Local Vosk model, auto-detected when missing. |

How to add an MCP tool

  1. Add the action to the core bridge. See core-bridge.md.
  2. Add a server.registerTool call in poc/src/control-bridge.ts.
  3. Wrap outbound calls in paced.
  4. Add a unit test if the tool has local logic.
  5. Run pnpm check in poc/.

Security notes

  • The MCP server binds to localhost by default.
  • The web UI binds to all interfaces by default and has no authentication. Use it over LAN or Tailscale only. Do not expose the port to the public internet. See webui.md.
  • Remote access must go over LAN or Tailscale. Do not expose the daemon to the public internet.
  • The daemon holds no Discord credentials. The client holds the session.
  • The daemon token file is separate from the client session. See auth.md.