PII scrubbing
Redacted before it's ever enqueued, let alone stored
A likely-sensitive value never reaches the job queue in the first place, only the request that reported it: key-name matches and shape-based pattern matching, combined, before anything is persisted.
Scrubbed before enqueueing, not in the background job
Redaction runs in the ingestion controller itself, before the event is ever handed to a background job: specifically so a raw value never gets serialized into the job queue's own storage either, only ever existing in memory for the span of that one request. Structured fields an app sets deliberately (exception class, environment, release, server name) are left alone entirely; only the free-text fields an app didn't fully control the contents of (message, backtrace, context, tags) go through scrubbing at all.
Two independent signals, combined
A value is redacted wholesale if its own field name matches a known-sensitive key (password, token, api_key, credit_card, ssn, and similar), the highest-confidence signal available: an app author who names a field credit_card is telling us, in plain language, what's in it. Separately, free text is scanned against a set of deliberately conservative shape-based patterns: emails, SSNs, formatted credit card numbers, bearer tokens, JWTs, and known API key formats (AWS, Stripe, GitHub).
Credit cards only match the formatted 4-4-4-4 shape, on purpose, not any bare run of 13-16 digits: this app's own domain is full of legitimate long numeric ids in exception messages ("Couldn't find Invoice with id=88214451") that a looser pattern would wrongly redact and make the product worse at the one thing it exists to do.
A shape match on the message and a key-name match in Context, both redacted, with an unrelated field left alone.
Extend the key list per project, never the pattern matching
A project can add its own sensitive field names on top of the built-in defaults (patient_mrn for a healthcare app, say), but deliberately can't extend the shape-based pattern matching itself with arbitrary custom regexes; that's its own can of worms (a slow, denial-of-service-prone pattern is a real risk when the pattern itself is user-supplied). Available on every plan, uniformly, not gated.
Source context capture has its own, separate off switch
An SDK can optionally capture a few lines of source around each in-app frame of a backtrace, so an issue's detail page shows the code that broke, not just its file and line number. That's a real, different kind of exposure from anything else on this page (literal source code, not a reference to it), so it gets its own protection on top of everything above: it never applies to a third-party dependency's code, only the app's own; and every project has its own setting (on by default) that's checked on arrival, not just trusted from an SDK's local configuration. Turn it off for a project and ForgeOps stops storing captured source lines for that project immediately, regardless of what any individual reporting app still has configured locally.
Database errors keep the shape of the query, never its values
When an error comes from a database call, an SDK can report which stored procedure, function, table or view the failing SQL touched, so a developer knows where to start looking. Those names are identifiers, not data, and are sent by default. Where an error can't carry the SQL (Go, Rust, C, C++, and a few others), one call hands it over. Sending the statement itself is a separate opt-in in the SDK, and even then every string and number in it is replaced with a question mark before it leaves your app and again when it arrives: "WHERE email = 'a@b.co' AND id = 42" is stored as "WHERE email = ? AND id = ?". Each project can also turn off storing the statement entirely; the object names are still kept, so an issue can still say which procedure or view was involved.
See it on your own data
Free plan included, no credit card required. New organizations start with 14 days of Business.
Get started freeGet