What Forge reads, stores, and never stores
You can send Forge two things: your app’s events and errors, and — if you connect it — your repository. Forge keeps less than it reads. This page lists what it keeps, for how long, and who can see it.
What your app sends
Your app sends events, errors and logs through @ambera/forge-tracking or a plain HTTP request, with your product’s tracking key, and the feedback its users write the same way (the SDK’s feedback from version 0.5.0). The key is public on purpose: it can only add rows to that one product, never read them. In a browser the SDK also records page views (the first of a visit with the host of the page that linked to it, the link’s utm tags, and whether the browser says it is automated) and uncaught errors unless you turn that off, and keeps an anonymous id in the browser’s storage (or only in memory, if you set it to). Everything else is sent only when your code calls it.
- Scrubbed before it is stored. In every message, name, stack and property value, e-mail addresses, IP addresses, long runs of digits and anything shaped like a secret or a token are replaced with a placeholder. A URL keeps its path and loses its query string and fragment. Of the page a visitor came from, the SDK sends only its host name, and Forge keeps nothing more of it; a link’s utm_source, utm_medium and utm_campaign are kept, scrubbed and cut at 100 characters. A referrer your app sends itself keeps its path, like any URL. Forge never reads the IP address of the request. To leave bots out of the counts, Forge reads the request’s user agent as a batch arrives. It does not keep it, only which rule a session met and on which day. A browser that says it is automated, or a session with 20 page views within 60 seconds, also meets a rule. The marks go with the rows.
- Ids stored as hashes. Forge stores a hash of the person and session ids, computed with the product’s id — never the ids as sent — so the same person is a different value in every product. Someone who knows an id could still check it against the hash, so send an opaque id, not an e-mail address. No screen shows these hashes or lists who is behind a count.
- Your own team. An admin may list the ids your app identifies your team with (at most 50). Each is hashed the same way and only the hash is kept, in the product’s store, so Settings says how many are listed but never which. Their sessions stay stored for your plan’s days and are left out of every figure that judges the product; Issues and Settings say how many are theirs, and Activity can leave their rows out but never shows them alone. Your app may ask for the person signed in to be added (an event named
$team_request); the request holds their hashed id until an admin confirms or dismisses it, 30 days at most. - From your website to your app. When a visitor clicks from your website into your app, the link carries the anonymous id the website gave them (forge_h), never the id your app knows them by. Your app may pass the same id to its server in an x-forge-id header.
- Feedback. What a person writes in your app’s own feedback form is a row like any other: scrubbed as above, cut at 2,000 characters, with the page it was written on and the person hashed. When Forge reads the product it copies each item — never one from a session already known as your team’s or a bot’s when it is copied — to your workspace’s database, where its themes are counted and it stays with the product, as imported feedback does. Themes are grouped by words a person writes, never by a model; no screen shows who wrote an item.
- Conversions. An event your app names $conversion may carry a value and a currency code, kept as sent and summed per currency — never converted, never added across currencies, never called revenue. Send no card, account or invoice data.
- One database per product. Each product’s rows sit in their own database on Cloudflare, separate from the one that holds your account, and not pinned to the EU. Every count is exact, never a sample.
- Your plan’s days. Rows are kept 30 days on Free, 90 on Solo, 180 on Team and 365 on Premium, then deleted. If you move to a plan with fewer days, older rows stay 14 more days first. Daily totals — counts per day, sessions by channel and Forge campaign tag, the pages visits began on, conversions and their value per currency, and each error group’s scrubbed title — are kept 365 days on every plan, so a release can be compared with the weeks before it. Issues and their observations stay with the product.
- Who sees them. Every member of your workspace, viewers included, can list the stored rows on the product’s Activity page and see an error’s latest occurrences — scrubbed as above, never with a person or session id — and read the feedback items on Issues and on a journey’s page, never with who wrote them.
What a repository read keeps
- A connected repository: through the GitHub App, installed on the repositories you select, Forge reads the file tree, commit history and merged pull requests; a full code read also reads the contents of source files. Its one write permission is the check result on a pull request. When a pull request merges into the default branch, Forge reads its branch name, title and description for a
Forge-Issue:line and moves the issue it names to awaiting release; the description is not stored. - A public repository you have not connected: Forge reads its file tree, commit history and merged pull requests through a shared token — no file contents — and stores nothing. A private one is refused.
- On your own machine: npx @ambera/forge map reads your git-tracked files, package manifests and imports, and prints the result. It sends nothing and has no telemetry. With --submit (signed in) it uploads a summary to one product: the capabilities found (payments, sign-in…), the packages behind them, the paths of the files importing them, counts and the commit id. File contents are never sent. Each upload replaces the last.
File contents are read in memory and discarded. A stored reading keeps:
- counts, up to 5 example file paths per finding, and line numbers;
- for most findings, up to 3 single lines of source, cut at 200 characters — never for a credential (the line is the credential) or a code comment (comments carry names and internal links);
- the repository’s description on GitHub, recent commit subjects, and the titles, authors and reviewers of merged pull requests;
- the commit it read, so an unchanged repository is not fetched again.
It never stores a whole file, a function or a diff. Only signed-in members of your workspace see those lines.
When a reading finds a package it cannot place, Forge counts it under a salted hash of its name and of your workspace. The name itself is kept, and given to a model to classify, only once 2 workspaces have it. Nothing else of a reading is pooled.
Where credentials live
A repository connected through the GitHub App stores no repository credential at all — access tokens are minted per request, expire within the hour, and are read-only except for writing the pull-request check. Older connections made before the GitHub App keep an encrypted OAuth token; reconnecting through the App deletes it and revokes it at GitHub. An API key is stored as a hash; the secret is shown once.
What leaves Forge
- Nothing is written to your repository. Forge opens no branch, no commit and no pull request. With the pull request check switched on it posts a check result, and that is the whole of what it writes.
- Text sent to an AI model, through OpenRouter, with prompt logging off and under terms that exclude training:
- drafting a work spec, on a paid plan: by itself when an issue opens (at most three a product each time Forge reads it) and when a member asks. On every plan an issue first gets a spec Forge writes from its counts with no model. The model gets the issue’s title and its numbers as sentences, an older issue’s notes, one scrubbed example of the error (or, for a website-to-app issue, the scrubbed titles of the errors that followed the clicks), at most 5 feedback texts written on a journey’s page in the issue’s window, and file paths from a repository read;
- describing your product: your website’s public text and the README of its linked repository, read through your connection when the repository is private;
- the market read: that description, who the product is for and what Forge found on the companies you watch; the search for pages mentioning your product gets its name and web addresses;
- proposing changes toward a target, for a product with one, in the weekly market read: that description, the targets, the program’s sentences about your counts (sessions by source, entry pages and their errors, journeys, arrivals, conversions, feedback themes as counts) and this week’s brief items;
- the brainstorm: what a member types there, with the same description.
- E-mail to your workspace: members are told within minutes when errors spike (the subject names the error), market briefs arrive on the schedule you set, and unread notifications are summed up in a daily digest.
Disconnecting, and deleting
Revoking a tracking key refuses new rows within 60 seconds; rows already sent are deleted when your plan’s days run out. Deleting a product deletes its rows the same day.
Disconnecting a repository removes the connection, and for a connection older than the GitHub App, its webhook and token. The GitHub App stays installed until you uninstall it on GitHub. Readings stay by default — a reconnect picks the history back up — and the disconnect dialog offers deleting them, with their waivers, permanently.
An account can be deleted from its own settings: it deactivates immediately, signs out everywhere, and its data is purged 25 days later — along with any workspace where it was the only member, all of its products’ rows included. If the workspace has other members, hand ownership to one of them first, or write to hello@ambera.app to delete the whole workspace. Billing records, the record of a Launch Audit purchase and a record that the deletion happened stay, as the privacy policy states.