Skip to content

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.

What the GitHub App can access
AccessLevelUsed for
Pull requestsRead and writeReads titles, reviewers, threads, approvals and merge state. Writes only when a person asks a reviewer or nudges.
IssuesRead and writeAssignees, labels and comments on PRs. Written only when a person assigns, sends to test or nudges.
ActionsRead and writeReads CI runs and production deploys. Writes only to re-run failed jobs when a person clicks.
Checks and commit statusesReadWhether CI passed, failed or is running, on PRs and on main.
ContentsReadCommit history, to see what shipped since the last deploy, and .pipexp/stages.json if the repo has one. PipeXP never fetches your source files.
MetadataReadRepository 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.

Every field a machine can send
FieldWhat it isSent on
eventIdA random id for this event, so a resend is stored once.textEvery run event, machine check
runIdA random id for the session or ship run.textEvery run event, question
occurredAtWhen it happened on the machine.textEvery run event, machine check
ticketThe tracker issue id, such as ABC-12. Never the issue's text.text, optionalEvery run event, question
skillWhich lane: ship, agent, or a skill of your own.textEvery run event
runtimeWhich coding agent, such as codex or cursor.textEvery run event
attemptIdA random id per claim of a run, so only the latest attempt moves the card.text, optionalEvery run event
repoThe GitHub repo, owner/name, so the event lands in its project.text, optionalEvery run event, question
originWhether 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, optionalEvery run event, question
sessionIdThe harness session id, linking its workflows into one agent row.text, optionalEvery run event
activityAgent activity, its observation time and source, and an optional scrubbed reason. The reason is dropped on minimal.state, observedAt, source, note, optionalEvery run event
typeWhat kind of event this is.fixedEvery run event, machine check
titleScrubbedDropped on minimalThe session or run title, such as repo and branch. Scrubbed. Dropped on minimal.textrun.started
ownerWho the run is for, as the skill names them.textrun.started
profileThe run's speed profile.one of standard, fast, xfastrun.started
branchDropped on minimalThe git branch name. Dropped whole if it looks like a secret, and on minimal.textrun.started
skillVersionThe skill's version number.text, optionalrun.started
skillTreeA hash of the skill's files, to tell versions apart.text, optionalrun.started
machineIdA random id for the machine, made by the plugin. Not the hostname.text, optionalrun.started, machine check, connect
runtimeVersionThe agent's version, such as codex 0.155.1.text, optionalrun.started
pluginVersionThe pipexp plugin's version.text, optionalrun.started
tokensReportedFalse when the agent gives no token counts.yes or no, optionalrun.started
tierThe run's cost tier.one of light, standard, value, optionalrun.started
claimWhether the run is new, resumed or taken over.one of new, resume, takeover, optionalrun.started
parentRunIdThe run that started this one.text, optionalrun.started
prNumberThe pull request number. The plugin learns it for a session from gh, with that machine's own GitHub login.number, optionalrun.started, step.entered, pr.status, run.finished
stepThe step number reached.number, optionalstep.entered, snag.reported
stageThe stage id reached, such as agent:S2.text, optionalstep.entered, snag.reported, usage.reported
countersLoop counts: review rounds, cycles, repairs, clean samples, and how many messages a person sent (never their words).reviewRound, cycle, repairs, cleanSamples, humanTurns, interrupts, optionalstep.entered
replayTrue when a resumed run catches up on steps already done.yes or no, optionalstep.entered
kindWhat sort of snag.one of snag, wrong-doc, missing-script, gate, evidence, workedsnag.reported
themeScrubbedA short snag topic, such as tests. Scrubbed.textsnag.reported
whatScrubbedOne line on what slowed the run down. Scrubbed.textsnag.reported
costMinMinutes the snag cost.numbersnag.reported
agentsPer agent: model, effort, token counts and times.list of agentId, parentAgentId, role, model, effort, effortSource, tokens, wallSeconds, startedAt, activeSeconds, toolWaitSeconds, compactions, runtimeVersionusage.reported
stateThe pull request's state.one of draft, open, merged, closedpr.status
checksHow many CI checks passed, failed or are pending.passed, failed, pendingpr.status
unresolvedThreadsHow many review threads are unresolved.numberpr.status
unansweredThreadsHow many review threads wait on the author.numberpr.status
reviewDecisionGitHub's review decision.one of approved, changes_requested, review_required, nonepr.status
approvalsHow many approvals.numberpr.status
mergeableWhether the pull request can merge.one of mergeable, conflicting, unknownpr.status
authorThe pull request author's GitHub login.text, optionalpr.status
assigneesAssignees' GitHub logins.list of texts, optionalpr.status
labelsThe pull request's labels.list of texts, optionalpr.status
outcomeHow the run ended.one of ready, merged, blocked, abandonedrun.finished
stopReasonWhy 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, optionalrun.finished
questionScrubbedWhat the run needs from a person. Scrubbed.text, optionalrun.finished, question
linkA link to a GitHub pull request or Linear issue.text, optionalrun.finished
postMergeWhether the after-merge check found new errors.one of clean, new-errors, not-checked, optionalrun.finished
followUpsTicket ids filed as follow-ups.list of texts, optionalrun.finished
ownerToldWhether the run's owner was told.yes or no, optionalrun.finished
gateWhich release gate was checked.one of gate-snapshot, ready-gategate.checked
headThe full commit id the gate or review looked at.textgate.checked, review.done
verdictThe gate's or reviewer's verdict.one of pass, fail, in_progress, technically_green, needs_decision, terminalgate.checked, review.done
partsThe gate's parts and their results.testProof, abuseReview, stackSize, reviewSettled, afterProof, findings, optionalgate.checked
reasonsScrubbedShort reasons for the gate verdict. Scrubbed.list of texts, optionalgate.checked
reviewerWhich reviewer ran.textreview.done
roundThe review round.numberreview.done
dispatchWhich try of the reviewer this was.numberreview.done
dispositionsHow many findings were fixed, already done, invalid, a preference or blocked.fixed, alreadyDone, invalid, preference, blocked, optionalreview.done
questionIdA random id for the question.textquestion
contextScrubbedMore about the question, for the person answering. Scrubbed.text, optionalquestion
optionsScrubbedThe answers to pick from. Scrubbed.list of texts, optionalquestion
timeoutMinHow long the question may wait, in minutes.number, optionalquestion
recipientWho the question waits on, when that is someone else: a GitHub login. Optional; not sent at the minimal content level.text, optionalquestion
nameThe machine's name: its computer name (and hostname when it differs) until its owner renames it.textmachine check, connect
osThe operating system and its version.textmachine check, connect
skillsSkills installed on the machine, and their versions.list of name, versionmachine check
rowsThe 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, fixmachine check
pluginThe 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, optionalmachine check
clientWhich agent is connecting, such as codex.textconnect
clientVersionThe plugin's version.text, optionalconnect
deviceCodeA random code a connecting machine sends with each check until a person approves it. It collects the key once.textconnect
An example: a session starts
{
  "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"
}
The scrubber's test cases
  • 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.

Data handling: PipeXP and five other tools
QuestionPipeXPGitHubCodeRabbitLinearSwarmiaAviator
Source code never storedYesReads 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 residencyYesEU by default: Vercel and Convex, Ireland.YesEnterprise Cloud with EU data residencySource Yes"EU SaaS deployment" on EnterpriseSource Not foundYesEU by default, Frankfurt; US on requestSource Not found
SAML single sign-onNoPlanned 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.dev

Status

The board, agent event ingest and GitHub sync, checked every minute.

See status

Sub-processors

The companies that handle personal data for PipeXP. We email customers who ask, 30 days before adding one.

Sub-processors
CompanyWhat forWhere
VercelHosts the website and appEU (Ireland)
ConvexDatabase and backendEU (Ireland)
GoogleSign in with Google, through WorkOS, if you choose itUS, under EU Standard Contractual Clauses
WorkOSRuns every sign-in: Google, GitHub, Microsoft, passkey or email code, and single sign-on on Business. SCIM directory sync on EnterpriseUS, under EU Standard Contractual Clauses
StripePayments and invoicesEU and US, under EU Standard Contractual Clauses
ResendInvites and notification emails (sign-in codes come from WorkOS)US, under EU Standard Contractual Clauses
GitHubSource of pull request data you connect, and sign in with GitHub, through WorkOS, if you choose itUS, under EU Standard Contractual Clauses
MicrosoftSign in with Microsoft, through WorkOS, if you choose it; Teams alerts, only if you connect TeamsSign-in: US, through WorkOS. Teams alerts: Microsoft's Bot Service, then your Microsoft 365 tenant. Both under EU Standard Contractual Clauses
PostHogProduct 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 contentEU (Frankfurt)
TypeSafeFiles human review comments by category (their text, PR title, file path and diff lines)US, under EU Standard Contractual Clauses
Get change notices by email