Access & permissions

Nobody talks to your agent without your say-so.

The inbound gate

The gate decides on the sender, never the chat: a stranger in an allowed group is still dropped.

PolicyBehavior
pairing (default)Unknown DM sender gets a pairing code; you approve or deny from the terminal
allowlistOnly listed user IDs are delivered; everyone else is silent
disabledNo DMs at all

Pairing

The first message from an unknown sender returns a 6-hex pairing code. Codes expire after 24 hours. Approve or reject from your terminal:

hotline pair <code>
hotline deny <code>

Approval happens only from your terminal. Claude never approves a pairing or edits access because a chat message asked it to. That request is exactly what a prompt injection looks like.

Groups

Groups are opt-in per group ID, with optional requireMention (deliver only when the bot is mentioned, replied to, or a mentionPatterns regex matches) and an optional per-group sender allowlist.

access.json

Configuration lives in ~/.claude/channels/tele-go/access.json and is re-read on every inbound message, so edits take effect live:

{
  "dmPolicy": "pairing",          // pairing (default) | allowlist | disabled
  "allowFrom": ["412587349"],     // numeric user IDs
  "groups": {
    "-1001234567890": { "requireMention": true, "allowFrom": [] }
  },
  "mentionPatterns": ["^hey claude"],
  "ackReaction": "👀",            // emoji reaction on receipt ("" disables)
  "replyToMode": "first",         // first | all | off
  "textChunkLimit": 4096,
  "chunkMode": "newline",         // newline | length
  "bubbleMode": "paced"           // paced | instant
}

Each provider keeps its own access.json in its own state directory. On Signal the allowlist holds phone numbers (E.164) and group ids look like group:<id>; on Discord, guild channels gate as groups keyed by channel ID.

Outbound is gated too

Every tool call checks the target chat against the same rules before touching the provider API, so Claude can only message chats that could message it. The channel also refuses to attach its own state files, and sanitizes uploader-controlled filenames before they enter the message metadata. Button-tap authorization mirrors the inbound gate.

The permission relay

When a token is configured, hotline declares the claude/channel/permission capability. Claude Code's permission prompts are relayed to allowlisted DMs (never groups) with See more / Allow / Deny buttons. You can also answer by text with yes <code> or no <code>; those replies are intercepted and converted to a verdict instead of being relayed as chat.

This is the piece that makes leaving the terminal viable. The session doesn't stall on a permission prompt nobody is looking at; it asks your pocket. On transports without buttons (Signal), the relay is text-only.

The transcript

Every message in both directions is appended to <stateDir>/transcript.jsonl. Telegram has no history API and Claude Code sessions compact or restart, so the transcript is how the assistant recalls the thread across resets. It's written 0600 and currently unbounded.

next: Security →