Skip to content

Data retention: what Shield360 stores

Shield360 receives a lot of telemetry that could hold sensitive content: the prompts people and applications send to AI models, the responses, what coding agents’ tools read and run, and the values guards find. None of that content is stored unless an administrator turns it on.

What is always kept, because it is not content: models, token counts, costs, timings, tool names, guard decisions, and the kinds and number of sensitive values found.

Data retention is set in Settings → Data retention. It applies to everything Shield360 stores, whichever way the telemetry arrives: SDKs, coding-agent hooks, the endpoint collector, the browser extension, the AI proxy (local or hosted) and OTLP sent straight to the collector.

SettingDefaultWhat it keeps
Store prompt and response contentOffPrompts, responses, system instructions and the text of AI log events.
Store tool-call arguments, results and codeOffWhat tools were called with and returned, shell commands beyond the program name, database queries, diffs and commit messages.
Store detected sensitive valuesOffThe keyed fingerprint of each value a guard finds, shown as a token such as [payment_card:fp_5a558416], and the vault secret it matches. Never any character of the value. Without it, only the kinds and how many are kept.
Store reasons people giveOffThe text someone types when they send a warned prompt anyway. Without it, Shield360 records [reason given].
Store actions (tokenized)OnThe exact command, tool call or file path an AI agent used, for activity provenance: every credential replaced by its type and a keyed fingerprint ([api_key:fp_3f9a0c51], never any of its characters), personal data masked, at most 1,024 characters. No prompt, response or file content. Without it, only the kind of action is kept.
Mask personal data in stored contentOnE-mail addresses, phone numbers, payment cards, IBANs and US social security numbers in stored content are replaced with their type, such as [email].

Secrets (API keys, tokens, private keys, connection strings) are always scrubbed from what is stored, whatever these settings say. See Ingest pipeline.

Store actions (tokenized) is the one category on by default, for new and existing organisations alike: it holds what an agent did, never what anyone wrote or a credential, so it does not break the promise above. Turn it off here, for the organisation or one environment, and agents’ actions are recorded by kind only.

Organisation default and environment overrides

Section titled “Organisation default and environment overrides”

The organisation’s setting applies to every environment. An environment can store less, never more: a switch the organisation has off cannot be turned on for one environment, and masking the organisation has on cannot be turned off. Turning a switch off for the organisation turns it off everywhere at once.

To make one environment stricter, select Edit beside it under Environments. Follow the organisation removes its override.

Traces, the trace index and log events (the otel_traces, otel_traces_trace_id_ts and otel_logs tables) are deleted after 730 hours (about 30 days) by default: the least that keeps the product useful. Settings → Data retention → How long logs are kept sets a longer or shorter period for the organisation. Metrics keep the default.

PresetPeriodWhen to use it
Minimal default730 hours (about 30 days)Nothing asks you to keep logs longer.
EU AI Act logs — keep at least 6 months183 daysYou are a provider (Art. 19(1)) or deployer (Art. 26(6)) of a high-risk AI system. Turns the EU AI Act check Logs are kept at least six months to met.

How long logs are kept is not what they hold. Keeping them longer keeps exactly what is already stored for longer: prompts, responses, tool content and sensitive values stay governed by the settings above, and stay off unless you turn them on.

When a provider or deployer role is recorded under Govern → Compliance program → EU AI Act → Your roles for an environment whose logs are kept less than six months, this page suggests the EU AI Act preset.

Every change shows its effect first and is applied only once confirmed:

  • The tables changed in each environment.
  • Each environment’s period, before and after. An environment that keeps logs for less than the organisation keeps its own period.
  • Storage: keeping rows for longer means the tables hold about that many times as much at the same volume, growing over the new period. Rows already deleted do not come back.
  • Lowering: rows older than the new period are deleted at ClickHouse’s next merge and cannot be recovered.

Environments keep logs for less, never more

Section titled “Environments keep logs for less, never more”

An environment can keep logs for a shorter period than the organisation, never a longer one, as with every other retention setting. Choose a period beside the environment under How long logs are kept, or Follow the organisation to remove it. A period longer than the organisation’s is refused, and if the organisation later shortens its own, the environment follows the shorter one.

Saving sets each environment’s tables’ delete TTL in its own ClickHouse database:

ALTER TABLE otel_traces MODIFY TTL toDateTime(Timestamp) + toIntervalHour(4392)
ALTER TABLE otel_traces_trace_id_ts MODIFY TTL toDateTime(Start) + toIntervalHour(4392)
ALTER TABLE otel_logs MODIFY TTL TimestampTime + toIntervalHour(4392)

Each table’s current TTL is read first and only a table whose period differs is altered, so applying the same period twice does nothing. ClickHouse re-evaluates the rows already written. A table that could not be changed (the database was unreachable, say) is reported on the page. The period is applied again after each ClickHouse migration run, which also covers tables created after it was chosen. An environment nobody chose a period for is left alone, so a TTL an operator set on a self-hosted database is not overwritten. The bundled collector’s exporter creates its tables only if they do not exist, so it never puts the default back.

Legal holds keep copies in their own table, which has no TTL, so held records are not affected by either direction.

Every change is recorded in the audit log as data_retention.log_retention.update (or data_retention.log_retention.override.update for an environment) with the period before and after, in days and hours, each environment’s period and the outcome for each table. Changing it needs the same Change what is stored permission as the rest of this page.

API: PUT /api/data-retention/log-retention with { "preset": "eu_ai_act_logs" }, { "preset": "default" } or { "days": 400 }; PUT /api/data-retention/environments/{id}/log-retention with { "days": 30 } or { "days": null }. Without "confirm": true both answer 409 with a preview of what the change would do.

Runtime protection data sets how long apps’ runtime protection keeps exploit attempts (default 400 days), the packages each app reported (default 90 days) and matched-input snippets (default 30 days, which is also the most). Leave a field empty to keep the default. Each environment can keep these for less, never more.

Each row’s expiry is stamped when it is stored, so a longer period applies to new rows. A shorter period applies at once: after you confirm, Shield360 removes that environment’s rows older than the new period, empties older snippets, and brings forward the expiry of the rows it keeps. The change and what was removed are recorded in the audit log.

APIBody
PUT /api/data-retention/rasp-retention{ "events": 180, "components": null, "snippets": 7 } (days; null for the default). Shortening needs "confirm": true; without it the answer is 409 with the environments affected.
PUT /api/data-retention/environments/{id}/rasp-retentionThe same fields, each no longer than the organisation’s (null follows it).

The findings of apps’ AI security checks (see AI app security) are kept for 400 days by default. Under AI security findings you can choose 30, 90, 180 or 400 days, or 7 years. An environment can keep them for less time than its organisation, never more.

Each finding carries its own expiry, stamped when it arrives from the period in effect for its environment. The table deletes a finding when it expires. Changing the period therefore applies to new findings. A shorter period also queues a purge for each affected environment: findings older than the new period are deleted, and the expiry of the rest is brought forward. The purge runs within about 15 minutes. Each change and each purge is recorded in the audit log.

Long-range charts and counts read hourly and daily rollups that are kept for 400 days, and list pages read a slim trace index kept as long as the spans. They hold counts, durations, cost, tokens and the names of services, span names, environments, models and providers — never prompts, responses or attribute values about a person — and they are built from what retention has already let through. See Long ranges, rollups and background queries.

Changing data retention needs the Change what is stored permission (data_retention.manage). It is an organisation-level permission: it comes from the person’s organisation role only, and a project role cannot grant it. Owners and administrators have it.

Turning on anything that stores more asks for confirmation and lists what will be stored. Every change is recorded in the audit log as data_retention.update (or data_retention.override.update for an environment) with the setting before and after.

While the environment you are viewing stores any content, a Content is being stored marker shows in the header.

Data retention is enforced on the server, wherever telemetry is written, whatever a client sends:

  • OTLP from SDKs and enrolled machines (/api/ingest/otlp, /api/collector/device/otlp): content attributes are removed, from spans, span events, log records and metric data points, before the platform writes them or forwards them to the collector. Content is recognised by key family rather than an exact list, so the OpenTelemetry GenAI conventions (gen_ai.input.messages, gen_ai.prompt.0.content, …), OpenInference (input.value, llm.input_messages.*), OpenLLMetry, the Vercel AI SDK (ai.prompt), vendors’ own exporters (Claude Code’s prompt, tool_parameters) and database spans (db.statement, db.query.text) are all covered. Numbers and flags are never treated as content, so token counts survive.
  • AI log events and errors: the bodies of AI log events and the status messages of AI spans (providers often echo the prompt in an error) are removed too. Span names are operation names and are kept.
  • Guard, prompt-injection and sequence records from devices, SDK apps and the hosted proxy: the masked form a client sends is never kept. The server rebuilds each token from the kind and the fingerprint, drops anything that is not a fingerprint, and converts partial masks from older clients (sk-proj-…Xy4z, •••• 4242) to the type alone. The file or command a guard stopped is kept only with tool content, with secrets tokenised.
  • Telemetry sent straight to ports 4317/4318: the collector’s shield360enrich processor applies the same setting, pushed to it over OpAMP. A collector that has not received the setting stores no content.
  • If something goes wrong: when the setting cannot be read, nothing is stored. When the ingest pipeline’s enrichment step fails, the batch is written without content and with secrets scrubbed; if even that fails, the batch is refused with a retryable error and the sender keeps it.

Credentials: type, fingerprint and vault reference

Section titled “Credentials: type, fingerprint and vault reference”

Even part of a secret helps an attacker. A key’s first and last characters narrow a guess, and a card’s last four digits are what account recovery asks for. So for every credential and every other sensitive value a guard finds, Shield360 keeps only:

  • The type, such as api_key, cloud_credential, token, password, payment_card or email.
  • A keyed fingerprint: HMAC-SHA256 of the type and the normalised value, keyed with your environment’s guard fingerprint key. It is the same every time the same value turns up, on any machine, in the browser extension, the AI proxy or an SDK app. It cannot be reversed or recomputed without the key, which never leaves Shield360 and your enrolled devices. Without the key, no fingerprint is made: an unkeyed hash of a short secret could be brute-forced.
  • A vault reference, when the fingerprint matches a secret stored in the Shield360 vault for the same environment. The finding names the vault secret (its id and name), never its value.
  • Optionally, the number of values found.

What you see is a token: the type and the first 8 hexadecimal characters of the fingerprint, for example [api_key:fp_3f9c2a1b]. Without a fingerprint (no key, or detected sensitive values not stored) the token is the type alone: [api_key]. The full 16-character fingerprint is stored beside it for correlation.

A Data protection event lists, for each value, how often the same value has been seen, by how many people, on how many machines, and when first and last. Show every event with this value filters Data protection to that fingerprint. The same fingerprint from a coding agent, the browser extension, the proxy and an SDK app means the same value: every guard computes it identically, and shared test vectors (sdk/guard-core/testdata/fingerprints.json and tokens.json) hold the implementations to it.

The server fingerprints each vault value under the environment’s key, for every type a guard could report it as, and keeps only the fingerprint-to-reference map in memory. The map is recomputed when the vault changes on that server, and at least every five minutes. Fingerprints are keyed per environment, so a secret found in one environment never matches another environment’s vault.

Vault entries that are references to an external secret store (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, Google Secret Manager) are not matched. Shield360 reads those values only when a connector needs one, and fetching every external secret to fingerprint it would be a new, broad read of your secrets.

Records stored before this change can hold partial masks such as sk-proj-…Xy4z or •••• 4242. They are not purged. They age out on the usual retention schedule. Until then, Data protection, Violations, the SIEM export, CSV exports and the AI analyst rebuild each token from the type and the fingerprint, and replace partial masks in stored text, so partials are not shown or exported.

The collector and the spend connectors match the API keys machines use to the keys vendors bill without keeping any character of a key. Both hash the part of the key the vendor shows (the provider, the key’s public family prefix and its last four characters) with the environment’s guard fingerprint key, and only the hashes are stored: in network observations (key_fingerprints) and in the vendor key list, beside the vendor’s own key id and name. Keys are shown as [key_hint:fp_…] tokens. See Key fingerprints.

Vendor key hints and collector hints stored before this change are not purged. A connector’s key list is replaced at its next sync. Network observations from older collectors age out on their usual schedule (365 days); until then they are neither shown, exported nor used for matching.

The agent data gateway leaves an access record for every governed read: the agent, the purpose, the table, which fields came back clear, masked or denied, the fields filtered on, the row count and the outcome. It never holds a row, a filter value or a result.

Access records are kept for 365 days: long enough to show a full annual audit period of governed reads as evidence, and no longer. A daily job deletes older records in each environment. Legal holds outrank deletion: a record stays while an active hold in the same environment names, as a person, the agent that read or the person who ran a Try it query (by email, the part before the @, or name), and the record falls inside the hold’s period. Once the hold is released, the next run deletes it.

Each environment’s purge is recorded in the audit log as data_gateway.access.purge, by the system, with how many records were deleted, how many were kept for a hold, and the cutoff. The horizon is listed with the others under How long data is kept on the Compliance overview.

Install keys, API keys, hosted proxy keys and agent tokens are stored as hashes. They are listed by their type marker and record id (shield360-… #k3x9q2ab), not by any of their own characters; previews kept before this change were removed. Alert channels show a webhook’s host or a mask, never the end of a URL or key.

When prompts and responses are not stored, Auto Evaluation judges each eligible span as it arrives, from the content that is about to be dropped. The content is held only in the receiving server’s memory, for at most 10 minutes, and let go once it has been scored; only the score, label, verdict and an explanation that does not quote the content are stored. Nothing about this stores content: if evaluation cannot happen in time, the span is recorded as not evaluated rather than its content kept.

Clients send no more than is stored, so content does not travel when it would only be dropped:

  • Coding-agent hooks follow the device policy’s contentCapture, which is metadata_only until the organisation stores prompt or tool content. See Collection policy.
  • The SDKs capture message content only when you ask for it (capture_message_content=True, captureMessageContent: true, CaptureMessageContent: true, or OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true). Even then it is stored only if data retention allows it. See SDK configuration.
  • The device policy carries the effective setting as dataRetention, beside contentCapture. Coding-agent hooks send the tokenized action (coding_agent.action.text) only when dataRetention.storeActions is on; the platform tokenizes it again at ingest whatever a client sent, and builds it from a command or tool arguments it receives when the client sent none.

contentPolicyFor(tenantId) (src/client/src/lib/platform/retention/store.ts) answers what may be stored for an environment, cached for 30 seconds. mayStoreContent(tenantId, family) answers it for one family. Content that is about to be dropped can be observed in flight by registering a tap with setInFlightContentTap (lib/platform/retention/in-flight.ts); the tap receives each piece of content with the tenant and setting before it is dropped, and for traces the same content grouped by span (spans, with ids and the span’s non-content attributes), and must not keep it. In-flight evaluation (lib/platform/evaluation/in-flight/) is the one tap.