Skip to content

feat: standardized trigger for DWN endpoint changes on connected identities#1284

Draft
poindex-bot wants to merge 2 commits into
enboxorg:mainfrom
poindex-bot:feat/dwn-endpoint-change-trigger
Draft

feat: standardized trigger for DWN endpoint changes on connected identities#1284
poindex-bot wants to merge 2 commits into
enboxorg:mainfrom
poindex-bot:feat/dwn-endpoint-change-trigger

Conversation

@poindex-bot

Copy link
Copy Markdown
Contributor

Summary

Adds a standardized, near-real-time trigger so a connected app learns that a DID's DWN service endpoints changed (a DWN added/removed) without a disconnect/reconnect.

Why it was needed

Endpoints live only in the DID document and are re-derived on every operation by re-resolving the connected DID. That resolution freezes in the AgentDidResolverCache (15 min TTL, longer for store-managed DIDs), the connect grant carries no endpoints, and the session stores none — so a wallet-side change was invisible to a live session until the cache turned over or the app reconnected. There was no event, push, or session-update path.

What this adds

Layer 0 — refresh primitive + event

  • AgentDidApi.refreshResolution(did) — evict the cached resolution and re-resolve.
  • SyncEngineLevel.invalidateSyncTargets() — drop the memoized sync-target endpoints (also on setDwnEndpoints, closing the ~30 s stale window).
  • AgentIdentityApi.refreshDwnEndpoints(did) — composes both.
  • AuthManager.refreshConnection() / Enbox.refreshConnection() — re-resolve the connected DID and emit the new connection-endpoints-changed event with the added/removed delta.

Layer 1 — service-config announcement (the trigger)

  • New published serviceConfig DWN protocol (owner-write, anyone read). setDwnEndpoints() publishes a "poke" record by default (announce: false opts out; publishServiceConfig() triggers it standalone).
  • Apps opt in by adding serviceConfigProtocolRequest() (read-only) to their connect protocols and calling AuthManager.startServiceConfigWatch(). Requesting the protocol yields the standard Messages.Read grant, so the record replicates via sync; the watch subscribes locally and calls refreshConnection() on change. Because apps go through the agent abstraction, the refresh happens in place — the cached Enbox instance keeps working against the new endpoints.

No changes to the security-sensitive connect approval ceremony. The record is a poke only — the DID document stays authoritative (the dapp re-resolves and adopts that), so there is no new trust surface. Connect never issues Records.Subscribe grants, so observers subscribe as the delegate and read the public record.

Opt-in is conservative: the dapp-side watch and the connect-request protocol are explicit (like startConnectionMonitor). Auto-appending the protocol in connect() is a deliberate follow-up.

Known edge case

Full migration to a brand-new DWN the app never synced can't deliver the poke over the old channel — covered by the resolver-TTL fallback; a migrating wallet should dual-write the announcement to old + new DWNs during the window.

Test plan

  • bun run lint — clean across the workspace
  • bun run build — 17/17 packages
  • New packages/agent/tests/service-config.spec.ts (4) — refreshResolution evicts+re-resolves; publishServiceConfig installs protocol + writes a readable record; updates the single record in place; setDwnEndpoints({ announce: false }) publishes nothing
  • New packages/auth/tests/connect-endpoints.spec.ts (6) — refreshConnection add/remove/unchanged deltas + event, resolution-failure baseline, session guards
  • packages/agent identity-api.spec (28) and did-api.spec (64) — no regressions
  • packages/auth full test:node — 576 pass, 0 fail
  • Full @enbox/agent test:node in CI (includes e2e-* specs needing a :3000 server)

Rollout notes

  • Changeset: @enbox/agent, @enbox/auth, @enbox/api at patch (internal deps auto-patch).
  • Behavior change: setDwnEndpoints() now publishes a service-config record by default and invalidates the local sync-targets cache. Publishing is best-effort (never fails the endpoint update); pass announce: false for the old pure-update behavior.

…tities

Connected apps previously stayed unaware of a DID's DWN service-endpoint
add/remove until a full disconnect/reconnect, because endpoints are only
re-derived by re-resolving the DID and that resolution freezes in the
AgentDidResolverCache. This adds a prompt, standardized trigger:

- refresh primitive + connection-endpoints-changed event (agent/auth/api)
- published serviceConfig announcement protocol + wallet publish + dapp watch

The DID document remains authoritative; the announcement only prompts a
fresh resolution.
…+ simplify

Rebased onto the merged enboxorg#1283 sync work, plus behavior-preserving cleanups.

sync-engine-level.ts:
- invalidateSyncTargets() now delegates to the canonical invalidateSyncTargetsCache()
  that enboxorg#1283 introduced (and which also resets the new _syncTargetsLastResolutionComplete
  flag), instead of inlining a stale copy of the invalidation triple. enboxorg#1283 consolidated
  that triple specifically to stop it drifting across call sites; this avoids reintroducing
  the drift.

service-config.ts (behavior-preserving):
- publishServiceConfig: build messageParams with a conditional spread instead of an
  explicitly-typed object plus post-write mutation.
- fanOutServiceConfigRecord: reuse the endpoints the caller already resolved rather than
  resolving getRemoteDwnEndpointUrls a second time; an empty set is the no-op path, which
  drops the redundant resolution and its try/catch.
@poindex-bot
poindex-bot force-pushed the feat/dwn-endpoint-change-trigger branch from 9cf35ae to d38e2b1 Compare July 16, 2026 19:10
@LiranCohen
LiranCohen marked this pull request as draft July 16, 2026 19:35
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