What is stored
A turn stores identifiers, timestamps, counts, and a small set of fields: agent, release, environment, session id, surface, prompt version, models, token counts, cost, output length, and whether the output parsed as JSON. A tool call stores the tool name, its status, an error type, and the argument hash described below. A span keeps its name, kind, timing, status, and an error type of up to 128 characters taken fromerror.type, exception.type, or the status message.
Spans keep an allowlist of attributes: the ones listed in the attribute reference, plus the attributes that agents name in Release attribute and Surface attribute. For Langfuse, every trace and observation metadata key, the trace tags, and the user id (langfuse.user.id) are stored as well. Content attributes are dropped even when named in an agent setting:
gen_ai.input.messages,gen_ai.output.messagesgen_ai.tool.call.arguments,gen_ai.tool.call.result- OpenInference
input.value,output.value,llm.input_messages,llm.output_messages,llm.prompts,llm.completions - Langfuse observation input and output
Tool arguments
Retry and loop detection compare tool arguments without keeping them.args_hash is an HMAC-SHA256 of the arguments, of their canonical JSON when they parse as JSON and of the raw string otherwise, under a key derived from APGAR_SECRET_KEY. Without the key, a hash cannot be checked against guessed arguments. Hashes are only compared within one turn.
Secrets
- Connection secrets (Langfuse keys, Slack webhook URLs, webhook secrets) are encrypted with AES-256-GCM before they are written, under a key derived from
APGAR_SECRET_KEYwith HKDF. The API returns Langfuse secret keys and webhook secrets masked. Slack webhook URLs are returned in full to signed in users. - Ingest tokens are shown once. Apgar stores a SHA-256 hash and a short prefix to identify them.
- The admin token is read from the environment and never stored.
Keys
APGAR_SECRET_KEY is the root secret. Losing it makes stored connection secrets unreadable. To rotate it with Docker Compose:
- In
.env, setAPGAR_SECRET_KEY_PREVIOUSto the current key andAPGAR_SECRET_KEYto a new one. - Run
docker compose up -d, which restarts the API and the worker. The worker decrypts every connection with the previous key and encrypts it again with the new one. - Remove
APGAR_SECRET_KEY_PREVIOUSfrom.envand rundocker compose up -dagain.
existingSecret, pass the chart version you run now, from helm list -n apgar, so that the rotation does not also upgrade Apgar:
-
Copy the current key into a second Secret, and put a new key into the chart’s Secret:
-
Upgrade with the second Secret as an extra variable source. This restarts the API and the worker, and the worker logs
re-encrypted N connections: -
Remove the previous key:
envFrom, add the Secret to your list and remove it from there instead. Without existingSecret, the chart’s Secret is apgar-secrets: read the current key from it in step 1, and set the new key with --set secret.secretKey=... in step 2 instead of patching the Secret.
Rotation signs out every UI session. Tool argument hashes written under the old key do not match new ones, which matters only for a turn that is in flight during the restart.
Outbound connections
The worker connects only to what you configured:- Langfuse, at the host of each Langfuse connection.
- Slack, at the webhook URL of each Slack connection.
- The URL of each webhook connection.
- PostHog, at the telemetry endpoint, once a day, unless telemetry is off.
APGAR_WORKER_METRICS_URL is set, to read the worker metrics for its own /metrics endpoint.
Deletion
- Delete agent hides the agent and revokes its ingest tokens at once. The retention job, which runs at worker startup and then daily, deletes its spans, turns, tool calls, releases, evaluations, and notifications.
apgar demo purgedeletes the rows of the demo agents the same way and hides the agents.- Retention deletes trace data after
trace_data_days, and evaluations and notifications afterverdict_history_days. Release rows and release events are kept. See Retention.