Skip to content

feat(service): Share one write token across repository tenants - #4

Merged
dcramer merged 1 commit into
mainfrom
feat/shared-write-token
Oct 10, 2026
Merged

dcramer merged 1 commit into
mainfrom
feat/shared-write-token

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

Adding a project to Roach used to mean adding it to tenants in Terraform, running apply, and copying a new token from the tenant_tokens output into the project's CI secrets. With this change, a project needs no service or Terraform change. Its CI job reads one shared organization secret, ROACH_TOKEN, and names its tenant as owner/repo, usually GITHUB_REPOSITORY.

What changed

  • RoachServiceConfig.tenants (one token hash per tenant) is now writeTokenHash, one SHA-256 for all tenants. The service has no list of tenants.
  • RunConfig.tenant must be owner/repo. Each part must start with a letter, a digit or _, and there must be exactly two parts. The tenant becomes the storage prefix, so this blocks .. and keeps one tenant's prefix from sitting inside another's (for example junior and junior/model).
  • The cap on open runs without a token is now one cap for the whole service (100), not 50 per tenant. Any tenant name is accepted now, so a per-tenant cap could be avoided by picking a new name.
  • Terraform makes one random_password.write_token and a sensitive write_token output. The tenants variable and the tenant_tokens output are removed.
  • The README explains the trust model and the setup: put the token in an org secret limited to selected repos, how to add a project, and how to change the token.

Trust tradeoff: the tenant name is a namespace label, not proof of identity. Any job that has ROACH_TOKEN can write the recordings of any tenant. Give the secret only to trusted repositories and workflows, never to a workflow that runs code from a fork. Reads stay public as before, and runs without a token still can't write or go live.

No compatibility path: a config with tenants fails at startup. Nothing is deployed yet, so this is a hard cutover.

Tests: tests/service.test.ts now covers these cases:

  • the shared token writes for an existing tenant and for a tenant it has never seen
  • a wrong token gets 401
  • junior, acme/.., ../acme, a/b/c and acme/ are refused
  • changing the tenant name doesn't get around the cap on runs without a token

With the old per-tenant cap, that last check fails. tests/deployed.test.ts runs the production path with getsentry/junior as the tenant. pnpm check passes with 16/16 tests, and terraform validate passes.

via David Cramer.

--

View Junior Session [Sentry]

Replace the per-tenant token map with one shared write token. A run names
its tenant as owner/repo, such as GITHUB_REPOSITORY, so a new project needs
no service or Terraform change. The tenant is a namespace label, not proof
of identity: a holder of the token can write any tenant's recordings.

Tenant names must be exactly two safe path parts, so no tenant's prefix
sits inside another's. The cap on runs without a token is now global,
because a per-tenant cap could be bypassed by picking a new name.

Terraform makes one write_token output instead of tenant_tokens, and the
tenants variable is gone.

Co-Authored-By: David Cramer <david@sentry.io>
@dcramer
dcramer merged commit 2de92ff into main Oct 10, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant