Skip to content
carakaSECURITYReport ↗
THREAT MODEL

Read this before you run npx caraka init

Caraka connects untrusted input, the contents of a chat, to code execution on your machine. That combination deserves a page rather than a paragraph.

NO PORTNo channel listens: Telegram is polled, Discord is an outbound socket, and there is no webhook anywhere.
NO KEYSModel API keys are never requested, stored, or transmitted. Those belong to your agent.
NO TOOLSCaraka ships no execution tools. Claude owns the tool policy and sandbox.
NO MARKETPLACENo plugin registry, no dynamic loading, no third-party skill supply chain.
ON THIS PAGE6
01

The trust boundary

One rule holds the whole design together: nothing on the untrusted side can ever change a policy, approve an action, or raise a privilege.

UNTRUSTED
×Telegram messages, Discord events, and callback payloads×Text streamed back by the coding agent×Tool titles, targets, and raw input from ACP×Unknown fields in an update from either channel
TRUSTED
config.yaml on diskSigned callbacks from a button pressCommands typed in your local terminalThe chat and sender allowlists
02

Threats and controls

Thirteen threats, each with a primary control and a fallback. Nothing here relies on the agent behaving well.

T1A stranger sends commandsMandatory allowlist; the gateway refuses to start without one. The operator pairs from the terminal, and a group is confirmed by a signed button in the DM of that same operator.
T2Direct prompt injectionApproval arrives only as a signed callback with a nonce. Text cannot approve itself.
T3Indirect injection via README or issueClaude owns content handling and tool policy. Caraka never turns agent output or ordinary chat text into approval.
T4Secret exfiltration through a replyOutbound text and audit details are scrubbed, including the shape of a Discord bot token. No variable whose name starts with CARAKA_ reaches an agent subprocess.
T5Destructive actionCaraka relays every ACP permission request and /stop sends session/cancel. Claude decides which tools require permission.
T6Leaking into a groupA group message needs its chat and its sender on the allowlists, and pairing is confirmed in the operator DM. Caraka never asks for group admin, so a bot left in privacy mode reads only commands and replies to itself. Every member of a paired group sees the approval cards, paths, and command output.
T7Gateway exposed to the internetTelegram is polled, Discord and the Baileys provider hold outbound sockets, so those open nothing. Two listeners exist and both bind 127.0.0.1: caraka dashboard, GET only and read-only, and since v0.6 the Cloud API webhook, which verifies X-Hub-Signature-256 in constant time even on loopback. Binding either anywhere else needs an explicit flag, which prints a warning and writes an audit row first.
T8Plugin supply chainNo marketplace or dynamic loading. Four direct runtime dependencies, pinned in package-lock.json. Baileys is an exact-version optional peer, so an install that does not choose it never downloads it — and CI never installs it either, which means npm audit does not see it.
T9WhatsApp account banReal, unpredictable, and only partly ours to reduce. Four mitigations are code that can fail: allowFrom is mandatory, one outbound function caps sends at 12 per rolling minute, spaces them 1,200–3,500 ms apart, and refuses to write to any number that has not written first. Choosing the unofficial provider stops start until acknowledgeRisk: true is written, and the message links the risk page. The fifth signal, datacenter traffic, is not ours to answer at all. Cloud API is the way out and carries none of this.
T10Runaway costTasks run serially. Caraka has no model provider, reasoning loop, heartbeat, or background scheduler.
T11No way to auditSQLite records inbound, outbound, runs, approvals, and errors; triggers reject audit updates and deletes.
T12Memory poisoningRecalled text is labelled as data, its memory markers are stripped so it cannot pose as instruction, and the injected context is capped at 6 items in 800 tokens.
T13Forged approval callbackThe payload stays server-side and only an HMAC-signed id travels — 33 characters, inside both Telegram’s 64-byte callback_data and Discord’s custom_id.
03

Telegram routes

The chat allowlist and the sender allowlist decide whether input reaches Claude; Claude decides when a tool needs permission. Caraka has one policy mode of its own, the trust window, and it is opened for a stated duration rather than set as a default.

ROUTEGETPROMPTAPPROVEREPLY
allowed DMbutton
unlisted chat or user
allowed group or guild channelbutton
signed callback
WHEN CLAUDE REQUESTS PERMISSION
allow_once onlyreject_once10-minute TTLprincipal-boundsession-boundsingle-useHMAC-signed
04

What ships turned on

These controls are fixed in v0.2. There is no configuration switch that disables them.

The allowlist cannot be empty. The gateway stops with an error that explains how to fix it.
A message reaches Claude only when its chat is on the chat allowlist and its sender is on the sender allowlist.
Approval only through a single-use bearer secret with a TTL, bound to the principal, the session, and the request. Where a channel has buttons that is a signed callback; where it has none it is the code on the card, generated server-side and printed nowhere else. A plain word is never a decision on any channel.
Token and approval key files use mode 0600 inside a mode-0700 secrets directory.
The outbound scrubber runs before every message on every channel, and before every audit detail.
SQLite triggers keep audit rows append-only.
Only the Cloud API webhook listens, on loopback by default and only with an unforgeable signature; every other channel is outbound. Where threads are unavailable, sessions fall back to linear mode with a header.
A trust window is opened by a signed callback, never by chat text, and it must state a duration. It closes on /lock, at expiry, or when the gateway restarts. Caraka still receives every permission request inside it, keeps the button on high-risk actions, and audits each decision it makes for you.
05

What we do not claim

·Caraka cannot guarantee that Claude asks before every operation. The coding agent owns its tool policy and sandbox.
·A signed button proves who approved one request; it does not make the approved operation safe.
·We have not had a third-party security audit. This will be stated openly until it changes.
·The local dashboard has no authentication. While caraka dashboard runs, anyone on that machine who can reach 127.0.0.1 can read it, including a local user with no read permission on the database file. Loopback is not an authentication boundary; what the page does refuse is a browser arriving under someone else’s hostname.
·Nothing on the Discord path has ever run against a real Discord. The evidence is a mocked gateway socket and a mocked REST surface, so the real payload shapes, the real 429 behaviour, and the real permissions are unproven.
·We cannot stop WhatsApp from blocking your number if you pick the unofficial provider. That risk is documented and accepted, not treated as a product defect, and docs/whatsapp-risiko.md says what is known about how often it happens.
·No WhatsApp number has ever been linked here and no live Cloud API webhook has ever been received. Every WhatsApp check answers a fake transport or a listener on port 0, so the real pairing flow, the real payload shapes, and the real ban behaviour are unproven.
·The bypassPermissions mode belongs to Claude and is opened from the terminal alone. Inside that window Caraka is never asked for permission, so its audit records that the window was open and not what ran inside it.
·Caraka v0.6 is a closed beta. Treat it as software that has not yet been attacked in the wild.
·Vulnerabilities in your coding agent, in Telegram, or in Discord are theirs to fix. We will help route the report.
06

Reporting a vulnerability

Email security@caraka.dev, or use GitHub private vulnerability reporting. Never open a public issue for a security problem.

72hAcknowledgement of any report sent to security@caraka.dev
7dInitial assessment, with a decision on severity and scope
90dPublic advisory, or sooner once a fix is available
IN SCOPE
Bypassing the approval flow in any wayEscaping the allowlist or the pairing mechanismForging, replaying, or reusing an approval nonceA message from a chat or sender outside the allowlists reaching ClaudeSecret leakage through Caraka messages or audit entriesCaraka opening an undocumented network listener
OUT OF SCOPE
Anything the operator explicitly approvedVulnerabilities in the coding agent itselfVulnerabilities in Telegram or in DiscordTool access enabled directly in the coding agent configurationPolicy modes, which are specified and have no gate on the run path in any channel