@keicoin/claims
v0.6.0
Published
Merkle-rooted claims for Kei: root building, proofs, and player-signed claim blocks.
Maintainers
Readme
@keicoin/claims
Merkle-rooted claims: root building, proofs, and player-signed claim blocks.
Part of
kei-transaction— real currencies and items for browser games. Installkei-transactioninstead unless you are counting bytes; these sub-packages exist for bundle size, not as a puzzle you have to solve.
bun add @keicoin/claims # or npm / pnpm / yarnWhat is in here
A thousand loot drops in one block. Minting per drop makes the issuer's account a global write lock, so the issuer publishes one root and each player writes their own claim from their own chain, in parallel.
const drop = await gems.commit([
{ to: playerA, amount: 500 },
{ to: playerB, amount: 120 },
// ...thousands more
])
send(playerA, drop.proofFor(playerA)) // plain JSON
await kei.claims.add(bundle) // player sideThe default claim store is memory-only, matching earlier releases. To make a browser wallet recover the proof after recreation, inject the provided adapter:
import { Kei, createBrowserClaimStore } from 'kei-transaction'
const kei = await Kei.start({
claimStore: createBrowserClaimStore(localStorage),
})
const status = await kei.claims.storageStatus()
status.durability // 'persistent' after opting into this adapter
status.diagnostics // typed, bounded remediation records; never secretsThe built-in browser adapter uses the origin-wide Web Locks API to serialize
each wallet/network namespace. If navigator.locks is unavailable or refuses a
lock, storage fails closed: no retained record is loaded or signed, and the
status contains an actionable diagnostic. Tests or embedded browser runtimes
may inject a shared ClaimWebLockManager as the adapter's second argument.
Records are namespaced by network, wallet address, and root. Every public claim path uses the adapter's optional atomic admission capability, then reads back the separately admitted exact bytes before signing. The built-in browser store requires a wallet signature bound to network, address, root, and those exact bytes. Rejected raw values are never signed; successful/already-claimed/closed roots are removed durably, and startup retries retained claims. The finite public limits are 128 records per wallet/network, 16,384 serialised bytes per record, 128 sibling hashes per proof, and 39 decimal amount digits (the ledger's unsigned 128-bit field). Unsupported versions, failed integrity checks, and malformed or over-budget records are diagnosed and never signed.
Bundles reveal award metadata even though they contain no seed, key, or server
credential. Browser storage is recovery, not a backup; inject a store whose
privacy and durability fit the application. A Node process can implement the
same small ClaimStore interface without making the game or issuer a custodian.
Persistent custom adapters should implement admit and readAdmitted; older
adapters fail closed before mutation until upgraded. Browser namespace v4 binds
a domain-separated wallet signature to the exact stored envelope and namespace.
The adapter rejects non-canonical Ed25519 signatures (including equivalent
S + L encodings) before verification, so failed admission cannot become valid
after a restart. The signed wallet scope also prevents cross-account replay.
Legacy browser namespace v1-v3 records (including v2 boolean markers and v3
public digests) and claim-envelope v1/v2 records have no current admission
authority: they are not signed and require explicit re-add, which writes a v3
envelope into the wallet-authorised v4 namespace.
A forged proof, a forged amount, or a second claim from the same account is rejected by the ledger, not by the SDK. Roots are salted, so two identical batches are two distinct drops.
The salt is a leaf like any other, and drop.saltProof is its path to the root.
Nothing claims against it — a salt is not an entitlement — but it is space in the
tree the root already commits to, and a caller that puts something meaningful in
the salt can prove it is there afterwards. @keicoin/economy uses exactly that to
bind a loot table's digest to the batch published for it. For a random salt the
path proves only that the salt is this root's, which is true and uninteresting.
Status
M3 of eleven. The public API now uses a real node at
https://testnet.keicoin.org/rpc; MockNode remains the hermetic reference
implementation. The testnet is one best-effort node with weak consensus and
nothing there holds value.
See the full documentation.
The storage design and failure ordering are recorded in
docs/decisions-claims-durability.md.
MIT.
