Skip to content

feat: close the surface gaps against rail0-go and rail0-ts - #19

Merged
pviti merged 1 commit into
mainfrom
feature/align-surface-with-go-ts
Aug 18, 2026
Merged

feat: close the surface gaps against rail0-go and rail0-ts#19
pviti merged 1 commit into
mainfrom
feature/align-surface-with-go-ts

Conversation

@pviti

@pviti pviti commented Aug 18, 2026

Copy link
Copy Markdown
Member

Why

Three calls the other two SDKs have had and this one did not. Each is reachable only through the SDK, so their absence was not an inconvenience — it was work a Ruby integration could not do at all.

call go ts ruby (before)
auth.logout
payments.dispute_submit_by_hash
payments.close_dispute_submit_by_hash

auth.logout

Revokes the token this client carries, not every session for the address — signing out one process leaves the others signed in.

It returns the body rather than nil on purpose. The gateway's denylist fails open by design (a store outage must not sign out the whole platform), so { revoked: false } means the token is still usable until it expires, and the caller should treat its own copy as compromised rather than assume the session is gone. Ruby was the one SDK where a long-lived process could not hand a session back at all.

The two dispute report-by-hash calls

The payer's counterpart to submit_by_hash. That generic method only covers the single-segment /payments/{id}/{operation}/submitted shape, and dispute/close is two segments — which is why these are separate methods rather than another operation argument. Without them, a Ruby caller signing with a wallet that broadcasts on its own (MetaMask) could open and close disputes with a raw signed transaction but could never report one it had already sent.

Payer-only, and the payer authenticates account-less via SIWE: a bare hash carries no signature, so the session is what proves who is reporting it.

Security

Nothing widened, and one thing improved: logout is the first way this SDK can end a session it holds. All three are existing gateway endpoints called with the client's existing credential, and authorisation stays entirely server-side (payer-only on the dispute paths, session-scoped on logout). No new local state, nothing logged.

The one judgement call worth a reviewer's eye is returning revoked verbatim rather than raising when it is false — the failure is soft on the gateway's side, so it is the caller's decision to make, and the README says what it means.

Performance

One request per call. No retries added (this SDK still retries network errors only — the 429 divergence across the three SDKs is a separate change), no extra round trips, no caching.

Tests

Four specs. The two that matter assert what would otherwise pass silently: that revoked: false is surfaced rather than swallowed, and that close-dispute posts to the two-segment path — the whole reason these are not one parameterised method.

bundle exec rspec: 143 examples, 0 failures.

Still open across the three SDKs

Not in this PR, for the record: the 429 policy diverges three ways (go retries honouring Retry-After, ts surfaces it but never retries, ruby ignores it), no SDK has an auto-pager, ProveAddress exists only in go, and this README does not document the rbsecp256k1 native-build flags that bundle install needs before the signing extras will install.

🤖 Generated with Claude Code

Three calls the other two SDKs have had and this one did not. Each is reachable only
through the SDK, so their absence was not an inconvenience — it was work a Ruby
integration could not do at all.

auth.logout — revokes the token THIS client carries, not every session for the address,
so signing out one process leaves the others signed in. It returns the body rather than
nil on purpose: the gateway's denylist fails open by design (a store outage must not
sign out the whole platform), so `revoked: false` means the token is still usable until
it expires, and the caller should treat its copy as compromised rather than assume the
session is gone. Ruby was the one SDK where a long-lived process could not hand a
session back.

payments.dispute_submit_by_hash / close_dispute_submit_by_hash — the payer's counterpart
to submit_by_hash. That generic method only covers the single-segment
/payments/{id}/{operation}/submitted shape, and `dispute/close` is two segments, which
is why these are separate methods rather than another operation argument. Without them a
Ruby caller signing with a wallet that broadcasts on its own could open and close
disputes with a raw signed transaction but never report one it had already sent.

Payer-only, and the payer authenticates account-less via SIWE: a bare hash carries no
signature, so the session is what proves who is reporting it.

Four specs, and the two that matter assert what would otherwise pass silently: that
`revoked: false` is surfaced rather than swallowed, and that close-dispute posts to the
two-segment path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pviti pviti added the enhancement New feature or request label Aug 18, 2026
@pviti
pviti merged commit edb2d33 into main Aug 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant