# Self-hosted data residency

Source: https://codeherder.com/docs/self-host-data-residency/

Where self-hosted data lives, every flow that leaves your AWS account, the vendor endpoints the install still calls, and the outbound channels.

This page says where self-host data lives, what leaves your deployment, and which channels carry it. On a self-host, you choose every receiver. This is the one residency statement. The full host table is on [Self-hosted infrastructure](https://codeherder.com/docs/self-host-infrastructure/#outbound-destinations).

Run the server, the database, the attachments bucket, the KMS key and the Cognito user pool in one AWS account. Then the list under “What leaves your deployment” is the full list of flows that leave that account.

## Where your data lives

Your data lives where you run the server, the database and the devices. You also run these stores:

- The PostgreSQL database and its backups.
- The attachments bucket.
- The KMS key for the secret store.
- The Cognito user pool, or your OIDC issuer.

You operate those stores. You choose the AWS region for each one. You choose the provider route for each agent. A device keeps its worktrees, its credential pool and its local store on the device.

Set `CH_AWS_REGION` once to place every AWS store in one region. A surface knob moves only its own store. See “Choose your AWS region” in [Self-hosted deployment](https://codeherder.com/docs/self-hosting/#choose-your-aws-region).

| Store | Data classes it holds | Where it lives | Region knob |
| --- | --- | --- | --- |
| PostgreSQL database | `public`, `internal`, `confidential`, `personal_data`, `customer_code`, `secret_material` | The region of your database host | None. CodeHerder does not place it. |
| Backups | The same six classes | Where you send them | None. You choose. |
| Attachments bucket | `public`, `confidential` | The bucket’s region | `CH_S3_REGION`, then `CH_AWS_REGION` |
| Secret-store KMS key | The key for `secret_material` | The key’s region | `CH_KMS_REGION`, then `CH_AWS_REGION` |
| Cognito user pool, or your OIDC issuer | `personal_data` | The pool’s region, or your issuer | `CH_COGNITO_REGION`, then `CH_AWS_REGION` |
| Server logs and journald | See [Self-hosted log retention and access](https://codeherder.com/docs/self-host-log-retention/) | The server host | None |
| Device stores: worktrees, transcripts, knowledge, credential pool | `customer_code`, `confidential` | Where the device runs | None |
| Audit receivers | The audit event envelope | Your receiver | None. See [Alarm on trace export failures](https://codeherder.com/docs/self-host-trace-export/). |

[Self-hosted retention and data subject requests](https://codeherder.com/docs/self-host-retention-and-dsar/) gives the retention of each store.

## Flows that leave your region

The [outbound destinations table](https://codeherder.com/docs/self-host-infrastructure/#outbound-destinations) lists every host the server and a device can call. These flows are not pinned to your AWS region:

- **AI providers.** A prompt goes to the provider’s region, not yours. Bedrock uses `CH_CLAUDE_AWS_REGION` on the device. See [Device AI credentials](https://codeherder.com/docs/device-ai-credentials/).
- **`models.dev`.** The server pulls a price catalogue from this global host. It sends no customer data. `CH_COST_PRICING_SYNC=0` turns it off.
- **The release host.** A device and the `ch` CLI fetch the manifest and binaries from a global vendor host. `CH_CLI_MANIFEST_URL` points the fetch at your mirror.
- **SES.** Mail leaves for SES in the SES region. It leaves your region only when `CH_SES_REGION` differs from `CH_AWS_REGION`.
- **OTLP and webhook receivers.** These are yours. Their region is your choice.

## What leaves your deployment

Each item below is a receiver you choose, unless the text says otherwise.

- **Devices.** A device outside the account receives task briefs, repository code, the session bearer and AI credentials over its tunnel. A laptop, or an EC2 instance in another account, is such a device. Its worktrees and transcripts stay on it. See [Self-hosted device isolation](https://codeherder.com/docs/self-host-device-isolation/).
- **AI providers.** Prompts, code context and tool output go to the route you select: the Anthropic direct API, Amazon Bedrock (`CH_CLAUDE_AWS_REGION`), or an LLM gateway that you run or buy. Bedrock in your own account stays in AWS. It leaves the region when the region differs from the server’s. See [Device AI credentials](https://codeherder.com/docs/device-ai-credentials/).
- **Git hosts.** Agents push branches and open merge requests on GitHub.com and GitLab.com (cloud) through gh or glab. The server reads merge state.
- **Mail.** The server sends mail through your SES identity.
- **Webhooks and OTLP.** The server sends to the receivers that you configure. See [Alarm on trace export failures](https://codeherder.com/docs/self-host-trace-export/).
- **Slack.** The server sends one OAuth code exchange to `slack.com`.

The install still calls these CodeHerder vendor endpoints:

| Endpoint | What it is | Knob that turns it off |
| --- | --- | --- |
| The release host. Official builds use the CodeHerder release host. | The manifest, its signature and the binaries. A device and the `ch` CLI fetch them. | `CH_AUTO_UPDATE=0` and `CH_DS_AUTO_UPDATE=0`. `CH_CLI_MANIFEST_URL` points the fetch at your mirror. |
| The installer script host | One-time install of `ch` | Install from your own mirror. |
| `models.dev` | Model price catalogue, pulled by the server. On by default. | `CH_COST_PRICING_SYNC=0` |

The server makes no licence check, sends no telemetry and runs no update check.

## Is CodeHerder a processor?

You operate the stores. You choose the provider routes. In normal operation, the install sends CodeHerder no customer data.

An update fetch shows the release host your IP address and user agent. It carries no other data.

A support transfer that you start needs its own scoped agreement. Logs or data that you send to CodeHerder fall under that agreement.

## Subprocessors

A self-host has no CodeHerder subprocessor list. Every receiver on this page is your choice. You hold the contract with each one.

## Outbound channels

| Channel | Data it carries | Owner | Knob |
| --- | --- | --- | --- |
| Email (SES) | Verification codes, invitation links, email-change mail and the recipient address | You. You own the SES identity. | `CH_MAILER`, `CH_MAILER_FROM`, `CH_SES_REGION` |
| Outbound webhooks | The event envelope, in full or as `metadata_only`, in the codeherder or OCSF format | The workspace admin who makes the subscription | A webhook subscription; `CH_WEBHOOK_SECRET_KEY` |
| Slack | The OAuth code exchange. The bot token is stored encrypted. | The workspace admin who connects it | `CH_SLACK_CLIENT_ID`, `CH_SLACK_CLIENT_SECRET` |
| Git host | Branches, commits, merge-request titles and descriptions, and merges. Agents send them through gh or glab. | Your git organisation and the token owner | The repositories a workspace binds |
| OTLP trace export | Identifiers only | You | `CH_OTEL_TRACES_ENDPOINT` |
| Survey `codeherder` channel | Nothing leaves the host. It is a read inside the same database. | You | It is off while `CH_CODEHERDER_WORKSPACE_ID` is unset. |

Set `CH_MAILER_FROM`, `CH_BASE_URL` and `CH_APP_BASE_URL` to your own domain. None of them has a default. The server refuses to start without them. Every link in mail uses them, so a wrong value sends your users to the wrong host.

Slack: CodeHerder requests the `chat:write` scope. No CodeHerder code posts to Slack today. Deleting the integration removes the stored token. It does not revoke the token at Slack. Revoke it in Slack.

CodeHerder code posts no merge-request comments. Agents on devices do the git-host work.
