Data & Security

Last updated 3 September 2026.

Natural-person operator: Léo Duquesnel. Security contact: leo@bravoure.co. Production access is founder-only. The GitHub App publisher is unverified. GitHub lists the App under the organization bravoure-inc. That is a GitHub account, not a company. An organization owner may have to approve the App.

No SOC 2, ISO 27001, or independent penetration test yet. Do not treat this page as one.

Permissions

Repository: Metadata read, Actions read, Contents write, Pull requests write, Workflows write. Account: Email addresses read.

GitHub’s install screen asks for Contents write (includes source read and push of any file in the selected repositories), Pull requests write, and Workflows write. Audit steps mint a read token: Contents, Actions, Metadata. A write token (Contents, Pull requests, Workflows) exists only after you click: we create a branch and the Bravoure GitHub App bot opens a draft pull request. If GitHub refuses drafts, we open a ready-for-review pull request. We do not merge, force-push, or update the default branch. We only change files under .github/workflows. Draft and ready-for-review pull requests can still run pull_request workflows. Workflow files can run with secrets after merge. Installation tokens are never stored.

GitHub grants Actions: read (run and job metadata; logs and artifacts are also reachable under that permission class). Enforced behavior: path and endpoint allowlist; transient parse of default-branch workflow files under .github/workflows/, plus CODEOWNERS, .github/dependabot.yml, and renovate.json when those files exist; read job metadata from the Actions jobs endpoint; never call log or artifact download endpoints; persist derived data only. Necessity: without the workflow file you cannot tell a scheduled job from a manual one. GitHub will not let an App change workflow files without Workflows write. That permission exists because workflow files can run with secrets.

Email addresses: read is the GitHub App account permission used for the completion email. It is not an OAuth user:email scope. User-token scope is empty. A marked copy of that email also goes to leo@bravoure.co.

What is stored

  • GitHub identity: numeric user id, display name, avatar URL, primary verified email.
  • Sessions and Auth.js account rows, with token columns left null.
  • Encrypted GitHub user authorization ciphertext.
  • Installation binding and account login.
  • Installation permission snapshot (names such as Contents write, not tokens).
  • Selected repository membership.
  • Audit runs.
  • Public-repo runs: GitHub owner, repo name, and repo id, plus the same derived observations as an install audit. There is no completion email on that path.
  • Derived workflow observations: path, commit SHA, trigger class, run counts and conclusions, whether a pull_request trigger is declared and which types if any, and booleans for whether a workflow references secrets or declares OIDC.
  • Per-job declared facts reduced to scalars: job id, display name, runner label or the fact that it is an expression, matrix cell count, declared timeout minutes if any, and whether a concurrency group or path filter is declared.
  • Job name, runner label, durations, derived minute and dollar estimates, and sampled GitHub run ids.
  • Per-sample job cells: label, seconds, cents, runner label.
  • Recorded decisions: fix, retire, keep, or wrong.
  • Run-to-run comparison rows: workflow path, transition class, resolved flag, removal reason.
  • Coverage gaps.
  • Findings with repo, path, and pinned GitHub source URL parameters.
  • Pull request records: repo, path, branch, sha, pull request URL, outcome.
  • Email recipient address and delivery state.
  • Webhook delivery ids, event, and action.
  • Watch opt-in time.
  • Workload rows for the life of the org: source, kind, path or name, container, owner kind, owning team slug when CODEOWNERS names a team, first and last seen, and declared facts (triggers, runner labels, permission names, whether secrets or OIDC are declared, unpinned actions, whether a known AI provider key name is present). CODEOWNERS user names are not stored.
  • Per-repo last processed time and head SHA.
  • Watch alert rows: repo, direction, projected cents.
  • Lifecycle rows: state, since, cause, and decision id, for the life of the org.
  • A weekly lifecycle digest when at least one workload changed state that week. Resend keeps that mail about 30 days.

Account

An org is the tenant. I store the org name, member user ids and roles, invited emails until the invite expires or is accepted, API token hashes and prefixes (the raw token is shown once), and webhook endpoint URLs when you add them.

Sources

GitHub is connected through the GitHub App install, not a pasted credential. Vendor credentials for other sources are encrypted at rest with the same key as GitHub tokens, used read-only, and deleted when you disconnect. Workloads, cost lines, lifecycle states, artifacts, ledger events, and customer rates are kept for the life of the org. Disconnecting a source wipes that source’s rows. Uninstalling GitHub wipes GitHub source rows the same way.

Never stored in the application database: YAML bodies, CODEOWNERS user names, logs, artifacts, raw API bodies, secret values, secret names, installation tokens, OAuth codes, GitHub Cookie headers, or authorization headers.

Watch mail may attach a .patch of workflow YAML, which then sits in Resend about 30 days. A completed public-repo report, its status JSON, and downloadable .patch files are readable at those URLs without signing in. Those patches are built from the public workflow file on GitHub at request time. They are not stored in the application database. The app sets a first-party CSRF cookie on every page, a session cookie when you are signed in, and short-lived cookies for the GitHub sign-in hop. Vercel may keep request logs.

Subprocessors

Application database and email storage are in the United States.

  • Vercel: application host and request logs, United States, production origin https://app.bravoure.co
  • Neon: Postgres, aws-us-east-1. Point-in-time restore window is 6 hours.
  • Resend: email, us-east-1, from send.bravoure.co. Open and click tracking off.
  • GitHub: API access for the selected installation, and for a public-repo run through the signed-in user’s App authorization. Repositories stay on GitHub.
  • Vercel AI Gateway: inference, United States. Used only when you ask for a model-written patch. Default provider: OpenAI. Measured numbers for the selected items go to that provider: repo id, workflow path, job id and display name, runner label or that it is an expression, runner class, share of CI minutes, estimate basis, durations including median and p90, unpriced and attempt seconds, derived minute and dollar estimates, sample sizes, matrix cell count and keys, match basis, declared pull_request types, declared timeout minutes if any, and whether a concurrency group, path filter, or pull_request trigger is declared. Workflow files are not sent. Those requests set zero data retention, disallow prompt training, and pin inference to the United States.

Retention

For a GitHub App install, audit runs and rows that cascade from them (including pull request records and watch alerts) are deleted within 90 days after that installation’s last run. A public-repo run has no installation. Those rows stay until you delete them. They are not in the 90-day install cascade. Watch repo cursors (repo id and last head SHA) stay until you uninstall or delete. Uninstall or delete clears live audit rows, apply rows, watch alerts, and watch cursors within 24 hours; the installation row is kept as a tombstone (installation id, account login, timestamps) so replayed webhooks can be rejected. Webhook delivery ids age out at 90 days. Your user record and encrypted GitHub authorization stay until you delete them on the install page or email leo@bravoure.co. Revoking the GitHub authorization ends access and deletes sessions but does not by itself delete those rows. Neon PITR ages out on the 6-hour window. An hourly Vercel Cron job applies those deletion windows in production; calls without the cron secret are rejected. Vercel Workflows retains receipts (ids, counts, gap codes, rate facts), not observations, for 7 days after completion on Pro; uninstall cannot wipe that vendor log inside 24 hours. Resend keeps email content and metadata about 30 days on current plans; uninstall cannot wipe Resend inside 24 hours. A successful apply turns watch on. You can stop it. Watch mail may name jobs and paths and, when we cannot open a pull request, attach a .patch of workflow YAML. That file then sits in Resend for that window. A marked copy of the completion email, not watch mail, also goes to leo@bravoure.co and then sits in that mailbox; uninstall cannot wipe that inbox.

No sale of customer data. No use of customer data for model training. No model in the audit path. If you ask for a written patch, the measured numbers for the items you selected are sent to the provider named above. Your workflow files are not.

Installs are selected repositories, at most 50. Access is rechecked against GitHub when you finish setup, start a run, open a report, and click an action. The in-progress status poll does not call GitHub.