Security model

ccr-cloud runs an AI agent on your server with real credentials. This page says what it protects, what it doesn't, and how to deploy it so the difference doesn't matter.

In host mode, a task can do anything your server user can, including reading ccr-cloud's own owner token. Approvals let you review what Claude does; they are not a security boundary against an agent that is compromised, for example by instructions hidden in a repository or web page. Run ccr-cloud as a dedicated account that holds only the access you're willing to give the agent.

  1. A dedicated Unix account for ccr-cloud and its tasks, separate from your personal account and with no sudo. Give it only the Git credentials and cloud permissions the agent should have.
  2. Keep the API on loopback (the default) and reach it through an SSH tunnel, Tailscale, or Cloudflare with an Access policy that allows only you. Never open ports 8787 or 8788 to the internet.
  3. Treat the owner token like a password for that account. Anyone with it can start tasks that run commands as the account.
  4. Use Docker mode when you need isolation between the agent and the host. See Docker and CCR sessions.
  5. Review commands before approving them, especially ones that touch credentials, the network or files outside the task.

#Who can do what

ActorCan
You, with the owner tokenEverything: start, control and delete tasks, answer approvals, read transcripts.
A paired deviceThe same as the owner token, until you revoke it. Revoking also closes anything it has open, such as a live task stream.
A host task (Claude and the commands it runs)Anything the server account can: its files and credentials, ccr-cloud's data directory and owner token, other tasks' workspaces, and the network.
A Docker taskIts own workspace, the network, the CCR client key and GH_TOKEN if set. It can't reach the host's files or credentials.
A task's runner tokenReport activity for that one task. It can't act for another task or as the owner.
Anyone else on the networkNothing, as long as the API stays on loopback or behind your access layer.

#What ccr-cloud enforces

  • Authentication: every API route except the health check, the app's static files and the pairing exchange requires the owner token, compared in constant time, or a paired device's token. Pairing links carry a single-use 128-bit code that expires after 10 minutes; device tokens are stored only as hashes and can be revoked one by one. There are no cookies, and no cross-origin access is allowed.
  • Browser hardening: the app is served with a strict Content Security Policy, can't be framed, and never inserts model or tool output as raw HTML: replies are rendered as safe Markdown and tool output as plain text.
  • Runner identity: each task has its own runner token, stored only as a hash. A runner can report only runner activity for its own task, in order; it can't record your messages or decisions.
  • Approvals: a decision applies only to the exact open request in that task, can be given once, and runs the tool with exactly the input you reviewed. Restarts and interrupts cancel open requests.
  • Notification buttons carry a token signed by the server that can make only its one decision on its one request, for 24 hours. Allow is offered and accepted only for read-only tools whose paths stay inside the task's workspace (symlinks resolved in host mode), checked again when the button is used. The token travels in the end-to-end encrypted push payload, never in a URL.
  • Secrets: task processes don't receive the owner token, Discord/WeChat/WhatsApp credentials or CCR client key from ccr-cloud's environment. A CCR-route task gets the client key it needs, and Docker mode passes secrets as environment variables, never on a command line. Repository check errors are redacted before logging.
  • Files: configuration, task records, event logs and task environments are readable only by the service account.
  • Input: repository URLs must be plain SSH or HTTPS URLs without credentials. Commands get your input as separate arguments, never inside a shell command string.
  • Docker tasks run as a non-root user with all capabilities dropped, no-new-privileges, CPU, memory and process limits, and no Docker socket.

#Known limitations

  • Host mode isn't isolation (see the warning above). A task can read server.env, other tasks' files and the runner's own token.
  • A Docker task's agent can read its runner token, because Claude Code and the runner share one user inside the container. With it, the agent could add misleading activity to its own task's log, but not act for you or other tasks.
  • Single owner. There are no user accounts or roles. Don't share a server's owner token.
  • The development client (npm run dev) passes the token in the event-stream URL. The production app doesn't.
  • Discord and optional WeChat/WhatsApp notifications send task content, such as tool input and replies, to Discord, Tencent or WhatsApp respectively. WeChat credentials and queued content remain in the private data directory. Web Push notifications are end-to-end encrypted.

#Report a vulnerability

Please report security issues privately to the maintainers rather than in a public issue, with steps to reproduce. Leave out real tokens and private logs.

#Next steps

Personal WhatsApp uses an unofficial Meta/WhatsApp Web client. In host mode any task can read DATA_DIR/whatsapp/auth, granting linked-device access to the account and messages. Use a spare number and a dedicated Unix user. The WhatsApp Terms of Service and account restrictions still apply.

Edit this page on GitHub ↗