Pro backend updates for 0.2.0 backend#95
Open
jagerman wants to merge 32 commits into
Open
Conversation
The proof and request signing hashes fed multi-byte integers (proof expiry, request unix timestamps, get-details count) into blake2b by reinterpret_cast'ing the host integer's raw memory, which matches the backend's explicit little-endian encoding only because our targets are little-endian. Wrap each in oxenc::host_to_little_inplace() before hashing so the little-endian encoding is explicit and correct regardless of host byte order. No wire change on little-endian platforms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
proof_hash_internal defined a constexpr PRO_BACKEND_BLAKE2B_PERSONALISATION = "SeshProBackend__" that was never used; the proof hash actually uses ProProof________ (SESSION_PROTOCOL_BUILD_PROOF_HASH_PERSONALISATION). The backend confirms SeshProBackend__ appears nowhere in its real signing path (only a stale example verifier), so it is pure dead code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The revocation-list ticket was a uint32, which can wrap or be rolled back by a disaster-recovery restore, silently making a client that is 'ahead' treat the list as unchanged and miss revocations. Widen it to int64_t in the C and C++ structs, docstrings, and JSON parse. json_require previously accepted only unsigned integer types, so json_require<int64_t> tripped its trailing static_assert. Extend the integer branch to signed types by switching the type dispatch to STL concepts std::floating_point/std::integral/std::same_as (dropping the is_one_of type lists); the bool check moves ahead of the integral one since bool satisfies std::integral. Also drop the needless value-init of the local 'type'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Convert all Session Pro timestamps and durations from millisecond to integer-second units, per the authoritative wire spec. Signed BLAKE2b hashes (proof expiry; generate-proof/get-details/set-refund request timestamps) fold in seconds; wire JSON keys drop the unit suffix (_unix_ts_ms -> _ts, request nonce -> ts, grace_period_duration_ms -> grace_period_duration); internal C++ time_points become sys_seconds and durations chrono::seconds; and the C API surface is likewise seconds (breaking it, which clients absorb in the client-migration phase). 8-byte little-endian hash encoding is unchanged (value is seconds now). Adds a session::sys_seconds alias. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When we load oxen-logging via system lib, the code that was supposed to also inject fmt/spdlog with it was not working properly because the "did we go via submodule" was falsely returning true. This fixes it by using an intermediate library so that we can properly distinguish between the cases (final oxen::logging exists - submodule, which already gives fmt/spdlog; otherwise use the intermediate target and set final oxen::logging alias manually). This also fixes the default STATIC_BUNDLE to depend on BUILD_STATIC_DEPS, rather than NOT BUILD_SHARED_LIBS, so that -DBUILD_STATIC_DEPS=OFF doesn't accidentally default to attempting a static bundle without static deps. (That case is also now detected and becomes a fatal error).
Extends the ms->seconds wire-format change (2758108) into the config layer, and switches all Pro timestamp/duration fields from uint64_t to signed int64_t to match the rest of the codebase (unsigned bought nothing for wall-clock values). - config (convo_info_volatile / user_profile / config-pro): proof expiry, contact pro-expiry, and pro access-expiry are stored and exposed in seconds; synced dict keys "e"/"E" reused (safe: Pro config is not live in shipped clients). Drops the _ms C-API name suffixes. - pro_backend + session_protocol C API: timestamps/durations uint64_t -> int64_t. Bitsets, counts, flags, and pfs hash-entry values are left unsigned. - tests: field/key/unit updates; fix test_session_protocol, which 2758108 left encoding expiryunixts and passing decode ts in ms after the code moved to seconds -- broke the proof-signature round-trip, undetected because the suite could not previously link. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per the wire spec (§2.1, delta session-foundation#2): rename the libsession symbol and the backend-JSON wire keys. The value stays an opaque 32-byte tag that clients compare for equality against the revocation list; the "random value, not BLAKE2b(counter, salt)" nature change is entirely backend-side. - C/C++ struct fields, C API, config fields (pro_revocation_tag), and the "gen_index_hash" JSON keys in the generate-proof response + revocation list. - Protobuf field genIndexHash (ProProof field session-foundation#2) is left as-is: the wire is keyed by field number, renaming it would require regenerating the committed SessionProtos.pb.* and touches the shared cross-client schema. - Config dict key "g" kept (internal, opaque; no format change). - Stale "hash of the generation index" comments updated to describe the opaque revocation tag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the gen_index_hash -> revocation_tag rename into the shared
SessionProtos schema so the name is consistent everywhere (no permanent
mismatch between the proto field and the C++/wire names).
Field number is unchanged (ProProof field 2), so this is wire-compatible
on the binary protobuf; only the schema field *name* and the generated
accessors change. Regenerated SessionProtos.pb.{cc,h} with protoc
3.21.12 (rename-only diff; WebSocketResources unchanged).
NOTE: SessionProtos is the cross-client message schema -- clients that
reference the generated `genIndexHash` / `genindexhash()` accessors must
update to `revocationTag` / `revocationtag()`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y_in/retain_for Per the wire spec (§4, delta session-foundation#6). The per-entry expiry_ts (whose value was the entitlement expiry -- up to a year for long subs, absurd for what is only a cleanup timer) is dropped and replaced by: - per-entry `effective_ts`: a matching proof is revoked only once the client clock reaches this (revoking on tag-match alone is too early); - response-level `retry_in` (recommended poll interval) and `retain_for` (memory-only local aging window, ~ the max proof-validity window). libsession-util parses and exposes these; the client applies the matching and aging semantics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…yment_id Delete the SESSION_PRO_BACKEND_PAYMENT_PROVIDER and SESSION_PRO_BACKEND_PLAN enums and the payment-provider metadata table; providers and plans are now opaque wire strings (canonical SESSION_PRO_BACKEND_PAYMENT_PROVIDER_CODE_* constants for the known ones). Collapse the per-provider payment fields (google_payment_token/google_order_id/apple_*/rangeproof_order_id) into a single opaque payment_id; multi-part providers fold their parts in per a backend-defined composite. Rename payment status REFUNDED -> REVOKED. The add-payment and set-refund signed hashes now cover provider_code | payment_id (dropping the provider enum byte and the separate order_id). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Pro hashing personalisation strings and proof digest routine pointed at pinned line numbers in the session-pro-backend GitHub source, which rot as that repo changes. Point them at the authoritative pro-wire-protocol.md sections (§2 proof, §3 signed requests) instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reshape the Session Pro request/response API for a clean, symmetric design
ahead of launch:
- Request builders are now free functions returning ProRequest{endpoint, body}
(add_payment_request, pro_proof_request, payment_details_request,
refund_request, revocations_request), each paired with a *_sigs/*_sig signer.
The request C++ structs are deleted.
- Response parsing is symmetric free functions (parse_add_payment,
parse_pro_proof, parse_payment_details, parse_refund, parse_revocations)
rather than X::parse methods; a shared fill_proof_response() backs the two
proof responses.
- Response base renamed ResponseHeader -> Response; the
AddProPaymentOrGenerateProProofResponse mega-type becomes ProProofResponse
with empty AddProPaymentResponse/GenerateProProofResponse derivations.
- Endpoint strings live in a single master table; C SESSION_PRO_BACKEND_*_ENDPOINT
symbols and C++ ProRequest.endpoint both view it.
- The wire version for each request type is library-owned (a per-type constant),
not a caller parameter; the version param and _request_build_to_json helpers
are removed.
- Key derivation happens once via normalize_privkey() rather than per hash.
- Re-add provider support/management URLs (URL-only, no English names) keyed by
provider code so clients don't each duplicate them: C++ provider_urls() returns
std::optional<ProviderUrls>; C session_pro_backend_get_provider_urls() returns
all-NULL fields for a provider with no URLs.
The C API (_request_build_sigs/sig, _request_to_json, _response_parse) is
preserved for the iOS client.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…redeemed→purchased) The get-details wire key `unredeemed_ts` is renamed `purchased_ts` (the field is the provider's purchase instant, not a payment state), and `purchased_ts` and `revoked_ts` are now JSON floats carrying the upstream provider's sub-second precision (pro-wire-protocol.md §1/§5). Parse those two as floating-point seconds and keep the precision rather than truncating: the C++ ProPaymentItem stores them as millisecond-resolution sys_ms, and the C API exposes them as double fractional seconds (millisecond-precise via sys_ms). All other timestamps remain whole-second integers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…the proof Per pro-wire-protocol.md Delta session-foundation#11, the in-digest version byte and the JSON `version` field are removed everywhere except the proof carries its version. - Requests (add-payment, generate-proof, get-details, set-refund, get-revocations): no version byte in the signed hash, no `version` in the body; the library-owned per-request version constants are deleted. A new request shape gets a new endpoint. - The proof keeps a plaintext `version` field — it is a free-floating, offline-verified credential with no endpoint. It is no longer hashed as a byte; instead it selects the personalisation (`ProProof________` -> `ProProof_v0_____`), and that choice is what binds the version into the signature. proof_hash_internal no longer takes/hashes it. - SetPaymentRefundRequestedResponse loses its `version` field (C++ + C). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Delete the three dead [[maybe_unused]] dump_pro_* stderr helpers (several with malformed format specifiers, e.g. "%" PRId64 "zu"), and convert the remaining fprintf(stderr, "ERROR: ...") diagnostics to Catch2 INFO/UNSCOPED_INFO so they surface through the test framework instead of raw stderr. Per-error messages use UNSCOPED_INFO so they survive to the post-loop assertion. Drops the now-unused <cinttypes> include. No test logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the incremental `nlohmann::json j; j["k"] = v; ...` pattern in the four
*_body helpers with a single braced json literal returned directly
(`return nlohmann::json{...}.dump();`, no named temporary). Cosmetic only:
nlohmann sorts object keys on dump, so the serialized bodies are byte-identical
(the C-vs-C++ request cross-check tests confirm), and the C _request_to_json
wrappers call the same helpers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per pro-wire-protocol.md §1 (Q13), the get-details payment-item `status` and the account `user_status` travel as stable string codes, not integers — as `payment_provider`/`plan` already do. libsession still parsed both as integers mapped to hand-synced C enums, which (a) was incompatible with the live backend (payment status has been a string on the wire) and (b) hard-failed the entire get-details parse on any unknown value. Parse both as opaque pass-through std::string (C: char[64] + _count), so unknown or future codes degrade gracefully instead of breaking old clients. Delete the SESSION_PRO_BACKEND_PAYMENT_STATUS and SESSION_PRO_BACKEND_USER_PRO_STATUS integer enums and their "must match backend" coupling. Known codes: payment status unredeemed/redeemed/expired/revoked; user_status never/active/expired. Backend shipped the matching change (session-pro-backend b3db4e3); in lockstep. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the all-zero PUBKEY placeholder with the Session Pro Backend's real Ed25519 public signing key (current test deployment, expected to carry through to production). Consumers verify a proof was issued by the backend by checking its signature against this key (ProProof::verify_signature). Trivially editable if the backend signing key ever changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add session::pro_backend::URL (= https://pro.session.codes) so clients read the canonical production base URL from libsession rather than hand-carrying it, and export the Ed25519 signing pubkey to the C API (SESSION_PRO_BACKEND_PUBKEY) for C consumers (iOS); C++ already has pro_backend::PUBKEY. Both C symbols point at the single C++ definitions (no duplicated literal/key). Clients may still override the URL for dev/test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
utils/test-bigendian.sh builds + runs the test suite inside an emulated s390x (qemu-user) so endian-sensitive byte serialization (Pro signed-digest hashing, config, protobuf) is exercised on a real big-endian host. Manual/occasional audit, deliberately not wired into CI. (The [endian] make_hashable known-answer test is pfs-only; on stable the [hash]/[pro_backend]/[session_protocol]/[config] tags still exercise the serialization paths.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the privileged tonistiigi/binfmt helper container in favour of a one-time host install of qemu-user-static + binfmt-support (registers the binfmt_misc handlers with the F flag, which is what makes them work inside containers). Guard on the handler being present. Also bump the base image bookworm -> sid so the audit runs against a modern toolchain representative of what the code targets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The *_unix_ts members/params are sys_seconds / sys_ms time points, so the name baked the representation into the identifier. Rename them to *_at (as the backend did) to stop embedding the type. Pure rename: JSON wire keys and protobuf accessors are untouched, so there is no wire change. Also drop the stale expiry_unix_ts_ms grace-period doc formula (the wire is integer seconds now; no *_ms field survives). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Requests now carry {endpoint, content_type, data} instead of {endpoint, body}.
Clients relay `data` verbatim under `content_type` and must NOT assume or parse
a format -- libsession owns the wire encoding and may change it. Renames the C
mirror to match: session_pro_backend_to_json -> session_pro_backend_request
(json field -> data, plus endpoint + content_type), *_request_to_json ->
*_request_build, to_json_free -> request_free. The content type is single-sourced
("application/json") next to the endpoint strings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The C request API reimplemented the wire pipeline: a per-request _build_sigs that signed, an input struct the caller hand-filled with pubkeys + precomputed sigs, and a _request_build that re-serialized the body from them. Replace all of it with loose-arg builders that take the same inputs as the C++ *_request() functions (private keys + params) and just call them -- one wire implementation, living in C++. session_pro_backend_request now owns the built C++ ProRequest in an opaque void* and points endpoint/content_type/data into it (zero copy); request_free deletes it. Deletes the _build_sigs/_build_sig functions, the per-request input structs, the add_pro_payment_user_transaction struct, and the master_rotating_signatures/signature return structs. Tests and the dev-server integration block are rewritten to the single-call API. Also tightens the internal *_hash/*_body helpers to fixed-extent spans (span<...,32> for pubkeys, span<...,64> for sigs) so sizes are enforced at compile time rather than silently truncated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Desktop consumes the backend's X25519 pubkey as a constant, so expose it alongside PUBKEY: session::pro_backend::PUBKEY_X25519 (C++) and the C export SESSION_PRO_BACKEND_PUBKEY_X25519. A unit test asserts the hardcoded bytes equal the runtime crypto_sign_ed25519_pk_to_curve25519(PUBKEY) conversion so they cannot drift. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ded tags Iterated the occasional big-endian audit into a working state (validated on real s390x): use the host's qemu-user-static + binfmt-support (no privileged tonistiigi/binfmt container); debian:sid for a modern toolchain; a clean build dir each run; -DENABLE_NETWORKING_SROUTER=OFF plus libngtcp2/gnutls/ libevent from apt to satisfy the mandatory oxen::quic without building it from source; and fail loudly if any filter tag matches no tests (guards against a tag rename silently shrinking the audit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Response::status was a uint32_t whose domain changed by endpoint (0/1/2 for
most, plus 100/101 for add-payment) -- and for every response except add-payment
it just duplicated !errors.empty(). Remove it: Response is now { errors } with a
success() helper (C: response_header drops status; success == errors_count == 0),
and add-payment collapses to the same -- the backend already sends the human
reason (including already-redeemed / unknown-payment) in errors, so display is
unaffected. Deletes the AddProPaymentResponseStatus C++ enum, the C
SESSION_PRO_BACKEND_ADD_PRO_PAYMENT_RESPONSE_STATUS enum, and its magic-int
values. Parsers still read the wire "status" int internally to decide success;
they just no longer surface it. (A future machine-readable discriminator, if
needed, should be an opaque string slug on the wire like the Q13 statuses.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r (Delta session-foundation#12) Reworks the response envelope to spec §5 / Delta session-foundation#12. The wire `status` becomes a string ok/fail/error parsed into a CLOSED ResponseStatus enum -- an unrecognized value is a protocol error, never passed through (backend confirmed status is exhaustive, Q14). The `errors` array becomes a single `error` string (diagnostic/fallback, first-reason-only). A new open/additive `error_code` slug (opaque; unknown values pass through) carries the machine-readable outcome. Response is renamed ResponseBase (it's only ever a base) and is now { ResponseStatus status; optional<string> error_code; optional<string> error; } with an explicit operator bool(). The C response_header mirrors it (status enum + error_code/error string8, {NULL,0} == none). get-details account status key renamed status -> user_status (Q15). Deletes the obsolete integer status enum. A libsession-side parse failure yields a protocol error (error_code "invalid_response"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… drop BLAKE2b Implements pro-wire-protocol.md Delta session-foundation#13 / §1.1: all five Ed25519 signatures (the offline proof §2 + the four signed requests §3.1–3.4) now sign the message bytes directly instead of a BLAKE2b-256 digest. - Add session::pro::signed_message() (src/pro_message.hpp): builds the signed message per §1.1 — 16-byte domain prefix, fixed-width raw keys/tags (self-delimiting), integers as canonical decimal ASCII (std::to_chars), strings verbatim, a single NUL between adjacent variable-length fields. - Replace ProProof::hash() with ProProof::signed_message() and delete the C session_protocol_pro_proof_hash() (the 32-byte digest no longer exists in the protocol). - Delete make_blake2b32_hasher; BLAKE2b is gone from the Pro signing path. - Move the 16-byte domain-prefix constants into C++ (session_protocol.hpp) as std::string_view and rename *_PERS -> *_DOMAIN; drop the C char[] ..._HASH_PERSONALISATION constants. - Update tests to sign via signed_message() and to compare the proof's signature (not a hash) across the encode/decode round-trip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Client-facing enumeration of the user-visible purchasable payment platforms (provider-code slugs: google_play, app_store) so clients build their "purchase via …" store list at runtime instead of hardcoding two stores. Hidden mechanisms (rangeproof) are handled but never listed — they surface only for users who already hold them. C++ `visible_platforms()` returns std::span<const std::string_view>; C `session_pro_backend_visible_platforms(size_t*)` returns a static pointer-array derived from it. Both built from the existing SESSION_PRO_BACKEND_PAYMENT_PROVIDER_CODE_* constants (single source). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 21, 2026
Pro backend 0.2 + libsession 1.7 compatibility updates
session-foundation/libsession-util-android#47
Open
Open
(+ formatting)
jagerman
force-pushed
the
pro-backend-updates
branch
from
July 21, 2026 20:17
6b7d55a to
4ea3941
Compare
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.
This PR updates libsession-util for compatibility with the changes made in session-foundation/session-pro-backend#2, along with some related refactoring for integrating the changes with Session clients.
Session Pro: finalize the pre-launch wire/proof protocol + redesign the
pro_backendAPIOverview
This branch finalizes the Session Pro wire protocol and reworks libsession's
pro_backend/session_protocolcode to match, ahead of launch. It is developedin lockstep with the authoritative spec (
session-pro-backend/docs/pro-wire-protocol.md)and the backend implementation — every wire-affecting change here has a matching
backend change, and the pairing is validated end-to-end (see Testing).
Net effect: a smaller, simpler, more consistent API (+2,122 / −3,559), an opaque
request/response boundary so clients never parse the wire, and a signing scheme that no
longer depends on BLAKE2b.
Wire-protocol changes (coordinated with the backend spec)
These change bytes on the wire and/or in signatures, and land in lockstep with the backend:
seconds as a signed
int64_t. The only exceptions are the two upstream-provider eventinstants (
purchased_ts,revoked_ts), emitted as JSON floats to preserve sub-secondprecision (kept internally as
sys_ms/double).Ed25519 signatures (the offline proof + four signed requests) are now
Ed25519(key, M)whereMis built by a single rule: 16-byte domain prefix, raw fixed-width keys/tags (self-delimiting),
integers as canonical decimal ASCII (
std::to_chars), strings verbatim (UTF-8), and one\0between adjacent variable-length fields. New
src/pro_message.hppimplements this.versionon requests; the proof keeps a plaintextversion. Request shapes aredomain-separated by endpoint + domain prefix, so requests carry no version at all. The proof
(free-floating, offline-verified) keeps a plaintext
versionthat selects its domain prefix(
v0→ProProof_v0_____) — it is a verification input, never a signed byte.gen_index_hash→revocation_tag. Renamed everywhere (C/C++ structs + protobuf field);it is now a stored random 32-byte value, not a computed hash — clients compare it for equality
only.
payment_id. The per-provider typed ID fields and theprovider/planinteger enums are gone; the wire carries an opaquepayment_idstring plusstring
provider_code/plancodes. Itemstatusand accountuser_statusare parsed asopaque pass-through strings.
effective_ts(the real gate) and a single list-levelretain_for(memory-only aging) plusretry_in. Revocation ticket widened to 64-bit.statusis a closed string enum(
ok/fail/error), with an open/additiveerror_codeslug and a single diagnosticerrorstring (was an int status +
errors[]).add_pro_paymentis idempotent (re-claim = success);already_redeemedis gone.SeshProBackend__personalisation constant and the historical saltedgeneration-token hashing.
API changes (libsession surface)
pro_backendis now free functions returning an opaqueProRequest. Each builder(
add_payment_request,pro_proof_request,payment_details_request,refund_request,revocations_request) takes loose args (keys,provider_code,payment_id, timestamps) andreturns
ProRequest{ endpoint, content_type, data }. Clients relaydataverbatim undercontent_typeand never parse it — libsession owns the wire encoding. Response parsing is thesymmetric set of free functions (
parse_*)...._request_buildfunctions own aC++
ProRequestvia an opaque handle and pointendpoint/content_type/datainto it(zero-copy); freeing deletes it. No duplicated request-building logic, no input structs.
ResponseBasecarriesResponseStatus status(closedOk/Fail/Error), plusstd::optional<std::string> error_code/error;explicit operator bool()for success.ProProof::hash()and Csession_protocol_pro_proof_hash()— the 32-byte signeddigest no longer exists.
ProProof::signed_message()returns the exact bytes signed, if needed.hardcoding): backend base
URL, the backend signingPUBKEY(+PUBKEY_X25519), per-providersupport/management
provider_urls(), andvisible_platforms()— the user-visible purchasableprovider slugs (
google_play,app_store; excludes the hiddenrangeproof) for building the"purchase via …" list. Signing domain prefixes moved into C++
std::string_viewconstants.*_unix_tsmembers to*_atwhere the value is asys_seconds(raw-integer unixtimestamps keep
_unix_ts);ProRequestgained an explicitcontent_type.Notable design decisions
verification is
verify_signature()/status(). This means the signing rework above isinvisible to client code — no client changes required for it.
proof binds its version via the domain prefix it selects. No version byte is signed.
std::to_charsdecimal integers, explicitly not locale-aware formatters, so the signed bytesare identical across languages/platforms and need no endian handling.
Testing
requests and proofs against a real backend clone (with the matching spec commit) — this catches
wire/signing drift (field order, integer encoding, framing, domain prefix) that self-consistent
unit tests cannot. Both the signing rework and the envelope redesign pass end-to-end.
utils/test-bigendian.shbuilds and runs the hash/protocol/pro/configsuites under emulated s390x (local
qemu-user-static) to guard against host-byte-orderassumptions.
Client migration notes
pro_backend*_request()/parse_*functions;relay
ProRequest.dataunderProRequest.content_typeunmodified.status(ok/fail/error); maperror_codeslugs to localizedstrings (the
errorstring is an English diagnostic, not user-facing). Drop anyalready-redeemed special-casing.
URL/PUBKEY/provider URLs/purchasable platforms from libsession instead of localcopies.