Files

6.3 KiB

Decisions

This document records the main design decisions and their reasons.

ADR-1: Use the real client

Decision. Run the real Vesktop client. Do not use a third-party selfbot protocol library.

Reason. The popular selfbot libraries are archived or deprecated. Stale protocol emulation is a ban risk. The real client sends official traffic.

ADR-2: Validated capability layer

Decision. The core exposes a small, fixed action list. The core validates all input. The core returns structured errors. All logic lives in the external daemon.

Reason. The split gives three properties:

  • The daemon cannot run arbitrary code in the client.
  • A daemon bug cannot crash the client or send malformed data to Discord.
  • Core patches stay small, so an upstream rebase is easy.

Consequence. A new capability needs a small core change. The action list is the contract.

ADR-3: CDP is a fallback, not the primary transport

Decision. The project validated control over Chrome DevTools Protocol. The primary transport is the validated core bridge.

Reason. CDP needs no Vesktop patch. However, CDP evaluates raw code in the page. It bypasses the core-side validation and rate limits. The project keeps the probe as a development tool.

Status. The primary transport is the validated core bridge. The CDP client and the probe stay in poc/ as development tools.

ADR-4: No bespoke client

Decision. Do not write a new Electron shell for Discord.

Reason. Vesktop maintains the desktop integration for Vencord, for example settings, themes, quick CSS, and the native plugin bridge. A bespoke shell must reimplement that surface. Vesktop already does this work.

Consequence. Keep the patch small and isolated. This approach is cheaper than a new shell.

ADR-5: Two pacing layers

Decision. The daemon paces all outbound sends with a human-emulation policy (ADR-12). The core adds a hard limit of 500 ms between sends and 30 sends per minute.

Reason. The daemon policy looks human. The core limit is a safety net. A daemon bug cannot cause a send flood.

ADR-6: Pin the profile directory

Decision. DATA_DIR is $VENCORD_USER_DATA_DIR, else $VESKSLOP_DATA_DIR, else <appData>/veskslop.

Reason. The Discord session lives in the Electron profile. An app rename or a version update must not move the profile. A stable path keeps the login.

ADR-7: Separate token file

Decision. The QR tool writes the account token to a file. The client seeds the token at startup when no session exists.

Reason. The token file survives a profile reset. It also survives a change in the Discord storage format. The recovery path is one QR scan.

ADR-8: Trigger log limits

Decision. Keep the last 100 triggers from trusted users and the last 30 triggers from untrusted users.

Reason. The limits keep memory small. The trusted limit is larger because those messages matter more.

ADR-9: One live test target

Decision. The only permitted live-send target is user zzib (177117296140091393).

Reason. Tests must not send messages to other users. DMs to zzib are safe and permitted.

ADR-10: Poll command

Decision. The poll command accepts | or // as separators, an optional quoted question, and many reactions per user. A bare command shows help.

Reason. The pipe is hard to type on some keyboards. // is safe in Discord markdown when it has whitespace on both sides, so URLs stay in one piece. Many selections match how people use polls in group chats. A bare command gives the user the syntax without a failed poll.

ADR-11: Persistent action log

Decision. Write triggers, continuations, commands, replies, and errors to a rotating JSONL file. Redact token-shaped strings before the write.

Reason. The in-memory trigger log resets at restart. The action log gives a durable audit trail without storing raw credentials.

ADR-12: Human send pacing

Decision. The first outbound send of a burst waits 30 to 200 ms. Later sends wait 500 to 2800 ms. A burst ends after a random 5 to 20 seconds of quiet. Reads take a small 100 to 300 ms gap and do not reset the burst.

Reason. A real client sends the first message quickly and then slows down. Reads are not visible in the same way and must not consume the send budget. The core hard limit stays as the safety net. See daemon.md.

ADR-13: Web UI on a separate listener

Decision. The daemon serves the web UI on its own listener (0.0.0.0:8789) with no authentication. The MCP server stays on localhost. The page is plain HTML, CSS, and JS behind a JSON API.

Reason. Remote control over LAN or Tailscale needs a reachable port. A separate listener keeps the MCP surface local. The JSON API is the stable boundary, so a future frontend replaces the page without a backend change. No auth fits a private network, but the port must never be exposed to the public internet.

ADR-14: Voice joins muted, presence first

Decision. voice.join joins muted. Audio playback is a later feature and unmutes only while playing. The bot leaves on its own after the owner or the stay target leaves, plus a delay.

Reason. A muted bot captures no audio and does not disturb the call. The lifecycle keeps the bot from lingering in empty calls. See voice.md.

ADR-15: Nocturne design and the debug shell

Decision. The web UI follows the Nocturne design system and the 7a debug shell from the design handoff (poc/WebUIMockup.zip). poc/public/nocturne.css carries the token set. The page stays plain HTML, CSS, and JavaScript with no build step. The JSON API stays the migration boundary.

Reason. The owner chose the design direction. Vanilla keeps the no-build constraint. Roadmap gaps (TTS, transcript, tool-call tracing, favourites, voice actions) show as marked placeholders so the shape of the finished UI is visible while the backend catches up.

ADR-16: Takeover is a read-only gate

Decision. The Takeover switch is client-side. Off, the composer is locked and the page cannot send. On, manual sends are allowed and each one is written to the action log as a manual entry.

Reason. The switch exists to stop accidental use of the bot account, not to model agent ownership. The API does not enforce it, so other clients can still send. See webui.md.