search ESC

Searching…

No results for "".

Type at least 2 characters to search.

Docs

Redaction

Telescope serves its buffers to AI agents and, with a file sink, writes them to disk. Two layers keep secrets out of both: the credential lists for HTTP records, and a host-supplied text redactor for every record that carries free text.

Credential lists (HTTP)

TelescopeStore.recordHttp masks credential values before it buffers a record. The request headers authorization, proxy-authorization, cookie, set-cookie and x-api-key, and credential keys in a JSON or form-encoded body (password, token, access_token, ...), read ********. Names match case-insensitively and at any depth. An empty value (null, false, '', [], {}) stays visible.

Extend the lists with TelescopeRedaction.hideRequestHeaders, hideRequestParameters and hideResponseParameters. Additions merge with the defaults and never replace them. An adapter that holds a body as a Dart Map runs it through TelescopeRedaction.redactParameters(data, keys) or redactBody(body, keys) before it stringifies or truncates it, because the store cannot mask a body it cannot parse.

The text redactor

TelescopeRedaction.redactor is a host hook, String Function(String)?, that is null until you set it:

TelescopeRedaction.redactor = (String text) =>
    text.replaceAll(RegExp(r'\b\d{13,19}\b'), '[card]');

When it is set, the store runs it at insert over every text field of a log, event, exception and HTTP record, before the record enters its queue or its stream:

Record Text the redactor sees
LogRecordEntry level, message, logger name, error, stack trace
ExceptionRecord exception type, message, stack trace, isolate
EventRecord event type, interaction fields, and every String value and every map key inside the payload's nested Maps and Lists
HttpRequestRecord URL, method, header values, request and response body, request id, interaction fields (after the credential key masking above)

A subtree nested past 64 levels is masked whole.

Contract

  • The redactor must be pure and total: it takes one String and returns the masked String.
  • A redactor that throws drops the record. The record is neither buffered nor emitted on its stream, because a record that could not be masked is never let through unmasked.
  • A record buffered while no redactor is registered is stored as is. Setting a redactor later does not rewrite what is already in the buffers.
  • TelescopeRedaction.resetForTesting() unregisters the redactor and restores the credential lists.

The redacted flag

The log, event, exception and HTTP records carry a read-only redacted getter, serialized in toJson. It is true only for a record the store's redaction pass produced with a redactor registered. The constructors take no redacted argument, and HttpRequestRecord.copyWith returns an unredacted copy, so no caller can build a record that a persisting consumer would take for masked. TelescopeFileSink relies on this: it writes only records whose redacted is true (see File sink).

Per-record entry points

redactLogRecord, redactExceptionRecord, redactEventRecord and redactHttpRecord apply the redactor to one record. The store calls them; a caller outside the store rarely needs to. Each returns null for a dropped record, so redactHttpRecord returns HttpRequestRecord? and a caller adds a null check.

JSON-safe event payloads

recordEvent stores any payload value that jsonEncode cannot write (a DateTime, a Duration, any object) as its toString(), and a non-String map key likewise. One bad payload can no longer fail a whole ext.telescope.events response. A payload that is already safe keeps its identity. This runs with or without a redactor.