Spike: notify when a dispenser sells or an order matches - #299
Draft
droplister wants to merge 1 commit into
Draft
Conversation
A spike, behind a setting that is off by default and an optional permission, so
it can be lived with locally before anyone decides whether it belongs.
The permission is optional rather than required on purpose. A new *required*
permission disables the extension on update until every existing user
re-consents, which is an unreasonable toll for a feature most will never switch
on. Declared as optional it costs nothing until someone turns it on, at which
point chrome.permissions.request() asks once -- from the click, since that call
needs a user gesture.
Addresses are mirrored into extension-local storage because settings live in the
keychain and read as defaults while locked, and the wallet auto-locks after five
minutes. A poller that could only read settings would be asleep exactly when
notifications are worth having. That is a real trade and the toggle's copy says
so: addresses are public data, but this writes them somewhere unencrypted they
were not before. Switching it off deletes them.
What keeps this off the node's back, since a background poller is the part that
could quietly become a nuisance:
- One request per poll for every watched address at once. Core's
/v2/addresses/events takes a comma-separated list and filters by event name
server-side, so cost is flat in the number of addresses rather than a call
per address per event type.
- Nothing is requested at all when the feature is off, the permission is
missing, or there are no addresses. For an install that never enables it the
steady-state traffic is zero.
- A failing node backs the poller off exponentially instead of retrying every
tick.
- The address list is capped, so one enormous wallet cannot turn one request
into a huge one.
- A burst is capped at five notifications and a summary. A wallet closed over a
busy weekend should not produce ninety alerts.
The watermark rule is what makes it bearable rather than noisy: on first sight,
adopt the current position silently. Enabling this should not replay last year.
The same applies when the address set changes -- a watermark taken against one
set says nothing about an address newly added to it, so it is dropped and
re-adopted rather than skipping that address's history forever.
Direction matters for a dispense, which names both sides: source is the seller
and destination is the buyer. Reading it the wrong way round would tell a buyer
their dispenser sold something they had in fact just bought.
The deciding is pure and tested without a browser (core/notifications/detect.ts);
the service owns storage, backoff and the Chrome calls.
Claude-Session: https://claude.ai/code/session_01QJS9Bj6uAMoYPATvfr6GZ1
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.
A spike, not a proposal to ship. Off by default, behind an optional permission, so it can be lived with locally before anyone decides whether it belongs.
To try it: load the unpacked build, Settings → Advanced → Notifications. Chrome asks once. Turning it off deletes the stored addresses.
Optional permission, deliberately
A new required permission disables the extension on update until every existing user re-consents — an unreasonable toll for a feature most of them will never switch on. Declared as
optional_permissionsit costs nothing until someone enables it, at which pointchrome.permissions.request()asks once. That call needs a user gesture, so it happens on the toggle click rather than in the poller.I could not test the Chrome Web Store's review treatment of this from here; that is worth confirming before it goes anywhere near a release.
What keeps it off the node's back
This was the part worth getting right — a background poller is exactly the thing that quietly becomes a nuisance.
/v2/addresses/eventstakes a comma-separated list and filters by event name server-side. Cost is flat in address count, not a call per address per event type.The first draft did two calls per address per block and gated on block height. Finding the plural-address events route made it one call regardless of address count, and made the block-height check pointless — it was itself a request, so dropping it removed one.
The rules that make it bearable rather than noisy
Adopt silently on first sight. Enabling this must not replay last year. Same when the address set changes: a watermark taken against one set says nothing about an address newly added to it, so it is dropped and re-adopted rather than skipping that address's history forever.
Direction matters. A dispense names both sides —
sourceis the seller,destinationthe buyer. Reading it the wrong way round would tell a buyer their dispenser sold something they had just bought.The trade I want you to look at
Addresses are mirrored into extension-local storage, unencrypted. Settings live in the keychain and read as defaults while locked, and the wallet auto-locks after five minutes — a poller that could only read settings would be asleep almost exactly when notifications are worth having.
Addresses are public data, so this is not a key leak, but it does write them somewhere they were not before. The toggle's copy says so plainly. If you would rather not, the alternative is notifications that only work while unlocked, which is close to not having them.
Structure
Deciding what is worth announcing is pure and tested without a browser (
core/notifications/detect.ts, 14 tests). The service owns storage, backoff and the Chrome calls (services/notificationService.ts, 14 tests). The layering invariant caught two of my comments mentioningchrome.insidecore/— reworded, since both modules are genuinely runtime-free.Verification
4414 unit tests pass;
tsc,biome,oxlintclean. Build produces"optional_permissions":["notifications"]in the manifest. Not manually exercised in a browser — that is what this branch is for.Known gaps
DISPENSEandORDER_MATCH. The BTC-leg match with a payment deadline is the one with real money at stake and deserves its own treatment.basicis used throughout to avoid depending on any of it.https://claude.ai/code/session_01QJS9Bj6uAMoYPATvfr6GZ1