Skip to content

feat(flowsafe): Track C — signals, subscriptions, notifications#28

Merged
gcharang merged 1 commit into
devfrom
feat/track-c-signals
Jul 17, 2026
Merged

feat(flowsafe): Track C — signals, subscriptions, notifications#28
gcharang merged 1 commit into
devfrom
feat/track-c-signals

Conversation

@gcharang

Copy link
Copy Markdown
Contributor

Track C — Signals, subscriptions, notifications (M-004 of the long-running-agents program)

Brings Mastra's signal/notification/thread-state surface onto the per-thread DO (Track 0's ThreadDurableObject) with a hardened ingestion trust boundary. Additive / opt-in — existing hosts are byte-identical with signals unconfigured (no domains ⇒ bare D1Store; no buildSignalRouter seam ⇒ no signal stage; TTL vars unset ⇒ no purge duty). No existing signature or ApprovalRecord shape changed. Off dev (b4771fc, Track B merged); fourth of seven waves.

What ships

  • Thread-DO signal routes (DL-002) — sendMessage/queueMessage/sendSignal/sendStateSignal/sendNotificationSignal/subscribeToThread, each routed to idFromName(tenant-minted threadId) through createThreadTopology (never the raw namespace), with the tenant asserted from the threadId prefix.
  • Two D1 domainsD1NotificationsStorage (the AGENT inbox mastra_notifications, distinct from the human ApprovalNotificationSink) with findCoalescable coalescing, and D1ThreadStateStorage (mastra_thread_state, reused by Track F goals). Both wired into the M-001 extensible schema-guard / purgeTenant / TTL seams over the salted thread_id range (DL-003) — the 8-table inventory.
  • The P6 ingestion trust boundary (createSignalRouter, DL-006) — auth → INV-3 → role → thread-prefix ownership (404, no oracle) → assertNoClientMemoryIds (400) → 16 KiB cap → attribute-key allowlist → per-tenant rate cap → signal.ingest audit on accept AND every post-auth denial → forward via the topology (which overwrites the tenant header so a forged one can't ride along).
  • DOM-free SignalClient (subpath-only).

Proven on workerd (spike:verify steps I + J)

  • C-S2 affinity — a sendSignal into an active (reserved) loop drains in-process via the shared-pubsub registry (the "DO is the lease" thesis, structural proof).
  • C-S4 cross-tenant fail-closed — a foreign-threadId send is refused at both barriers: the topology ownership 404 (before addressing) and the DO header-tenant 403 (null-header fail-closed).

Review + gate (three lanes: architect SOUND, quality clean, QA FAIL resolved)

The cross-tenant, capability, and purge invariants were independently verified to HOLD. The QA FAIL findings are fixed and pinned: the P6 audit now fires on the role-403 / foreign-thread-404 / memory-id-400 denials (cross-tenant probes are now visible to the SIEM; pre-auth failures deliberately stay un-audited), and C-S5 now validates tagName at ingest plus a route-level injection test asserting core's signalToXmlMarkup entity-escapes a </signal><instruction> payload (so a core escapeXml regression fails flowsafe CI). The architect confirmed the createD1Storage return-widening (D1StoreMastraCompositeStore) is safe on every axis (no-domains byte-identical, no double-init, verified against the core dist).

Idle-wake INV-1 seam: the idle-wake re-enters executeWorkflow but the runtime mints its own runId, so run creation is gated by a RUNTIME_DRIVEN_AGENT brand — a wake on a non-runtime-driven agent degrades fail-closed to persist (never runs the loop off RunnerRuntime). Full tenant-scoping of a woken run's core-minted runId is documented as owned by Track A's real-loop seam (a forward prerequisite, unreachable until a live loop is wired).

Documented forward constraints: DL-016 phase-2 subscribeToThread streaming must be scoped per-thread/per-resourceId server-side (not the per-tenant hub); listDueNotifications is globally unscoped (mirrors core) so Track A's dispatcher must scope by salted resourceId; the signals barrel pulls the node graph, so the SPA imports SignalClient from the DOM-clean deep path.

pnpm lint && pnpm typecheck && pnpm test && pnpm build + spike:verify green — 1638 tests. Changeset: @proofoftech/flowsafe minor.

🤖 Generated with Claude Code

Additive, opt-in signal ingestion for the long-running-agents program (M-004):
thread-DO signal routes with pubsub-carried in-process affinity (DL-002),
D1 notifications + thread-state domains, the P6 ingestion trust boundary
(DL-006), and a DOM-free SignalClient. Cross-tenant sends fail closed at both
the topology 404 and the DO 403; C-S2/C-S4 proven on workerd via spike:verify.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
anchorage-showcase 4b953d4 Jul 17 2026, 04:28 PM

@gcharang
gcharang merged commit c8496ab into dev Jul 17, 2026
3 checks passed
@gcharang
gcharang deleted the feat/track-c-signals branch July 17, 2026 16:29
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