From f7de68a5f4c44d2f6c0c10e69d7708c2adaf8d23 Mon Sep 17 00:00:00 2001 From: epoberezkin Date: Wed, 30 Sep 2026 07:51:31 +0000 Subject: [PATCH] deploy: b1dc25dc3ebb2967ecca0da1b8e666fe67006b3e --- docs/protocol/badges-overview.html | 27 +++++++++++++++++++++------ docs/protocol/badges-rpc.html | 6 +++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/protocol/badges-overview.html b/docs/protocol/badges-overview.html index f84d393a28..e542b63acb 100644 --- a/docs/protocol/badges-overview.html +++ b/docs/protocol/badges-overview.html @@ -992,7 +992,7 @@ window.addEventListener('click',(e)=>{ @@ -1030,9 +1030,9 @@ window.addEventListener('click',(e)=>{

A purchase is identified by an Ed25519 key pair that the app generates for it and does not use for any other purpose. Every request concerning the purchase, the redemption and each monthly renewal, is signed with this key, and the signature covers the request together with a value derived from the ratchet of that exchange, so it is valid for that exchange only and cannot be replayed. The agent verifies the signature and delivers the verified public key to the service with the request, and the service accepts a request about a purchase only when the verified key is the purchase key.

Buying and redeeming

A badge is bought for one or more months, either on the web or in the app.

-

On the web the purchase produces a code, which is then redeemed in the app. The code is the only data passed from the web site to the app: the web site does not learn which app redeems a code, and the app does not see the payment. The code is generated in the buyer's browser, and the service stores only its hash; at redemption the app presents the code itself, and the service matches it against the stored hash. A badge issued without a sale, for example in compensation for a problem, is a code generated by the operator and redeemed in the same way.

+

On the web the purchase produces a code, which is then redeemed in the app. The code is the only data passed from the web site to the app: the web site does not learn which app redeems a code, and the app does not see the payment. The code is generated in the buyer's browser, and the service stores only its hash; at redemption the app presents the code itself, and the service matches it against the stored hash. A badge issued without a sale, for example in compensation for a problem, is a code generated by the operator, or by a moderator of the service's managed group, and redeemed in the same way.

In the app, the user pays by card, in cryptocurrency, or through the app store. The app requests an invoice from the service, or presents the receipt of the app store, and the service issues the credential once the payment is confirmed.

-

In both cases the app generates the master key and the purchase key pair before the purchase. To redeem a code, the app sends the code and the master key to the service, which issues the credential. A code redeemed a second time with the same purchase key returns the same credential; a code redeemed with a different purchase key is refused.

+

In both cases the app generates the master key and the purchase key pair before the purchase. To redeem a code, the app sends the code and the master key to the service, which issues the credential. A code redeemed a second time with the same purchase key returns its latest credential; a code redeemed with a different purchase key is refused once all its uses are taken.

Monthly issuance

A credential is issued for one month at a time, however many months were bought. The service keeps a count of the months remaining for each purchase key, and issues the next credential when the app asks for it. Unused months, e.g. if the app was offline, lapse at the next badge issuance.

Credentials are issued monthly to limit what the expiry date discloses. If credentials were issued for the whole term, a user who bought a year would hold a credential expiring on a day a year ahead, when few other credentials expire, and this date would be disclosed in every proof for a year. Instead, every credential is issued for one month, and its expiry is rounded to the end of the Monday following the end of the paid month (UTC). For example, if the paid month ends on Wednesday 14 October 2026, the credential expires at the end of Monday 19 October, and so does every credential whose month ends between Monday 12 and Sunday 18 October. All credentials whose paid month ends in the same week thus expire at the same instant.

@@ -1070,7 +1070,7 @@ window.addEventListener('click',(e)=>{
  • A proof discloses no value that links it to another proof or to the purchase.
  • A proof used in one context, whether a session, a conversation or a file, cannot be used in another context.
  • The timing of presentations does not identify the holder: all credentials expiring in the same week share the same expiry, and the renewal request and the profile update are made on different days.
  • -
  • Requests to the badge service cannot be linked to each other, to a profile or to a network address, and a request about a purchase can be made only by the holder of the purchase key.
  • +
  • Requests to the badge service cannot be linked to each other, to a profile or to a network address, other than the redemptions of one multi-use code and the codes issued in the service's managed group or redeemed by its members, and a request about a purchase can be made only by the holder of the purchase key.
  • A credential cannot be forged: the issuer keys are fixed in apps and servers, and the app verifies a credential before storing it.
  • A missing or failed proof leaves the default limit in place, and a server cannot be configured to grant a badge type less than the default.
  • @@ -1081,10 +1081,12 @@ window.addEventListener('click',(e)=>{

    cannot:

    @@ -1099,6 +1101,19 @@ window.addEventListener('click',(e)=>{
  • Reuse a profile proof or a file proof in another context.
  • Distinguish the holder from the other supporters whose badges expire in the same week.
  • +

    A member of the badge service's managed group

    +

    can:

    + +

    cannot:

    +

    A server operator

    can: