Data retention
This page lists what a self-hosted instance stores, where, and for how long. Each period comes from the code or the shipped configuration. "No automatic deletion" means that no scheduled job deletes the data. The Deleted by column then names what still removes it. The personal-data map describes workspace purge, disconnection, and verified person erasure. The operator's record of processing gives the legal basis of each category.
The Setting column names the environment variable when the supported stack forwards it from .env.
Otherwise, it names the Spring property, and the value is fixed in the supported stack.
The configuration reference lists the settings that have an environment variable.
The times of the scheduled jobs are in the time zone of the server process.
Provider events
The provider event inbox in PostgreSQL keeps each verified webhook delivery with its payload and headers.
| Event | Retention | Where to change it |
|---|---|---|
| Handled | 3 days after its handling | hephaestus.integration.consumer.replay-window |
| Dead letter | 30 days after its failure | hephaestus.integration.consumer.dead-retention |
| Waiting or in handling | Until it is handled or becomes a dead letter | — |
Workspace deletion removes that workspace's events through the workspace foreign key. Person erasure deletes the events that name the person. Webhook ingestion operations explains the windows. Unassigned installation events have no workspace and remain until the instance is removed.
Replay completion records the requesting account ID and time without deleting the metadata. Person export includes the replay actor's metadata, without subjects or payloads. Person erasure clears the actor reference through the integration store contributor. Account deletion also clears it through the foreign key.
Person erasure does not extend the inbox retention period or create another payload copy. Replay still uses the current adapter admission checks. Restrict database and backup access as for other provider data. See Replay a failed webhook event.
Server disk
Each server keeps these files under /data/git-repos, on its git-cache volume, an emptyDir of server.gitCache.sizeLimit.
| Data | Retention | Setting | Deleted by |
|---|---|---|---|
| Debug transcript of an agent run | 24 hours. It goes earlier when the job's evidence expires. | None | A server sweep every minute |
| Temporary review capture and context copies | Until close succeeds. A failed close retains custody and is retried. Startup removes abandoned copies before admitting work. Failed startup cleanup stops the server. | None | The copy owner closes it. The existing evidence reconciler retries failed closes. Temporary evidence and restarts inventories the paths. |
| Inputs of an attempt, including review gateway archives | Until the result is admitted. Otherwise, 1 hour after the attempt ends. A server restart deletes them. An erased copy is removed by reconciliation once its lease is released and removal succeeds. | None | A server reconcile pass every 30 seconds, and at once after each erasure |
| Checkouts that a server built for another server's capture | As their mirror: they are kept inside it | None | The mirror check every minute |
| Repository mirrors, one for each workspace and repository | As Git repository history and short-lived attempts describes | None | A server check every minute |
PostgreSQL
The jobs that delete review job data run only when AGENT_ENABLED is true.
| Data | Retention | Setting | Deleted by |
|---|---|---|---|
| Capture and output of a review job | 14 days after the job ends. A job that waits for delivery keeps them. | hephaestus.agent.payload-retention | A server job every 6 hours |
| Review job rows, with their script runs | 90 days after the job ends | hephaestus.agent.row-retention | The same job |
| Original review inputs and composition tool trace | Until their one-day expiry. An ended job deletes inputs with no pending delivery or tool trace. | None | The same job |
| Evidence-copy receipts | As long as their review job row. A pending sandbox cleanup identity stays until sandbox deletion succeeds. | hephaestus.agent.row-retention | The same job. Person erasure and workspace purge clear the identities earlier, and each server retries physical removal until it succeeds. |
| Recorded files of a running attempt, for a server that rebuilds its folder | While the attempt runs | None | A server reconcile pass every 30 seconds. Person erasure and workspace purge delete them at once. |
| Evidence of an item event | Replaced by a marker 30 days after it is recorded | hephaestus.item-event-ledger.retain-for | A server job every 15 minutes |
| Item events that nothing references | 30 days after their decision | hephaestus.item-event-ledger.retain-for | The same job |
| Item events that stay pending | They expire after 7 days. The expired row goes 30 days later. | ITEM_EVENT_LEDGER_PENDING_LAPSE_AFTER, hephaestus.item-event-ledger.retain-for | The same job |
| Feedback, its delivery records and readback of its own posted text and rendering | 400 days after delivery. Feedback that was never delivered: 400 days after it was written. Feedback whose post started and never confirmed waits for its cleanup receipt. At least 15 days. | HEPHAESTUS_FEEDBACK_RETENTION (Helm config.feedbackRetention) | A server job at 03:00 every day. Also workspace purge or person erasure |
| Practice results and their problems | 400 days after each is recorded, once no kept feedback cites it and no running review reads it | HEPHAESTUS_FEEDBACK_RETENTION (Helm config.feedbackRetention) | The same job |
| Raw proof of scans | 400 days after the scan ends | HEPHAESTUS_FEEDBACK_RETENTION (Helm config.feedbackRetention) | The same job |
| LLM usage records | 400 days | HEPHAESTUS_LLM_USAGE_RETENTION | A server job at 04:15 every day |
| Conversations with Hephaestus: messages, answers, digests and summaries | 400 days after each entry is written, erased in the two weeks before. At least 15 days. | HEPHAESTUS_FEEDBACK_RETENTION (Helm config.feedbackRetention) | A server job at 03:45 every Sunday. Also workspace purge, person erasure, or Slack disconnection |
| Tool results in conversations that can copy another source | The Slack retention of the workspace, 30 days by default, erased in the two weeks before, while a Slack connection exists. Otherwise, as the conversation. | None | A server job at 03:40 every Sunday. Slack disconnection erases all of them |
| Slack messages and threads | 30 days for each connection, at most 180 days. A message deleted in Slack goes earlier, as Messages deleted or edited in Slack says. | None | A server job at 03:30 every day |
| Outline documents | No age limit. Document bodies above 200 MB for each workspace are evicted. | hephaestus.integration.outline.cache.max-size-mb | Outline sync |
| GitHub and GitLab mirror tables | No age limit while a workspace monitors the repository | None | Repository monitoring |
| Sync job history | The newest 50 runs of each connection | None | Each new sync run |
| Connection audit records | No automatic deletion | None | Workspace purge |
| Authentication events, including sign-ins, user views, IP address, and user agent | 12 months, in monthly partitions | None | Partition maintenance at 00:10 every day |
| Configuration audit events | 365 days | None | A server job at 00:20 every day |
| Issued access tokens | Until they expire. Access tokens last 24 hours. | hephaestus.auth.access-ttl | A server job at 03:30 every day |
| Client sessions | At most 7 days. A revoked session goes 1 day after revocation. | hephaestus.auth.session-max-lifetime | A server job every hour |
| OAuth state values | 7 days | hephaestus.integration.oauth-state.nonce-retention | A server job at 04:00 every day |
| Activity facts waiting for recording | Deleted when recorded. Person erasure deletes matching queued facts. Delivery ends a fact without a write if its workspace, repository or actor is missing. It also ends a fact if monitoring stops or the provider connection is inactive. | None | Activity delivery and the existing retry job |
| Outbox actions, such as notification email | A waiting email expires if it is not sent in time. Security, alert and weekly digest mail expires after 24 hours. Product feedback mail expires after 7 days. The payload goes when the action ends. The ended action goes 30 days later. | None | The outbox executor on every server replica |
| Self-service account exports | The download goes 48 hours after it is ready. | None | A server job every hour |
| Deleted accounts | Removed as soon as the deletion is saved. If removal does not finish, the next hourly sweep tries again. Removal waits while feedback that Hephaestus posted on a provider remains: follow Un-deliver external feedback, then purge. | None | A server job after each deletion, and every hour |
| Product feedback reports, drafts, and choices | No automatic deletion by age | None | Account or person erasure removes account-owned rows. Workspace purge removes reports from that workspace. Both start applicable GitHub cleanup. |
| Product feedback cleanup locators | Until external cleanup completes. After 30 days, incomplete work needs manual action. The locator remains until completion or admin confirmation. | None | Cleanup or confirmed manual completion (Product feedback) |
| Research disclosure records | No automatic deletion. The released file is not stored. | None | Closure of the disclosure removes its mapping |
Messages deleted or edited in Slack
Slack sends an event when a person deletes or edits a message in a monitored channel. For a deletion, Hephaestus erases the stored text and keeps only an empty marker. For an edit, it stores the new text.
The event can fail to arrive: Slack stops after its retries, or the provider stops retrying during a long outage. The nightly Slack sync then finds the change. It reads the history of each active monitored channel and compares it with the stored messages:
- A stored message that a complete read no longer returns is erased, as the event would erase it.
- A stored message that Slack shows as edited gets its new text.
A read that stops early removes nothing: an error, a rate limit, a request budget that ran out, a consent change or a cancelled job.
These limits remain:
- The sync reads only active channels. In a paused or archived channel, a lost deletion stays until the channel is active again. Otherwise, the retention sweep deletes it with its thread.
- The sync compares only messages inside the retention window and from the last 89 days. Slack's free plan hides messages older than 90 days from apps, so their absence proves nothing. A thread that is still active keeps its older messages until the thread ages out.
- Messages stored in the last 10 minutes are compared on a later night.
- The sync compares a reply only when the reply count of its thread differs from the stored replies. It finds the edit of a reply only then.
hephaestus.sync.slack.reconcile-request-budgetlimits the requests for messages older than the last sync. The default is 30 requests for each workspace each night. Each request waitshephaestus.sync.slack.history-request-interval, 60 seconds by default. Thus the check can add 30 minutes to the nightly sync of each workspace. At 15 messages a request, the default budget reads about 450 messages of Slack history a night. The check continues each channel on the day where it stopped, and the channel checked longest ago goes first. A larger workspace thus takes more nights for a full check. One day of a channel that needs more than the whole budget is not compared.- The check keeps up only while one night's budget reads more than one day of the workspace's history. At the defaults, that is about 450 messages and replies a day. In a busier workspace, the oldest messages leave the 89-day window before the check reaches them.
- The default budgets and interval suit the rate limits of commercially distributed apps outside the Slack Marketplace. An app that you create for your own Slack workspace has higher limits. For such an app, raise the budgets and shorten the interval.
Direct messages with Hephaestus are not part of this sync.
Tool results in conversations
A tool result is what Hephaestus read during an answer: a file of its context, a command's output or a repository. It can copy other people's data, such as messages from a monitored Slack channel. Hephaestus does not record which source a tool result copied. Thus each one is erased with the shortest retention of a source that it could have read, which is the Slack retention. A tool result can copy a message that is near the end of its own retention. Thus the words of a channel message can stay for up to twice the Slack retention.
The person's own messages and Hephaestus's answers keep the conversation retention. An answer can repeat what Hephaestus read, including channel messages, and its reasoning can too. The conversation retention applies to them.
An erased entry keeps its place in the conversation, as a deleted message does. Erasing an entry also erases each summary that covers it, so a long conversation is summarized again after a weekly run. A weekly run erases at most 20,000 entries and 20,000 tool results for each workspace, and a larger backlog takes more weeks.
Logs and traces
| Data | Retention | Where to change it |
|---|---|---|
| Container logs | The kubelet's configured size and file-count limits | The node's kubelet configuration |
| Logs shipped to Loki | The retention that you set in Loki | Loki |
| Traces | Off by default. The OTLP recipient keeps them under its own retention. | TRACING_OTLP_ENABLED |
No container writes an HTTP access log.
Log lines carry account, user and provider ids, which identify a person indirectly. They carry no provider login and no email address. Thus, give your log store an age limit, for example 30 days in Loki.
Backups
| Data | Retention | Where to change it |
|---|---|---|
| pgBackRest backups, when enabled in the chart | Two full backups, with their differential backups and WAL. Back up the database gives the schedule. | postgresql.backup.retentionFull in site values |
A plain pg_dump file | No automatic deletion | Delete the file yourself |
A backup contains the whole database, so it also contains data that the instance deleted later. Erasure does not reach a backup. Backup & restore gives the restore procedure.