From 82216b536dbf70e18e3af8e0a9370610a71e1189 Mon Sep 17 00:00:00 2001 From: gavrielc Date: Sat, 2 May 2026 21:19:23 +0300 Subject: [PATCH] Add /add-deltachat skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skill files only — copied from PR #2192 (channels branch). Source adapter (src/channels/deltachat.ts) lives on the channels branch and is installed by the skill. Co-Authored-By: Axel McLaren Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/add-deltachat/REMOVE.md | 62 ++++++ .claude/skills/add-deltachat/SKILL.md | 254 +++++++++++++++++++++++++ .claude/skills/add-deltachat/VERIFY.md | 54 ++++++ 3 files changed, 370 insertions(+) create mode 100644 .claude/skills/add-deltachat/REMOVE.md create mode 100644 .claude/skills/add-deltachat/SKILL.md create mode 100644 .claude/skills/add-deltachat/VERIFY.md diff --git a/.claude/skills/add-deltachat/REMOVE.md b/.claude/skills/add-deltachat/REMOVE.md new file mode 100644 index 0000000..7cb2d31 --- /dev/null +++ b/.claude/skills/add-deltachat/REMOVE.md @@ -0,0 +1,62 @@ +# Remove DeltaChat + +## 1. Disable the adapter + +Comment out the import in `src/channels/index.ts`: + +```typescript +// import './deltachat.js'; +``` + +## 2. Remove credentials + +Remove the `DC_*` lines from `.env`: + +```bash +DC_EMAIL +DC_PASSWORD +DC_IMAP_HOST +DC_IMAP_PORT +DC_SMTP_HOST +DC_SMTP_PORT +``` + +## 3. Rebuild and restart + +```bash +pnpm run build + +# Linux +systemctl --user restart nanoclaw + +# macOS +launchctl kickstart -k gui/$(id -u)/com.nanoclaw +``` + +## 4. Remove account data (optional) + +To fully remove all account data including DeltaChat encryption keys: + +```bash +rm -rf dc-account/ +``` + +> **Warning:** This deletes the Autocrypt keys. Contacts who have verified your bot's key will need to re-verify if the same email address is re-used with a new account. + +To keep the account for later reinstall, leave `dc-account/` intact. + +## 5. Remove the package (optional) + +```bash +pnpm remove @deltachat/stdio-rpc-server +``` + +## Verification + +After removal, confirm the adapter is no longer starting: + +```bash +grep "deltachat" logs/nanoclaw.log | tail -5 +``` + +Expected: no `Channel adapter started` entry after the last restart. diff --git a/.claude/skills/add-deltachat/SKILL.md b/.claude/skills/add-deltachat/SKILL.md new file mode 100644 index 0000000..45aa416 --- /dev/null +++ b/.claude/skills/add-deltachat/SKILL.md @@ -0,0 +1,254 @@ +--- +name: add-deltachat +description: Add DeltaChat channel integration via @deltachat/stdio-rpc-server. Native adapter — no Chat SDK bridge. Email-based messaging with end-to-end encryption. +--- + +# Add DeltaChat Channel + +The adapter drives the `@deltachat/stdio-rpc-server` JSON-RPC subprocess directly — pure Node.js against the DeltaChat core library. Messages are delivered over email with Autocrypt/OpenPGP encryption. + +## Install + +### Pre-flight (idempotent) + +Skip to **Credentials** if all of these are already in place: + +- `src/channels/deltachat.ts` exists +- `src/channels/index.ts` contains `import './deltachat.js';` +- `@deltachat/stdio-rpc-server` is listed in `package.json` dependencies + +Otherwise continue. Every step below is safe to re-run. + +### 1. Fetch the channels branch + +```bash +git fetch origin channels +``` + +### 2. Copy the adapter + +```bash +git show origin/channels:src/channels/deltachat.ts > src/channels/deltachat.ts +``` + +### 3. Append the self-registration import + +Append to `src/channels/index.ts` (skip if already present): + +```typescript +import './deltachat.js'; +``` + +### 4. Install the adapter package (pinned) + +```bash +pnpm install @deltachat/stdio-rpc-server@2.49.0 +``` + +### 5. Build + +```bash +pnpm run build +``` + +## Account Setup + +A dedicated email account is strongly recommended — it will accumulate DeltaChat-formatted messages and store encryption keys. Not all providers work well with DeltaChat; check https://providers.delta.chat/ before picking one. + +**Default security modes:** IMAP uses SSL/TLS (port 993), SMTP uses STARTTLS (port 587). Both are configurable via `.env` — see Credentials below. + +To find the correct hostnames for a domain: + +```bash +node -e "require('dns').resolveMx('example.com', (e,r) => console.log(r))" +``` + +Most providers publish their IMAP/SMTP hostnames in their help docs under "manual setup" or "IMAP access." + +## Credentials + +Add to `.env`: + +```bash +DC_EMAIL=bot@example.com +DC_PASSWORD=your-app-password +DC_IMAP_HOST=imap.example.com +DC_IMAP_PORT=993 +DC_IMAP_SECURITY=1 # 1=SSL/TLS (default), 2=STARTTLS, 3=plain +DC_SMTP_HOST=smtp.example.com +DC_SMTP_PORT=587 +DC_SMTP_SECURITY=2 # 2=STARTTLS (default), 1=SSL/TLS, 3=plain +``` + +Security settings are applied on every startup, so changing them in `.env` and restarting takes effect without wiping the account. + +Sync to container: `mkdir -p data/env && cp .env data/env/env` + +### Optional settings + +The following are read from the process environment (not `.env`). To override them, add `Environment=` lines to the systemd service unit or your launchd plist: + +| Variable | Default | Description | +|----------|---------|-------------| +| `DC_ACCOUNT_DIR` | `dc-account` | Directory for DeltaChat account data (IMAP state, keys, blobs) | +| `DC_DISPLAY_NAME` | `NanoClaw` | Bot display name shown in DeltaChat | +| `DC_AVATAR_PATH` | _(none)_ | Absolute path to avatar image; set at startup only | + +The `/set-avatar` command (send an image with that caption) is the easiest way to set the avatar at runtime without modifying the service file. Only users with `owner` or global `admin` role can use it. + +### Restart + +```bash +# Linux +systemctl --user restart nanoclaw + +# macOS +launchctl kickstart -k gui/$(id -u)/com.nanoclaw +``` + +On first start the adapter configures the email account (IMAP/SMTP credentials, calls `configure()`). Subsequent starts skip straight to `startIo()`. Account data is stored in `dc-account/` in the project root (or your `DC_ACCOUNT_DIR`). + +## Wiring + +### DMs + +**DeltaChat contacts cannot be added by email alone** — to start a chat, the user must open the bot's invite link in their DeltaChat app or scan its QR code. This triggers the SecureJoin handshake. + +#### Step 1 — Get the invite link + +After the service starts, the adapter logs the invite URL and writes a QR SVG: + +```bash +grep "invite link" logs/nanoclaw.log | tail -1 +# url field contains the https://i.delta.chat/... invite link +# also written to dc-account/invite-qr.svg (or $DC_ACCOUNT_DIR/invite-qr.svg) +``` + +The invite URL is stable (tied to the bot's email and encryption keys) so it stays valid across restarts. + +#### Step 2 — Add the bot in DeltaChat + +Two options for the user to connect: + +- **Link**: Copy the `https://i.delta.chat/...` URL and open it on the device running DeltaChat. The app recognises it and shows a "Start chat" prompt. +- **QR code**: Open `dc-account/invite-qr.svg` in a browser or image viewer, display it on screen, and scan it from the DeltaChat app using the QR-scan button on the new-chat screen. + +After accepting, DeltaChat exchanges keys and creates the chat automatically. + +#### Step 3 — Wire the chat to an agent + +Once the first message arrives the router auto-creates a `messaging_groups` row. Look up the chat ID: + +```bash +sqlite3 data/v2.db \ + "SELECT platform_id, name FROM messaging_groups WHERE channel_type='deltachat' AND is_group=0 ORDER BY created_at DESC LIMIT 5" +``` + +Then run `/init-first-agent` — it creates the agent group, grants the user owner access, and wires the messaging group in one step: + +```bash +pnpm exec tsx scripts/init-first-agent.ts \ + --channel deltachat \ + --user-id deltachat:user@example.com \ + --platform-id \ + --display-name "Your Name" +``` + +### Groups + +Add the bot email to a DeltaChat group. When any member sends a message, the router creates a `messaging_groups` row with `is_group = 1`. Run `/manage-channels` to wire it to an agent group. + +## Next Steps + +If you're in the middle of `/setup`, return to the setup flow now. + +Otherwise, run `/init-first-agent` to create an agent and wire it to your DeltaChat DM (see Wiring above), or `/manage-channels` to wire this channel to an existing agent group. + +## Channel Info + +- **type**: `deltachat` +- **terminology**: DeltaChat calls them "chats" (1:1 DMs) and "groups" +- **supports-threads**: no — DeltaChat has no thread model +- **platform-id-format**: numeric chat ID as a string (e.g. `"12"`) — the DeltaChat core's internal chat identifier +- **user-id-format**: `deltachat:{email}` — the contact's email address +- **how-to-find-id**: Send a message from DeltaChat to the bot email, then query `messaging_groups` as shown above +- **typical-use**: Personal assistant over DeltaChat DMs; small groups where participants use DeltaChat +- **default-isolation**: One agent per bot identity. Multiple chats with the same operator can share an agent group; groups with other people should typically use `isolated` session mode + +### Features + +- File attachments — inbound and outbound; inbound waits up to 30 seconds for large-message download to complete +- Invite link logged on every startup — URL + QR SVG written to `dc-account/invite-qr.svg`; see Wiring for the bootstrap flow +- `/set-avatar` — send an image with this caption to change the bot's DeltaChat avatar (admin/owner only) +- Connectivity watchdog — restarts IO if IMAP goes quiet for 20 minutes or connectivity drops below threshold for two consecutive 5-minute checks +- Network nudge — `maybeNetwork()` called every 10 minutes to recover from prolonged idle + +Not supported: DeltaChat reactions, message editing/deletion, read receipts. + +### Connectivity model + +`isConnected()` returns `true` when the internal connectivity value is ≥ 3000: + +| Range | Meaning | +|-------|---------| +| 1000–1999 | Not connected | +| 2000–2999 | Connecting | +| 3000–3999 | Working (IMAP fetching) | +| ≥ 4000 | Fully connected (IMAP IDLE) | + +## Troubleshooting + +### Adapter not starting — credentials missing + +```bash +grep "Channel credentials missing" logs/nanoclaw.log | grep deltachat +``` + +All six required vars (`DC_EMAIL`, `DC_PASSWORD`, `DC_IMAP_HOST`, `DC_IMAP_PORT`, `DC_SMTP_HOST`, `DC_SMTP_PORT`) must be present in `.env`. + +### Account configure fails + +```bash +grep "DeltaChat" logs/nanoclaw.log | tail -20 +``` + +Common causes: +- Wrong IMAP/SMTP hostnames — double-check provider docs +- App password not generated — Gmail and some others require this when 2FA is enabled +- Port/security mismatch — defaults are port 993 + SSL/TLS for IMAP and port 587 + STARTTLS for SMTP; override with `DC_IMAP_PORT`/`DC_IMAP_SECURITY` or `DC_SMTP_PORT`/`DC_SMTP_SECURITY` in `.env` + +### Provider uses SMTP port 465 (SSL/TLS) instead of 587 + +Set `DC_SMTP_SECURITY=1` and `DC_SMTP_PORT=465` in `.env`, then restart. + +### Messages not arriving + +1. Check the service is running and the adapter started: `grep "Channel adapter started.*deltachat" logs/nanoclaw.log` +2. Check connectivity: `grep "DeltaChat: IO started" logs/nanoclaw.log` +3. Check the sender has been granted access — run `/init-first-agent` to create their user record and wire the chat +4. Verify the messaging group is wired: `sqlite3 data/v2.db "SELECT mg.platform_id, mga.agent_group_id FROM messaging_groups mg JOIN messaging_group_agents mga ON mg.id = mga.messaging_group_id WHERE mg.channel_type='deltachat'"` + +### Stale lock file after crash + +```bash +rm -f dc-account/accounts.lock +systemctl --user restart nanoclaw +``` + +### Bot not responding after restart + +The account is already configured — IO restarts automatically on service start. If the RPC subprocess is stuck, restart the service. Check for errors: + +```bash +grep "DeltaChat" logs/nanoclaw.error.log | tail -20 +``` + +### Messages received but agent not responding + +The messaging group exists but may not be wired to an agent group. Run: + +```bash +sqlite3 data/v2.db "SELECT id, platform_id, name FROM messaging_groups WHERE channel_type='deltachat'" +``` + +If the group has no entry in `messaging_group_agents`, wire it with `/manage-channels`. diff --git a/.claude/skills/add-deltachat/VERIFY.md b/.claude/skills/add-deltachat/VERIFY.md new file mode 100644 index 0000000..839fa85 --- /dev/null +++ b/.claude/skills/add-deltachat/VERIFY.md @@ -0,0 +1,54 @@ +# Verify DeltaChat + +## 1. Check the adapter started + +```bash +grep "Channel adapter started.*deltachat" logs/nanoclaw.log | tail -1 +``` + +Expected: `Channel adapter started { channel: 'deltachat', type: 'deltachat' }` + +## 2. Check IMAP/SMTP connectivity + +Replace with your provider's hostnames from `.env`: + +```bash +DC_IMAP=$(grep '^DC_IMAP_HOST=' .env | cut -d= -f2) +DC_SMTP=$(grep '^DC_SMTP_HOST=' .env | cut -d= -f2) + +bash -c "echo >/dev/tcp/$DC_IMAP/993" && echo "IMAP open" || echo "IMAP blocked" +bash -c "echo >/dev/tcp/$DC_SMTP/587" && echo "SMTP open" || echo "SMTP blocked" +``` + +## 3. End-to-end message test + +1. Open DeltaChat on your device +2. Add the bot email address as a contact +3. Send a message +4. The bot should respond within a few seconds + +If nothing arrives, check: + +```bash +grep "DeltaChat" logs/nanoclaw.log | tail -20 +grep "DeltaChat" logs/nanoclaw.error.log | tail -10 +``` + +## 4. Check messaging group was created + +```bash +sqlite3 data/v2.db \ + "SELECT id, platform_id, name FROM messaging_groups WHERE channel_type='deltachat' ORDER BY created_at DESC LIMIT 5" +``` + +If a row appears, the inbound routing is working. If not, the adapter isn't receiving the message — check logs for `DeltaChat: error handling incoming message`. + +## 5. Verify user access + +If the message arrived but the agent didn't respond, the sender may not have access: + +```bash +sqlite3 data/v2.db "SELECT id, display_name FROM users WHERE id LIKE 'deltachat:%'" +``` + +Grant access as shown in the SKILL.md "Grant user access" section.