Skip to content

user_profile: add pro auto-renewing status (config key A) - #121

Open
jagerman wants to merge 1 commit into
session-foundation:devfrom
jagerman:pro-auto-renewing-config
Open

user_profile: add pro auto-renewing status (config key A)#121
jagerman wants to merge 1 commit into
session-foundation:devfrom
jagerman:pro-auto-renewing-config

Conversation

@jagerman

@jagerman jagerman commented Aug 6, 2026

Copy link
Copy Markdown
Member

Clients sometimes need to know whether a Pro subscription is terminal or auto-renewing (e.g. "renews on X" vs "expires on X"). Store the backend's auto_renewing (from get_pro_status) as a presence-only config flag A: 1 when auto-renewing, absent otherwise (terminal / unknown / not Pro).

Deliberately not tri-state: unlike blinded_msgreqs M, this is backend- derived fact, not a defaulted client preference, so there's no upgrade- default edge case that a distinct "unset" would guard. And no t/T bump -- it's synced pro state like E/I/R, not a user-initiated profile edit.

Exposes get_/set_pro_auto_renewing (C++ bool; C 0/1) with unit + C-API coverage.

Clients sometimes need to know whether a Pro subscription is terminal or
auto-renewing (e.g. "renews on X" vs "expires on X"). Store the backend's
`auto_renewing` (from get_pro_status) as a presence-only config flag `A`:
1 when auto-renewing, absent otherwise (terminal / unknown / not Pro).

Deliberately not tri-state: unlike blinded_msgreqs `M`, this is backend-
derived fact, not a defaulted client preference, so there's no upgrade-
default edge case that a distinct "unset" would guard. And no t/T bump --
it's synced pro state like E/I/R, not a user-initiated profile edit.

Exposes get_/set_pro_auto_renewing (C++ bool; C 0/1) with unit + C-API
coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mpretty-cyro added a commit to mpretty-cyro/libsession-util that referenced this pull request Aug 10, 2026
Completes the config side of session-foundation#121 against the Pro status-refresh spec, which asks
for both `auto_renewing` and `grace` to be synced alongside `E`. session-foundation#121 ships
`auto_renewing`; this adds the grace period.

The backend folds the grace period into the stored expiry for auto-renewing
subscriptions (`payment_expiry_at = expiry_at + grace if auto_renewing`) and sends
that verbatim as `get_pro_status.expiry_ts`, so `E` is the end of coverage rather
than the date a renewal is due. With `G` synced, any device recovers the
paid-through instant as `E - G`; without it a config-only consumer cannot compute
it at all.

Not presence-checked, unlike `A`: the backend sends 0 whenever the subscription
isn't auto-renewing, so an absent key and a stored zero describe the same account
and both give `E - 0 == E`. There is no state a caller could act on differently.

Clearing `E` clears `G` with it. A grace that outlived its expiry would pair with
whatever wrote `E` next, and `set_pro_access_expiry` already clears `I` and `R` as
side effects, so this follows the existing shape.
mpretty-cyro added a commit to mpretty-cyro/libsession-util that referenced this pull request Aug 10, 2026
`set_pro_access_expiry(nullopt)` already clears `G` -- a grace is only meaningful
as `E - G`, so it must not outlive its expiry. `A` has the same relationship and
was not being cleared: a renewing flag with no expiry beside it describes a
subscription that is not there.

Every caller that clears `E` is handling an account with no entitlement -- a proof
cleared, a proof revoked, or a non-positive `expiry_ts` -- and none of those is
auto-renewing, so there is no state in which the flag should survive its expiry.

Without this the three keys are coherent only because every *consumer* happens to
test `E` before reading `A`. That is true today on all three clients and nothing
enforces it; a new consumer inherits the obligation without knowing it has one.
Maintaining the invariant on the write side is what removes that.

Note this changes the lifecycle of `A`, which is session-foundation#121's key rather than mine --
raised deliberately as its own commit so it can be taken or dropped independently
of the `G` work it sits beside.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant