feat(push): PR-1b — Rust-owned registration against the push server - #467
Conversation
api/push.rs: the PushServer trait with its reqwest implementation (10 s timeout, lowercase hex, mostro_pubkey, Retry-After, 403 as a per-node refusal), reconcile_core over the persisted state, reconcile_push as single-flight with coalesced triggers (trade row writes, status emissions, claim upserts, restore, a 6 h timer, resync), the bridge surface (set_push_token, clear_push_token, set_push_enabled, get_push_status, on_push_status_changed), and the JSON mirror the OS-scheduled refresh will read. delete_identity unregisters first; selecting a node clears its refusal. Maker and restored rows now carry the issuing node in creator_pubkey, and the kind-14 filter's authors include the issuing node of every live trade row. Host: https://mostro-push-server.fly.dev, PUSH_SERVER_URL at build time. Under cargo test every production path talks to an unreachable stub. docs/PUSH_NOTIFICATIONS.md Phase 1, T1.2.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d152ca496f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The wanted set no longer consults the in-memory dispute record: a live dispute's row reads Dispute, which is not terminal, and an admin outcome ends both, so a record never marked resolved cannot keep a finished trade's key registered. Clearing a node refusal runs under the reconcile lock and touches the refusals only, never the registration map a reconcile in flight owns.
Summary
Phase 1, task T1.2 of
docs/PUSH_NOTIFICATIONS.md(§7.1, §8.2). Stacked on PR-1a (#466), whose pure rules this wires to the trade rows, the settings store and the push server athttps://mostro-push-server.fly.dev.Nothing in Dart calls this yet (PR-1c hands the token over), so the app's behavior is unchanged: without a token nothing is registered and nothing is asked of the server.
PushServertrait with thereqwestimplementation: 10 s timeout on every platform, lowercase hex,mostro_pubkeyon every register,Retry-Afteron429,403recorded as a refusal of that node. Only the JSON bodies of §3.1 are sent: no auth, no request id, no sender.reconcile_coreapplies the rules to the persisted state against a server, and is what the tests exercise with a fake.reconcile_pushis single-flight with a dirty flag, so a request that arrives mid-pass runs it again rather than in parallel.persist_trade_row, every trade update emission (so wipes and terminal outcomes count), every claim upsert, the restore, a 6 h timer started when the pool comes up, andresync()on resume.set_push_token,clear_push_token,set_push_enabled(off unregisters everything the persisted map knows; on clears every node refusal),get_push_status,on_push_status_changed. TypesPushPlatform { Android, Ios, Web }andPushStatus.delete_identityunregisters first. Selecting a node clears that node's refusal.create_orderand the restore placeholder now seedcreator_pubkeywith the active node; an empty legacy value reads as the active node.authorsnow include the issuing node of every live trade row, so a registered key is always one the filter can hear after a node switch.push_mirror.jsonnext to the database, native only) with the token and the accepted registrations, for the OS-scheduled refresh of T1.5. Removed when there is nothing to refresh.cargo testevery production path talks to an unreachable stub: the trade-row tests trigger reconciles as a side effect and none may reach the real server.https://mostro-push-server.fly.dev, overridable at build time withPUSH_SERVER_URL.Test plan
api/push.rsagainst a scripted fake server: keys registered under their node and recorded; a second pass over an unchanged world makes no request; disabled unregisters everything the state knows; no token means nothing asked; a failure backs off and retries past it;429waits outRetry-After;403refuses the node, skips its keys, and clears on "try again"; a rotated token re-registers every key and a grace ends in unregister; status classification; the status report; the URL default and override.cargo test(659),cargo clippy --all-targets(no new warnings),cargo check --target wasm32-unknown-unknown,./scripts/frb-generate.sh(bindings committed),flutter analyze, all in the pre-commit hook.Manual testing
./scripts/frb-generate.sh, thenflutter run -d linux. Trade normally. Expect log lines[push] reconcile: wanted=N registered=0 +0 -0 failed=0 refused=0: no token, so nothing is sent.push_api.setPushToken(token: 'test-token', platform: PushPlatform.android)while holding one open trade. Expect[push] reconcile: … +1 …and apush_mirror.jsonnext tomostro.dbnaming that trade's pubkey.push_registrationsfrom the settings table and confirm one entry withregistered_at,token_hash(64 hex, not the token) andmostro_pubkeyequal to the active node.push_api.setPushEnabled(enabled: false). Expect… -1 …and the mirror file gone. Call it withtrueand expect+1again.push_api.reconcilePush(). Expectfailed=1, then anext_attempt_at60 s ahead in the registration, and no further attempt until then.unwanted_since, and to stay registered for 24 h before an unregister.