Summary
Add and tighten privacy.redactPII so OpenClaw reduces human-identifying system-injected inbound metadata before that metadata is injected into the LLM.
This PR is intentionally limited to metadata added by OpenClaw itself. It does not rewrite user-authored message body content.
Scope
In scope:
- trusted inbound metadata:
chat_id
- user-role inbound metadata blocks:
- current sender identity fields
InboundHistory.sender
ReplyToSender
ForwardedFrom
- human-readable group/thread labels
- forwarded profile-like labels
Out of scope:
- user-authored message bodies
- quoted / forwarded body text
- auth/routing internals that still use raw values before the LLM call
- logs / exports / backups
- unrelated prompt privacy surfaces such as
Authorized Senders
Why
OpenClaw previously exposed raw or human-identifying inbound metadata to the LLM prompt context.
Even with initial identifier redaction, the prompt could still contain human-readable participant names, group/thread labels, and forwarded profile labels. That still exposed who a person is or what a conversation is called.
This PR narrows that gap while preserving the minimal speaker continuity and structural context the model still needs.
Behavior
| Field / path |
Behavior when privacy.redactPII=true |
chat_id |
hash ID portion, preserve channel prefix |
SenderE164 |
strip |
SenderId |
pseudonymize as user_<hash> |
conversationInfo.sender |
pseudonymize |
| sender metadata label |
pseudonymize |
InboundHistory[*].sender |
pseudonymize |
ReplyToSender |
pseudonymize |
ForwardedFrom |
pseudonymize |
ConversationLabel |
strip |
GroupSubject / GroupChannel / GroupSpace |
strip |
ThreadLabel |
strip |
ForwardedFromUsername / ForwardedFromTitle / ForwardedFromSignature |
strip |
Stable pseudonymous labels are used where speaker continuity still matters:
- identifier-backed participants →
user_<hash>
- name-only participants →
participant_<hash>
Non-goals
This PR does not:
- rewrite user-authored body text
- sanitize arbitrary identifiers inside message body content
- change auth/routing behavior that still relies on raw identifiers internally
- implement full anonymization
- normalize every upstream identity source into a single cross-source pseudonym
Design choices
- Pseudonymization, not deletion, for participant identity where speaker continuity still matters
- Strip human-readable labels where they are not needed for model behavior
- Keep structural/behavioral context like
message_id, reply_to_id, topic_id, was_mentioned, and history_count
- Preserve field semantics:
e164 is removed rather than replaced with a pseudonym
Known trade-offs
- This is pseudonymization, not strong anonymization
- Deterministic hashes intentionally preserve speaker continuity
- Different upstream raw identifiers can still map to different pseudonyms
- User-authored body text may still mention identifying information; that is outside this PR boundary
Changes
src/config/zod-schema.ts
src/auto-reply/reply/get-reply-run.ts
- thread
redactPII into inbound metadata builders
src/auto-reply/reply/inbound-meta.ts
- hash
chat_id
- strip
SenderE164
- pseudonymize sender labels across current/history/reply/forward metadata
- strip human-readable group/thread labels under
redactPII
- strip forwarded profile-like fields under
redactPII
src/auto-reply/reply/inbound-meta.test.ts
- add focused coverage for all in-scope metadata path classes
Validation
npx vitest run src/auto-reply/reply/inbound-meta.test.ts
- 40/40 tests passing
Closes #47958
Summary
Add and tighten
privacy.redactPIIso OpenClaw reduces human-identifying system-injected inbound metadata before that metadata is injected into the LLM.This PR is intentionally limited to metadata added by OpenClaw itself. It does not rewrite user-authored message body content.
Scope
In scope:
chat_idInboundHistory.senderReplyToSenderForwardedFromOut of scope:
Authorized SendersWhy
OpenClaw previously exposed raw or human-identifying inbound metadata to the LLM prompt context.
Even with initial identifier redaction, the prompt could still contain human-readable participant names, group/thread labels, and forwarded profile labels. That still exposed who a person is or what a conversation is called.
This PR narrows that gap while preserving the minimal speaker continuity and structural context the model still needs.
Behavior
privacy.redactPII=truechat_idSenderE164SenderIduser_<hash>conversationInfo.senderInboundHistory[*].senderReplyToSenderForwardedFromConversationLabelGroupSubject/GroupChannel/GroupSpaceThreadLabelForwardedFromUsername/ForwardedFromTitle/ForwardedFromSignatureStable pseudonymous labels are used where speaker continuity still matters:
user_<hash>participant_<hash>Non-goals
This PR does not:
Design choices
message_id,reply_to_id,topic_id,was_mentioned, andhistory_counte164is removed rather than replaced with a pseudonymKnown trade-offs
Changes
src/config/zod-schema.tsprivacy.redactPIIsrc/auto-reply/reply/get-reply-run.tsredactPIIinto inbound metadata builderssrc/auto-reply/reply/inbound-meta.tschat_idSenderE164redactPIIredactPIIsrc/auto-reply/reply/inbound-meta.test.tsValidation
npx vitest run src/auto-reply/reply/inbound-meta.test.tsCloses #47958