# 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](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](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. | | `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 ```sh # 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](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. ```json { "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_STATE_FILE` | `poc/.veskslop-state.json` | Owner, trust list, and config | | `VESKSLOP_ACTION_LOG` | `/actions.jsonl` | Persistent action log | | `VESKSLOP_ACTION_LOG_MAX_BYTES` | 5 MiB | Action log rotation limit | | `VESKSLOP_CACHE_DIR` | `/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. | | `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. | ## How to add an MCP tool 1. Add the action to the core bridge. See [core-bridge.md](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](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](auth.md).