Skip to content

Add live roster / presence to A2ABus (announce + roster)#40

Merged
Jacobobber merged 1 commit into
mainfrom
feat/presence-roster
Jul 8, 2026
Merged

Add live roster / presence to A2ABus (announce + roster)#40
Jacobobber merged 1 commit into
mainfrom
feat/presence-roster

Conversation

@Jacobobber

Copy link
Copy Markdown
Owner

Live roster / presence (A2ABus.announce / A2ABus.roster)

Who is on a channel right now. The bus already answers "who may post/read here"; this adds "who is here", gated the same way:

  • announce(principal, channel) heartbeats a live seat and returns the timestamp — call it on a timer to stay live.
  • roster(principal, channel, within=30.0) returns the subjects seen within the window, sorted.

It reuses the auth core, it doesn't bolt on a side channel:

  • announce needs the channel's post scope — asserting presence is a write, so a read-only watcher can't claim a seat it couldn't post from.
  • roster needs the read scope, and denial stays opaque (an unknown channel and a declared-but-unauthorized one fail identically — who-is-here leaks channel existence no more than the messages do).
  • Presence is soft state: a subject that stops heartbeating ages off, and a roster seat is never a grant, so it can't smuggle access past the scope gate.

That lets an orchestrator dispatch only to workers that are actually up, and notice when one goes dark — without a second identity system.

Backed by a per-channel a2a_presence table on the bus's existing connection.

Verification

  • tests/test_presence.py — 9 tests: on-roster after announce, sorted+deduped, announce needs post scope, roster needs read scope, opaque denial, stale ages off, re-announce refreshes the heartbeat, per-channel isolation, decisions audited.
  • Full suite 301 passed (+9). mypy --strict, ruff, catalog freshness all green.

Additive. Advances the "A2A depth: presence/live-roster" roadmap item.

Who is on a channel right now, gated by the same scopes as the messages:

- announce(principal, channel) heartbeats a live seat; roster(channel,
  within=) lists subjects seen in the window, sorted. announce needs the
  channel's post scope (asserting presence is a write, so a read-only
  principal can't claim a seat); roster needs the read scope; denial stays
  opaque (who-is-here doesn't leak channel existence).
- Presence is soft state: a subject that stops heartbeating ages off, and a
  roster seat is never a grant, so it can't smuggle access past the gate. An
  orchestrator can dispatch only to workers that are up and notice a dark one.
- Backed by a per-channel presence table on the existing bus connection.

301 tests (+9), mypy --strict, ruff, catalog freshness all green. v1.7.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Jacobobber Jacobobber merged commit 6dd2f34 into main Jul 8, 2026
12 checks passed
@Jacobobber Jacobobber deleted the feat/presence-roster branch July 8, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant