<!-- Source: https://ccr-cloud.uubright.com/docs/security/ -->
# 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.

> [!WARNING]
> **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.

## Recommended deployment

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](https://ccr-cloud.uubright.com/docs/docker/).
5. **Review commands before approving them**, especially ones that touch credentials,
   the network or files outside the task.

## Who can do what

| Actor | Can |
|---|---|
| You, with the owner token | Everything: start, control and delete tasks, answer approvals, read transcripts. |
| A paired device | The 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 task | Its 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 token | Report activity for that one task. It can't act for another task or as the owner. |
| Anyone else on the network | Nothing, 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

- [How it works](https://ccr-cloud.uubright.com/docs/how-it-works/)
- [Access from your phone](https://ccr-cloud.uubright.com/docs/remote-access/)
- [Failure and recovery](https://ccr-cloud.uubright.com/docs/reliability/)

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.
