Skip to content

RFC 0028: Channel-agnostic sender authentication strength - #51

Open
omarshahine wants to merge 4 commits into
mainfrom
omarshahine/rfc-sender-authentication-strength
Open

RFC 0028: Channel-agnostic sender authentication strength#51
omarshahine wants to merge 4 commits into
mainfrom
omarshahine/rfc-sender-authentication-strength

Conversation

@omarshahine

Copy link
Copy Markdown
Contributor

Summary

Generalizes the existing boolean dangerous identifier flag in the ingress kernel into a small ordered authentication strength on the same field, evaluated by the same gate, so an operator can require that a sender's identifier was actually authenticated by the transport before it authorizes anything.

Today "not mutable" conflates "the transport authenticated this identifier" with "the transport handed us a string." A Signal ACI, a Discord snowflake, and an unauthenticated SMTP From: header are all equally non-dangerous. docs/security/THREAT-MODEL-ATLAS.md already tracks this as T-ACCESS-002 (residual risk Medium) with recommendation R-008.

Shape

Three levels, verified > asserted > mutable. mutable is today's dangerous: true. The default policy minimum is asserted, which is exactly today's default behavior, so no current deployment changes. Requiring verified is new and opt-in.

Deliberately not in scope: DKIM/SPF/DMARC parsing in core, capability tiers, a second gate, toolsBySender keying, and any new operator-facing config surface in v1.

Status

status: draft, per the repo lifecycle. issue left blank until accepted. A maintainer-discussion thread still needs to be created.

Notes for reviewers

Two things I want pushback on specifically:

  1. Three levels versus four. An earlier draft split verified into cryptographic and authenticated. I dropped it because the kernel offers one admission threshold and no channel currently documents where it sits, so encoding a cross-channel ordering would assert something the codebase cannot substantiate. Rationale section covers this.
  2. PR 4 is the go/no-go point. The proposed stack puts bundled-channel migration fourth deliberately. If the six channels that declare dangerous: true today cannot each state a defensible strength for their identifiers, the abstraction is wrong and the stack should stop there.

The RFC also surfaces an adjacent gap it does not fix: plugin channels only reach the DM-policy security audit if they implement the optional plugin.security.resolveDmPolicy hook (src/security/audit-channel.ts:333-346), so a plugin channel can silently skip that audit. That likely deserves its own issue.

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal priority bug or improvement with limited blast radius. labels Jul 28, 2026
@clawsweeper

clawsweeper Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed August 15, 2026, 7:08 PM ET / 23:08 UTC.

ClawSweeper review

What this changes

This PR adds RFC 0028, defining ordered sender-identifier authentication strengths for channel ingress authorization while preserving the existing default.

Merge readiness

⚠️ Ready for maintainer review - 6 items remain

Keep open: the RFC is marked accepted and linked to implementation, but the lifecycle-required maintainer-discussion record and authorized merge path remain unresolved.

Priority: P2
Reviewed head: dd03a4d375220835fdca6cd97a4466c08e3b41c7
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) The RFC is substantial and focused, but the required acceptance-process evidence remains a merge blocker.
Proof confidence 🌊 off-meta tidepool Not applicable: This PR changes only an RFC document, so runtime behavior proof is not applicable.
Patch quality 🦐 gold shrimp (3/6) 1 actionable review finding remain.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: This PR changes only an RFC document, so runtime behavior proof is not applicable.
Evidence reviewed 4 items RFC lifecycle requirement: The repository lifecycle requires a maintainer-discussion thread before an RFC is accepted and merged.
Current RFC state: The PR marks the RFC accepted and links an implementation issue, but contains no maintainer-discussion reference.
Prior finding remains applicable: The current head was authored as the accepted/renumbered update; the prior review’s required-discussion finding has not gained a linked discussion record in the supplied context.
Findings 1 actionable finding [P2] Record the required maintainer discussion before merging
Security None None.

How this fits together

The RFC repository records accepted OpenClaw architecture decisions. This document guides OpenClaw channel-ingress implementation, where sender identifiers are evaluated before authorization.

flowchart LR
A[Channel transport] --> B[Sender identifier]
B --> C[Authentication strength]
C --> D[Ingress authorization]
D --> E[Agent access]
F[RFC 0028] --> C
Loading

Decision needed

Question Recommendation
Which authorized RFC approver path should confirm the accepted decision and allow this RFC to merge under the current ruleset? Confirm and merge through the approved path: An authorized RFC approver records or links the required discussion outcome and merges the accepted document.

Why: The repository’s approval rules and maintainer-discussion process cannot be satisfied or reconfigured by an automated document review.

Before merge

  • Add real behavior proof - Not applicable: This PR changes only an RFC document, so runtime behavior proof is not applicable.
  • Record the required maintainer discussion before merging (P2) - The repository lifecycle requires a maintainer-discussion thread before an RFC is accepted and merged. This document now declares status: accepted, but neither the document nor the supplied PR context identifies that required record. Please link or record it through the authorized RFC process before merge.
  • Resolve merge risk (P1) - Merging without the required maintainer-discussion record would bypass the repository’s documented RFC lifecycle.
  • Resolve merge risk (P1) - The stated RFC approver ruleset blockage requires an authorized repository-process decision rather than a contributor-side code change.
  • Complete next step (P2) - An authorized RFC approver must resolve the documented discussion and ruleset path; no bounded automated repair can do that.

Findings

  • [P2] Record the required maintainer discussion before merging — rfcs/0028-channel-agnostic-sender-authentication-strength.md:7-9
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
RFC scope 1 document added, 926 lines The PR is documentation-only but defines a cross-channel authorization contract for later implementation.

Merge-risk options

Maintainer options:

  1. Complete the acceptance record before merge (recommended)
    Link or record the required maintainer discussion and have an authorized RFC approver merge the accepted RFC.
  2. Pause until governance is available
    Keep the PR open without merging if the ruleset currently has no authorized approval route.

Technical review

Best possible solution:

Record the required maintainer discussion and use the authorized RFC approver path to merge the accepted design; retain the linked OpenClaw implementation work as the delivery vehicle.

Do we have a high-confidence way to reproduce the issue?

Not applicable: this PR changes an RFC document; runtime behavior belongs to the linked implementation work.

Is this the best way to solve the issue?

Unclear: the design is a focused path, but merging it should wait for the repository-required maintainer-discussion and authorized acceptance record.

Full review comments:

  • [P2] Record the required maintainer discussion before merging — rfcs/0028-channel-agnostic-sender-authentication-strength.md:7-9
    The repository lifecycle requires a maintainer-discussion thread before an RFC is accepted and merged. This document now declares status: accepted, but neither the document nor the supplied PR context identifies that required record. Please link or record it through the authorized RFC process before merge.
    Confidence: 0.94

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: not found in the target repository.

Codex review notes: model internal, reasoning high; reviewed against af708f0ddb6f.

Labels

Label justifications:

  • P2: This is a bounded architecture decision with an unresolved repository-process requirement.
  • merge-risk: 🚨 compatibility: The RFC defines an ingress authorization contract that subsequent implementation must preserve for existing channel configurations.
  • merge-risk: 🚨 security-boundary: The proposed contract governs which transport-authenticated sender identifiers may authorize agent access.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🌊 off-meta tidepool and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Not applicable: This PR changes only an RFC document, so runtime behavior proof is not applicable.

Evidence

What I checked:

Likely related people:

  • kevinlin-openai: Authored the most recent accepted RFC merged on current main, providing the closest repository-history signal for RFC acceptance and landing. (role: recent RFC lifecycle contributor; confidence: medium; commits: af708f0ddb6f; files: rfcs/0027-openclaw-enterprise.md, README.md)
  • joshavant: The PR discussion explicitly asks this person for the intended approver-flow process after acceptance. (role: requested RFC-process contact; confidence: low; files: rfcs/0028-channel-agnostic-sender-authentication-strength.md)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Record or link the maintainer-discussion outcome.
  • Have an authorized RFC approver resolve the ruleset path and merge the accepted document.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (29 earlier review cycles; latest 8 shown)
  • reviewed 2026-08-01T19:17:06.649Z sha 405c422 :: needs real behavior proof before merge. :: [P1] Assign an unused RFC identifier | [P2] Create the required maintainer discussion before advancing
  • reviewed 2026-08-01T20:35:50.112Z sha 405c422 :: needs real behavior proof before merge. :: [P1] Assign an unused RFC identifier | [P2] Create the required maintainer discussion before advancing
  • reviewed 2026-08-02T09:40:34.734Z sha 405c422 :: needs real behavior proof before merge. :: [P1] Assign an unused RFC identifier | [P2] Create the required maintainer discussion before advancing
  • reviewed 2026-08-02T17:06:13.972Z sha 405c422 :: needs real behavior proof before merge. :: [P1] Assign an unused RFC identifier | [P2] Create the required maintainer discussion before advancing
  • reviewed 2026-08-03T22:21:15.210Z sha 3237319 :: needs real behavior proof before merge. :: [P1] Use an unused RFC identifier | [P2] Create the required maintainer discussion
  • reviewed 2026-08-05T12:22:10.893Z sha 3237319 :: needs real behavior proof before merge. :: [P1] Assign an unused RFC identifier | [P2] Create the required maintainer discussion
  • reviewed 2026-08-14T20:18:44.895Z sha 5857203 :: needs real behavior proof before merge. :: [P1] Use an unused RFC identifier | [P2] Create the required maintainer discussion | [P2] Declare the sender address used by the example | [P3] Refresh the RFC update date
  • reviewed 2026-08-15T17:35:55.935Z sha dd03a4d :: needs real behavior proof before merge. :: [P2] Record the required maintainer discussion before merging

@omarshahine

Copy link
Copy Markdown
Contributor Author

Pushed an amendment that came out of trying to write the email consumer end to end. It found a real hole in the first draft.

Per-message strength could not reach the kernel. Strength was field-declared and resolved through a value-only predicate (runtime-identity.ts:56), and the gate compared only the allowlist entry side (allowlist.ts:100-118). That is fine for every channel that exists today, because whether a Slack senderName is mutable is static. It is insufficient for email: the same address is verified in a DMARC-aligned message and asserted in a spoofed one, and no predicate over the value string can tell those apart. The RFC did not support its own motivating transport.

Two changes:

  • ChannelIngressIdentitySubjectInput gains an optional per-message authentication map, keyed like aliases. Static channels omit it and keep declaring on the field.
  • The gate compares min(entry.authentication, subject.authentication) against the minimum, so a display-name entry stays weak regardless of message authentication, and a well-configured address entry still cannot authorize an unauthenticated message.

Also added a Worked Example: Email Channel section, illustrative only, nothing in core. It exists to prove the primitive is sufficient, on the theory that if the example cannot be written cleanly the primitive is wrong. It covers the three-layer split (per-provider adapter, shared verdict normalization, shared strength mapping, none of it in the SDK), the provenance rules that stop a forged Authentication-Results header from ever being read, the per-identifier mapping built on DMARC proving a domain rather than a mailbox, and an end-to-end outcomes table including the row where an operator who changes nothing sees no behavior change.

Two new unresolved questions, one of which argues against part of my own amendment: no bundled channel needs the per-message map today, so by this RFC's own speculative-surface standard it is fair to ask whether it belongs in v1 or whether email should wait for a later change.

@omarshahine

Copy link
Copy Markdown
Contributor Author

Grounded the worked example in a real implementation, which improved the design in two ways and turned up two live bypasses worth recording here as motivation.

The example is now an Apple Mail channel bound to the agent's own mailbox, rather than a generic email sketch. That removes any question of whose inbox is being authorized, and it is the harder of the two email cases, because Mail.app aggregates accounts sitting behind different trust boundaries (iCloud, Gmail over IMAP, Exchange). A single channel therefore has no single authoritative authserv-id, so boundary config must be per account, not per channel. Any design assuming one boundary per channel breaks on the first multi-account mailbox.

Replaced my localPartBindingDomains sketch with expectedDkimDomains, taken from the working implementation. It states, per sender address, which signing domains are legitimate for it. That is a tighter and simpler assertion than a domain-level "this domain binds local parts" flag, and it supplies the mailbox-level claim DMARC alignment cannot. It also handles relayed mail correctly, where the signing domain legitimately differs from the From domain.

Two provenance rules are now normative, because a production implementation was missing both, in two independent reimplementations, and each omission was independently sufficient for a full bypass:

  1. The authserv-id was parsed and never compared to anything. RFC 8601 only makes a boundary strip instances bearing its own id, so a forged header with any other id survives and was counted.
  2. An SPF pass was accepted without checking smtp.mailfrom alignment, so a pass for the attacker's own envelope domain counted as authentication of the claimed From.

The end-to-end outcomes table now includes both as blocked rows. This is also the strongest evidence I have for the RFC's central claim: the derivation is where the security lives, every channel that reinvents it will get it wrong, and a shared vocabulary plus a shared mapping is how that stops.

Added an unresolved question this raises against my own design: core stays ignorant of how strength was derived, but the failure above suggests core should at least document the provenance obligation next to the type, or ship a conformance checklist for channel authors, even though it cannot enforce it.

omarshahine added a commit to omarshahine/apple-pim that referenced this pull request Jul 28, 2026
* feat(openclaw): map mail auth results onto per-identifier strength

First code for the Apple Mail channel, and the shim boundary for OpenClaw
RFC 0027 (openclaw/rfcs#51). Core does not ship IdentifierAuthentication yet,
so the type is declared locally and the gate is applied plugin-side; when the
kernel lands the primitive this module keeps its logic and only its consumers
change.

Scores each identifier a message yields separately, which is the point of the
RFC being per-identifier rather than one per-message trust score:

- display name is never above mutable
- domain reaches verified on a DKIM pass or an aligned SPF pass
- address reaches verified only when auth-check's own verdict does, which
  requires a signature from that sender's configured expectedDkimDomains
- nothing is promoted when provenance was never established, because the only
  evidence available then is sender-writable

Tests use node:test with Node's native type stripping, so no test-runner
dependency is added to the plugin. Covers the forged-header case, unaligned
SPF, an unexpected signer (domain verified, address not), unenrolled senders,
and the min(entry, subject) composition rule.

Verified against the live mailbox: both sample messages produce
{address: verified, domain: verified, displayName: mutable}, matching the
unit expectations.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxM7Yy33nFJM71NCaBqhyz

* fix(openclaw): require DKIM alignment before verifying the sender domain

domain was promoted on any passing DKIM signature, including one whose header.d
is unrelated to the From domain. That authenticates the signer, not the sender,
so any domain able to sign anything could vouch for any sender. Same defect
alignment fixes for SPF, which this module already required.

Adds the alignment check plus a domainsAlign helper mirroring the one in
MailCLI.swift, so the two sides cannot drift in interpretation.

A test asserted the old behavior and was wrong; it now expects asserted for an
unaligned signature. Added coverage for subdomain alignment and for the case
that actually motivates per-identifier scoring: an aligned but unexpected
signer, where the domain claim stands and the address claim does not.

Found by Greptile on #80.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxM7Yy33nFJM71NCaBqhyz

---------

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@omarshahine
omarshahine marked this pull request as ready for review July 28, 2026 14:19

@omarshahine omarshahine left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read this end to end against the shipped apple-pim POC (#78-#85). The three-level scale, min(entry, subject), and PR 4 as the explicit go/no-go are all stronger than where I'd have landed, and cutting cryptographic because no channel documents its trust chain is the right asymmetry (adding a level later is cheaper than removing one).

One substantive finding, inline: the worked-example code promotes sender domain to verified on an unaligned DKIM pass. That is the defect class the RFC exists to prevent, and the implementation this RFC cites as proof already gets it right - so the fix is to bring the sample up to strength.ts, not the other way around.

Worth noting for the Motivation section's own argument: this is the third instance of the same defect class in this line of work, after the two authserv-id/SPF bypasses fixed in apple-pim#78. "Deriving strength is subtle and easy to get wrong, which is why it belongs in a shared primitive" is a claim the RFC's own sample just demonstrated.


Separate, smaller, and outside this file (apple-pim, not blocking the RFC): MailCLI.swift emits verdict: "suspicious" for two different failures - no Authentication-Results from a trusted authserv-id (a provenance failure, ~L2288) and checks that ran and failed (~L2376). strength.ts gates provenance on verdict !== "unknown", so the provenance failure passes that gate. It fails closed today only because that Swift branch emits an empty checks object; populate checks there later and it silently promotes. Either emit unknown for the no-trusted-AR branch, or gate provenanceEstablished() on an explicit provenance field rather than inferring it from the verdict.

| Identifier | Strength | Justification |
| --- | --- | --- |
| display name | `mutable` | Sender-chosen, always, exactly like every other channel's display name |
| sender domain | `verified` when a trusted-boundary result shows DKIM pass, or aligned SPF pass | The domain-level claim the transport actually proved |

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DKIM pass, or aligned SPF pass - the alignment qualifier needs to apply to both.

Suggest: aligned DKIM pass, or aligned SPF pass.

Same omission in provenance rule 4 (L419, An unaligned pass proves nothing), which is written for SPF only and should cover DKIM too. See the inline note on the sample below for the exploit.

Comment on lines +529 to +530
const dkimOk = verdict.dkim === "pass";
const domain = dkimOk || spfOk ? "verified" : "asserted";

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This promotes the domain identifier on a bare DKIM pass with no alignment between header.d and the From domain.

Attacker owns evil.com and signs validly for it, with From: omar@shahine.com. The boundary stamps a genuine dkim=pass header.d=evil.com, so source is trusted-authserv and provenance rules 1-3 all pass. The sender-domain identifier normalizes from the From address, so shahine.com lands at verified on a signature from a domain with no relationship to it. An operator running allowFrom: ["shahine.com"] with minIdentifierAuthentication: "verified" gets driven - which is precisely the Motivation incident, one level up.

strength.ts in the POC already does this correctly, with the reasoning spelled out:

// A DKIM pass authenticates the *signing* domain (header.d), which is not automatically
// the sender's domain. Promoting on an unaligned signature would let any domain that can
// sign anything vouch for this sender, the same defect alignment fixes for SPF.
const dkimPassed = result.checks?.dkim?.result === "pass";
const dkimAligned =
  dkimPassed && domainsAlign(result.checks?.dkim?.signingDomain, senderDomain(result.sender));
const domain = dkimAligned || spfPassedAligned ? "verified" : "asserted";

Suggested sample fix:

const dkimOk = verdict.dkim === "pass";
const dkimAligned =
  dkimOk && domainsAlign(verdict.dkimSigningDomain, senderDomain(sender.address));
const domain = dkimAligned || spfOk ? "verified" : "asserted";

Note signerExpected on L533 is unaffected - the expectedDkimDomains check is a stronger operator assertion and is exactly what makes the Fastmail relay case (header.d=messagingengine.com for a shahine.com address) work at the address level. This finding only concerns the domain level, where there is no such assertion to lean on.

The acceptance-scenario row at L616 (asserted on address, verified on domain) also reads as a pass under the current sample when the signing domain is unrelated to the From domain, and should be re-checked after the fix.

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jul 29, 2026
@clawsweeper clawsweeper Bot added feature: ✨ showcase ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 30, 2026
omarshahine added a commit to openclaw/openclaw that referenced this pull request Jul 31, 2026
… kernel

Implements PR 2 of openclaw/rfcs#51. Replaces the two-level mutable-identifier
gate with an ordered `IdentifierAuthentication` scale (verified > asserted >
unverified > mutable) plus a per-message subject strength map, so a channel can
say how strongly an identifier names its holder AND how strongly one message
proved the sender holds it. The gate takes min(entry, subject).

Behavior is unchanged for every channel on main: an entry no channel described
resolves to `asserted` (clears the shipped minimum), `dangerous: true` resolves
to `mutable`, and `mutableIdentifierMatching` maps onto the same scale. No new
SDK-exported names (the surface budget enforces this); the types stay internal
to the kernel until the RFC's SDK-surface PR. `subjectAuthentication` is optional
on the public `ChannelIngressState` so external constructors are not broken.

Test fixtures use neutral example addresses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxM7Yy33nFJM71NCaBqhyz
omarshahine added a commit to openclaw/openclaw that referenced this pull request Jul 31, 2026
… kernel

Implements PR 2 of openclaw/rfcs#51. Replaces the two-level mutable-identifier
gate with an ordered `IdentifierAuthentication` scale (verified > asserted >
unverified > mutable) plus a per-message subject strength map, so a channel can
say how strongly an identifier names its holder AND how strongly one message
proved the sender holds it. The gate takes min(entry, subject).

Behavior is unchanged for every channel on main: an entry no channel described
resolves to `asserted` (clears the shipped minimum), `dangerous: true` resolves
to `mutable`, and `mutableIdentifierMatching` maps onto the same scale. No new
SDK-exported names (the surface budget enforces this); the types stay internal
to the kernel until the RFC's SDK-surface PR. `subjectAuthentication` is optional
on the public `ChannelIngressState` so external constructors are not broken.

Test fixtures use neutral example addresses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxM7Yy33nFJM71NCaBqhyz
@omarshahine
omarshahine force-pushed the omarshahine/rfc-sender-authentication-strength branch from 6c450c0 to 1238682 Compare July 31, 2026 02:43
omarshahine added a commit to openclaw/openclaw that referenced this pull request Jul 31, 2026
… kernel

Implements PR 2 of openclaw/rfcs#51. Replaces the two-level mutable-identifier
gate with an ordered `IdentifierAuthentication` scale (verified > asserted >
unverified > mutable) plus a per-message subject strength map, so a channel can
say how strongly an identifier names its holder AND how strongly one message
proved the sender holds it. The gate takes min(entry, subject).

Behavior is unchanged for every channel on main: an undescribed entry resolves
to `asserted` (clears the shipped minimum), `dangerous: true` resolves to
`mutable`, and `mutableIdentifierMatching` maps onto the same scale. No new
SDK-exported names (the surface budget enforces this); the types stay internal
until the RFC's SDK-surface PR. `subjectAuthentication` is optional on the public
`ChannelIngressState` so external constructors are not broken. Test fixtures use
neutral example addresses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxM7Yy33nFJM71NCaBqhyz
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 31, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. and removed merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jul 31, 2026
Draft RFC for a graded IdentifierAuthentication scale
(verified > asserted > unverified > mutable) gating channel ingress,
with per-kind min(entry, subject) matching. PR 2 of the stack in
openclaw/openclaw#116281.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@omarshahine
omarshahine force-pushed the omarshahine/rfc-sender-authentication-strength branch from f6ccdf6 to 405c422 Compare August 1, 2026 06:09
@omarshahine

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Aug 1, 2026

Copy link
Copy Markdown

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Aug 1, 2026
…ion PRs

Explicitly scopes out a new operator-facing config surface in v1 (default
minimum stays asserted; requiring verified is opt-in in code), and links the
kernel (#116281) and SDK (#117121) PRs from the stack table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Supersede the closed implementation PRs with #123782 (kernel, exact match
provenance) and #123793 (SDK + folded-in channel migration), resolve the
per-kind collapse question the refresh eliminated, and add the
not-a-second-identity-system non-goal from the execution-identity
harmonization.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@omarshahine

Copy link
Copy Markdown
Contributor Author

Maintainer decision: accepted.

I accept RFC 0027's four-level contract as proposed:

  • The scale and its normative meanings are final: verified > asserted > unverified > mutableverified = a trusted boundary bound this exact identifier to this sender; asserted = vouched without exact binding (the default); unverified = exact and stable but ownership unproven; mutable = a changeable/shared alias (the old dangerous: true).
  • The gate is min(entry, subject) against a minimum that defaults to asserted, so no current deployment changes behavior. Requiring verified is new and opt-in.
  • The trust boundary is accepted as designed: channels declare identifier strength on their own authority, and every verified declaration must document the transport/session fact behind it. That evidence obligation is enforced by review, and reviewers should hold that bar.
  • IdentifierAuthentication is channel-ingress policy input only. It is not a principal or grant, and it never maps automatically to execution-assurance strength.

Implementation: land #123782 (kernel, exact match provenance) then #123793 (SDK contract + the six bundled-channel migrations). The kernel must not ship alone.

0027 was taken by the enterprise-platform RFC merged 2026-08-01; this RFC
becomes 0028. Status accepted per the maintainer decision on PR #51;
implementation tracked in openclaw/openclaw#124218.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@omarshahine

Copy link
Copy Markdown
Contributor Author

@joshavant question on process: this RFC is accepted (decision recorded above, frontmatter updated, implementation tracked in openclaw/openclaw#124218), but merging is blocked by the RFC merge approvers ruleset created Aug 3, which currently has an empty bypass list — so no one, including admins, can merge RFC PRs. How is the new approver flow meant to work — should specific people be added as bypass actors, or is there another intended path for merging accepted RFCs? Happy to follow whatever the intended process is.

@omarshahine omarshahine changed the title RFC 0027: Channel-agnostic sender authentication strength RFC 0028: Channel-agnostic sender authentication strength Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: ✨ showcase ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants