Security
PipeXP sees how your code moves, not the code itself. Here is exactly what it reads, stores and does.
How PipeXP handles your data
Reads how code moves, not your files
PipeXP does not fetch or store your source files. It reads pull requests, reviews, checks and commit history, plus one settings file, .pipexp/stages.json, if you add it.
Writes only when a person clicks
It acts on GitHub only when someone clicks: assign a reviewer, send to test, re-run CI or nudge. Nothing runs on a timer.
Stored in the EU
Your data and the backend that reads it live on Convex, in Ireland. Pages are served by Vercel from the edge nearest you.
Tokens encrypted
Third-party tokens are encrypted server-side with AES-GCM. They never reach the browser.
Reporting keys hashed
Each agent reporting key is stored as a SHA-256 hash. The key itself is shown once.
Access you control
Only people your team adds can sign in, and an audit log shows who changed what. SAML single sign-on comes with Business; on Enterprise the log also streams to your SIEM, signed.
GitHub App permissions
PipeXP connects through the PipeXP Board GitHub App. It uses short-lived installation tokens, and you choose which repos it sees.
| Access | Level | Used for |
|---|---|---|
| Pull requests | Read and write | Reads titles, reviewers, threads, approvals and merge state. Writes only when a person asks a reviewer or nudges. |
| Issues | Read and write | Assignees, labels and comments on PRs. Written only when a person assigns, sends to test or nudges. |
| Actions | Read and write | Reads CI runs and production deploys. Writes only to re-run failed jobs when a person clicks. |
| Checks and commit statuses | Read | Whether CI passed, failed or is running, on PRs and on main. |
| Contents | Read | Commit history, to see what shipped since the last deploy, and .pipexp/stages.json if the repo has one. PipeXP never fetches your source files. |
| Metadata | Read | Repository names. Required by GitHub for every app. |
Exactly what an agent sends
Agents send short events: which stage a run is at, what slowed it down, tokens and time, a question for a person, a daily check of the machine, and a request to connect one. Never code, prompts, diffs or file contents. This is every field the board accepts; anything else is refused.
Scrubbed on the machine, then again here
Free text (marked below) has keys, tokens, env values, emails, home folders, IP addresses and hostnames removed before it is sent, and again when it arrives.
Minimal, for the whole project
An owner can set the project to minimal in Settings. The board then keeps no session titles or branch names: it drops them as events arrive, whatever a machine sends (title and branch become “Agent session” and empty).
Checked by anyone
The plugin is open to read, and the scrubber cases below are the board's own tests, run on every change.
| Field | What it is | Sent on |
|---|---|---|
eventId | A random id for this event, so a resend is stored once.text | Every run event, machine check |
runId | A random id for the session or ship run.text | Every run event, question |
occurredAt | When it happened on the machine.text | Every run event, machine check |
ticket | The tracker issue id, such as ABC-12. Never the issue's text.text, optional | Every run event, question |
skill | Which lane: ship, agent, or a skill of your own.text | Every run event |
runtime | Which coding agent, such as codex or cursor.text | Every run event |
attemptId | A random id per claim of a run, so only the latest attempt moves the card.text, optional | Every run event |
repo | The GitHub repo, owner/name, so the event lands in its project.text, optional | Every run event, question |
origin | Whether the session runs in a folder with a GitHub remote (repo) or none, so it is never filed on the wrong project.one of repo, none, optional | Every run event, question |
sessionId | The harness session id, linking its workflows into one agent row.text, optional | Every run event |
activity | Agent activity, its observation time and source, and an optional scrubbed reason. The reason is dropped on minimal.state, observedAt, source, note, optional | Every run event |
type | What kind of event this is.fixed | Every run event, machine check |
titleScrubbedDropped on minimal | The session or run title, such as repo and branch. Scrubbed. Dropped on minimal.text | run.started |
owner | Who the run is for, as the skill names them.text | run.started |
profile | The run's speed profile.one of standard, fast, xfast | run.started |
branchDropped on minimal | The git branch name. Dropped whole if it looks like a secret, and on minimal.text | run.started |
skillVersion | The skill's version number.text, optional | run.started |
skillTree | A hash of the skill's files, to tell versions apart.text, optional | run.started |
machineId | A random id for the machine, made by the plugin. Not the hostname.text, optional | run.started, machine check, connect |
runtimeVersion | The agent's version, such as codex 0.155.1.text, optional | run.started |
pluginVersion | The pipexp plugin's version.text, optional | run.started |
tokensReported | False when the agent gives no token counts.yes or no, optional | run.started |
tier | The run's cost tier.one of light, standard, value, optional | run.started |
claim | Whether the run is new, resumed or taken over.one of new, resume, takeover, optional | run.started |
parentRunId | The run that started this one.text, optional | run.started |
prNumber | The pull request number. The plugin learns it for a session from gh, with that machine's own GitHub login.number, optional | run.started, step.entered, pr.status, run.finished |
step | The step number reached.number, optional | step.entered, snag.reported |
stage | The stage id reached, such as agent:S2.text, optional | step.entered, snag.reported, usage.reported |
counters | Loop counts: review rounds, cycles, repairs, clean samples, and how many messages a person sent (never their words).reviewRound, cycle, repairs, cleanSamples, humanTurns, interrupts, optional | step.entered |
replay | True when a resumed run catches up on steps already done.yes or no, optional | step.entered |
kind | What sort of snag.one of snag, wrong-doc, missing-script, gate, evidence, worked | snag.reported |
themeScrubbed | A short snag topic, such as tests. Scrubbed.text | snag.reported |
whatScrubbed | One line on what slowed the run down. Scrubbed.text | snag.reported |
costMin | Minutes the snag cost.number | snag.reported |
agents | Per agent: model, effort, token counts and times.list of agentId, parentAgentId, role, model, effort, effortSource, tokens, wallSeconds, startedAt, activeSeconds, toolWaitSeconds, compactions, runtimeVersion | usage.reported |
state | The pull request's state.one of draft, open, merged, closed | pr.status |
checks | How many CI checks passed, failed or are pending.passed, failed, pending | pr.status |
unresolvedThreads | How many review threads are unresolved.number | pr.status |
unansweredThreads | How many review threads wait on the author.number | pr.status |
reviewDecision | GitHub's review decision.one of approved, changes_requested, review_required, none | pr.status |
approvals | How many approvals.number | pr.status |
mergeable | Whether the pull request can merge.one of mergeable, conflicting, unknown | pr.status |
author | The pull request author's GitHub login.text, optional | pr.status |
assignees | Assignees' GitHub logins.list of texts, optional | pr.status |
labels | The pull request's labels.list of texts, optional | pr.status |
outcome | How the run ended.one of ready, merged, blocked, abandoned | run.finished |
stopReason | Why the run stopped, from a fixed list.one of green, draft, merged, closed, decision, credentials, active-owner, review-cap, repair-cap, attempt-cap, cycle-cap, external, preview-failed, head-moved, timeout, optional | run.finished |
questionScrubbed | What the run needs from a person. Scrubbed.text, optional | run.finished, question |
link | A link to a GitHub pull request or Linear issue.text, optional | run.finished |
postMerge | Whether the after-merge check found new errors.one of clean, new-errors, not-checked, optional | run.finished |
followUps | Ticket ids filed as follow-ups.list of texts, optional | run.finished |
ownerTold | Whether the run's owner was told.yes or no, optional | run.finished |
gate | Which release gate was checked.one of gate-snapshot, ready-gate | gate.checked |
head | The full commit id the gate or review looked at.text | gate.checked, review.done |
verdict | The gate's or reviewer's verdict.one of pass, fail, in_progress, technically_green, needs_decision, terminal | gate.checked, review.done |
parts | The gate's parts and their results.testProof, abuseReview, stackSize, reviewSettled, afterProof, findings, optional | gate.checked |
reasonsScrubbed | Short reasons for the gate verdict. Scrubbed.list of texts, optional | gate.checked |
reviewer | Which reviewer ran.text | review.done |
round | The review round.number | review.done |
dispatch | Which try of the reviewer this was.number | review.done |
dispositions | How many findings were fixed, already done, invalid, a preference or blocked.fixed, alreadyDone, invalid, preference, blocked, optional | review.done |
questionId | A random id for the question.text | question |
contextScrubbed | More about the question, for the person answering. Scrubbed.text, optional | question |
optionsScrubbed | The answers to pick from. Scrubbed.list of texts, optional | question |
timeoutMin | How long the question may wait, in minutes.number, optional | question |
recipient | Who the question waits on, when that is someone else: a GitHub login. Optional; not sent at the minimal content level.text, optional | question |
name | The machine's name: its computer name (and hostname when it differs) until its owner renames it.text | machine check, connect |
os | The operating system and its version.text | machine check, connect |
skills | Skills installed on the machine, and their versions.list of name, version | machine check |
rows | The setup check: each requirement's name, status and fix. A check whose text looks like a secret is refused whole, not redacted.list of name, status, fix | machine check |
plugin | The plugin's own health: its version, agents and their versions, whether hooks are trusted, how many events wait or were lost before sending, and the last error as a code.version, harnesses, hooksTrusted, queued, lastError, lastEventAt, canRestart, dropped, optional | machine check |
client | Which agent is connecting, such as codex.text | connect |
clientVersion | The plugin's version.text, optional | connect |
deviceCode | A random code a connecting machine sends with each check until a person approves it. It collects the key once.text | connect |
{
"type": "run.started",
"eventId": "11111111-1111-4111-8111-111111111111",
"runId": "0b5c7c1e-4a8e-4d3a-9c55-2f1e6a7b8c90",
"occurredAt": "2026-09-25T10:00:00.000Z",
"skill": "agent",
"runtime": "codex",
"title": "orbit-app · codex/checkout-retry",
"owner": null,
"profile": null,
"branch": "codex/checkout-retry",
"runtimeVersion": "codex 0.155.1",
"pluginVersion": "pipexp 0.1.0",
"machineId": "7d2f0a9b-1c3e-4f5a-8b6d-9e0f1a2b3c4d"
}An env value
Deploy failed: GITHUB_TOKEN=ghp_abcDEF123
Deploy failed: GITHUB_TOKEN=[REDACTED]
A GitHub token on its own
used ghp_abcdefghijklmnop to push
used [REDACTED] to push
A Stripe secret key
key sk_live_4eC39HqLyjWDarjtT1zdp7dc leaked
key [REDACTED] leaked
A PipeXP reporting key
PIPEXP_KEY is pipexp_rk_Qx9Lm2
PIPEXP_KEY is [REDACTED]
An AWS access key
AKIAIOSFODNN7EXAMPLE in logs
[REDACTED] in logs
A JWT
cookie eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.sig
cookie [REDACTED]
A bearer token
Authorization: Bearer abc.def.ghi
Authorization: Bearer [REDACTED]
A password in a URL
postgres://app:hunter2@db.internal/app
postgres://[REDACTED]@db.internal/app
A private key
-----BEGIN RSA PRIVATE KEY----- …
[REDACTED]
A home folder
Failed in /Users/ana/Github/x
Failed in ~/Github/x
A Linux home folder
cd /home/ana/src/app
cd ~/src/app
An email address
ask ann@shop.com
ask [REDACTED]
An IPv4 address
server at 10.0.0.4 down
server at [REDACTED] down
A .local machine name
built on build-mac.local
built on [REDACTED]
A Shopify store domain
on snug.myshopify.com
on [REDACTED]
A database id
user 507f1f77bcf86cd799439011 missing
user [REDACTED] missing
Kept: a file name
see .env.local
see .env.local
Kept: already redacted
TOKEN=[REDACTED]
TOKEN=[REDACTED]
Kept: a plain sentence
Flaky test in checkout
Flaky test in checkout
What we store
Agent run events (step, snag, tokens, cost, time), PR metadata, and the names and emails of people on your team. Nothing from your repos' files. How far back we keep it depends on your plan.
Report a problem
Found a security issue? Email security@pipexp.dev. We reply within two working days and credit you when it is fixed, if you want.
Get help
Questions about PipeXP, its GitHub App or its Teams app? Email hello@pipexp.dev. We reply within two working days.
Where your data lives
PipeXP against the five tools buyers compare us with. Their facts as of 25 Sep 2026, each linked to its source.
| Question | PipeXP | GitHub | CodeRabbit | Linear | Swarmia | Aviator |
|---|---|---|---|---|---|---|
| Source code never stored | YesReads PR metadata, reviews and checks. Never fetches your source files; the one file it reads is .pipexp/stages.json, if you add one. | NoGitHub hosts the codeSource | NoCaches encrypted code and stores code vectors; learnings kept unless opted outSource | NoDiffs needs a GitHub org owner to grant code accessSource | Yes"We request permission to access source code but never store it"Source | Not found |
| EU data residency | YesEU by default: Vercel and Convex, Ireland. | YesEnterprise Cloud with EU data residencySource | Yes"EU SaaS deployment" on EnterpriseSource | Not found | YesEU by default, Frankfurt; US on requestSource | Not found |
| SAML single sign-on | NoPlanned on Business with WorkOS, not only Enterprise. | YesEnterprise CloudSource | YesEnterpriseSource Source | YesEnterpriseSource | YesOkta, Microsoft Entra ID, GoogleSource | YesEnterpriseSource |
Full comparisons: GitHub, CodeRabbit, Linear, Swarmia, Aviator.
SOC 2 Type II
In preparation. We will publish the auditor, the compliance tool and the target date here as soon as they are signed. Until then we answer security questionnaires directly.
DPA and questionnaires
Most questionnaires are answered by our security pack: architecture, data flow, encryption, access control, incident response, backups, and pre-filled CAIQ Lite answers. Our Data Processing Agreement follows the EU Standard Contractual Clauses; ask for it, or send your own questionnaire, and we reply within two working days.
Read the security packprivacy@pipexp.devSub-processors
The companies that handle personal data for PipeXP. We email customers who ask, 30 days before adding one.
| Company | What for | Where |
|---|---|---|
| Vercel | Hosts the website and app | EU (Ireland) |
| Convex | Database and backend | EU (Ireland) |
| Sign in with Google, through WorkOS, if you choose it | US, under EU Standard Contractual Clauses | |
| WorkOS | Runs every sign-in: Google, GitHub, Microsoft, passkey or email code, and single sign-on on Business. SCIM directory sync on Enterprise | US, under EU Standard Contractual Clauses |
| Stripe | Payments and invoices | EU and US, under EU Standard Contractual Clauses |
| Resend | Invites and notification emails (sign-in codes come from WorkOS) | US, under EU Standard Contractual Clauses |
| GitHub | Source of pull request data you connect, and sign in with GitHub, through WorkOS, if you choose it | US, under EU Standard Contractual Clauses |
| Microsoft | Sign in with Microsoft, through WorkOS, if you choose it; Teams alerts, only if you connect Teams | Sign-in: US, through WorkOS. Teams alerts: Microsoft's Bot Service, then your Microsoft 365 tenant. Both under EU Standard Contractual Clauses |
| PostHog | Product analytics: per project, when it is created, a service or machine is connected and an agent run starts (no person or content). Pages and feature use only after you press Allow, with a user id, never names, emails or PR content | EU (Frankfurt) |
| TypeSafe | Files human review comments by category (their text, PR title, file path and diff lines) | US, under EU Standard Contractual Clauses |