// Security

Trust and your data

Short answers about your code and data. The details are in the security model.

What we see and store

Upseam downloads your repository's archive to find affected code. The archive exists only in a temporary directory during a step.

Stored Not stored
Installation, repositories, settings Your code, code lines, diffs, pull request and issue bodies
Surfaces: provider, SDK, versions, pins as file and line Repository archives, after the step
Matches: path, line, symbol The model's answer, after the step
Change events from your internal contracts: changed operations, types and fields
Proposals: branch, commit, pull request number, CI status GitHub installation tokens: in memory only, for up to an hour
Approvals: channel, decision, who decided Your model key, when you generate in your GitHub Actions
Encrypted: Slack bot token, model key (when stored)
Product events: ids, flags and fixed values, for 13 months

Rows that belong to an installation are scoped to it by the database itself, with row-level security. Upseam's internal tables, such as the work queue and the analytics export state, are limited by database role grants instead.

Where keys live

  • A model key stored with Upseam is encrypted with AES-256-GCM under a data key of its own, and the data key is wrapped with a master key. The installation id is bound into both layers, so the ciphertext cannot be moved to another installation.
  • The master key is a secret of the hosting platform, not a value in the database. Anyone who holds both the master key and a copy of the database can decrypt the stored key.
  • The stored model key is decrypted only for Test and inside the patch step, and is never logged. Disconnect model deletes it.
  • When you generate in your GitHub Actions, the key stays in your GitHub secrets and Upseam never sees it. See Generate in your GitHub Actions.
  • When your own coding agent writes the fixes, its credential is an Actions secret in your repository. The task Upseam sends has no key and no token. See Bring your own agent.
  • The Slack bot token is encrypted the same way as the model key. It is decrypted each time Upseam sends a Slack message.

Who else receives your data

  • GitHub, where your code already lives.
  • Your Slack workspace, if connected: file paths and line numbers, no code.
  • The model vendor you choose: the change data and the files with matches, sent with your key.
  • PostHog: pseudonymous product events with ids, flags and fixed values, no code, repository names or user logins.
  • TypeSafe labels vendor changes with Jev when Upseam ingests them. The App never sends your code to TypeSafe. The CLI's agent commands send TypeSafe what you pass them; see The CLI.

How long data stays

  • Product events are deleted after 13 months.
  • Records of finished or abandoned work are deleted after 30 days.
  • Records of received webhook deliveries are deleted after 14 days.
  • The rest of an installation's data stays while the App is installed.
  • When GitHub reports the uninstall, Upseam deletes the installation with its repositories, surfaces, findings, settings, stored model key and Slack link. Its product events are deleted and Upseam asks PostHog to delete the pseudonymous person.
  • Two markers with the installation id stay: a removal marker with the time of removal, so that late GitHub notices do not bring the installation back, and an analytics marker, deleted after 13 months, so that late product events are not exported.
  • Records of queued work and received webhook deliveries are not part of the uninstall; they follow the periods above.
  • Uninstalling does not revoke the Slack bot token with Slack. Disconnect Slack before uninstalling, or remove the Upseam app from your Slack workspace. See Uninstall.
  • Repository archives exist only in a temporary directory during a step.

What your CI runs

Upseam never runs your code or the model's answer. Your CI does.

  • The model's answer becomes a commit on an upseam/* branch of your repository. Your push and pull_request workflows run on it with your repository's secrets before anyone reviews the pull request.
  • Deterministic patch gates decide what may be pushed. A patch that fails any of them is not pushed, and the finding goes to Needs you.
  • ask is the default mode: nothing is pushed before a person approves the finding. See Why ask is the default.
  • Dependabot and Renovate bumps always go through ask.
  • On your side, keep secrets away from jobs that run on upseam/* before review, and protect the default branch.

What the App may do

Permission Access
Metadata read
Contents read & write
Pull requests read & write
Issues read & write
Checks read
Commit statuses read

Contents write is used for commits to upseam/* branches only and for repository_dispatch when fixes are generated in your Actions. Upseam does not ask for Workflows, Actions, Administration, Secrets or organization permissions. Upseam never pushes to your default branch. Why each permission is needed: Permissions.

The CLI

The upseam CLI runs locally or in your CI, with no Upseam account and no Upseam server; the agent commands need a TypeSafe API key. These commands are local only: init, inspect <provider>, inspect internal without --consumer-repo and --github, and report --dry-run. These use the network:

  • inspect internal --consumer-repo downloads each consumer repository from GitHub with your token.
  • --github on inspect internal and report sends the rendered comment or issue body to the GitHub API.
  • fix sends the change and the matched files to your model vendor with your key, except with --pulls, --publish, or --group with --edits. fix --github then pushes one branch to your origin and opens a pull request. With --stage <dir> it only writes the patch to that directory and pushes nothing; the separate --publish step pushes and opens the pull request. With --group it only pushes the branch, and the App opens the pull request.
  • fix --pulls looks up Upseam's pull requests through the GitHub API.
  • agent route and agent prune send the task and the fragments you pass to TypeSafe with your TYPESAFE_API_KEY.

See Data and privacy.