Address Codex review feedback on #62942 about preserving paired senders
during the trusted-policy migration.
After tracing the actual policy enforcement in
extensions/matrix/src/matrix/monitor/handler.ts (the dmPolicy switch
around line 606) and the access state resolver in
extensions/matrix/src/matrix/monitor/access-state.ts (which merges
allowFrom and storeAllowFrom into a single effectiveAllowFrom), I found
that 'pairing' and 'allowlist' are equivalent for ACCEPTING already-
trusted senders — both consult the pairing store and the explicit
allowFrom list via the same directAllowMatch path.
The only semantic difference is what happens to an unknown sender:
- 'pairing' sends a pairing request reply (operator can approve)
- 'allowlist' drops silently (no path for new senders to gain access)
We don't know exactly what behavior the legacy 'trusted' policy had for
unknown senders, but 'pairing' is a strict superset of 'allowlist' for
accepting existing senders, AND it preserves the ability to onboard new
ones — which is the most likely intent of an operator who chose
'trusted' in the first place.
Mapping 'trusted' unconditionally to 'pairing' (instead of branching on
allowFrom presence) also moots the whitespace edge case from the prior
fixup commit, since allowFrom is now always preserved as-is.
Updates the existing 'with allowFrom' and 'per-account' tests to expect
'pairing' instead of 'allowlist'. Tests added in the prior fixup commit
for the empty / whitespace-only allowFrom cases continue to assert
'pairing' as well, so they remain valid as additional documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address Codex review feedback on #62942: migrateLegacyTrustedDmPolicy
counted raw string length to decide between 'allowlist' and 'pairing',
so an allowFrom list of whitespace-only entries (e.g. [' ']) would
migrate to 'allowlist'. Downstream Matrix allowlist normalization later
trims and drops those entries, leaving an effectively empty allowlist
while the policy stays 'allowlist' — silently blocking all DMs instead
of falling through to the intended 'pairing' default.
Trim before the length check so whitespace-only entries are treated as
empty and the safer 'pairing' fallback is used.
Adds a regression test for the whitespace-only edge case.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The "trusted" value for channels.matrix.dm.policy (and the per-account
channels.matrix.accounts.<id>.dm.policy variant) is no longer accepted
by the schema as of 2026.4.7 — only "pairing" | "allowlist" | "open" |
"disabled" are valid. Configs that previously held "trusted" cause the
gateway to hard-fail at startup with:
channels.matrix.dm.policy: Invalid option: expected one of
"pairing"|"allowlist"|"open"|"disabled"
The error message tells the operator to run "openclaw doctor --fix",
but doctor had no migration for this case and only re-printed the same
validation error.
This adds a doctor compatibility migration in
extensions/matrix/src/doctor-contract.ts that rewrites legacy "trusted"
to a safe equivalent:
- dm.policy "trusted" + non-empty allowFrom -> "allowlist"
(preserves the explicit allowlist semantics that "trusted" had)
- dm.policy "trusted" + no allowFrom -> "pairing"
(defaults to the secure mode rather than silently widening access)
The same migration is applied to per-account
channels.matrix.accounts.<id>.dm.policy.
Adds two legacyConfigRules so the issue is surfaced under the existing
"Legacy config keys detected" channel before --fix is applied, plus
four unit tests covering top-level allowlist, top-level pairing,
per-account, and a no-op case for modern policy values.
Refs: #62931
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>