Add direct messages (open_dm): a private 1:1 channel, auth-native#41
Merged
Conversation
A DM is not a new transport -- it is one A2A channel whose policy is a single scope only the two parties hold, so privacy is the same scope gate as every other channel: - direct_channel(a, b) purely derives an order-independent channel name + pair scope (truncated SHA-256 of the NUL-separated sorted subjects; (a,b)==(b,a), distinct pairs don't collide, self-DM rejected). - open_dm(bus, a, b) declares that channel gated to the pair scope for post and read, and returns the descriptor; the caller mints each party a token carrying descriptor.scope. That scope is the whole access control -- a third party who learns the name is denied opaquely, like any other channel. - Ordinary channel once opened: post/catch_up/ack and the live roster apply and stay gated to the pair. Demo: examples/direct_messages.py. 310 tests (+9), mypy --strict, ruff, catalog freshness green. v1.8.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Direct messages (
open_dm)A private 1:1 channel between two agents — built as an auth-native construct, not a new transport. A DM is one
A2ABuschannel whose policy is a single scope that only the two parties hold, so privacy is the same scope gate that guards every other channel, with no separate access list to keep in sync.direct_channel(a, b)— pure, deterministic derivation of an order-independent channel name and pair scope (truncated SHA-256 over the NUL-separated sorted subjects).direct_channel(a, b) == direct_channel(b, a), distinct pairs don't collide, and a self-DM is rejected.open_dm(bus, a, b)— declares that channel on the bus with a policy requiring the pair scope to both post and read, and returns theDirectChanneldescriptor. Idempotent to re-open (existing messages untouched).descriptor.scope. That scope is the entire access control: a third party who learns the channel name still can't post or read it (it lacks the scope, and the bus denies it opaquely — indistinguishable from an unknown channel).Once opened it's an ordinary channel, so
post/catch_up/ackand the v1.7 live roster all work and stay gated to the pair.Verification
tests/test_dm.py— 9 tests: order-independent derivation, no collision between distinct pairs (incl. the NUL-separator case), self-DM rejected, the two parties can talk, a third party can't read even knowing the name, a third party can't post,open_dmidempotent, different pairs isolated, roster works on a DM.examples/direct_messages.pyruns and prints the exchange + the opaque denial.mypy --strict,ruff, catalog freshness all green.Additive.