Legal

Monitoring Data Processing Addendum

The monitoring part of our Article 28 DPA, in full: what a telemetry event contains, what is redacted inside your process before it is sent, how it travels, where it is stored, for how long, and how it is deleted. Pre-signed and in force for every project with monitoring enabled.

Last updated

1How this addendum works

This Monitoring Data Processing Addendum (“Addendum”) forms part of the Data Processing Agreement (“DPA”) and applies to every customer who enables Monitoring on any project. Like the DPA, it is pre-signed and in force from the moment monitoring is switched on; there is nothing to countersign. Capitalised terms have the meanings the DPA gives them.

The DPA carries the Article 28 terms, the Standard Contractual Clauses, breach notification, audit rights and liability, and none of that is repeated here. This Addendum adds the detail that is specific to telemetry: what an event contains, what is removed before it is sent, how it travels, where it is stored, for how long, and how it is deleted. Where this Addendum and section 4 of the DPA say different things about monitoring, this Addendum wins.

It covers every way telemetry reaches us:

  • error reporting compiled into the SDKs we generate for you, in all ten languages, once the developer using the SDK turns it on;
  • the standalone backend packages (@octri/node and its siblings) you add to your own API;
  • distributed traces and custom metrics sent by either of the above;
  • uptime checks you configure, which we run against URLs you choose;
  • source files and source maps you upload so stack traces resolve to your original code; and
  • alert rules, and the deliveries they make to destinations you configure.

2Roles, and who decides what is sent

You are the controller of any personal data that reaches your monitoring project, and we are your processor. That is the DPA’s allocation and it does not change here. What this section adds is that three different pieces of software can put data into a project, and only one of them is ours.

  • Your own backend, through a standalone package. Your code decides which errors to report and what to attach.
  • Your own application, through a generated SDK. Same again: your code enables reporting and grants consent.
  • Somebody else’s application, where you publish a generated SDK to a registry and a third-party developer installs it. Telemetry is compiled into that SDK but is off; nothing is sent unless that developer turns it on in their own code. The README shipped with every generated SDK says so, and says what is redacted, so the developer can make that decision with the facts in front of them.

In the third case the developer who enables reporting is responsible to their own users for doing so, in the same way they are responsible for any other library they configure. You remain the controller of what your project stores, because you decided to offer the reporting and you decide how long to keep it and who on your team can read it. We remain your processor throughout.

Categories of data subject: the users of your API, the users of applications built on your SDKs, and members of your own team who appear in stack traces, release markers or alert rules.

3What an event contains

This is the monitoring row of the DPA’s Annex I, expanded. The tables list every field an SDK can put in an error event, grouped by whether it is always present, present when your code sets it, or present only after consent. The redaction described in sections 5 and 7 applies on top of all of it.

Always present

FieldSentWhat it holds
eventId, traceId, spanIdEvery eventRandom identifiers generated by the SDK. They identify the event, not a person.
timestamp, levelEvery eventWhen it happened and how severe it was.
operationId, method, pathEvery eventWhich SDK operation ran, its HTTP method, and the request path. The query string is never included, and the standalone packages strip it off explicitly. Path parameters are sent as your API received them, so a path that carries a user id carries it here too; an email address in a path is masked at ingest (j***@acme.com), and the issue row stores the templated form (/users/<id>).
statusCode, requestIdEvery failed requestThe response status and the request id your API returned in its own header, so you can match an event to a server log.
errorEvery errorThe error class, its message, and the stack trace as function, file and line. The standalone packages and the Go SDK add a few lines of your own source around each frame so the dashboard can show where it failed. That is your code, not your users’ data. Credential assignments (password = "…", api_key: "…") and well-known key formats in those lines are masked at ingest, but check the surrounding code before enabling source context in a repository you would not share.
environmentEvery eventYour project id. Ingest takes it from the authenticated token rather than trusting the payload.

Present when your code adds it

FieldSentWhat it holds
release, tagsWhen your code sets themA version string and a flat map of labels. Sent as you set them, after key redaction. A tag named after a person is a tag we store.
userWhen your code sets it, and after consent in the browser and Python SDKsWhatever object you attach. Keys named email, phone, name, address and the rest of the identifier list are replaced with [REDACTED] before sending, including where the word sits inside a longer key (billingEmail, customer_phone_number); an opaque id survives, which is what the affected-users count is built on.
contextOnly after consent, browser and Python SDKsDevice language, online state and screen size, and the page title. The user agent, page URL and referrer are collected but redacted by default, because they identify a device or a person.
breadcrumbsOnly after consent, browser and Python SDKsThe last thirty SDK calls as method, path and status, plus anything your code records with addBreadcrumb. URLs inside breadcrumbs are redacted by default.

Never present

  • Request bodies and response bodies. No runtime reads either into the payload, and the keys body and request_body are on the redaction list in case a future field ever carried one.
  • Request or response headers, including cookies and the Authorization header. The keys headers and request_headers are on the redaction list for the same reason.
  • The query string of a request.
  • The IP address of the machine that sent the event. The SDK does not include it, ingest does not record it on the event, and in the hosted service the rate limiter keys on the project token instead.
  • Your OpenAPI spec, your documentation, or anything from another Octri product. Monitoring has its own store.

The other record types

  • Spans carry a name built from the method and path, the service that produced them, the operation id, start and end times and an ok/error status. No payload.
  • Metrics carry a name, a numeric value and the tags you set.
  • Uptime results carry the URL you configured, the status code, the latency and, on failure, the error text. Do not point a check at a URL that embeds a credential.
  • Source files and source maps are your code, stored so frames resolve. They are kept until you delete the release or the project.
  • Alert deliveries carry the rule name, the alert message, the value and the threshold, and go to the Slack or webhook URL you configured. That destination is your choice and your processor, not ours.

Special-category data. Monitoring is not designed for it and the redaction list does not look for it. Do not attach health, biometric, political, religious or sexual-life data, criminal-offence data, payment card numbers or government identifiers to an event, and do not build a request path that carries them.

4Two switches, both off

Nothing is sent until two separate decisions have been made, by two different people.

  • The project owner enables Monitoring from the dashboard. Until then the project has no ingest credential and an SDK has nowhere to report to.
  • The developer using the SDK enables reporting in their own code. Every generated runtime ships with logging.enabled false, even when an endpoint and credential are compiled in. The standalone packages send nothing until init() is called.

Disabling monitoring on a project is checked on every delivery, so an SDK that was reporting is refused immediately rather than at its next release. What was already stored stays until it expires or you delete it; see section 11.

5Redaction before anything is sent

The first and most important pass runs inside your process, on the payload, immediately before the HTTP request is made. By the time an event reaches us the values below have already been replaced with [REDACTED]. This is not a promise about how we handle data; it is a property of the code, and you can read it in the SDK we generated.

In all ten generated SDKs

filterPii is on by default in every runtime. The whole payload is walked recursively and any key on the identifier list is replaced. Keys are compared after lower-casing and normalising separators and camel case, so apiKey, api_key and API-Key all match. The list has 69 whole-key entries, and 43 of its terms also match as a word inside a longer key, so billingEmail, customer_phone_number and stripeSecretKey are caught while avatarUrl and queryTimeMs are left alone. It covers:

  • credentials: authorization, cookie, set-cookie, password, passcode, secret, token, api_key, access_token, refresh_token, client_secret;
  • direct identifiers: email, phone, address, street, city, postal and zip code, first, last and full name, username, ip, user_agent, referrer, url, uri, query, latitude, longitude;
  • government and financial identifiers: ssn, tax_id, national_id, passport_number, date_of_birth;
  • request snapshots that should never exist: headers, body, request_headers, request_body.

In the standalone backend packages

The same credential and identifier families, matched anywhere in a key, plus a second layer that scans free text, because an exception message is where a secret usually leaks. Bearer tokens, JSON Web Tokens, card numbers that pass a Luhn check and email addresses are masked wherever they appear in a string, stack frames included. The id you set on the user survives; its email, phone and names do not. Two hooks let you extend it:

  • addScrubFields("accountNumber", "otp") adds your own domain’s sensitive keys to the list.
  • setBeforeSend(hook) runs your function on every payload before it is sent. Return null to drop the event. Redaction runs after the hook, so a hook cannot put a credential back.

Turning it off

filterPii: false exists for one case: a private, self-hosted monitoring endpoint you trust with raw data. If you set it while reporting to us, you are instructing us to process whatever the payload then contains, and you take on the obligations that come with it. The ingest pass in section 7 still runs.

What it cannot do

Redaction is a safety net with a fixed list. It cannot know that a field you named notes holds a home address, and the generated SDKs do not scan message text for patterns. Tags and context you attach are stored as sent once the keys have been checked. Sending less is the only measure that removes a risk rather than reducing it, and deciding what leaves your application is your responsibility as controller.

7Redaction again at ingest

Every payload that arrives passes through a second redaction before it is written to storage, unconditionally, whether or not the sender filtered it. It exists for events that did not come through our SDKs: a generic HTTP client posting to the ingest endpoint, an old build from before client-side filtering existed, or a consumer who turned filterPii off. Two layers run:

  • By key. A superset of the list the SDKs use: every credential key (passwords, secrets, tokens, API keys, authorization and cookie headers, session identifiers, private keys, client secrets, card numbers, CVV) and every direct identifier key (phone, address, names, username, user agent, SSN, passport, tax and national ids, dates of birth, coordinates), matched as a whole key or as a word inside a longer one, the same way the SDKs match them. An email-shaped key is masked rather than removed.
  • By value. Payment card numbers that pass a Luhn check, JSON Web Tokens, bearer tokens, well-known API key formats (AWS, GitHub, Stripe, Slack, Google), private key blocks and password = "…"-style assignments are masked wherever they appear in a string, including inside exception messages, request paths, stack frames and source context lines, even under a key that looks harmless. Only the secret is replaced; the rest of the message is kept.
  • Email addresses are partially masked (j***@acme.com) wherever they appear, in a field, a message or a path, so an issue can still be grouped per affected user without the address being stored.

The pipeline then templates ids, numbers and quoted literals out of the path and the message to compute a grouping fingerprint. The templated forms are derived for grouping only; the event is stored with the values it arrived with, after redaction.

8Transport and authentication

Telemetry is sent over HTTPS only. The public ingest endpoint sits behind Cloudflare’s edge, which terminates TLS, applies a web application firewall and forwards the request to our server over TLS again. Cloudflare does not store the payload; it is listed on the subprocessors page for the transit.

The ingest credential

  • Every request carries a project-scoped token minted by our API when monitoring is enabled. It is an HMAC-signed claim naming the project and a version number. The signing secret never leaves our server, and the token cannot be used to read anything.
  • Ingest takes the project from the token, not from the payload. An SDK holding one project’s token cannot write into another project, whatever its payload says.
  • Each time monitoring is enabled on a project the version is incremented, and every token carrying an older version is refused from that moment. A credential you believe has leaked is revoked by disabling and re-enabling monitoring from the dashboard, after which SDKs need a build with the new credential.

How delivery behaves

  • The SDK posts fire-and-forget and never waits on us, so monitoring cannot slow or break your application. The endpoint answers 202 Accepted once the event is durably stored.
  • Each event carries a random eventId sent as an idempotency key. A retried delivery of the same event is discarded, so a network hiccup is not a duplicate on your screen.
  • Error and fatal events are never sampled. Lower-severity events may be sampled to protect the service, and where they are, the sampling happens before storage rather than after.
  • Past your plan allowance, ingestion continues up to a burst ceiling so an incident is never the moment telemetry stops. Past that ceiling we refuse with 429 and a Retry-After, and the SDK backs off. The figures are on the Fair Use Policy.
  • An issue that exceeds a per-minute occurrence cap keeps being counted but stops being stored in full, so a runaway loop cannot fill your retention with one error.

9Where telemetry lives

Monitoring has its own analytics database, ClickHouse, on a server dedicated to it. We rent the server and operate everything on it ourselves: the database, the ingest service and the reverse proxy. No managed database vendor, no third-party observability or APM product, and no analytics pipeline sits between your SDK and the disk.

  • The database listens on the loopback interface only. It is not reachable from the internet and not reachable from our other servers.
  • The dashboard reaches the monitoring service over a private WireGuard network from our API server, with a separate dashboard credential. The server’s firewall is default-deny inbound.
  • Every read is scoped by project id, folded into the query on the way out in the same way the token scopes writes on the way in. Who on your team can read a project is governed by your organisation’s roles.
  • Telemetry is never used to train a model, never joined across customers, and never used for any purpose of our own beyond operating the service and producing the aggregate, de-identified statistics the DPA already permits.

Operator access

Our operator console does not display customer telemetry. Access to the monitoring server is by SSH key held by named infrastructure staff and is used to operate the service, not to read events. Support access to your organisation in the dashboard is written to your own audit log under the staff member’s address, as the DPA describes.

Location and transfers

The monitoring server is rented from OVH in its United Kingdom region and telemetry is stored there. For data subject to the EU GDPR, the United Kingdom is covered by an adequacy decision, so storage there needs no further transfer mechanism. Octri is operated from the United States, and access by our staff from there is a transfer made under the Standard Contractual Clauses and the UK Addendum in section 13 of the DPA. The hosting provider is listed on the subprocessors page.

10Retention

Raw events, spans and metrics expire after the retention window your plan includes. The window is a plan entitlement resolved by our API and stamped on each record as it is admitted, and expiry is enforced by the storage engine on that stamp rather than by a sweep job, so it does not depend on anything running on schedule.

PlanRaw event retention
Free7 days
Starter30 days
Growth90 days
Business180 days
Enterprise365 days, or as agreed in your order form
  • Aggregate rollups (counts, error rates, latency percentiles per day) outlive the raw events so long-range charts keep working. They carry no event payload and no user identifier.
  • Issues do not expire, identifiers on them do.An issue keeps its title, the templated operation and path it was seen on, its counts, first and last seen, triage status, and the error name, message and stack of its most recent occurrence, until you delete the project. The occurrence records behind the count (timestamp, level and release, with no payload) are kept the same way, but the user id on each record is stamped with the raw event’s own expiry: the storage engine resets it when the stamp passes, a scheduled job rewrites each issue’s affected-users list to the ids still live, and every “users affected” figure you see already counts only those. An issue’s users are therefore the distinct users within your retention window; its count is all-time.
  • Uptime check results are kept for 30 days on every plan.
  • Source files and source maps are kept until you delete the release or the project.
  • Alert rules and dashboards are configuration and are kept until you delete them.

Moving to a plan with a shorter window applies the shorter window to events admitted from that point; events already stored keep the stamp they were given.

11Deletion and data subject requests

  • Deleting a project deletes its monitoring data as part of the same operation: every event, span, metric, issue, uptime result, source file and alert rule for that project. Deleting an organisation does this for each of its projects. If the monitoring service cannot confirm the purge at that moment, the request is recorded and retried every hour until it does.
  • Disabling monitoring stops ingestion at once but keeps what was already stored until it expires, so you can turn reporting off during an investigation without losing the evidence. Ask us if you want it purged sooner.
  • On request, we will delete monitoring data for a project or an organisation within 30 days. Write to support@octri.dev from an owner’s address.

Requests from your users

A data subject request reaches you, not us, and the DPA sets out how we assist. For monitoring specifically:

  • Access. The log query in the dashboard filters events by the user id you attached, and the API behind it returns the same records, so you can answer an access request yourself without waiting on us.
  • Erasure.An owner or admin can erase one user id from a project’s monitoring data in the dashboard (Monitoring, then the connection settings, then “Erase a user’s data”). The events carrying that id are deleted, the id is removed from the occurrence records and from every issue’s affected-users list, and the counts are kept. It is accepted immediately, completed in the background, written to your audit log, and retried hourly if our monitoring service could not be reached. If you would rather we did it, tell us the project and the identifier and we will do the same within 30 days.
  • Objection or restriction. Close the consent gate for that user, or stop setting user, and no further personal context is sent. Ask us to erase what exists as above.

12Subprocessors for telemetry

Telemetry payloads reach two third parties, both already on the subprocessors page: the hosting provider whose server stores them, and Cloudflare, whose edge carries them in transit. No other subprocessor receives monitoring data. In particular, none of the AI providers do; monitoring is not connected to documentation generation or Ask-the-docs.

Alert deliveries go to the Slack workspace or webhook endpoint you configured. Those are services you chose and connected, so they are your processors rather than ours, and the alert message is the issue title, which has already been through both redaction passes.

Our own internal operational alerts, delivered to our Slack, carry organisation names and metrics that crossed a threshold. They never include an event payload.

13Your obligations

Beyond section 15 of the DPA, when you use monitoring you agree to:

  • have a lawful basis for any personal data your instrumentation sends, and open the consent gate, or set user, only where that basis exists;
  • keep the ingest credential out of client-side code you would not want copied, and rotate it if it is exposed;
  • add your own domain’s sensitive field names with addScrubFields, or the equivalent, where the built-in list would not recognise them;
  • leave filterPii on when reporting to us, and not send special-category data in any field;
  • where you publish an SDK to third-party developers, leave the shipped README’s telemetry notice in place so they can make an informed decision; and
  • tell us at support@octri.dev if you discover that personal data was sent that should not have been, so we can purge it rather than wait for expiry.

14Technical and organisational measures, monitoring

These supplement Annex II of the DPA for the monitoring service. They are the measures in force today; we may improve them and will not reduce them during your term.

  • Data minimisation by construction: no runtime places request or response bodies, headers, cookies, query strings or the sender’s IP address in an event.
  • Client-side pseudonymisation: 69 credential and identifier keys redacted in every generated SDK before sending, matched as whole keys and as words inside longer keys; the same families plus value-shape scrubbing of tokens, cards and emails in the standalone packages; a consent gate on personal context in the browser and Python SDKs.
  • Server-side pseudonymisation: unconditional key and value redaction at ingest with a superset of the SDK list, secret-pattern masking inside messages, paths and source context lines, and partial masking of email addresses wherever they appear.
  • Authentication: project-scoped HMAC-signed ingest tokens, versioned and revocable, with the project taken from the token; a separate credential for dashboard reads; constant-time comparison throughout.
  • Encryption in transit: TLS on the public endpoint, TLS from the edge to the origin, and an encrypted WireGuard tunnel between our API and the monitoring service.
  • Isolation: a dedicated server; the database bound to loopback; a default-deny firewall; every read and write scoped by project.
  • Storage limitation: per-record expiry stamped at admission and enforced by the storage engine, on raw events and on the user identifier of every occurrence record; per-plan windows published in the Fair Use Policy; cascade purge on project deletion, retried until confirmed; self-serve erasure per user id.
  • Availability and abuse controls: admission control with burst headroom, idempotent delivery, sampling of low-severity events, per-issue spike protection, and a fire-and-forget SDK that cannot block your application.
  • Accountability: support access to your organisation, and every erasure you perform, recorded in your own audit log; no operator screen that displays telemetry; an automated test that fails the build if any dashboard read stops being scoped to one project.

15Changes to this addendum

We will change this Addendum when the product changes: a new field in an event, a new runtime, a change to the redaction list or to a retention window. Changes that reduce what we commit to here are announced to account owners 30 days in advance in the same way as a new subprocessor. Changes that only add protection or describe the current behaviour more precisely take effect when published, with the date below updated.

Questions about this Addendum go to founders@octri.dev. If your procurement process needs a countersigned copy naming your legal entity, ask there and we will return one together with the DPA.