Security at Balladeer
Balladeer stores recorded intent, agreed promises, code anchors, and bounded results from agentic checks and sealed CI verification. It never receives your code. This page says exactly what we hold, how it is protected, and what we don’t have yet.
Report a vulnerability
If you find a security problem in Balladeer, please tell us privately first:
security@balladeer.aiA person at Balladeer reads every report. Please include the affected behavior and steps to reproduce it.
What to include
- What you found and why it matters.
- Steps to reproduce it, and the page, command or component involved.
- How to reach you, and whether you’d like to be credited.
In scope
The Balladeer web app and hosted service, the Balladeer command-line tool, and the open-source CI runner, Balladeer-Labs/attestor.
Please
- Test only against a workspace and accounts that are yours.
- Don’t access, change or keep other people’s data. If you reach some by accident, stop and tell us.
- Don’t run denial-of-service tests, send spam, or try to trick our staff or customers.
- Give us a reasonable chance to fix the problem before you talk about it publicly.
If you act in good faith and follow these guidelines, we will not take legal action against you for your research. We don’t run a paid bug bounty.
What stays with you
- Code, tests, fixtures, logs and test output
- Balladeer never receives them. The service rejects any CI field outside a fixed list.
- Repository access
- Balladeer holds no GitHub App, repository token or deploy key.
- Prompts and transcripts
- The session hook sends no prompt content. Agent tools send only the short quotes and task descriptions described below.
What Balladeer receives
- Setup
- GitHub repository name, IDs and default branch; the person’s GitHub username; the computer’s short hostname; CLI version; a daily-salted hash of their IP address.
- Agent sessions
- Each session start and prompt in a connected repository sends one request with no content. We log member, repository and time, kept 30 days, plus monthly counts.
- Agent tools
- Promise text (title, outcome, conditions, pass and fail cases; each field capped at 8,000 characters), its owner, short word-for-word quotes of what the person said, and reasons for changes. Searches send a task description of up to 1,200 characters, kept only as a fingerprint for 30 days, and up to 20 file paths, kept only when one matches a promise’s scope. Free text is not scanned, so keep secrets and code out of it.
- CI
- Repository, branch, workflow, run and commit identifiers, and GitHub’s signed token claims, including who triggered the run. Per promise: outcome, reason and exit codes, duration, case counts, output fingerprints.
- Web sign-in
- Name, email, identity-provider user ID.
What runs after an agent pushes
The local check uses your existing coding-agent login and usage allowance. It reads and executes code in your environment through that agent’s permissions; model processing follows your provider’s terms. It needs no separate model API key.
Balladeer receives code anchors containing paths and symbol names, plus bounded verdict metadata and digests. Full behavior maps, reproduction files, raw outputs and the local judge log stay out of the hosted store. A push verdict never establishes sealed Protected status.
See checks on push and the full data boundary.
What runs in your CI
Your workflow calls the public runner, Balladeer-Labs/attestor, pinned to a full commit SHA, with only contents: read and id-token: write permissions. The runner is open source under Apache 2.0, as is the command-line tool.
- Jobs that check out your code cannot request an identity token.
- Jobs that report to Balladeer never check out your code. They use short-lived GitHub OIDC tokens with a destination and audience hard-coded in the workflow, so a caller cannot redirect them.
- The runner uses only Node.js built-ins and makes no network calls of its own.
- Jobs run on GitHub-hosted runners. Your own tests are not sandboxed.
- To connect CI, an administrator’s CLI uses their own local
ghandgitlogin to set two Actions variables and open a pull request that adds the workflow.
Missing, stale, replayed or untrustworthy evidence is shown as Unknown, never as a pass.
Access and isolation
- Sign-in is through WorkOS AuthKit. Joining a workspace needs an administrator’s invitation.
- Workspace isolation. Every application table has PostgreSQL row-level security enabled and forced, keyed to the workspace, alongside checks in the application. The app uses a non-owner database role and refuses to start with one that could bypass those rules.
- Agent credentials are issued only after the person approves a code in their browser. Each is bound to one workspace and repository, stored on our side only as a SHA-256 hash, and kept on the laptop in
~/.config/balladeer/credentials.jsonwith owner-only file permissions. - Removing a person from a workspace revokes their agent credentials immediately, and our own CI checks that behavior continuously. Credentials do not otherwise expire; administrators can revoke them per connection, repository or workspace.
- Only a promise’s named owner can agree to or change what it means: in the web app with a one-time code or, to agree a proposal, in their own agent session, which records their words and the exact version they saw. Agents can propose, but never approve.
- Lifecycle history is append-only. A changed meaning needs fresh agreement and never inherits the old approval.
- Staff access. One designated Balladeer staff account can see workspace names, member names, email addresses and activity counts across workspaces, but no promise text. Operators with database access can read stored data.
Where it runs, and who else sees data
- Fly.io
- Hosts the service in Virginia, United States. HTTPS only.
- Supabase
- The database, managed PostgreSQL.
- WorkOS
- Sign-in and invitations: names, email addresses, workspace name.
- Voyage AI
- Task descriptions and promise text, other than cases, quotes and owners, for search ranking.
- Anthropic
- Only with an optional setting: bounded task description and selected promise meaning for advisory discovery or duplicate comparison. Separate discovery path fields stay on Balladeer’s server.
- Slack
- Only if you install it: member email and first name, promise title and outcome, repository, branch, short commit SHA.
There are no analytics, crash-reporting or email SDKs in the command-line tool or the service.
What we don’t have yet
We would rather you hear these from us:
- No SOC 2 report.
- No data deletion. Offboarding exports your data and revokes access; promises and history are kept. Most diagnostic records expire in 30 to 90 days.
- Limited application-level encryption. It covers only the Slack bot token. Encryption at rest otherwise depends on our hosting providers.
- No version pinning for the CLI. It updates itself from npm, with no setting to hold a version.
Questions
Ask us about whether the optional Anthropic setting is on for your workspace, database region and backups, data processing agreements, penetration testing, incident response and retention: support@balladeer.ai. For anything that could be a vulnerability, use security@balladeer.ai instead.