Skip to content
Privacy Notice

Production data stays in the customer boundary.

The hosted control plane holds organizations, policy, aggregated reports, and plan limits. Raw snapshots, secrets, and captured request bodies stay in your cloud by default.

Trust boundary

Two planes. Evidence can leave. Records of production should not.

Control planeOrganization metadata, account names and emails, GitHub identifiers, session records including IP address and browser user agent, policy configuration, aggregated reports, historical comparisons, audit entries, and the plan that sets an organization's limits.
Your boundaryRaw snapshots, secrets, captured request bodies until redacted, raw logs and traces, sanitization, provisioning, egress enforcement, and cleanup.
Careers applicationsName, email, selected role, an optional work link, your introduction, and acknowledgment of the current compensation. These are stored separately for recruitment review by an authorized operator, not added to customer analytics or a mailing list. No applicant IP address or browser user agent is stored in the application record. Applications expire through scheduled maintenance after 180 days, or an operator can delete them sooner. Backups expire separately. Contact us privately to request removal and include your application reference.
This siteNothing, until you use the contact form. That writes your name, work email, company, an optional seat count and your message into the control plane's own database, with the page it came from and the time. The role that serves public requests can insert into that table and cannot read it back, so no request to this site can ever return somebody else's contact details.

Sanitization happens where the data already lives.

Customer-hosted data planeMasking, subsetting, and credential deletion execute inside your cloud.
Outbound-only agentCommunication leaves the customer agent where possible, with short-lived credentials.
No snapshots in the hostThe hosted control plane is not a backup target for production-derived state.

No card ever reaches this product.

That part is unconditional and it is architectural rather than a promise: checkout and the billing portal are pages Stripe hosts, so a card is entered on Stripe’s own form and never passes through anything here. No card details or billing addresses exist anywhere in this product and none can.

What is conditional is everything else. The control plane contains a real Stripe integration, and it is active only where AF_STRIPE_SECRET_KEY and AF_STRIPE_WEBHOOK_SECRET are set. Where they are, Stripe holds the customer, subscription and invoice records for that deployment and is a processor for it, listed on the subprocessor page. Where they are not, the billing routes refuse and name the missing variables, and an organization carries nothing but a plan name, which sets its rate limits and quotas. The control plane says which of the two it is on the first line it logs when it starts.

This page previously said there was no billing at all. That was true when it was written and stopped being true when the billing work landed, which is the reason the numbers and capabilities on these pages are now checked against the code by a test rather than kept in step by hand.

This site

It counts page views itself, PostHog watches the rest, and both stop if you say so.

There are two measurements on this site and one of them has a vendor in it. The first is a counter this repository wrote, sending to this project’s own control plane. The second is PostHog, for autocapture and session replay, and it is here because the first one cannot answer where somebody gave up: it sends no address, no element and no ordering, deliberately. There is no Google Analytics, no Datadog, no Sentry and no crash reporter in anything this repository wrote. Two scripts are fetched while you read, and both are worth naming. PostHog’s session replay recorder, which comes from the endpoint we run rather than from any vendor address. And the booking widget on the contact page, which is cal.com’s, and whose frame runs its own error reporting to Sentry. That last one is their document doing their thing on their origin, and it is named here because your browser makes the connection and a page listing what it loads should not stop at the ones it likes.

Five things leave your browser for the counter: a page shape from a closed list, a channel from a closed list, a random identifier for one browsing session, a timestamp, and a campaign tag when you followed a link carrying one. The referrer and the URL are turned into those bounded values in your browser, so the address you came from is never put on the network at all.

PostHog sees more, and this is the whole of it: the address and title of the page you are on including its query string, the page you arrived from, each route you move to, how far down each one you got before leaving it, the clicks and form submissions you make along with the tag, classes and visible label of what you clicked, your browser, operating system, device type, screen and window size, browser language and timezone, and a recording of the pages you visit. A recording holds their structure and styling, your cursor, your clicks and your scrolling. Not your raw browser identification string, which is stripped before anything is sent, and not your address.

Every value you type is masked before it leaves your browser. The careers form and the contact form ask for a name, a work email, a company and a paragraph in your own words, and a recording of either one shows the fields filling up with asterisks and never what you wrote. It is not withheld on receipt and it is not deleted afterwards: it is replaced in the page, so there is nothing in the recording that could be unmasked later.

Neither of them sets a cookie, and neither keeps an identifier that outlives this tab, so nothing here can join two of your visits. PostHog would do both by default, for a year; it is configured here not to, and that choice is what keeps the sentence before this one true.

PostHog, Inc. receives all of that, and we will not dress that up. Your browser does not talk to a posthog.com host: it talks to an endpoint we run at app.antifailure.dev, which forwards. That changes where the request goes and not who reads it, so PostHog is on our subprocessor list with a row of its own saying so. What the arrangement genuinely buys you is two things. A content blocker does not recognise the request, so the numbers are not quietly half missing and nobody here is tempted to guess at the gap. And your IP address is not forwarded, so PostHog never receives it, which costs us any real geography on those dashboards and is worth it.

One claim is unaffected and it is a different claim: your production data never leaves your own boundary. That is about the engine and the runner, neither of which has an analytics client to leave with. This section is about a website you are reading.

Global Privacy Control and Do Not Track are honoured by both, without asking. The switch below is for everybody else, and it takes effect on the page you are reading rather than on the next one: anything captured and not yet sent is thrown away with it, and the recording ends. If you arrive with any of those already set, PostHog’s code is never fetched at all, so there is no recorder that read this page before something told it not to.

This browser

Reading this browser.

Reading

The answer depends on settings only your browser can be asked for, so it is read here rather than guessed on the server.

Drafted, not reviewed by counsel
This notice describes the architecture and the code as they stand. It is not a counsel-reviewed privacy policy, and it names no legal entity, because there is not yet a generally available control plane for one to contract about.

Signing in creates a session record and grants membership of the organization the GitHub App was installed on. The three documents that a security review asks for by name are now drafted rather than promised: the Data Processing Agreement, the subprocessor list, and the retention and deletion commitments. Read them as the current shape of the answer, not as a signed one.

Next

Know what happens before you deploy.

Create a disposable production twin for every risky change. Catch migration failures before they reach customers.