docs(skills): merge add-signal-v2 lessons into add-signal, drop v2
Absorbs battle-tested knowledge from the v2 skill into the upstream
add-signal: registration paths (new number + linked device), CAPTCHA
flow, VoIP SMS-first timing, Java prereq, config-lock warning, wiring
SQL for groups, not_member silent-drop fix, GroupV2 groupId extraction
note, and UUID-based platform ID format.
Corrects a factual error in the upstream: DM platform IDs are
signal:{UUID} (ACI), not phone numbers.
Removes the now-redundant add-signal-v2 skill.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,311 +0,0 @@
|
||||
# Add Signal Channel (v2)
|
||||
|
||||
Adds Signal messaging support to NanoClaw v2 using `signal-sdk` (a TypeScript
|
||||
wrapper around `signal-cli`). Unlike Telegram/Discord, Signal has no bot API —
|
||||
NanoClaw registers as a full Signal account on a dedicated phone number.
|
||||
|
||||
**Two registration paths:**
|
||||
- **New number (recommended):** Register a dedicated SIM or VoIP number as a
|
||||
standalone Signal account. NanoClaw owns the number entirely.
|
||||
- **Linked device:** Join an existing Signal account as a secondary device via
|
||||
QR code. Simpler, but NanoClaw shares your personal number.
|
||||
|
||||
Both paths are documented below. The new-number path is battle-tested.
|
||||
|
||||
---
|
||||
|
||||
## Pre-flight
|
||||
|
||||
Check if `src/channels/signal.ts` exists and the import is uncommented in
|
||||
`src/channels/index.ts`. If both are in place, skip to Registration.
|
||||
|
||||
## Install
|
||||
|
||||
### 1. Check Java
|
||||
|
||||
Java 17+ is required. Check:
|
||||
|
||||
```bash
|
||||
java -version
|
||||
```
|
||||
|
||||
If missing:
|
||||
- **RHEL/CentOS/Fedora:** `sudo dnf install -y java-17-openjdk`
|
||||
- **Debian/Ubuntu:** `sudo apt-get install -y default-jre`
|
||||
- **macOS:** `brew install --cask temurin@17`
|
||||
|
||||
Java 17–25 all work. Java 25 (RHEL9 default) is confirmed working.
|
||||
|
||||
### 2. Install signal-cli
|
||||
|
||||
The `signal-sdk` npm package bundles signal-cli, but the bundled version is
|
||||
often outdated. Install the latest standalone binary:
|
||||
|
||||
```bash
|
||||
SIGNAL_CLI_VERSION=$(curl -fsSL https://api.github.com/repos/AsamK/signal-cli/releases/latest | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'][1:])")
|
||||
curl -fsSL "https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/signal-cli-${SIGNAL_CLI_VERSION}-Linux-native.tar.gz" \
|
||||
| tar -xz -C ~/.local
|
||||
ln -sf ~/.local/signal-cli ~/.local/bin/signal-cli
|
||||
signal-cli --version
|
||||
```
|
||||
|
||||
> **Note:** The Linux native tarball extracts a single binary directly to
|
||||
> `~/.local/signal-cli` (not into a subdirectory). The symlink above handles this.
|
||||
|
||||
### 3. Install signal-sdk
|
||||
|
||||
```bash
|
||||
npm install signal-sdk
|
||||
```
|
||||
|
||||
### 4. Enable the adapter
|
||||
|
||||
Uncomment the Signal import in `src/channels/index.ts`:
|
||||
|
||||
```typescript
|
||||
import './signal.js';
|
||||
```
|
||||
|
||||
### 5. Build
|
||||
|
||||
Always build with Node 22 (nvm):
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && nvm use 22 && npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Path A: Register a new Signal number
|
||||
|
||||
Use this if you have a dedicated SIM or VoIP number for NanoClaw.
|
||||
|
||||
> **VoIP numbers:** Signal requires SMS verification before voice. Some VoIP
|
||||
> providers are blocked even for voice calls. If registration fails with an auth
|
||||
> error, try a different provider or a physical SIM.
|
||||
|
||||
### Step 1: Request SMS verification
|
||||
|
||||
Signal requires a CAPTCHA on first registration:
|
||||
|
||||
1. Open `https://signalcaptchas.org/registration/generate.html` in a browser
|
||||
2. Solve the captcha
|
||||
3. Right-click the **"Open Signal"** button → **Copy Link**
|
||||
4. The link starts with `signalcaptcha://...`
|
||||
|
||||
```bash
|
||||
SIGNAL_CLI_CONFIG_PATH=/path/to/nanoclaw/data/signal \
|
||||
signal-cli -u +YOURNUMBER register \
|
||||
--captcha "PASTE_CAPTCHA_TOKEN_HERE"
|
||||
```
|
||||
|
||||
The captcha token is everything after `signalcaptcha://` in the copied link.
|
||||
|
||||
### Step 2: Voice call fallback (VoIP numbers without SMS)
|
||||
|
||||
If your number cannot receive SMS, wait ~60 seconds after the SMS request then
|
||||
request a voice call:
|
||||
|
||||
```bash
|
||||
SIGNAL_CLI_CONFIG_PATH=/path/to/nanoclaw/data/signal \
|
||||
signal-cli -u +YOURNUMBER register --voice \
|
||||
--captcha "SAME_CAPTCHA_TOKEN"
|
||||
```
|
||||
|
||||
Signal will call your number and read a 6-digit code.
|
||||
|
||||
> The captcha token from Step 1 is reusable for the voice retry — no need to
|
||||
> solve a new one.
|
||||
|
||||
### Step 3: Verify
|
||||
|
||||
```bash
|
||||
SIGNAL_CLI_CONFIG_PATH=/path/to/nanoclaw/data/signal \
|
||||
signal-cli -u +YOURNUMBER verify CODE
|
||||
```
|
||||
|
||||
No output = success.
|
||||
|
||||
### Step 4: Set profile name (optional)
|
||||
|
||||
> ⚠ signal-sdk holds an exclusive lock on `data/signal/` while nanoclaw is
|
||||
> running. Stop the service before running signal-cli commands, then restart.
|
||||
|
||||
```bash
|
||||
systemctl --user stop nanoclaw
|
||||
SIGNAL_CLI_CONFIG_PATH=/path/to/nanoclaw/data/signal \
|
||||
signal-cli -u +YOURNUMBER updateProfile --name "YourBotName"
|
||||
systemctl --user start nanoclaw
|
||||
```
|
||||
|
||||
To set an avatar too:
|
||||
```bash
|
||||
signal-cli -u +YOURNUMBER updateProfile --name "YourBotName" --avatar /path/to/avatar.jpg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Path B: Link as secondary device
|
||||
|
||||
Use this to join an existing Signal account as a secondary device.
|
||||
|
||||
```bash
|
||||
mkdir -p data/signal
|
||||
export SIGNAL_CLI_CONFIG_PATH=$(pwd)/data/signal
|
||||
npx signal-sdk link -n "NanoClaw" -a +YOURNUMBER
|
||||
```
|
||||
|
||||
This prints a QR code. On your phone: **Settings → Linked Devices → Link New Device**.
|
||||
Scan the code within ~30 seconds.
|
||||
|
||||
---
|
||||
|
||||
## Configure environment
|
||||
|
||||
Add to `.env`:
|
||||
|
||||
```bash
|
||||
SIGNAL_PHONE_NUMBER=+YOURNUMBER
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Wire to an agent
|
||||
|
||||
### DMs
|
||||
|
||||
After the service starts, **send any message** to the Signal number from your
|
||||
personal Signal app. The router auto-creates a `messaging_groups` row. Then:
|
||||
|
||||
```bash
|
||||
sqlite3 data/v2.db \
|
||||
"SELECT id, platform_id FROM messaging_groups WHERE channel_type='signal' ORDER BY created_at DESC LIMIT 5"
|
||||
```
|
||||
|
||||
Pass the `id` and `platform_id` to `/init-first-agent` or wire manually.
|
||||
|
||||
**Important:** DM `platform_id` is UUID-based, not phone-based:
|
||||
- DM: `signal:3de71d7f-ffa3-437e-b4db-097534bccd46` (UUID of sender)
|
||||
- Group: `signal:UwaIz6bc09Olg1pBr/XeBuQf6z3fyCZoNj/Y3Tpe3hI=` (base64 group ID)
|
||||
|
||||
### Groups
|
||||
|
||||
Add the Signal number to a group from your phone. Send any message — the router
|
||||
auto-creates the group's `messaging_groups` row. Wire it:
|
||||
|
||||
```bash
|
||||
sqlite3 data/v2.db \
|
||||
"SELECT id, platform_id FROM messaging_groups WHERE channel_type='signal' ORDER BY created_at DESC LIMIT 5"
|
||||
```
|
||||
|
||||
Then insert the wiring (replace IDs):
|
||||
|
||||
```bash
|
||||
NOW=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
||||
sqlite3 data/v2.db "
|
||||
INSERT OR IGNORE INTO messaging_group_agents
|
||||
(id, messaging_group_id, agent_group_id, session_mode, priority, created_at)
|
||||
VALUES
|
||||
('mga-'||hex(randomblob(8)), 'mg-GROUPID', 'ag-AGENTID', 'isolated', 0, '$NOW');
|
||||
"
|
||||
```
|
||||
|
||||
Use `session_mode='isolated'` for groups so each group has its own session.
|
||||
|
||||
### Grant user access
|
||||
|
||||
Users who message via Signal need to be granted membership. Without this,
|
||||
messages are silently dropped with `not_member`. After first contact:
|
||||
|
||||
```bash
|
||||
NOW=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
||||
sqlite3 data/v2.db "
|
||||
INSERT OR REPLACE INTO user_roles (user_id, role, agent_group_id, granted_by, granted_at)
|
||||
VALUES ('signal:UUID', 'owner', NULL, 'system', '$NOW');
|
||||
INSERT OR IGNORE INTO agent_group_members (user_id, agent_group_id, added_by, added_at)
|
||||
VALUES ('signal:UUID', 'ag-AGENTID', 'system', '$NOW');
|
||||
"
|
||||
```
|
||||
|
||||
Find the UUID from `messaging_groups.platform_id` or the `users` table.
|
||||
|
||||
---
|
||||
|
||||
## Voice Transcription (optional)
|
||||
|
||||
Inbound voice messages are automatically transcribed using a local whisper
|
||||
binary. The feature degrades gracefully — if whisper or ffmpeg is missing,
|
||||
voice messages are still delivered as attachments with no transcript.
|
||||
|
||||
See `/add-voice-transcription-free-whisper` for full setup instructions
|
||||
(ffmpeg, whisper backends, model download, environment variables, troubleshooting).
|
||||
|
||||
---
|
||||
|
||||
## Channel Info
|
||||
|
||||
- **type**: `signal`
|
||||
- **terminology**: "Chats" (1:1) and "groups" (multi-member). No threads.
|
||||
- **supports-threads**: no
|
||||
- **inbound**: text, reactions (forwarded to agent with emoji + targetTimestamp), images, files, voice (transcribed via whisper-cli if installed)
|
||||
- **outbound**: text, reactions (sendReaction), file attachments
|
||||
- **platform-id-format**:
|
||||
- DM: `signal:{UUID}` — sender's Signal UUID, **not** their phone number
|
||||
- Group: `signal:{base64GroupId}`
|
||||
- **how-to-find-id**: Send a message to the bot, then query `messaging_groups`
|
||||
as shown above.
|
||||
- **config-lock**: signal-sdk holds an exclusive lock on `data/signal/` while
|
||||
nanoclaw is running. Stop the service before running any `signal-cli` commands.
|
||||
- **attachment storage**: signal-sdk launches signal-cli **without** a `--config`
|
||||
flag, so signal-cli stores attachments at the XDG default
|
||||
(`~/.local/share/signal-cli/attachments/`), not under `data/signal/`. The
|
||||
adapter checks both locations. Verify with:
|
||||
`ps aux | grep signal-cli` — if there is no `-c` argument, XDG default is in use.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**`Config file is in use by another instance`** — nanoclaw is running and
|
||||
signal-sdk has the lock. Stop the service, run the command, restart.
|
||||
|
||||
**Messages dropped with `not_member`** — the Signal user hasn't been granted
|
||||
membership. See "Grant user access" above. This affects every new Signal user,
|
||||
including the owner's Signal identity (which is separate from their Telegram
|
||||
identity even if it's the same person).
|
||||
|
||||
**Captcha required** — Signal requires captcha for new registrations. Go to
|
||||
`https://signalcaptchas.org/registration/generate.html`, solve it, right-click
|
||||
"Open Signal", copy the link, extract the token after `signalcaptcha://`.
|
||||
|
||||
**`Invalid verification method: Before requesting voice verification…`** —
|
||||
You must request SMS first, wait ~60 seconds, then request voice. Both can use
|
||||
the same captcha token.
|
||||
|
||||
**`The provided model identifier is invalid` (Bedrock)** — unrelated to Signal;
|
||||
this is a LiteLLM model ID issue.
|
||||
|
||||
**Group replies going to DM instead of group** — modern Signal groups use
|
||||
GroupV2. The adapter must extract the group ID from
|
||||
`envelope?.dataMessage?.groupV2?.id` (not just `groupInfo?.groupId`, which is
|
||||
GroupV1/legacy). Check `src/channels/signal.ts` and confirm the groupId
|
||||
extraction falls through to `groupV2.id`.
|
||||
|
||||
**Voice messages / attachments silently skipped, no transcript** — signal-sdk
|
||||
launches signal-cli without a `--config` flag, so attachments land at the XDG
|
||||
default (`~/.local/share/signal-cli/attachments/`) rather than under
|
||||
`data/signal/`. Confirm with `ps aux | grep signal-cli` — if there is no `-c`
|
||||
argument in the process line, the XDG default is in use. The adapter falls back
|
||||
to that location automatically. If you still see no "Signal attachment saved"
|
||||
log lines, add a debug log around the `if (!storedPath) continue` guard in
|
||||
`src/channels/signal.ts` to inspect `att.storedFilename` and `att.id`.
|
||||
|
||||
**Java not found** — install Java 17+ (see Install step 1).
|
||||
|
||||
**QR code expired (Path B)** — QR codes expire in ~30 seconds. Re-run the
|
||||
link command to generate a new one.
|
||||
|
||||
**signal-cli binary location** — The native Linux tarball extracts directly to
|
||||
`~/.local/signal-cli` (a single file, not a directory). The system `aws` or
|
||||
other tools named `signal-cli` won't be in PATH by default; check
|
||||
`~/.local/bin/signal-cli`.
|
||||
@@ -5,20 +5,116 @@ description: Add Signal channel integration via signal-cli TCP daemon. Native ad
|
||||
|
||||
# Add Signal Channel
|
||||
|
||||
Adds Signal messaging support via a native adapter that speaks JSON-RPC to a [signal-cli](https://github.com/AsamK/signal-cli) TCP daemon. No Chat SDK bridge, no npm deps — only Node.js builtins.
|
||||
Adds Signal messaging support via a native adapter that speaks JSON-RPC to a [signal-cli](https://github.com/AsamK/signal-cli) TCP daemon. No Chat SDK bridge — only Node.js builtins (`node:net`, `node:child_process`, `node:fs`).
|
||||
|
||||
Unlike Telegram or Discord, Signal has no bot API. NanoClaw registers as a full Signal account on a dedicated phone number (recommended) or links as a secondary device on your existing number.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
`signal-cli` installed and a Signal account linked:
|
||||
### Java
|
||||
|
||||
- macOS: `brew install signal-cli`
|
||||
- Linux: download from [GitHub releases](https://github.com/AsamK/signal-cli/releases)
|
||||
- Link your account: `signal-cli -a +1YOURNUMBER link` (follow the QR instructions)
|
||||
signal-cli requires Java 17+:
|
||||
|
||||
```bash
|
||||
java -version
|
||||
```
|
||||
|
||||
If missing:
|
||||
- **macOS:** `brew install --cask temurin@17`
|
||||
- **Debian/Ubuntu:** `sudo apt-get install -y default-jre`
|
||||
- **RHEL/Fedora:** `sudo dnf install -y java-17-openjdk`
|
||||
|
||||
Java 17–25 all work.
|
||||
|
||||
### signal-cli
|
||||
|
||||
- **macOS:** `brew install signal-cli`
|
||||
- **Linux:** download the native binary from [GitHub releases](https://github.com/AsamK/signal-cli/releases):
|
||||
|
||||
```bash
|
||||
SIGNAL_CLI_VERSION=$(curl -fsSL https://api.github.com/repos/AsamK/signal-cli/releases/latest | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'][1:])")
|
||||
curl -fsSL "https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/signal-cli-${SIGNAL_CLI_VERSION}-Linux-native.tar.gz" \
|
||||
| tar -xz -C ~/.local
|
||||
ln -sf ~/.local/signal-cli ~/.local/bin/signal-cli
|
||||
signal-cli --version
|
||||
```
|
||||
|
||||
> The Linux native tarball extracts a single binary directly to `~/.local/signal-cli` (not into a subdirectory). The symlink above puts it on PATH.
|
||||
|
||||
## Registration
|
||||
|
||||
Two paths. The new-number path is recommended and battle-tested.
|
||||
|
||||
### Path A: Register a new number (recommended)
|
||||
|
||||
Use a dedicated SIM or VoIP number. NanoClaw owns it entirely.
|
||||
|
||||
> **VoIP numbers:** Signal requires SMS verification before voice. Some VoIP providers are blocked even for voice calls. If registration fails with an auth error, try a different provider or a physical SIM.
|
||||
|
||||
**Step 1: Solve the CAPTCHA**
|
||||
|
||||
Signal requires a CAPTCHA on first registration:
|
||||
|
||||
1. Open `https://signalcaptchas.org/registration/generate.html` in a browser
|
||||
2. Solve the captcha
|
||||
3. Right-click the **"Open Signal"** button → **Copy Link**
|
||||
4. The link starts with `signalcaptcha://` — the token is everything after that prefix
|
||||
|
||||
**Step 2: Request SMS verification**
|
||||
|
||||
```bash
|
||||
signal-cli -a +1YOURNUMBER register --captcha "PASTE_TOKEN_HERE"
|
||||
```
|
||||
|
||||
**Step 3: Voice call fallback (if your number can't receive SMS)**
|
||||
|
||||
Wait ~60 seconds after the SMS request, then:
|
||||
|
||||
```bash
|
||||
signal-cli -a +1YOURNUMBER register --voice --captcha "SAME_TOKEN"
|
||||
```
|
||||
|
||||
Signal calls your number and reads a 6-digit code. The same captcha token is reusable — no need to solve a new one.
|
||||
|
||||
> You must request SMS first. Requesting voice immediately fails with `Invalid verification method: Before requesting voice verification…`
|
||||
|
||||
**Step 4: Verify**
|
||||
|
||||
```bash
|
||||
signal-cli -a +1YOURNUMBER verify CODE
|
||||
```
|
||||
|
||||
No output = success.
|
||||
|
||||
**Step 5: Set profile name (optional)**
|
||||
|
||||
> ⚠ Stop NanoClaw before running signal-cli commands — the daemon holds an exclusive lock on its data directory while running.
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||
signal-cli -a +1YOURNUMBER updateProfile --name "YourBotName"
|
||||
# optionally: --avatar /path/to/avatar.jpg
|
||||
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||
|
||||
# Linux
|
||||
systemctl --user stop nanoclaw
|
||||
signal-cli -a +1YOURNUMBER updateProfile --name "YourBotName"
|
||||
systemctl --user start nanoclaw
|
||||
```
|
||||
|
||||
### Path B: Link as secondary device
|
||||
|
||||
Joins an existing Signal account as a secondary device. Simpler, but NanoClaw shares your personal number.
|
||||
|
||||
```bash
|
||||
signal-cli -a +1YOURNUMBER link --name "NanoClaw"
|
||||
```
|
||||
|
||||
This prints a `tsdevice:` URI. Scan it as a QR code on your phone: **Settings → Linked Devices → Link New Device**. QR codes expire in ~30 seconds — re-run if it expires.
|
||||
|
||||
## Install
|
||||
|
||||
NanoClaw doesn't ship channels in trunk. This skill copies the Signal adapter and its tests in from the `channels` branch.
|
||||
|
||||
### Pre-flight (idempotent)
|
||||
|
||||
Skip to **Credentials** if all of these are already in place:
|
||||
@@ -55,7 +151,7 @@ import './signal.js';
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
No npm packages to install — the adapter uses only Node.js builtins (`node:net`, `node:child_process`, `node:fs`).
|
||||
No npm packages to install — the adapter uses only Node.js builtins.
|
||||
|
||||
## Credentials
|
||||
|
||||
@@ -97,27 +193,73 @@ launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
||||
systemctl --user restart nanoclaw
|
||||
```
|
||||
|
||||
## Wiring
|
||||
|
||||
### DMs
|
||||
|
||||
After the service starts, send any message to the Signal number from your personal Signal app. The router auto-creates a `messaging_groups` row. Then:
|
||||
|
||||
```bash
|
||||
sqlite3 data/v2.db \
|
||||
"SELECT id, platform_id FROM messaging_groups WHERE channel_type='signal' ORDER BY created_at DESC LIMIT 5"
|
||||
```
|
||||
|
||||
Pass the `id` to `/init-first-agent` or `/manage-channels` to wire it to an agent group.
|
||||
|
||||
### Groups
|
||||
|
||||
Add the Signal number to a group from your phone, send any message, then wire the resulting row the same way. For isolated per-group sessions:
|
||||
|
||||
```bash
|
||||
NOW=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
||||
sqlite3 data/v2.db "
|
||||
INSERT OR IGNORE INTO messaging_group_agents
|
||||
(id, messaging_group_id, agent_group_id, session_mode, priority, created_at)
|
||||
VALUES
|
||||
('mga-'||hex(randomblob(8)), 'mg-GROUPID', 'ag-AGENTID', 'isolated', 0, '$NOW');
|
||||
"
|
||||
```
|
||||
|
||||
### Grant user access
|
||||
|
||||
New Signal users (including the owner's Signal identity) are silently dropped with `not_member` until granted access. After the user's first message appears in `messaging_groups`:
|
||||
|
||||
```bash
|
||||
NOW=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
||||
sqlite3 data/v2.db "
|
||||
INSERT OR REPLACE INTO user_roles (user_id, role, agent_group_id, granted_by, granted_at)
|
||||
VALUES ('signal:UUID', 'owner', NULL, 'system', '$NOW');
|
||||
INSERT OR IGNORE INTO agent_group_members (user_id, agent_group_id, added_by, added_at)
|
||||
VALUES ('signal:UUID', 'ag-AGENTID', 'system', '$NOW');
|
||||
"
|
||||
```
|
||||
|
||||
Find the UUID from `messaging_groups.platform_id` or the `users` table.
|
||||
|
||||
## 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 Signal DM, or `/manage-channels` to wire this channel to an existing agent group. Signal is direct-addressable — your phone number is the platform ID.
|
||||
Otherwise, run `/init-first-agent` to create an agent and wire it to your Signal DM, or `/manage-channels` to wire this channel to an existing agent group.
|
||||
|
||||
## Channel Info
|
||||
|
||||
- **type**: `signal`
|
||||
- **terminology**: Signal has "chats" (1:1 DMs) and "groups."
|
||||
- **how-to-find-id**: DMs use your phone number (e.g. `+15555550123`). Groups use `group:<groupId>` — find group IDs via `signal-cli -a +1YOURNUMBER listGroups`.
|
||||
- **terminology**: Signal has "chats" (1:1 DMs) and "groups"
|
||||
- **supports-threads**: no
|
||||
- **platform-id-format**:
|
||||
- DM: `signal:{UUID}` — sender's Signal UUID (ACI), **not** their phone number
|
||||
- Group: `signal:{base64GroupId}` — base64-encoded GroupV2 ID
|
||||
- **how-to-find-id**: Send a message to the bot, then query `messaging_groups` as shown above
|
||||
- **typical-use**: Personal assistant via Signal DMs or small group chats
|
||||
- **default-isolation**: One agent per Signal account. Multiple chats with the same operator can share an agent group; groups with other people should typically be separate.
|
||||
- **default-isolation**: One agent per Signal account. Multiple chats with the same operator can share an agent group; groups with other people should typically use `isolated` session mode
|
||||
|
||||
### Features
|
||||
|
||||
- Markdown formatting — `**bold**`, `*italic*` / `_italic_`, `` `code` ``, ` ```code fence``` `, `~~strike~~`, `||spoiler||` (converted to Signal's offset-based text styles)
|
||||
- Quoted replies — `replyTo*` fields populated from Signal quotes
|
||||
- Typing indicators — DMs only (Signal doesn't support group typing)
|
||||
- Echo suppression — outbound messages are matched on `(platformId, text)` within a 10 s TTL to avoid syncMessage loops
|
||||
- Echo suppression — outbound messages matched on `(platformId, text)` within a 10 s TTL to avoid syncMessage loops
|
||||
- Note to Self — messages you send to your own account from another device route to the agent as inbound with `isFromMe: true`
|
||||
- Voice attachments — detected but not transcribed by default; the agent receives `[Voice Message]` placeholder text. Run `/add-voice-transcription` for local transcription via parakeet-mlx
|
||||
|
||||
@@ -145,4 +287,32 @@ If you see `Signal daemon not reachable at 127.0.0.1:7583` and `SIGNAL_MANAGE_DA
|
||||
|
||||
### Lost connection mid-session
|
||||
|
||||
If you see `Signal channel lost TCP connection to signal-cli daemon` in the logs, the daemon dropped us. There's no auto-reconnect yet — restart the service to re-establish.
|
||||
If you see `Signal channel lost TCP connection to signal-cli daemon` in the logs, the daemon dropped the connection. Restart the service to re-establish.
|
||||
|
||||
### Messages dropped with `not_member`
|
||||
|
||||
The Signal user hasn't been granted membership. See "Grant user access" above. This affects every new Signal user, including the owner's Signal identity — which is a separate user record from their identity on other channels even if it's the same person.
|
||||
|
||||
### Captcha required
|
||||
|
||||
Signal requires a captcha for new registrations. Go to `https://signalcaptchas.org/registration/generate.html`, solve it, right-click "Open Signal", copy the link, extract the token after `signalcaptcha://`.
|
||||
|
||||
### `Invalid verification method: Before requesting voice verification…`
|
||||
|
||||
You must request SMS first, wait ~60 seconds, then request voice. Both steps can use the same captcha token.
|
||||
|
||||
### Config file in use / daemon lock
|
||||
|
||||
signal-cli holds an exclusive lock on its data directory while the daemon is running. Stop NanoClaw before running any `signal-cli` commands directly, then restart afterward.
|
||||
|
||||
### Group replies going to DM instead of group
|
||||
|
||||
Modern Signal groups use GroupV2. The adapter must extract the group ID from `envelope?.dataMessage?.groupV2?.id` — not `groupInfo?.groupId`, which is GroupV1/legacy. If group messages are routing as DMs, check `src/channels/signal.ts` and confirm the groupId extraction falls through to `groupV2.id`.
|
||||
|
||||
### Java not found
|
||||
|
||||
Install Java 17+ — see the Prerequisites section above.
|
||||
|
||||
### QR code expired (Path B)
|
||||
|
||||
QR codes expire in ~30 seconds. Re-run the link command to generate a new one.
|
||||
|
||||
Reference in New Issue
Block a user