@kirkelabs/walletless-kit 0.1.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 ADDED
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+
3
+ All notable changes are documented here. Format: [Keep a Changelog](https://keepachangelog.com/);
4
+ versioning: [SemVer](https://semver.org/).
5
+
6
+ ## [0.1.0] — 2026-06-20
7
+
8
+ - Initial release. A walletless web-architecture toolkit built on
9
+ `@kirkelabs/open-agent-access-core` and `@kirkelabs/oaa-agent-kit`.
10
+ - **Onboarding** (`createEphemeralAccount`, `rotateAccount`, `expireAccount`,
11
+ `isExpired`) — tightly-scoped, round-relative auto-expiring custodial accounts,
12
+ authority-bounded via an oaa-agent-kit mandate.
13
+ - **Identity** (`OtpIdentity`) — email/SMS OTP adapter: CSPRNG codes, single-use,
14
+ expiring, rate-limited, lockout, constant-time compare; stores only keyed
15
+ (peppered) pseudonymous contact references.
16
+ - **Receipts** (`buildOrderReceipt`, `deterministicOrderId`, `attestOnChain`) —
17
+ hash-chained, signed, non-PII receipts with compact, size- and network-bound
18
+ on-chain attestation; x402-charged actions reuse oaa-agent-kit `payAndFetch`.
19
+ - **Audit** (`createTrail`, `append`, `merkleRoot`, `anchor`, `verifyTrail`) —
20
+ append-only hash-chained events + an RFC 6962-style domain-separated Merkle root,
21
+ periodically anchored on-chain; tamper/removal is detectable.
22
+ - **Ledger** (`createLedger`, `reconciliationSheet`) — three segregated append-only
23
+ books (inflow/charity/escrow), integer-only money, immutable snapshots, and a
24
+ human-readable per-draw reconciliation sheet.
25
+ - **Draw** (`runDraw`, `publishDrawProof`, `blockHashSeed`/`vrfSeed`/`beaconSeed`) —
26
+ deterministic, recomputable winner selection (seeded Fisher–Yates, rejection
27
+ sampling, no `Math.random`), with a commit step and honest seed-manipulability docs.
28
+ Ships a frozen test vector.
29
+ - **Privacy** (`hashPii`, `pseudonymRef`, `eraseSubject`) — keyed hashing and
30
+ random, erasable references; PII off-chain only.
31
+ - CLI (`walletless init|keygen|draw|reconcile|verify|help`), a TestNet raffle
32
+ example, README, SECURITY, and LEGAL (gambling + AML + data-protection). MIT.
33
+
34
+ > ⚠ Some modules are **EXPERIMENTAL · UNAUDITED**. TestNet by default; obtain an
35
+ > independent audit before holding material value or processing real personal data.
package/LEGAL.md ADDED
@@ -0,0 +1,79 @@
1
+ # Legal, regulatory & acceptable-use notice
2
+
3
+ `@kirkelabs/walletless-kit` is free, open-source developer software (MIT). It provides
4
+ **transparency and bookkeeping tooling** for walletless commerce and prize-draws — it
5
+ does **not** provide legal, regulatory, financial, or compliance services, and using it
6
+ does not make your activity lawful. This notice is **not legal advice**; if you operate
7
+ in any regulated context, take your own advice. You, the operator, are solely
8
+ responsible for compliance with all applicable laws in every jurisdiction you serve.
9
+
10
+ ## Nature of the software
11
+
12
+ A toolkit of independent modules: ephemeral custodial onboarding, receipt-only on-chain
13
+ proofs, a tamper-evident audit trail, segregated money ledgers, a verifiable draw, and
14
+ privacy/erasure helpers. Kirke Labs operates no servers in your flow, holds no funds, and
15
+ holds no keys or personal data. Everything runs in **your** infrastructure under **your**
16
+ control.
17
+
18
+ ## 1. Prize draws, raffles & lotteries (gambling law)
19
+
20
+ Prize draws, raffles, sweepstakes, and lotteries are **heavily regulated** and the rules
21
+ differ by country, state, and province (e.g. the UK Gambling Act 2005 and the Gambling
22
+ Commission; US state lottery/raffle statutes; varied EU regimes). **This package ships
23
+ transparency tooling, not legal compliance.** In particular, **you** are responsible for:
24
+
25
+ - obtaining any **licence/registration/exempt-lottery** status you need;
26
+ - providing a genuine **free-entry route** of equal standing where required;
27
+ - **age and geographic gating** (excluding minors and prohibited jurisdictions);
28
+ - prize fulfilment, terms & conditions, advertising rules, and record-keeping;
29
+ - handling and protecting entrant funds appropriately.
30
+
31
+ A "verifiable" or "recomputable" draw means the winner can be re-derived from a published
32
+ seed — it is **not** a representation that your draw is lawful, nor a substitute for
33
+ licensing. **Draw fairness is exactly as strong as the public seed you choose** (see the
34
+ README): block-hash seeds are validator-manipulable; use the commit step and prefer a VRF
35
+ or randomness beacon for anything of value. Do not describe a draw as "provably fair"
36
+ beyond what the chosen seed actually guarantees.
37
+
38
+ ## 2. Money handling, custody & AML
39
+
40
+ The kit can hold value in **ephemeral custodial accounts** and **segregated escrow/charity
41
+ ledgers**. Holding or transmitting other people's money may make you a regulated entity
42
+ (money transmission / e-money / payment services) and typically triggers **AML/CFT and
43
+ sanctions** obligations (KYC, screening, record-keeping). The custodial keys are
44
+ **dev/TestNet-grade**: production custody requires your own KMS/HSM, segregated trust
45
+ accounts, reconciliation controls, and an independent audit. The kit performs **no** KYC,
46
+ sanctions screening, or licensing checks — those are yours. It transacts in the native
47
+ Algorand coin (ALGO) only and is **TestNet by default**; MainNet is an explicit,
48
+ cautioned opt-in.
49
+
50
+ ## 3. Personal data & privacy (GDPR / data protection)
51
+
52
+ If you process entrants' contact details or other personal data, **you are the data
53
+ controller**. The kit keeps personal data **off-chain** (you encrypt and can erase it) and
54
+ places only **non-identifying references** on-chain. Two honest limits:
55
+
56
+ - **Hashed contact references are pseudonymous, not anonymous** — they remain *personal
57
+ data*. Use the keyed (peppered) hashing provided, never bare hashes of low-entropy
58
+ values like emails or phone numbers.
59
+ - **The blockchain is immutable.** "Erasure" works only because on-chain data is designed
60
+ to be non-identifying and unlinkable once the off-chain record (and any random reference
61
+ mapping) is deleted. Do **not** put personal data on-chain; if you do, it cannot be
62
+ erased. You remain responsible for lawful basis, retention, subject-access, and erasure.
63
+
64
+ ## No warranty; experimental
65
+
66
+ Provided **"as is", without warranty** (see [LICENSE](./LICENSE)). Modules — and especially
67
+ anything labelled **EXPERIMENTAL · UNAUDITED** — are not guaranteed fit for any purpose.
68
+ Crypto-assets are volatile and largely unregulated. **You may lose funds and you bear all
69
+ compliance risk.** Nothing here is financial, legal, or investment advice.
70
+
71
+ ## Jurisdiction
72
+
73
+ Published globally as open source; **not targeted at any jurisdiction** and not an offer to
74
+ provide any regulated service. You determine whether your use is lawful where you operate.
75
+
76
+ ## Reporting
77
+
78
+ Security issues: **security@kirkelabs.com** (please do not open public issues for
79
+ vulnerabilities affecting funds or personal data).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kirke Labs — Soleman El Gelawi and Steve Kirton
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.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @kirkelabs/walletless-kit
2
+
3
+ [![License: MIT](https://img.shields.io/badge/license-MIT-00dc94?style=flat)](./LICENSE)
4
+ [![Node](https://img.shields.io/badge/node-%3E%3D20-00dc94?style=flat)](https://nodejs.org)
5
+ [![Algorand](https://img.shields.io/badge/Algorand-TestNet%20by%20default-00dc94?style=flat)](https://algorand.co)
6
+
7
+ **Walletless web architecture: onboard users with no wallet, prove what happened with receipts (not personal data), keep a tamper-evident audit trail and clean money trails, and run a draw anyone can recompute.** Charity prize-draws are the flagship example; every module is reusable for any walletless commerce flow.
8
+
9
+ ```bash
10
+ npm i @kirkelabs/walletless-kit
11
+ npx walletless init my-raffle
12
+ ```
13
+
14
+ > ⚠️ **Experimental developer tooling, provided "as is."** It handles **custodial keys, money, and personal data**, and prize draws are **regulated**. This is *transparency tooling, not legal compliance*. Read **[Guardrails](#guardrails)** and **[LEGAL.md](./LEGAL.md)** before using real funds or real personal data. Nothing here is financial or legal advice. TestNet by default.
15
+
16
+ Built on [`@kirkelabs/open-agent-access-core`](https://www.npmjs.com/package/@kirkelabs/open-agent-access-core) (proof/receipt spine) and [`@kirkelabs/oaa-agent-kit`](https://www.npmjs.com/package/@kirkelabs/oaa-agent-kit) (Algorand spend/identity/x402). The only new cryptographic primitive here is the Merkle root in `audit.js`.
17
+
18
+ ## What's in the box
19
+
20
+ | Module | What it does |
21
+ |--------|--------------|
22
+ | **onboarding** | `createEphemeralAccount` / `rotateAccount` / `expireAccount` / `isExpired` — tightly-scoped, **round-relative auto-expiring** custodial accounts; authority bounded by an oaa-agent-kit mandate. |
23
+ | **identity** | `OtpIdentity` — email/SMS OTP: CSPRNG codes, single-use, expiring, rate-limited, lockout, constant-time compare; stores only **keyed (peppered) pseudonymous** contact refs. |
24
+ | **receipt** | `buildOrderReceipt` / `deterministicOrderId` / `signReceipt` / `verifyReceiptChain` / `attestOnChain` — hash-chained, signed, **non-PII** receipts; only the receipt *hash* goes on-chain. x402 actions via `chargeForAction`. |
25
+ | **audit** | `createTrail` / `append` / `merkleRoot` / `merkleProof` / `verifyMerkleProof` / `anchor` / `verifyTrail` — append-only hash-chained events + an **RFC 6962** Merkle root, periodically anchored on-chain. |
26
+ | **ledger** | `createLedger` — three segregated append-only books (inflow / charity / escrow), **integer-only money**, immutable snapshots, and a per-draw `reconciliationSheet`. |
27
+ | **draw** | `runDraw` / `publishDrawProof` / `verifyDraw` + `commitSeedSource` / `blockHashSeed` / `vrfSeed` / `beaconSeed` — deterministic, recomputable winner selection (no `Math.random`). |
28
+ | **privacy** | `hashPii` / `pseudonymRef` / `eraseSubject` / `assertNoPii` — keyed hashing and random, **erasable** references; PII stays off-chain. |
29
+
30
+ ## Quickstart
31
+
32
+ ```js
33
+ import {
34
+ createLedger, OtpIdentity, buildOrderReceipt, createTrail, append,
35
+ runDraw, publishDrawProof, verifyDraw,
36
+ } from '@kirkelabs/walletless-kit';
37
+
38
+ // 1) Identity-lite: OTP, storing only a keyed pseudonymous ref (never the email).
39
+ const id = new OtpIdentity({ pepper: process.env.PEPPER, send: sendEmail });
40
+ await id.issueChallenge('alice@example.com');
41
+ const { ok, contactRef } = await id.verifyChallenge('alice@example.com', code);
42
+
43
+ // 2) A non-PII order receipt, hash-chained and (optionally) signed.
44
+ const receipt = buildOrderReceipt({ orderId: 'o1', action: 'buy_ticket', quantity: 1, price: '1000000', agent: contactRef });
45
+
46
+ // 3) A tamper-evident audit trail.
47
+ let trail = createTrail();
48
+ trail = append(trail, { type: 'ticket_sold', orderId: 'o1' });
49
+
50
+ // 4) Segregated money books.
51
+ const ledger = createLedger();
52
+ ledger.post({ book: 'inflow', amountMicro: 1_000_000, kind: 'ticket', txRef: 'tx1' });
53
+
54
+ // 5) A verifiable draw — anyone can recompute the winner from the proof.
55
+ const entries = [contactRef /* … */];
56
+ const proof = publishDrawProof({ entries, seed: committedSeed, winners: 1 });
57
+ console.log(verifyDraw(proof, entries).ok); // true
58
+ ```
59
+
60
+ Run the full on-chain flow on TestNet: [`examples/raffle.js`](./examples/raffle.js).
61
+
62
+ ## CLI
63
+
64
+ ```bash
65
+ walletless init [dir] # scaffold a starter raffle (ships a .gitignore)
66
+ walletless keygen --out owner.json # dev account, written 0600 (never printed)
67
+ walletless draw --entries entries.json --seed <s> # run + print a recomputable draw proof
68
+ walletless reconcile --ledger ledger.json # print a reconciliation sheet
69
+ walletless verify --proof proof.json --entries entries.json # recompute a draw / audit trail
70
+ walletless help
71
+ ```
72
+
73
+ ## Guardrails
74
+
75
+ These are non-negotiable; the modules enforce or document them.
76
+
77
+ - **TestNet by default.** MainNet is an explicit, cautioned opt-in.
78
+ - **Custodial keys are sensitive.** Ephemeral account keys are server-held, tightly-scoped, and auto-expiring — and **dev/TestNet-grade**. JS cannot wipe key memory; production custody needs your own **KMS/HSM** and an independent audit. Secret keys are never logged, serialized into receipts/events/snapshots, or written on-chain.
79
+ - **Personal data stays off-chain.** The chain holds only non-identifying references. **Hashed contact refs are pseudonymous, not anonymous** — still personal data; use the keyed (peppered) hashing provided, never bare hashes. For erasable references, `pseudonymRef` gives a **random, deletable** on-chain ref so `eraseSubject` truly unlinks the subject.
80
+ - **Draw fairness equals the seed — no more.** A block-hash seed is **manipulable** (a block producer can withhold/grind a block). Use `commitSeedSource` to announce the exact future round **before entries close**, and prefer a **VRF / drand beacon** for anything of value. Winner selection is a deterministic, recomputable function of `(seed, entries)` with rejection sampling — there is **no `Math.random`**. Don't call a draw "provably fair" beyond what your seed guarantees.
81
+ - **Money is integer microALGO.** Never floats (they drift and break reconciliation). Ledger books are append-only with immutable snapshots.
82
+ - **Regulated use.** Prize draws/lotteries are regulated; custodial money handling implies AML/custody duties; processing entrant data makes you a **GDPR data controller**. This package ships transparency tooling, **not** legal compliance — you own licensing, the **free-entry route**, and **age/geo gating**. See **[LEGAL.md](./LEGAL.md)**.
83
+ - **Anything experimental is labelled EXPERIMENTAL · UNAUDITED.** Get an independent audit before holding material value or processing real personal data.
84
+
85
+ ## Supply chain
86
+
87
+ Pinned dependency floors + committed lockfile; CI runs lint, tests, and `npm audit` on Node 20 & 22 with least-privilege permissions and SHA-pinned actions; `release.yml` publishes with **npm provenance** via OIDC. Report security issues to **security@kirkelabs.com** ([SECURITY.md](./SECURITY.md)).
88
+
89
+ ## Licence
90
+
91
+ [MIT](./LICENSE) © 2026 Kirke Labs — [www.kirkelabs.com](https://www.kirkelabs.com)
package/bin/cli.js ADDED
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * walletless-kit CLI
4
+ *
5
+ * walletless init [dir] scaffold a starter raffle project
6
+ * walletless keygen [--out <file>] generate a dev Algorand account
7
+ * walletless draw --entries <f> --seed <s> [--winners N] run + print a draw proof
8
+ * walletless reconcile --ledger <f> [--draw id] print a reconciliation sheet
9
+ * walletless verify --proof <f> [--entries <f>] recompute a draw proof / audit trail
10
+ * walletless help
11
+ */
12
+
13
+ import { writeFile, mkdir, readFile } from 'node:fs/promises';
14
+ import { resolve, join } from 'node:path';
15
+ import {
16
+ algosdk,
17
+ publishDrawProof,
18
+ verifyDraw,
19
+ verifyTrail,
20
+ createLedger,
21
+ } from '../src/index.js';
22
+
23
+ // Copied from @kirkelabs/oaa-agent-kit — same flag-parsing convention.
24
+ function parse(argv) {
25
+ const o = { _: [] };
26
+ for (let i = 3; i < argv.length; i++) {
27
+ const a = argv[i];
28
+ if (a.startsWith('--')) o[a.slice(2)] = argv[i + 1]?.startsWith('--') ? true : argv[++i];
29
+ else o._.push(a);
30
+ }
31
+ return o;
32
+ }
33
+
34
+ function fail(msg) {
35
+ console.error(`✗ ${msg}`);
36
+ process.exit(1);
37
+ }
38
+
39
+ async function main() {
40
+ const cmd = process.argv[2];
41
+ const o = parse(process.argv);
42
+
43
+ if (cmd === 'keygen') {
44
+ const a = algosdk.generateAccount();
45
+ const account = { address: String(a.addr), mnemonic: algosdk.secretKeyToMnemonic(a.sk) };
46
+ if (o.out) {
47
+ await writeFile(resolve(o.out), JSON.stringify(account, null, 2), { mode: 0o600 });
48
+ console.log(`Wrote account to ${resolve(o.out)} (keep the mnemonic secret).`);
49
+ } else {
50
+ console.log(JSON.stringify(account, null, 2));
51
+ console.log(
52
+ '\n⚠ The mnemonic above is a SECRET (printed to your terminal). Anyone with it' +
53
+ '\n controls the account. Prefer: walletless keygen --out owner.json' +
54
+ '\n⚠ Dev only. Fund on TestNet via https://bank.testnet.algorand.network/',
55
+ );
56
+ }
57
+ return;
58
+ }
59
+
60
+ if (cmd === 'draw') {
61
+ if (!o.entries || !o.seed) return fail('draw requires --entries <file.json> and --seed <seed>');
62
+ const entries = JSON.parse(await readFile(resolve(o.entries), 'utf8'));
63
+ const proof = publishDrawProof({
64
+ entries,
65
+ seed: String(o.seed),
66
+ winners: parseInt(o.winners || '1', 10),
67
+ });
68
+ console.log(JSON.stringify(proof, null, 2));
69
+ return;
70
+ }
71
+
72
+ if (cmd === 'reconcile') {
73
+ if (!o.ledger) return fail('reconcile requires --ledger <file.json> (an array of ledger entries or {drawId,entries})');
74
+ const data = JSON.parse(await readFile(resolve(o.ledger), 'utf8'));
75
+ const entries = Array.isArray(data) ? data : data.entries || [];
76
+ const l = createLedger();
77
+ for (const e of entries) l.post(e);
78
+ const drawId = o.draw || data.drawId || 'draw';
79
+ console.log(JSON.stringify(l.reconciliationSheet(drawId, { winnerProofLink: o.proof || null }), null, 2));
80
+ return;
81
+ }
82
+
83
+ if (cmd === 'verify') {
84
+ const path = o.proof || o.file || o._[0];
85
+ if (!path) return fail('verify requires --proof <file.json> (a draw proof or an audit trail)');
86
+ const obj = JSON.parse(await readFile(resolve(path), 'utf8'));
87
+ if (obj && obj.entriesRoot) {
88
+ if (!o.entries) return fail('verifying a draw proof also requires --entries <file.json>');
89
+ const entries = JSON.parse(await readFile(resolve(o.entries), 'utf8'));
90
+ console.log(JSON.stringify(verifyDraw(obj, entries), null, 2));
91
+ } else {
92
+ console.log(JSON.stringify(verifyTrail(obj), null, 2));
93
+ }
94
+ return;
95
+ }
96
+
97
+ if (cmd === 'init') {
98
+ const dir = resolve(o._[0] || 'my-raffle');
99
+ await mkdir(dir, { recursive: true });
100
+ await writeFile(join(dir, 'package.json'), STARTER_PKG);
101
+ await writeFile(join(dir, 'raffle.js'), STARTER_RAFFLE);
102
+ await writeFile(join(dir, '.env.example'), STARTER_ENV);
103
+ await writeFile(join(dir, '.gitignore'), STARTER_GITIGNORE);
104
+ await writeFile(join(dir, 'README.md'), STARTER_README);
105
+ console.log(
106
+ `Scaffolded a raffle in ${dir}\n cd ${dir} && npm install && cp .env.example .env && node raffle.js`,
107
+ );
108
+ return;
109
+ }
110
+
111
+ help();
112
+ }
113
+
114
+ function help() {
115
+ console.log(`
116
+ walletless-kit — walletless web architecture (onboarding · proofs · audit · ledger · draw)
117
+
118
+ Commands
119
+ init [dir] scaffold a starter raffle project
120
+ keygen [--out <file>] generate a dev Algorand account (mnemonic)
121
+ draw --entries <f> --seed <s> [--winners N]
122
+ run a draw and print its recomputable proof
123
+ reconcile --ledger <f> [--draw id] print a per-draw reconciliation sheet
124
+ verify --proof <f> [--entries <f>] recompute a draw proof or an audit trail
125
+ help
126
+
127
+ TestNet by default. Prize draws/lotteries are regulated — see LEGAL.md.
128
+ MIT · Kirke Labs · free & open source
129
+ `);
130
+ }
131
+
132
+ const STARTER_PKG = `{
133
+ "name": "my-raffle",
134
+ "private": true,
135
+ "type": "module",
136
+ "dependencies": { "@kirkelabs/walletless-kit": "^0.1.0" }
137
+ }
138
+ `;
139
+
140
+ const STARTER_ENV = `# Operator account (dev). Generate with: npx walletless keygen --out owner.json
141
+ # SECRET — never commit your real .env. The 25 words control all funds.
142
+ OPERATOR_MNEMONIC="word1 word2 ... word25"
143
+ NETWORK=algorand-testnet
144
+ `;
145
+
146
+ const STARTER_GITIGNORE = `# Secrets — NEVER commit these. The mnemonic controls all funds.
147
+ .env
148
+ *.key
149
+ owner.json
150
+ node_modules/
151
+ `;
152
+
153
+ const STARTER_RAFFLE = `import {
154
+ getAlgod, runDraw, publishDrawProof, verifyDraw,
155
+ createLedger, OtpIdentity, deterministicOrderId,
156
+ } from '@kirkelabs/walletless-kit';
157
+
158
+ // A minimal, OFFLINE walletless raffle: entries -> draw -> proof -> verify.
159
+ // See the package's examples/raffle.js for the full on-chain TestNet flow.
160
+ const entries = ['ref_alice', 'ref_bob', 'ref_carol', 'ref_dave'];
161
+ const seed = 'demo-seed-replace-with-a-committed-block-hash';
162
+
163
+ const proof = publishDrawProof({ entries, seed, winners: 1 });
164
+ console.log('Winner:', proof.winners[0]);
165
+ console.log('Proof verifies:', verifyDraw(proof, entries).ok);
166
+
167
+ // NOTE: a real draw must use a COMMITTED public seed (announce the future block
168
+ // round before entries close) — see the README guardrails. Prize draws are
169
+ // regulated; you own licensing, the free-entry route, and age/geo gating.
170
+ `;
171
+
172
+ const STARTER_README = `# my-raffle
173
+
174
+ A starter walletless raffle built with @kirkelabs/walletless-kit.
175
+
176
+ 1. \`npx walletless keygen --out owner.json\` (TestNet operator account)
177
+ 2. Fund it on TestNet: https://bank.testnet.algorand.network/
178
+ 3. \`npm install && node raffle.js\`
179
+
180
+ ⚠ Prize draws/lotteries are REGULATED. This is transparency tooling, not legal
181
+ compliance: you are responsible for licensing, a genuine free-entry route, and
182
+ age/geo gating. Draw fairness equals the public seed you choose — use a committed
183
+ block hash or a VRF/beacon. See the package LEGAL.md.
184
+ `;
185
+
186
+ main().catch((e) => {
187
+ console.error(e.message || e);
188
+ process.exit(1);
189
+ });
@@ -0,0 +1,101 @@
1
+ /**
2
+ * examples/raffle.js — a full walletless charity prize-draw on Algorand TestNet.
3
+ *
4
+ * Flow: ephemeral guest account -> OTP (stub) -> non-PII order receipts +
5
+ * on-chain attestation -> audit trail + on-chain anchor -> a draw seeded by a
6
+ * TestNet block hash -> reconciliation sheet -> verify recomputes the winner.
7
+ *
8
+ * Run: OPERATOR_MNEMONIC="..." node examples/raffle.js
9
+ * (Fund the operator on TestNet: https://bank.testnet.algorand.network/)
10
+ *
11
+ * ⚠ Demo only. Prize draws are regulated and a block-hash seed is manipulable —
12
+ * see the README guardrails and LEGAL.md. TestNet by default.
13
+ */
14
+
15
+ import {
16
+ getAlgod,
17
+ createEphemeralAccount,
18
+ isExpired,
19
+ OtpIdentity,
20
+ buildOrderReceipt,
21
+ deterministicOrderId,
22
+ attestOnChain,
23
+ createTrail,
24
+ append,
25
+ trailRoot,
26
+ anchor,
27
+ createLedger,
28
+ publishDrawProof,
29
+ verifyDraw,
30
+ blockHashSeed,
31
+ } from '@kirkelabs/walletless-kit';
32
+ import { LocalOwnerSigner } from '@kirkelabs/oaa-agent-kit';
33
+
34
+ const mnemonic = process.env.OPERATOR_MNEMONIC;
35
+ if (!mnemonic) throw new Error('Set OPERATOR_MNEMONIC (a funded TestNet account).');
36
+
37
+ const algod = getAlgod({ network: 'algorand-testnet' });
38
+ const operator = new LocalOwnerSigner({ mnemonic });
39
+ const PEPPER = process.env.PEPPER || 'demo-pepper-at-least-16-chars';
40
+ const log = (...a) => console.log(...a);
41
+
42
+ log('Operator:', operator.address);
43
+ const sp = await algod.getTransactionParams().do();
44
+ log('Current round ~', Number(sp.firstValid), '\n');
45
+
46
+ // 1) Ephemeral custodial guest account (round-relative expiry).
47
+ const guest = await createEphemeralAccount({ algod, ttlRounds: 10_000, scope: { perTxMicroAlgos: 1_000_000 } });
48
+ log('[1] Ephemeral guest account:', guest.address);
49
+ log(' expiresRound', guest.expiryRound, '| expired now?', isExpired(guest, Number(sp.firstValid)));
50
+
51
+ // 2) Identity-lite: OTP (we capture the code instead of emailing it).
52
+ const codes = {};
53
+ const id = new OtpIdentity({ pepper: PEPPER, send: async (c, code) => (codes[c] = code) });
54
+ const entrants = ['alice@example.com', 'bob@example.com', 'carol@example.com'];
55
+ const refs = [];
56
+ for (const e of entrants) {
57
+ await id.issueChallenge(e);
58
+ const v = await id.verifyChallenge(e, codes[e]);
59
+ refs.push(v.contactRef);
60
+ }
61
+ log('\n[2] OTP-verified', refs.length, 'entrants (stored as keyed refs, not emails)');
62
+
63
+ // 3) Non-PII order receipts (hash-chained) + attest the latest hash on-chain.
64
+ const ledger = createLedger();
65
+ let trail = createTrail();
66
+ let prevHash = null;
67
+ let lastReceipt = null;
68
+ refs.forEach((ref, i) => {
69
+ const orderId = deterministicOrderId({ ref, item: 'raffle-ticket', n: i });
70
+ const receipt = buildOrderReceipt({ orderId, action: 'buy_ticket', quantity: 1, price: '1000000', agent: ref, previousHash: prevHash });
71
+ prevHash = receipt.receiptHash;
72
+ lastReceipt = receipt;
73
+ ledger.post({ book: 'inflow', amountMicro: 1_000_000, kind: 'ticket', ref: orderId });
74
+ trail = append(trail, { type: 'ticket_sold', orderId, eventId: `evt_${i}` });
75
+ });
76
+ const att = await attestOnChain(algod, operator, lastReceipt);
77
+ log('\n[3] Receipts chained; latest hash attested on-chain in round', att.confirmedRound);
78
+ log(' txid', att.txid);
79
+
80
+ // 4) Audit trail -> Merkle root -> on-chain anchor.
81
+ const root = trailRoot(trail);
82
+ const anc = await anchor(algod, operator, root);
83
+ log('\n[4] Audit Merkle root', root.slice(0, 16) + '… anchored in round', anc.confirmedRound);
84
+
85
+ // 5) Allocate fee + charity in segregated books.
86
+ ledger.post({ book: 'inflow', amountMicro: 100_000, kind: 'fee' });
87
+ ledger.post({ book: 'charity', amountMicro: 2_500_000, ref: operator.address, kind: 'charity_payout', txRef: att.txid });
88
+
89
+ // 6) Draw, seeded by a recent TestNet block hash (committed in advance in production).
90
+ const seedRound = Number(sp.firstValid) - 5; // a confirmed round
91
+ const seed = await blockHashSeed(algod, seedRound);
92
+ log('\n[5] Seed from TestNet block', seedRound, '(⚠ manipulable — commit in advance / use VRF for value)');
93
+ const proof = publishDrawProof({ entries: refs, seed: seed.value, winners: 1 });
94
+ log(' Winner ref:', proof.winners[0]);
95
+
96
+ // 7) Reconciliation sheet + independent verification.
97
+ const sheet = ledger.reconciliationSheet('demo-draw-1', { winnerProofLink: `algo:${anc.txid}` });
98
+ log('\n[6] Reconciliation:', JSON.stringify(sheet.charity), '| net', sheet.netMicroAlgos, 'µALGO');
99
+ log('[7] verifyDraw recomputes the winner:', verifyDraw(proof, refs).ok);
100
+
101
+ log('\n✅ Walletless raffle complete on TestNet: ephemeral onboarding → OTP → receipts+attestation → audit anchor → verifiable draw → reconciliation.');
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@kirkelabs/walletless-kit",
3
+ "version": "0.1.0",
4
+ "description": "Walletless web architecture toolkit: low-friction onboarding (ephemeral custodial accounts), receipt-only on-chain proofs, a tamper-evident hash-chained + Merkle audit trail, segregated money ledgers, and a verifiable, recomputable draw. Charity prize-draws are the flagship example; every module is reusable for any walletless commerce flow. Built on @kirkelabs/open-agent-access-core and @kirkelabs/oaa-agent-kit. Free & open source from Kirke Labs.",
5
+ "type": "module",
6
+ "bin": {
7
+ "walletless": "bin/cli.js"
8
+ },
9
+ "exports": {
10
+ ".": "./src/index.js"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "src/",
15
+ "examples/",
16
+ "LICENSE",
17
+ "LEGAL.md",
18
+ "README.md",
19
+ "CHANGELOG.md"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "scripts": {
25
+ "test": "node --test",
26
+ "lint": "eslint . --ext .js",
27
+ "format": "prettier --write \"**/*.{js,json,md}\"",
28
+ "prepublishOnly": "npm run lint && npm test"
29
+ },
30
+ "keywords": [
31
+ "walletless",
32
+ "algorand",
33
+ "custodial",
34
+ "onboarding",
35
+ "audit-trail",
36
+ "merkle",
37
+ "verifiable-draw",
38
+ "raffle",
39
+ "charity",
40
+ "receipts",
41
+ "x402",
42
+ "oaa",
43
+ "privacy",
44
+ "gdpr",
45
+ "cli",
46
+ "nodejs"
47
+ ],
48
+ "author": "Soleman El Gelawi <soleman@kirkelabs.com> (https://www.kirkelabs.com)",
49
+ "contributors": [
50
+ "Steve Kirton <steve@kirkelabs.com> (https://www.kirkelabs.com)"
51
+ ],
52
+ "license": "MIT",
53
+ "homepage": "https://github.com/KirkeLabs/walletless-kit",
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/KirkeLabs/walletless-kit.git"
57
+ },
58
+ "bugs": {
59
+ "url": "https://github.com/KirkeLabs/walletless-kit/issues"
60
+ },
61
+ "engines": {
62
+ "node": ">=20"
63
+ },
64
+ "dependencies": {
65
+ "algosdk": "^3.6.0",
66
+ "@kirkelabs/open-agent-access-core": "^0.1.0",
67
+ "@kirkelabs/oaa-agent-kit": "^0.7.1"
68
+ },
69
+ "devDependencies": {
70
+ "eslint": "^9.0.0",
71
+ "prettier": "^3.0.0"
72
+ }
73
+ }