Repository navigation
chore: adopt Paykit v0.1.1-rc6 - #131
Draft
ben-kaufman wants to merge 5 commits into
Draft
ben-kaufman wants to merge 5 commits into
ben-kaufman wants to merge 5 commits into
Conversation
Move paykit-lib and paykit-sdk from the crates.io 0.1.0 release to the paykit-rs v0.1.1-rc6 Git tag (commit 9919198). Paykit release candidates are only published as Git tags. The lockfile change is limited to the two Paykit packages and their new libc and pubky-common edges. The TLS crates stay at webpki-roots 1.0.11, webpki-root-certs 1.0.11 and rustls-webpki 0.103.16. The pubky and pubky-testnet pins stay at 0.16.2. The only API change the server meets is the two new PaymentRequestRecord record time fields in test fixtures. Point the local Docker source substitution and the local demo build at the tag and its commit, and document that this Paykit release stores hosted state in a format earlier Paykit releases cannot read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paykit before v0.1.1-rc6 marked a payee's Payment Request invalid when it stored an otherwise-valid Acceptance at or after the proposal expiry, such as an in-time Acceptance the server fetched late. v0.1.1-rc6 records that Acceptance and derives accepted, or proof_submitted once a Payment Proof arrived, from the same stored events. A lifecycle row stored as invalid_conflict for that reason then meets a projection with the same source cursors, a new state and a new state event. The equal-cursor guard refused it with a conflict on every projection, which failed status, drain and rotation reads for every invoice of that Creator. Carry the SDK's acceptance_recorded_at in the lifecycle projection and let invalid_conflict take accepted or proof_submitted at equal cursors only when the Acceptance was recorded at or after the proposal expiry. Every other equal-cursor change still conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paykit v0.1.1-rc6 adds opt-in SSE-assisted private intake for Rust runtimes. The server keeps polling. The intake supervisor holds one lease over the whole identity-wide state, polling every established Linked Peer of the identity fails while it is live, and only its owner can drain what it buffers. Bitkit shares each Creator's state and its Swift and Kotlin bindings can only poll, so a server supervisor would stop the Creator's wallet from receiving. Status reads also need a receive made for the answer, not the supervisor's last audit. Name the PrivateIntakeModeConflict error in SDK failure labels, and pin that a receive refused for that reason fails the transport pass and is never read as an empty inbox. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A payee must budget for a proof profile before offering it. Measure the largest Payment Proof a reader's wallet can send for the server's USDT method with paykit_sdk::payment_proof_event_len from v0.1.1-rc6, using the longest App ID, an Allowance ID, the ERC-20 hash and signature lengths and a 78-digit receipt log index. It is 847 bytes with the server's UUID Payment References, under the 1000-byte limit. The same proof with a 256-character reference does not fit, which the second test pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paykit v0.1.1-rc2 added claim_payment_request_for_preparation, which rejects a blocked counterparty before App Registry access and rechecks that in the claim transaction. Paykit's guidance is to use it before preparing payment and to compare the claimed proposal with the approved terms. The local SDK payment example and the reader wallet in the Allowance end-to-end test now use it and check that they claimed the proposal whose terms they evaluated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Caution
Do not merge or deploy this until a Bitkit release with Paykit rc6 is out (synonymdev/bitkit-android#1803 and synonymdev/bitkit-ios#1237) and Bitkit users have updated.
rc6 writes hosted SDK state in a newer format. Once this server writes a Creator's state, Bitkit builds on Paykit rc5 or older, including Bitkit 3.1.0, can no longer load it, and there is no downgrade.
Summary
Move
paykit-libandpaykit-sdkfrom the crates.io=0.1.0release to the paykit-rsv0.1.1-rc6tag (commit9919198a8a810652945f7366defacae8c3135632), and adopt what the server should use from 0.1.0..rc6. Paykit release candidates are only published as Git tags, so the pin follows thelocks-corepattern.Cargo.tomluses the tag. TheCargo.lockdiff is limited to the two Paykit packages and their newlibcandpubky-commonedges.pubkyandpubky-testnetstay at=0.16.2, andpubky-noisestays at 0.1.0. The local Docker source script, its test anddocs/local-locks-demo.mdfollow the pin. The demo build uses the tag's commit, so a moved tag cannot change it.accepted, orproof_submittedafter a Payment Proof, from the same stored events. A row the server stored asinvalid_conflictfor that reason then got a projection with equal source cursors and a new state, which the equal-cursor guard refused on every projection. That conflict failed status, drain and rotation reads for every invoice of the Creator. The projection now carriesacceptance_recorded_at, andinvalid_conflictmay takeacceptedorproof_submittedat equal cursors only when that time is at or after the proposal expiry. Every other equal-cursor change still conflicts.docs/architecture.mdrecords why.PrivateIntakeModeConflictgets its own SDK failure label, and tests pin that such a refused receive fails the transport pass and never counts as an empty inbox.payment_proof_event_len: 847 bytes with the server's UUID Payment References, under the 1000-byte limit.claim_payment_request_for_preparationand check the claimed proposal.Paykit changes since 0.1.0
webpki-roots1.0.11,webpki-root-certs1.0.11,rustls-webpki0.103.16. Unchanged heresignup_code_rejected,republish_identityimport_session, whose errors did not change. The reader demo maps every sign-in failure to one resultis_message_too_largepayment_proof_event_lenis adopted for the USDT proof budgetproposal_recorded_at,acceptance_recorded_at, payee attribution ruleacceptance_recorded_atis adopted for late Acceptances. Bitcoin uses invoice-specific addresses. The USDT not-before bound is in #122accept_pending_allowance_proposalsHEADper send. No server send path has a time budget it affectsclaim_payment_request_for_preparationsync_public_endpointsstateRevisionProtocol and integration impact
invalid_conflictonly because its Acceptance was fetched after the proposal expiry reportsacceptedorproof_submittedinstead of409 conflict.0.1.1-rc2.Out of scope
PrivateIntakeModeConflicton every SDK handle sharing that state, and only the supervisor's owner can drain what it buffers. Bitkit shares each Creator's state, and Paykit's Swift and Kotlin bindings do not expose the supervisor, so a server supervisor would stop the Creator's wallet from receiving. The status, drain and rotation contracts also need a receive made for each answer, while the supervisor reports stream state and an audit up to about a minute old. Adopting it would need a platform lifecycle contract in Paykit and a redesign of server freshness, not a version bump.accept_pending_allowance_proposals, lifts theallowance_intakeguard and appliesproposal_recorded_atto USDT. It pins rc5. Whichever PR merges second takes the other's pin.Verification
Run with Rust 1.91.1 on the final source. The test suites ran on a tree that differs from the head only in two README lines.
cargo fmt --all --check: passcargo clippy --locked --workspace --all-targets -- -D warnings, and the same with--all-features: passcargo test --locked -p paykit-server -- --test-threads=1: pass, 500 testscargo test --locked -p paykit-server --example paykit-companion-auth -- --test-threads=1: pass, 14 testscargo test --locked -p paykit-server-e2e -- --test-threads=1against a disposable PostgreSQL 16 container: pass, 243 tests, 3 ignored live testsgit diff --check: passscripts/test-prepare-local-docker-sources.shin the Dockerfile's Debian runtime image: pass. The prep script also runs cleanly on the realCargo.tomlandCargo.lock.paykit-serverCreator and a reader, proposed two requests, and had the server fetch one Acceptance after its proposal expiry, which 0.1.0 stored asInvalidConflict. rc6 then loaded that hosted state: both requests wereAccepted, the late one with the same stream cursor, outbound cursor and event time and anacceptance_recorded_atafter the expiry, and the link was stillLinked. rc6 sent a third request that the 0.1.0 wallet received. After that write, 0.1.0 failed to load the Creator's state with a storage error. A 0.1.0 backup decoded in rc6 both from its frozen postcard form and from JSON.New tests:
an_acceptance_fetched_after_proposal_expiry_leaves_invalid_conflictandinvalid_conflict_stays_without_a_late_fetched_acceptance(domain)projection_carries_the_acceptance_record_timeandonly_an_acceptance_fetched_after_expiry_moves_a_stored_invalid_request(projection)a_stored_invalid_request_takes_the_state_derived_for_a_late_fetched_acceptanceanda_stored_invalid_request_stays_invalid_without_a_late_fetched_acceptance(PostgreSQL)a_peer_owned_by_sse_intake_is_a_named_receive_failureanda_peer_owned_by_sse_intake_is_neither_contention_nor_an_empty_inboxthe_largest_usdt_payment_proof_fits_one_private_messageandthe_usdt_payment_proof_budget_catches_a_proof_that_does_not_fitNot run: the ignored live Pubky, Electrum and Marketplace harness tests,
sdk-example/run.sh(the example compiles, and the e2e reader wallet runs the same guarded claim), and Docker image builds.🤖 Generated with Claude Code