feat(license): lease-model client — offline-verifiable short tokens w/ auto-refresh - #361
Open
runyourempire wants to merge 1 commit into
Open
feat(license): lease-model client — offline-verifiable short tokens w/ auto-refresh#361runyourempire wants to merge 1 commit into
runyourempire wants to merge 1 commit into
Conversation
…hort tokens Consumes the deployed lease server (/api/license/refresh). The user activates once with a stable `4DA-LIC-...` refresh credential; the app exchanges it for a short-lived, OFFLINE-verifiable `4DA-...` entitlement token and re-runs the exchange on startup + every 6h, so revocation (a cancel/refund reflected LIVE in Stripe) reaches online users within the interval while offline users keep working until the token's embedded expiry. - settings/license/lease.rs (new): is_refresh_credential, refresh_entitlement, maybe_refresh_lease, RefreshOutcome. Pure classify_response() is unit-tested. INVARIANT: network/5xx/malformed NEVER downgrade a paying user (KeepCurrent); only an authenticated "not entitled" revokes. Durable credential stored in the keychain (survives a settings.json license-block wipe) with settings rehydration. - settings/types.rs: LicenseConfig gains `refresh_key: Option<String>` (#[serde(default)], zeroized on drop, redacted in Debug). All existing struct literals updated. - settings_commands_license.rs: activate_license matches `4DA-LIC-` BEFORE the `4DA-` signed-token branch; stores credential + first token, slides activated_at. - app_setup.rs: spawns the 6-hourly refresh task after startup validation. - Verification path (verify.rs), reconcile, gating: UNCHANGED — the token is the same signed 4DA- format they already handle. Legacy 4DA-<sig> + Keygen keys still activate. Additive, no migration. Verified: cargo test settings::license 27/27 pass (6 new lease tests incl. "5xx never revokes"; all reconcile/gating/serde-roundtrip tests still green); cargo fmt clean; clippy introduces zero new warnings in changed files. GATE: ships behind a 7-day founder dogfood on a RELEASE build (doctrine) before any customer. Delivery switch (success page/recovery returning the 4DA-LIC credential instead of the legacy long token) is a follow-up, sequenced AFTER this client is in users' hands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PNQqR59PomAowHWXJoAzha
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Consumes the deployed lease server (
/api/license/refresh, PR #357) to deliver the at-scale licensing architecture: offline-verifiable short-lived entitlement tokens, auto-refreshed, with revocation driven live by Stripe.How it works
4DA-LIC-…refresh credential.4DA-…entitlement token (verified offline by the existingverify.rs— unchanged) and re-runs the exchange on startup + every 6h.Safety invariant (unit-tested)
Network error / HTTP 5xx / malformed response never downgrade a paying user (
KeepCurrent). Only an authenticated "not entitled" revokes. Durable credential stored in the keychain (survives asettings.jsonlicense-block wipe — the recurring "Signal→Free" bug class) with settings rehydration.Changes
settings/license/lease.rs(new) — refresh client +classify_response(unit-tested).settings/types.rs—LicenseConfig.refresh_key: Option<String>(serde default, zeroized, redacted).settings_commands_license.rs—activate_licensematches4DA-LIC-before the4DA-branch.app_setup.rs— 6-hourly refresh task.verify.rs/ reconcile / gating: untouched. Legacy4DA-<sig>+ Keygen keys still work. Additive, no migration.Verification
cargo test settings::license→ 27/27 pass (6 new lease tests incl. "5xx never revokes"; all reconcile/gating/serde-roundtrip tests still green).cargo fmtclean;clippyadds zero new warnings in changed files.Per intelligence doctrine, this ships behind a 7-day founder dogfood on a RELEASE build before any customer. The delivery switch (success page / recovery returning the
4DA-LICcredential instead of the legacy long token) is a deliberate follow-up, sequenced after this client is in users' hands so no buyer ever receives a credential an older app can't use.🤖 Generated with Claude Code