ckb-session-kit 0.0.0-stage → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +337 -0
- package/LICENSE +21 -0
- package/README.md +718 -3
- package/dist/deployment.d.ts +21 -0
- package/dist/deployment.js +92 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9 -0
- package/dist/lock.d.ts +70 -0
- package/dist/lock.js +106 -0
- package/dist/onchain.d.ts +451 -0
- package/dist/onchain.js +1150 -0
- package/dist/paywall.d.ts +60 -0
- package/dist/paywall.js +175 -0
- package/dist/place.d.ts +113 -0
- package/dist/place.js +155 -0
- package/dist/session.d.ts +71 -0
- package/dist/session.js +57 -0
- package/dist/store.d.ts +27 -0
- package/dist/store.js +192 -0
- package/dist/subscription.d.ts +317 -0
- package/dist/subscription.js +959 -0
- package/dist/transfer.d.ts +19 -0
- package/dist/transfer.js +33 -0
- package/package.json +80 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
This project follows [semantic versioning](https://semver.org/). The session lock is
|
|
4
|
+
deployed on CKB **testnet only**; there is no mainnet deployment yet.
|
|
5
|
+
|
|
6
|
+
## 2.0.0 (2026-11-03)
|
|
7
|
+
|
|
8
|
+
### Upgrading from 1.0.0
|
|
9
|
+
Every 1.0.0 export is still there with the same signature; new parameters are
|
|
10
|
+
optional. Some calls that 1.0.0 accepted are now refused, on purpose:
|
|
11
|
+
- `assertSigningLock`, `openSession`, `openSubscription` refuse an owner (or
|
|
12
|
+
charger) lock that opens with no signature: PW-Lock (its deployed build is
|
|
13
|
+
anyone-can-pay) and the Nostr lock in proof of work mode
|
|
14
|
+
(`reason: "owner-lock-unsafe"`; no option lets these through). Sessions
|
|
15
|
+
already open under such a lock should be closed or recovered now.
|
|
16
|
+
- They also refuse configurations not reviewed: Omnilock auth 0x07 or mode bits
|
|
17
|
+
other than time-lock (0x04) and supply (0x08), secp256k1, multisig or Nostr
|
|
18
|
+
locks with args their code would not accept, and any unknown lock
|
|
19
|
+
(`reason: "owner-lock-unreviewed"`). Pass `allowUnreviewedOwnerLock: true`
|
|
20
|
+
once you have checked the lock to get the 1.0.0 behaviour back, with a warning;
|
|
21
|
+
for the creator's charger lock in `openSubscription`, pass
|
|
22
|
+
`allowUnreviewedChargerLock: true`.
|
|
23
|
+
- Anyone-can-pay, PW-Lock and always-success referenced by data hash
|
|
24
|
+
(`data`, `data1`, `data2`) are refused like their type-hash versions.
|
|
25
|
+
- A send whose outcome cannot be checked throws `SendOutcomeUnknownError`
|
|
26
|
+
(with the tx hash); it is never retried, so check the hash before resending.
|
|
27
|
+
- `openSession` and `openSubscription` refuse `TESTNET_LEGACY_DEPLOYMENTS`,
|
|
28
|
+
`hashType: "data"`, and a deployment whose cell dep does not hold its code on
|
|
29
|
+
the client's network (one `getCellLive` per client and deployment).
|
|
30
|
+
- `closeSession` refuses a signer whose lock is not the session's owner lock.
|
|
31
|
+
- `openSession` with no rate limit, or `minInterval` 1, calls `console.warn`
|
|
32
|
+
(or `opts.onWarning`).
|
|
33
|
+
- `SessionPolicyError` and `SessionBalanceError` carry a `reason`; code that
|
|
34
|
+
matched error messages should match `reason` instead.
|
|
35
|
+
|
|
36
|
+
The wallet matrix's locks (secp256k1, Omnilock 0x12 / Bitcoin / Dogecoin,
|
|
37
|
+
Nostr signing mode, JoyID) all pass unchanged.
|
|
38
|
+
|
|
39
|
+
### Fixed (Week 6 adversarial review, 2 Nov)
|
|
40
|
+
- Token subscriptions: look-alike cells typed `xUDT(token.args ++ extra)`
|
|
41
|
+
(xUDT accepts them, anyone can create them with 0 units, and the indexer's
|
|
42
|
+
type filter matched them by prefix) counted toward `MAX_SUBSCRIPTION_CELLS`
|
|
43
|
+
but were never swept. The filter now asks for the exact script length, only
|
|
44
|
+
cells really of the subscription's kind count, raw reads are bounded, and
|
|
45
|
+
`clearPlantedCells` and `cancelSubscription` sweep spendable look-alikes back
|
|
46
|
+
to the subscriber (M1).
|
|
47
|
+
- A status or a charge counted every recent transaction under the lock toward
|
|
48
|
+
its budget of 64, creations by anyone included, and before the age check.
|
|
49
|
+
Now only spends count, an entry older than the period is never counted, and
|
|
50
|
+
creation-only entries are paged past under their own budget of 1024
|
|
51
|
+
(`MAX_CREATED_HISTORY`); past either budget it still refuses (M2).
|
|
52
|
+
- `clearPlantedCells`: sweeps at most 30 cells per transaction, so a status
|
|
53
|
+
never dates the cleanup as a charge (L3); with cells left unread, sweeps the
|
|
54
|
+
planted cells it read instead of refusing (L1); marks its transaction with
|
|
55
|
+
`CLEAR_PLANTED_MEMO` in the subscriber's own witness, so a status does not
|
|
56
|
+
take it for a cancel and a charge of the whole balance stays paid (L2).
|
|
57
|
+
- `signAndSend` throws `SendOutcomeUnknownError` (new export) when a send fails
|
|
58
|
+
and the status lookup fails too, instead of rethrowing the send's error: a
|
|
59
|
+
`Dead` answer from CCC's fallback node after a lost reply was retried and
|
|
60
|
+
could pay twice (L4). It is never retried.
|
|
61
|
+
- Payout cells under `payTo` are listed by the indexer's filter (plain and
|
|
62
|
+
empty, or exactly the token with 16 data bytes), so junk planted under an
|
|
63
|
+
anyone-can-pay address no longer pushes the real cells out of reach (L5).
|
|
64
|
+
- `assertSigningLock` refuses anyone-can-pay, PW-Lock and always-success by the
|
|
65
|
+
data hash of their deployed binaries under every data hash_type (`data`,
|
|
66
|
+
`data1`, `data2`), not only by type id (L6).
|
|
67
|
+
|
|
68
|
+
### Changed (Week 6 adversarial review, 2 Nov)
|
|
69
|
+
- `openSubscription` checks the creator's charger lock with its own opt-in,
|
|
70
|
+
`allowUnreviewedChargerLock`; `allowUnreviewedOwnerLock` now covers the
|
|
71
|
+
subscriber's lock only (I1).
|
|
72
|
+
- `openSession` raises `no-rate-limit` for `minInterval` 1 too (I3).
|
|
73
|
+
- The testnet scripts keep `.env.smoke` at the repository root whatever the
|
|
74
|
+
current directory (I4).
|
|
75
|
+
- Docs: a multisig with a since or an Omnilock time-lock also delays the
|
|
76
|
+
owner's sweep (I2).
|
|
77
|
+
|
|
78
|
+
### Added (mentor review, tracker #47, 2 Nov)
|
|
79
|
+
- Payout cell contention. A top-up (`spendInSession` with `topUp`,
|
|
80
|
+
`chargeSubscription`, `openSubscription`) picks the recipient's cell with
|
|
81
|
+
`pickPayoutCell`: confirmed cells first, shuffled, and the first free one of
|
|
82
|
+
up to `MAX_POOL_CHECKS` (4) cells checked with `get_live_cell` and
|
|
83
|
+
`include_tx_pool`, so a cell another payment in the node's pool spends is
|
|
84
|
+
skipped. At most `MAX_PAYOUT_CELLS` (64) of the recipient's cells are read.
|
|
85
|
+
- `spendInSession` and `chargeSubscription` take `retry: { attempts, delayMs }`
|
|
86
|
+
(`RetryOptions`, `DEFAULT_RETRY`: 3 attempts, 1000 ms doubling, with jitter).
|
|
87
|
+
A send the node refuses for a spent input (`Dead`, `Unknown`, or an RBF
|
|
88
|
+
refusal, `poolConflict`) is built again with another payout cell; the cell a
|
|
89
|
+
`Dead` refusal names is not picked again. It used to be retried once, with no
|
|
90
|
+
wait, and only with a client cache. A send whose outcome is unknown is still
|
|
91
|
+
never retried.
|
|
92
|
+
- `SessionBalanceError` reason `"payout-busy"`: every payout cell checked is
|
|
93
|
+
spent in the pool and none is left. Nothing is sent; it is retried like a
|
|
94
|
+
refusal. The demo's charge route answers it with 503 and retry.
|
|
95
|
+
- `createPayoutCells(funder, payTo, { count, capacity?, token? })` gives an
|
|
96
|
+
anyone-can-pay address more cells to top up. `npm run setup:creator` uses it
|
|
97
|
+
and now gives the demo's creator eight cells (was three).
|
|
98
|
+
- `clearPlantedCells(subscriber, deployment, subscription)`: the subscriber
|
|
99
|
+
sweeps cells a third party created under the subscription's lock back to
|
|
100
|
+
itself, keeping the subscription's own cells, so it no longer has to cancel
|
|
101
|
+
and open again past `MAX_SUBSCRIPTION_CELLS`.
|
|
102
|
+
|
|
103
|
+
### Changed (mentor review, 2 Nov)
|
|
104
|
+
- `assertSigningLock` accepts only reviewed owner lock configurations:
|
|
105
|
+
secp256k1, multisig (v1, v2), Omnilock with auth 0x00-0x06 or 0x12 and no
|
|
106
|
+
mode other than time-lock or supply, the Nostr lock in signing mode
|
|
107
|
+
(`args[0]` = 0), and JoyID. The wallet matrix's locks all still pass.
|
|
108
|
+
- PW-Lock is now refused. The build deployed on mainnet and testnet is
|
|
109
|
+
lay2dev/pw-lock's anyone-can-pay variant: with no signature in the witness it
|
|
110
|
+
unlocks by payment, whatever the args, so anyone could sweep every session.
|
|
111
|
+
- The Nostr lock in proof of work mode (`args[0]` not 0) is now refused: it
|
|
112
|
+
unlocks with work, not the holder's key.
|
|
113
|
+
- Anyone-can-pay is refused by code hash on both networks (and the older
|
|
114
|
+
deployments pw-core lists), whatever the client's network; always-success
|
|
115
|
+
too. These refusals carry `reason: "owner-lock-unsafe"`.
|
|
116
|
+
- Locks and configurations not reviewed (Omnilock auth 0xFC-0xFE, other Omnilock
|
|
117
|
+
auth flags or mode bits, the ckb-proxy-locks, unknown locks, args the lock's
|
|
118
|
+
code would not accept, multisig v2 beta) are refused with
|
|
119
|
+
`reason: "owner-lock-unreviewed"`. Omnilock auth 0x07 and unknown Omnilock mode
|
|
120
|
+
bits used to pass.
|
|
121
|
+
- Subscription cells are listed with the indexer's type filter (no type, or the
|
|
122
|
+
subscription's token), so cells under any other type script no longer count
|
|
123
|
+
toward `MAX_SUBSCRIPTION_CELLS`.
|
|
124
|
+
- The last charge is the newest spend with an input under the charger's lock,
|
|
125
|
+
not any spend: a `clearPlantedCells` does not delay the next charge. A spend
|
|
126
|
+
that cannot be told apart still counts as a charge.
|
|
127
|
+
- Chained payments are not used; the README says why.
|
|
128
|
+
|
|
129
|
+
### Added
|
|
130
|
+
- `openSession` warns when a session has no rate limit (`minInterval` left out
|
|
131
|
+
or 0): every spend is within `maxPerTx`, but spends can repeat every block
|
|
132
|
+
until the session is empty. The warning (`SessionWarning`, code
|
|
133
|
+
`"no-rate-limit"`) goes to the new `opts.onWarning` before anything is
|
|
134
|
+
signed, which may throw to stop the opening, or to `console.warn` without one.
|
|
135
|
+
The demos' sessions open with no rate limit, so they now warn in the console.
|
|
136
|
+
- 23 CKB-VM tests of aggregate outflow across inputs (mentor review, 2 Nov):
|
|
137
|
+
cells with the same args share one limit per transaction (CKB and token), a
|
|
138
|
+
subscription's merged cells pay one price, cells with other args are checked
|
|
139
|
+
on their own, and owner mode sweeps several cells at once. No fault found; the
|
|
140
|
+
lock is unchanged.
|
|
141
|
+
- README and SECURITY-EVIDENCE: browser expiry revokes nothing on chain (funds
|
|
142
|
+
stay at risk until the owner's sweep is confirmed); a period is a block count,
|
|
143
|
+
not a fixed duration; the aggregate-outflow behaviour.
|
|
144
|
+
- `allowUnreviewedOwnerLock` (`SigningLockOptions`) on `assertSigningLock`,
|
|
145
|
+
`openSession` and `openSubscription`: lets an unreviewed owner or charger lock
|
|
146
|
+
through with a `console.warn`. It never lets an "owner-lock-unsafe" lock
|
|
147
|
+
through.
|
|
148
|
+
- `SessionPolicyReason` gains `"owner-lock-unsafe"` and `"owner-lock-unreviewed"`.
|
|
149
|
+
- `SessionBalanceError.reason` (`SessionBalanceReason`): `"no-fee-cell"` or
|
|
150
|
+
`"no-payout-cell"` when the cells missing are the payer's or the recipient's own
|
|
151
|
+
(a charger with no fee cell, a `payTo` with no cell to top up), so a server can
|
|
152
|
+
tell them from the subscription's state without matching messages. The demo's
|
|
153
|
+
charge route now uses it.
|
|
154
|
+
- `SessionPolicyError.reason` (`SessionPolicyReason`): `"payto-not-anyone-can-pay"`
|
|
155
|
+
when a charge needs to top up `payTo` and `payTo` is not anyone-can-pay. The
|
|
156
|
+
demo's charge route answers it with 503 (the deployment's setup, not the
|
|
157
|
+
subscription's state) instead of 409.
|
|
158
|
+
|
|
159
|
+
### Fixed (audit round 2, 31 Oct)
|
|
160
|
+
- Demo: enforced CSP now also sets `object-src 'none'`, `base-uri 'none'` and
|
|
161
|
+
`form-action 'self'`; the full policy (scripts, styles, images, testnet RPC and
|
|
162
|
+
JoyID aggregator only) is sent as `Content-Security-Policy-Report-Only`.
|
|
163
|
+
- CI: GitHub actions pinned to commit SHAs, checkouts with
|
|
164
|
+
`persist-credentials: false`.
|
|
165
|
+
- `openSession` and `openSubscription` refuse `TESTNET_LEGACY_DEPLOYMENTS`
|
|
166
|
+
(`assertCurrentDeployment`, new). `openSession` used to accept them: a token
|
|
167
|
+
session under the 13 Oct lock was stuck for good (that code refuses 128/160-byte
|
|
168
|
+
args before owner mode), and new sessions under the 8 Oct one lacked errors
|
|
169
|
+
16-18. Spending, closing, recovering, charging and cancelling still accept old
|
|
170
|
+
deployments, so funds under them can come back.
|
|
171
|
+
- Before building an opening, the deployment's cell dep is read from the
|
|
172
|
+
client's network (`getCellLive`; a dep group is resolved) and must hold code
|
|
173
|
+
hashing to `codeHash` (data hash, or type hash for `hashType: "type"`).
|
|
174
|
+
`TESTNET_DEPLOYMENT` on a mainnet client, a typo in `codeHash` or a wrong out
|
|
175
|
+
point used to put funds under a lock with no code. One lookup per client and
|
|
176
|
+
deployment. `hashType: "data"` is refused too: the lock faults under CKB-VM
|
|
177
|
+
version 0. There is still no mainnet deployment; README and API.md say how to
|
|
178
|
+
pass your own.
|
|
179
|
+
- `spendInSession` and `chargeSubscription`: a key or charger cell above its
|
|
180
|
+
floor but short of the fee threw CCC's plain "Insufficient CKB"; it is now
|
|
181
|
+
`SessionBalanceError` with reason `"no-fee-cell"`. The charger's fee cells must
|
|
182
|
+
hold at least the floor plus 0.01 CKB. Found by the round-2 property suite,
|
|
183
|
+
added in `test/property-library.test.ts` and `test/property-fuzz.test.ts`,
|
|
184
|
+
which now fails on any plain error.
|
|
185
|
+
- `addTopUp` (top-ups in `spendInSession`, `chargeSubscription`,
|
|
186
|
+
`openSubscription`) skips a payee token cell the payment would take past
|
|
187
|
+
2^128 - 1 units, and throws `SessionBalanceError` `"no-payout-cell"` if every
|
|
188
|
+
cell would, instead of a plain overflow error.
|
|
189
|
+
- `closeSession` refuses a signer whose lock is not `binding.ownerLock` with a
|
|
190
|
+
`SessionPolicyError`, before asking it to sign. After a wallet switch, the
|
|
191
|
+
new wallet was shown a sweep of the old wallet's session to itself (the lock
|
|
192
|
+
refused it on-chain, so nothing was lost).
|
|
193
|
+
- `closeSession` can finish once the session is empty on-chain: with the
|
|
194
|
+
funding committed and nothing indexed, it asks the node whether the funding's
|
|
195
|
+
outputs under the session and the key are still live, and reports
|
|
196
|
+
`nothingFound` when all are spent. Before, a close whose key return was taken
|
|
197
|
+
but whose reply was lost (or a close from a second tab) left a key that could
|
|
198
|
+
never be forgotten. The key return now goes through `signAndSend`, so a lost
|
|
199
|
+
reply the node did take is reported as `keyTx`.
|
|
200
|
+
It reports `nothingFound` only once the node's indexer has reached the tip
|
|
201
|
+
and a second search still finds nothing, so a later spend's key cell that is
|
|
202
|
+
not indexed yet never lets the key be forgotten.
|
|
203
|
+
- CCC's client cache lists a client's own outputs as live until cleared, so a
|
|
204
|
+
tab (or a warm server instance) built every later spend on cells another tab
|
|
205
|
+
had spent, and the node rejected each one. `spendInSession`, `closeSession`,
|
|
206
|
+
`chargeSubscription` and `cancelSubscription` now leave out cached-only cells
|
|
207
|
+
the node no longer has, and on a rejection for a spent or unknown input clear
|
|
208
|
+
the cache and build the transaction once more.
|
|
209
|
+
- `verifyAccess`, `verifySubscription` and `subscriptionStatus` read payments
|
|
210
|
+
with `getTransactionNoCache`. `getTransaction` answers from the cache when
|
|
211
|
+
the node no longer knows a transaction, so a payment that was reorged out kept
|
|
212
|
+
granting access on a long-lived server client.
|
|
213
|
+
- Demo: one "unknown" answer just after a lost send no longer makes a page
|
|
214
|
+
forget the session key (about 62 CKB stranded once the funding lands) or a
|
|
215
|
+
payment record. A key or record is forgotten only when the node rejected the
|
|
216
|
+
transaction, or has not known it for 10 minutes (the rule the payment polls
|
|
217
|
+
already used); meanwhile the page keeps polling it.
|
|
218
|
+
- Demo: every page's wallet lookup drops a late answer from the previous
|
|
219
|
+
wallet, and the subscribe page drops a status refresh started for the
|
|
220
|
+
previous wallet, asset or opening, and clears its status and notes on
|
|
221
|
+
disconnect.
|
|
222
|
+
- Demo: the CKB Place board asks again about a placement `get_transaction`
|
|
223
|
+
missed once, instead of leaving it off the board until a reload.
|
|
224
|
+
- Demo: the charge route builds a new CCC client per request.
|
|
225
|
+
|
|
226
|
+
### Fixed (security audit, 30 Oct)
|
|
227
|
+
- `recoverSessions`: when the one sweep is refused, sessions are now swept one
|
|
228
|
+
session lock at a time (full lock hash), not one session key hash at a time. A
|
|
229
|
+
planted cell under other args carrying the victim's public key hash (a foreign
|
|
230
|
+
type script the sweep cannot consume) joined the victim's group and blocked its
|
|
231
|
+
recovery on every retry. `findOwnedSessionCells` keeps a typed cell only if its
|
|
232
|
+
type is a plain xUDT on the network (`assertPlainXudt`'s rules) holding exactly
|
|
233
|
+
the 16-byte amount a session makes.
|
|
234
|
+
- `spendInSession` pays the fee from the largest key cell (ties: lowest out
|
|
235
|
+
point), not the first one listed: a 61 CKB cell anyone sent to the key address,
|
|
236
|
+
or the old key cell beside a top-up, blocked every spend. Still one key cell in
|
|
237
|
+
and one smaller one out, as the lock requires.
|
|
238
|
+
- `verifyAccess` and `checkPlacement` count only memos in witnesses a signature
|
|
239
|
+
covers: the first witness of each secp256k1 lock group, and any past the
|
|
240
|
+
inputs. The session cell's and an anyone-can-pay cell's witnesses are signed by
|
|
241
|
+
nobody, so whoever relayed a payment could add a memo there and void it for
|
|
242
|
+
good (or, on a CKB Place board, choose the pixels of a payment without one).
|
|
243
|
+
Several signed memos still fail. `placementMemo` takes the resolved inputs as
|
|
244
|
+
an optional third argument for the exact check; without them it is a prefilter.
|
|
245
|
+
- Docs: with a recipient set, up to `max_per_tx` per transaction can still leave
|
|
246
|
+
as the miner fee (the recipient rule covers outputs only). The README and
|
|
247
|
+
`openSession` now say so and recommend a rate limit (`minInterval`) whenever a
|
|
248
|
+
recipient is set. The lock is unchanged.
|
|
249
|
+
- `subscriptionStatus`, `chargeSubscription` and `findSubscriptionCells` read at
|
|
250
|
+
most `MAX_SUBSCRIPTION_CELLS` (16, exported) cells under a subscription's lock,
|
|
251
|
+
and at most 64 recent transactions of its history. Beyond that they throw
|
|
252
|
+
`SessionPolicyError` with the new reason `"too-many-cells"` instead of reading
|
|
253
|
+
part of the cells, which could deny a paid-up subscriber or let a cell left out
|
|
254
|
+
of a charge pay a second price in the period. Planted cells (anyone can create
|
|
255
|
+
them) made each status request look up every creating transaction and all its
|
|
256
|
+
inputs again: 100 cells cost 3,307 RPC calls. `cancelSubscription` still reads
|
|
257
|
+
and sweeps every cell, so the subscriber can clear planted ones.
|
|
258
|
+
- Subscription payments are checked once per transaction within a call, and a
|
|
259
|
+
transaction whose outputs to `payTo` already fall short is refused before its
|
|
260
|
+
inputs are looked up.
|
|
261
|
+
- `chargeSubscription` picks its fee cell from the first 64 cells under the
|
|
262
|
+
charger's lock instead of reading all of them.
|
|
263
|
+
- Demo: `/api/subscription` answers `"too-many-cells"` with 409 (the page shows
|
|
264
|
+
it), not 503 with retry.
|
|
265
|
+
- Demo: the charger address is the creator's key, so `CHARGER_KEY` on the server
|
|
266
|
+
could spend the creator's income and payout cells. New `npm run setup:charger`
|
|
267
|
+
creates a separate key (`.env.charger`, written once, mode 0600) and prints its
|
|
268
|
+
address and next steps; the docs now require a charger key of its own holding
|
|
269
|
+
only fee cells. `CHARGER` is replaced once that key is funded and set on Vercel.
|
|
270
|
+
|
|
271
|
+
### Fixed (30 Oct)
|
|
272
|
+
- `chargeSubscription` and `nextChargeIn` date the last charge from the
|
|
273
|
+
subscription lock's indexer history, not from its change cell: a charge of the
|
|
274
|
+
whole balance (the last 153 CKB with a price of 153 CKB or more) leaves no
|
|
275
|
+
change, so a younger top-up could pay a second price under a period later.
|
|
276
|
+
- `subscriptionStatus` counts that last whole-balance charge as a paid period;
|
|
277
|
+
it used to end access as soon as the last period was paid. A cancel still ends
|
|
278
|
+
access at once.
|
|
279
|
+
|
|
280
|
+
### Fixed (code review, 28 Oct)
|
|
281
|
+
- Token overflow guard: a planted cell of 2^128-1 free tokens could push the
|
|
282
|
+
subscriber's own cell out of a cancel (the guard kept cells in indexer order), and
|
|
283
|
+
blocked `closeSession` and `recoverSessions` of token sessions (no guard at all).
|
|
284
|
+
One guard now serves sessions, recovery and subscriptions; it keeps cells created
|
|
285
|
+
by the owner or the key first.
|
|
286
|
+
- `recoverSessions`, `closeSession`, `cancelSubscription`: an owner sweep the node
|
|
287
|
+
accepted is reported as sent even when the reply times out (it skipped the
|
|
288
|
+
check `signAndSend` does). `recoverSessions` now throws when the owner holds no
|
|
289
|
+
plain cell, instead of reporting every session as failed.
|
|
290
|
+
- `chargeSubscription` waits a full period after the last charge, not only after
|
|
291
|
+
the oldest cell: a top-up cell older than the last charge's change could pay a
|
|
292
|
+
second price in the same period. `nextChargeIn` counts the same way.
|
|
293
|
+
- Session stores: a save of the same key without the on-chain binding (from a
|
|
294
|
+
stale tab) is refused with `SessionStoreConflict` instead of dropping the binding.
|
|
295
|
+
- Demo: the charge route answers 503 with `retry` when the creator's own cells are
|
|
296
|
+
missing (no fee cell for the charger, no payout cell to top up), not 409.
|
|
297
|
+
- `chargeSubscription` pays its fee from a random one of the charger's cells, and a
|
|
298
|
+
CKB `openSubscription` tops up a random payout cell, so concurrent charges and
|
|
299
|
+
openings rarely compete for one cell.
|
|
300
|
+
|
|
301
|
+
### Changed
|
|
302
|
+
- `verifyAccess` resolves a payment's inputs in parallel once the memo check
|
|
303
|
+
passes; subscription charges and status fetch each creating transaction once.
|
|
304
|
+
|
|
305
|
+
## 1.0.0 (2026-10-27)
|
|
306
|
+
|
|
307
|
+
First stable release. Everything below is on testnet, with a smoke script and explorer
|
|
308
|
+
links in the README.
|
|
309
|
+
|
|
310
|
+
### Session lock (Rust)
|
|
311
|
+
- Delegates to a standard owner lock; in session mode it enforces a per-payment
|
|
312
|
+
outflow limit, an optional recipient, and a per-cell rate limit (`min_interval`).
|
|
313
|
+
- Refuses splitting (error 16), a growing key cell when a recipient is set (17), and
|
|
314
|
+
type scripts in session mode (18).
|
|
315
|
+
- Token sessions (xUDT): a per-payment limit in token units (20); tokens always come
|
|
316
|
+
back as tokens (19).
|
|
317
|
+
- Current testnet deployment exported as `TESTNET_DEPLOYMENT`; earlier ones stay in
|
|
318
|
+
`TESTNET_LEGACY_DEPLOYMENTS` so old sessions remain sweepable.
|
|
319
|
+
|
|
320
|
+
### Library
|
|
321
|
+
- `openSession`, `spendInSession`, `closeSession`: one owner signature to open, none to
|
|
322
|
+
pay, one to close. Keys are saved before funding (`beforeSend`).
|
|
323
|
+
- Browser stores (`indexedDbStore`) that refuse cross-tab overwrites.
|
|
324
|
+
- Device-loss recovery: `recoverSessions` / `findOwnedSessionCells` sweep every
|
|
325
|
+
session back with the owner's wallet alone.
|
|
326
|
+
- Pay-per-read: payments top up the creator's anyone-can-pay cell; `paymentMemo`,
|
|
327
|
+
`signAccess`, `verifyAccess` check paid access on-chain, in CKB or tokens, with no
|
|
328
|
+
database. One payment, one claim.
|
|
329
|
+
- CKB Place: a pixel board rebuilt from the chain (`place` exports).
|
|
330
|
+
- Subscriptions on the same lock: the creator charges a fixed price once per period,
|
|
331
|
+
the subscriber cancels alone; paid in CKB or in a plain xUDT token.
|
|
332
|
+
|
|
333
|
+
### Docs and demo
|
|
334
|
+
- `docs/API.md` (a test checks every export is documented), runnable `examples/`,
|
|
335
|
+
`docs/WALKTHROUGH.md`, a "Limits" section in the README.
|
|
336
|
+
- Demo at ckb-session-kit.vercel.app: pay-per-read (CKB and tokens), CKB Place,
|
|
337
|
+
pay-per-minute, subscriptions (CKB and tokens), recovery.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luong Thanh Tung
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|