A product that checks whether claims are true has to tell the truth about itself.
CommitOps is in early access, and most of the product is still being built. So this page does not say “we do” about anything that is not in the code. Each statement below carries its status and the file or document it rests on, and moves up a section when it ships.
“In the code” means the code is merged and tested. The service is not yet open to customers, so none of it has handled a customer’s data.
We hold no SOC 2 report, have had no penetration test, and have no certification today. When that changes, this page will say so with a date.
In the code today
Model calls keep nothing
in the codeEvery request to a language model goes through OpenRouter with zero data retention routing required and data collection denied. A model with no such route fails rather than falling back to one that keeps data.
src/extract/model.ts · src/extract/embed.tsA quote must be the customer's exact words
in the codeA quote a model returns is checked character for character against the source text. If it does not match, it is thrown away, never repaired.
src/extract/guard.tsOnly threads with someone outside your company are read
in the codeA thread among colleagues is dropped before any of its text reaches a model.
src/extract/corpus.tsErrors from the model call carry no message text
in the codeWhen a model call fails, the error holds a status code and a field name, not the passage that was sent.
src/extract/model.tsSign-in grants no mailbox access
in the codeSign-in asks Google for openid, email and profile only. Reading mail and calendar is a separate consent, and creating drafts is another.
src/auth/config.ts · src/connect/providers.ts · src/connect/connect.test.tsOAuth tokens are encrypted at rest
in the codeAES-256-GCM with a fresh random IV per value, sealed to the company and account it belongs to, before it reaches the database. Kept on the server, never in a browser cookie.
src/lib/crypto/index.ts · src/connect/tokens.ts · src/lib/crypto/crypto.test.tsOne company's rows never reach another
in the codeEvery table carries the company it belongs to, every query runs inside a helper that sets that company first, and row-level security is enabled and forced in the database, so a query with no company set sees nothing.
db/migrations/0001_ledger.sql · src/db/org.ts · src/db/rls.test.tsDisconnecting a source deletes what came from it
in the codeIts quotes and passages are deleted, and so is any commitment left with no evidence. The record that a decision was made stays, without the customer's words.
db/migrations/0001_ledger.sql · src/db/assertion.test.tsAn agent never keeps its own promise
in the codeExtraction only proposes. An agent can keep nothing without a policy a person granted, and the database refuses an agent keeping an assertion it made itself, policy or not.
db/migrations/0001_ledger.sql · src/db/assertion.test.tsAPI and agent keys are stored as hashes
in the codeA key for the HTTP API or the MCP tools is shown once when issued. We store a SHA-256 of its secret, never the secret, and a key carries only the permissions its holder has.
src/db/credentials.ts · src/db/credentials.test.tsAgents act through a scoped MCP server and API
in the codeAgents read and write the ledger through an MCP server and an HTTP API. Each key acts as one principal, its permissions are checked on every call, and every change lands in the same history as a person's. No key, request body or response body is written to a log.
src/agent/registry.ts · src/agent/auth.ts · src/agent/api.test.ts · src/agent/mcp.test.tscheck_claim keeps no statement
in the codeWhen an agent asks whether a sentence is true, we keep a salted hash of it to count repeat questions and nothing else. The sentence is compared with your claim register in memory, embedded through the same zero retention routing as every model call, and dropped.
src/agent/claims/check.ts · src/claims/register.ts · src/agent/agents.test.tsYour pages are read politely, and never your API docs
in the codeThe claim register reads only the pages you add, once a day. Our reader names itself (CommitOpsClaimReader), reads robots.txt first and stays out of what it disallows, stops at 2 MB and 10 seconds, refuses private and local addresses (including a public name that points at one, checked again on every redirect), and never reads a path like /api, /reference or /openapi. A page it cannot read leaves what it said before untouched.
src/claims/fetcher.ts · src/claims/fetcher.test.ts · db/migrations/0016_claims.sqlA signed contract keeps only its entitlement clauses
in the codeA contract you upload is read once, under the same zero retention routing as every model call. We keep the clauses that grant something, each word for word with its page, and a hash of the file; never the rest of its text. A clause that is not exactly on a page is thrown away. Contract text never goes to error reports, logs or analytics. Deleting the contract deletes every clause read from it.
src/entitlements/contracts.ts · src/entitlements/entitlements.test.ts · db/migrations/0017_entitlements.sqlYour code is read, never run, and only the files you name
in the codeTo find a hardcoded plan limit we read the files a source names: handed to us by you or your agent, or through our GitHub App once you grant it Contents read, which it does not hold today. We keep the value, the file, the line number and that one line; never the file. Nothing executes your code.
src/entitlements/code-reader.ts · src/entitlements/sources.ts · src/entitlements/github-files.tsError reports carry no customer content
in the codeBefore an error leaves for Sentry, it is rebuilt from an allowlist: the error type, file and line numbers, the route, the release, and our own ids. Messages, request bodies, query strings, cookies, breadcrumbs and any field we did not list are dropped. A test throws an error carrying a real quote and checks no fragment of it reaches what Sentry receives. Session Replay is not in the code.
src/lib/observability/scrub.ts · src/lib/observability/sentry.ts · src/instrumentation.ts · src/instrumentation-client.ts · src/lib/observability/scrub.test.tsProduct analytics carry no customer content
in the codePostHog receives seven named events from our server, each limited to fixed choices, numbers and our own ids, so none can hold a word of text. There is no PostHog code in the browser, so no click capture, session recording, heatmaps or surveys. A company can turn the events off.
src/lib/observability/analytics.ts · src/agent/observability.test.tsLogs carry codes and ids, not words
in the codeServer logs go through one helper that writes a code and fields, and replaces any value that is not a number, a yes or no, or one of our own ids with the word dropped.
src/lib/observability/log.ts · src/lib/observability/log.test.tsSingle sign-on, bound to your own domains
in the codeOn Scale, a workspace signs in through its own identity provider, SAML 2.0 or OIDC. Only addresses at domains the workspace already proved it holds can use it, and one domain belongs to one workspace. Each sign-in starts with a request we record and accept once; a SAML response must be signed by your provider's key, name us as its audience and our sign-in address as its recipient, fall inside its validity window with two minutes for clock drift, and carry an assertion id we have not seen. An OIDC token is checked against your provider's published keys, our client id, its expiry and the nonce we sent, and the client secret is stored encrypted. Someone new arrives as a member with no authority. When SSO is required, Google sign-in is refused at those domains and a Google session there stops on its next request.
src/sso/flow.ts · src/sso/saml.ts · src/sso/oidc.ts · src/sso/sso.test.ts · db/migrations/0020_scale.sqlTeams see their own accounts
in the codeOn Scale, with teams turned on, a person or an agent who is not an owner sees only the accounts and commitments of the teams they belong to, plus those no team holds. This is enforced by the database's row level security, so every screen, export and API call obeys it, and authority granted on a team acts only on that team's rows.
db/migrations/0020_scale.sql · src/teams/teams.ts · src/agent/scale.test.tsA signed audit export
in the codeOn Scale, an admin can export every commitment the company has made with its source: each quote, who said it, where and when, its whole history and every dated check. The file carries a SHA-256 for every part and an HMAC-SHA256 signature over the list, made with a server key named by its id, so a reader can tell whether anything was changed. The file is kept seven days and then deleted; the record that it was made stays. It is built from what the person asking may see.
src/audit/bundle.ts · src/audit/sign.ts · src/audit/verify.ts · scripts/verify-audit.tsThe subprocessor list is checked against the code
in the codeEvery company that receives data from us is listed below with what it receives. A test reads every outbound address in our code and fails when one belongs to no listed company, or when a listed company is no longer used, so the list cannot fall behind the code.
src/lib/subprocessors.ts · src/lib/subprocessors.test.tsChanges to the subprocessor list are announced
in the codeEach change to the list is dated and published on a feed at /security/subprocessors.xml, and emailed to anyone on the notice list. A new company is announced at least 30 days before it receives customer data.
src/lib/subprocessor-feed.ts · src/app/(marketing)/security/subprocessors.xml/route.ts · src/lib/subprocessor-feed.test.ts
Decided, being built
A data processing agreement on every plan
decided, being builtOur DPA is published at /dpa for any plan to read. It is a draft until our lawyer has reviewed it; until then you can ask to sign, and we send the final text when it is ready.
openspec/specs/trust-program/spec.md
Planned
Google's review of Gmail access
plannedGoogle's verification of the restricted Gmail scopes, including its independent security assessment (CASA), before Gmail access opens beyond test users.
docs/owner-setup.html, step 6SOC 2
plannedPlanned: a Type I audit, then a Type II window. No auditor is chosen, no audit has started, and no report exists.
openspec/specs/trust-program/spec.md
Who processes data for us
The app runs on Vercel and is served over HTTPS. The database is Postgres on Neon. Language models are reached through OpenRouter. These are every company that receives data from us, and what each receives. A test fails when our code sends data anywhere not on this list.
| Company | What for | What it receives |
|---|---|---|
| Vercel | Hosts the website and the app | Requests to the app, including the pages you view. Pages can show customer content. |
| Neon | The database | Everything we store, as described above. |
| OpenRouter, and the model providers it routes to | Runs the language models that find candidate commitments | Short passages of text, under zero data retention routing with data collection denied. Nothing is kept or used for training. |
| Sentry | Error reports | Error details with every field that can hold customer content removed before sending. |
| PostHog | Product analytics | Named product events with counts and settings. No customer content, no click capture, no session recording. |
| Stripe | Billing | Your billing contact and payment details. No customer content. |
| Sign-in, and the Gmail, Calendar and Drive access you grant | The requests we make to read the mail, calendar and Drive files you connected, and to create drafts if you turn drafting on. | |
| Microsoft | The Outlook mail and Microsoft Calendar access you grant | The requests we make to read the mail and calendar you connected. |
| Integrations you connect | GitHub, GitLab, Linear, Jira, HubSpot, Attio, Salesforce, Pipedrive, Notion, Slack, and your transcript provider (Gong, Zoom, Fireflies or Granola) | What you direct us to send: for example a comment on your pull request naming the commitment it satisfies, or a digest in your Slack channel. |
We announce a new company at least 30 days before it receives customer data. Follow the subprocessor feed, or join the email notice list. The last change: Microsoft listed on its own row, for the Outlook mail and Microsoft Calendar access you grant. Google's row now names Drive. The integrations row names every service you can connect. No new service receives data.
Every plan can sign our data processing agreement from the site, without a sales call. The privacy policy says what we collect and keep.
Write to security@commitops.ai with what you found and how to reproduce it. We will reply, keep you told as we fix it, and not take action against research done in good faith that avoids other people’s data.