Gnome toolbar for Tangled code review.

Sign in to Tangled: notifications, dismissal, token storage and re-prompting #11

The extension only reads public data, so it can't know what you've already seen. That's why "Replies on your PRs" was removed (see the PR that drops it): a reply stayed in the menu until you acted on the PR, or for up to 30 days. There was no way to dismiss it.

Tangled already tracks this. Its notification service, deliberi (notifs.tangled.network, the org.tangled.temp.notification.* lexicons), offers:

  • listNotifications, filterable by category and by read state, with types such as pull_commented, pull_merged, pull_closed and user_mentioned;
  • updateSeen (one notification), markEntityRead (everything about one PR or issue) and markAllRead;
  • getUnreadCount (the bell badge).

Read state syncs with tangled.org both ways. Visiting a PR on the web marks its notifications read; dismissing one in the panel would mark it read on the web. The same feed would also cover two backlog items: mentions, and issue activity.

What's needed #

All of this needs a signed-in user, which is the bulk of the work:

  1. Login. An OAuth flow to the user's PDS from GNOME: a loopback redirect (as atgc does), PKCE, DPoP-bound tokens, and the rpc:org.tangled.temp.notification.*?aud=* scopes. It probably belongs in Settings ("Sign in to Tangled…"), opening the browser.
  2. Service auth. deliberi takes an atproto service-auth JWT per call, minted by the PDS (com.atproto.server.getServiceAuth, with aud set to deliberi's did:web and lxm to the method). Today it answers 403 without one.
  3. Token storage. The refresh token and DPoP key go in the user's keyring (libsecret), never in GSettings or the state dir. Sign-out deletes them, and revokes the session if the PDS supports it.
  4. Refresh and re-prompt.
    • Refresh access tokens before they expire.
    • When refresh fails (revoked, expired, scopes changed), drop to signed-out mode with one clear "Sign in again" notification, not a stream of errors.
    • Survive the PDS being unreachable for a while.
  5. Where the code runs. The helper process, so the shell never holds secrets and a crash doesn't take the panel down. The extension only shows the state ("Signed in as @x", "Sign in again").
  6. Fallback. Signed out, everything works exactly as now. Signed in adds:
    • a "Notifications" section (or the return of replies, driven by unread pull_* notifications on your PRs);
    • dismissal, via updateSeen / markEntityRead;
    • optionally, the unread count as its own pill.

Open questions #

  • Build our own OAuth client, or use atgc? atgc already logs in, stores tokens and refreshes them. If atgc api learned a --host for deliberi (minting service auth the way it does for knot procedures), the helper could call it and skip steps 1–4. Today it can't: through the PDS it gets 501 MethodNotImplemented, and called directly it gets 403 (no token).
  • Stability. The endpoints are temp, so expect renames. Keep the method names in one place, as with mergeCheck.
  • Client metadata. A loopback client needs no hosted metadata. A hosted client_id would show a proper name on the consent screen.

Done when #

  • Signing in and out from Settings works, and survives a restart.
  • A notification read on tangled.org disappears from the panel within one refresh, and one dismissed in the panel is read on tangled.org.
  • Expired or revoked tokens lead to exactly one "Sign in again" prompt, and the extension keeps working signed-out meanwhile.
  • No secrets in GSettings, the state dir, the fetch log or diagnostics.
Sign up or Sign in to add to the discussion.