@defdone/annon 0.0.0-stage → 0.1.0-alpha.1
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 +63 -0
- package/CLI.md +378 -0
- package/LICENSE +201 -0
- package/NOTICE +7 -0
- package/README.md +591 -2
- package/dist/api/annon-types.d.ts +108 -0
- package/dist/api/annon-types.js +2 -0
- package/dist/api/annon.d.ts +117 -0
- package/dist/api/annon.js +1152 -0
- package/dist/api/hosted-record.d.ts +13 -0
- package/dist/api/hosted-record.js +149 -0
- package/dist/api/sharing-types.d.ts +109 -0
- package/dist/api/sharing-types.js +1 -0
- package/dist/api/sharing.d.ts +36 -0
- package/dist/api/sharing.js +879 -0
- package/dist/browser/download.d.ts +19 -0
- package/dist/browser/download.js +52 -0
- package/dist/browser/index.d.ts +23 -0
- package/dist/browser/index.js +22 -0
- package/dist/browser/login-bundle.js +151 -0
- package/dist/browser/login.d.ts +1 -0
- package/dist/browser/login.js +216 -0
- package/dist/browser/purchase-bundle.js +84 -0
- package/dist/browser/purchases.d.ts +98 -0
- package/dist/browser/purchases.js +735 -0
- package/dist/browser/record.d.ts +55 -0
- package/dist/browser/record.js +16 -0
- package/dist/browser/selling.d.ts +146 -0
- package/dist/browser/selling.js +418 -0
- package/dist/browser/wallets.d.ts +53 -0
- package/dist/browser/wallets.js +286 -0
- package/dist/bytes.d.ts +4 -0
- package/dist/bytes.js +16 -0
- package/dist/download/download.d.ts +59 -0
- package/dist/download/download.js +384 -0
- package/dist/download/host.d.ts +56 -0
- package/dist/download/host.js +161 -0
- package/dist/download/http.d.ts +23 -0
- package/dist/download/http.js +87 -0
- package/dist/download/proof.d.ts +20 -0
- package/dist/download/proof.js +79 -0
- package/dist/download/upload.d.ts +130 -0
- package/dist/download/upload.js +408 -0
- package/dist/envelope/evm.d.ts +4 -0
- package/dist/envelope/evm.js +32 -0
- package/dist/envelope/pay2.d.ts +176 -0
- package/dist/envelope/pay2.js +541 -0
- package/dist/envelope/replay.d.ts +45 -0
- package/dist/envelope/replay.js +186 -0
- package/dist/envelope/stream.d.ts +78 -0
- package/dist/envelope/stream.js +372 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.js +300 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +23 -0
- package/dist/keys/near-ckd.d.ts +47 -0
- package/dist/keys/near-ckd.js +96 -0
- package/dist/limits.d.ts +4 -0
- package/dist/limits.js +4 -0
- package/dist/near/nep413.d.ts +22 -0
- package/dist/near/nep413.js +39 -0
- package/dist/near/rpc.d.ts +170 -0
- package/dist/near/rpc.js +423 -0
- package/dist/near/texts.d.ts +39 -0
- package/dist/near/texts.js +60 -0
- package/dist/net/ip.d.ts +7 -0
- package/dist/net/ip.js +62 -0
- package/dist/net/retry.d.ts +3 -0
- package/dist/net/retry.js +31 -0
- package/dist/node/adapter-checks.d.ts +6 -0
- package/dist/node/adapter-checks.js +13 -0
- package/dist/node/adapter.d.ts +8 -0
- package/dist/node/adapter.js +98 -0
- package/dist/node/agent.d.ts +49 -0
- package/dist/node/agent.js +266 -0
- package/dist/node/amounts.d.ts +2 -0
- package/dist/node/amounts.js +20 -0
- package/dist/node/browser-login.d.ts +25 -0
- package/dist/node/browser-login.js +219 -0
- package/dist/node/budget.d.ts +24 -0
- package/dist/node/budget.js +59 -0
- package/dist/node/buy-commands.d.ts +24 -0
- package/dist/node/buy-commands.js +301 -0
- package/dist/node/cli.d.ts +60 -0
- package/dist/node/cli.js +201 -0
- package/dist/node/command-context.d.ts +82 -0
- package/dist/node/command-context.js +77 -0
- package/dist/node/config.d.ts +37 -0
- package/dist/node/config.js +189 -0
- package/dist/node/core-adapter.d.ts +43 -0
- package/dist/node/core-adapter.js +122 -0
- package/dist/node/errors.d.ts +8 -0
- package/dist/node/errors.js +12 -0
- package/dist/node/index.d.ts +7 -0
- package/dist/node/index.js +5 -0
- package/dist/node/io.d.ts +13 -0
- package/dist/node/io.js +112 -0
- package/dist/node/keychain.d.ts +10 -0
- package/dist/node/keychain.js +67 -0
- package/dist/node/main.d.ts +2 -0
- package/dist/node/main.js +21 -0
- package/dist/node/offers.d.ts +16 -0
- package/dist/node/offers.js +91 -0
- package/dist/node/origin.d.ts +11 -0
- package/dist/node/origin.js +80 -0
- package/dist/node/paths.d.ts +3 -0
- package/dist/node/paths.js +71 -0
- package/dist/node/payment-terms.d.ts +26 -0
- package/dist/node/payment-terms.js +127 -0
- package/dist/node/purchase-commands.d.ts +16 -0
- package/dist/node/purchase-commands.js +237 -0
- package/dist/node/purchase-guard.d.ts +19 -0
- package/dist/node/purchase-guard.js +53 -0
- package/dist/node/relay-transport.d.ts +6 -0
- package/dist/node/relay-transport.js +173 -0
- package/dist/node/resources.d.ts +2 -0
- package/dist/node/resources.js +26 -0
- package/dist/node/retry-gate.d.ts +15 -0
- package/dist/node/retry-gate.js +53 -0
- package/dist/node/seller-commands.d.ts +12 -0
- package/dist/node/seller-commands.js +207 -0
- package/dist/node/sharing.d.ts +3 -0
- package/dist/node/sharing.js +28 -0
- package/dist/node/shops.d.ts +17 -0
- package/dist/node/shops.js +74 -0
- package/dist/node/signer-mock.d.ts +11 -0
- package/dist/node/signer-mock.js +79 -0
- package/dist/node/signer-wallet.d.ts +8 -0
- package/dist/node/signer-wallet.js +149 -0
- package/dist/node/signing.d.ts +29 -0
- package/dist/node/signing.js +200 -0
- package/dist/node/storage-commands.d.ts +11 -0
- package/dist/node/storage-commands.js +50 -0
- package/dist/node/sync-dir.d.ts +1 -0
- package/dist/node/sync-dir.js +11 -0
- package/dist/node/wallets.d.ts +13 -0
- package/dist/node/wallets.js +146 -0
- package/dist/oneclick/oneclick.d.ts +132 -0
- package/dist/oneclick/oneclick.js +342 -0
- package/dist/pay/backup.d.ts +70 -0
- package/dist/pay/backup.js +402 -0
- package/dist/pay/base.d.ts +135 -0
- package/dist/pay/base.js +386 -0
- package/dist/pay/eip3009.d.ts +59 -0
- package/dist/pay/eip3009.js +72 -0
- package/dist/pay/pay.d.ts +86 -0
- package/dist/pay/pay.js +297 -0
- package/dist/pay/purchase.d.ts +103 -0
- package/dist/pay/purchase.js +20 -0
- package/dist/pay/recover.d.ts +31 -0
- package/dist/pay/recover.js +34 -0
- package/dist/pay/seller.d.ts +130 -0
- package/dist/pay/seller.js +444 -0
- package/dist/pay/settle.d.ts +96 -0
- package/dist/pay/settle.js +308 -0
- package/dist/pay/split.d.ts +115 -0
- package/dist/pay/split.js +256 -0
- package/dist/pay/store-node.d.ts +8 -0
- package/dist/pay/store-node.js +224 -0
- package/dist/pay/store.d.ts +54 -0
- package/dist/pay/store.js +135 -0
- package/dist/pay/x402.d.ts +95 -0
- package/dist/pay/x402.js +136 -0
- package/dist/relay/admission.d.ts +8 -0
- package/dist/relay/admission.js +58 -0
- package/dist/relay/capabilities.d.ts +36 -0
- package/dist/relay/capabilities.js +232 -0
- package/dist/relay/fees.d.ts +17 -0
- package/dist/relay/fees.js +105 -0
- package/dist/relay/index.d.ts +10 -0
- package/dist/relay/index.js +8 -0
- package/dist/relay/keeper-balance.d.ts +17 -0
- package/dist/relay/keeper-balance.js +46 -0
- package/dist/relay/keeper-runner.d.ts +114 -0
- package/dist/relay/keeper-runner.js +703 -0
- package/dist/relay/keeper.d.ts +38 -0
- package/dist/relay/keeper.js +73 -0
- package/dist/relay/ledger.d.ts +155 -0
- package/dist/relay/ledger.js +392 -0
- package/dist/relay/relay-checks.d.ts +3 -0
- package/dist/relay/relay-checks.js +244 -0
- package/dist/relay/relay-reconciliation.d.ts +15 -0
- package/dist/relay/relay-reconciliation.js +293 -0
- package/dist/relay/relay-request.d.ts +6 -0
- package/dist/relay/relay-request.js +139 -0
- package/dist/relay/relay-transactions.d.ts +23 -0
- package/dist/relay/relay-transactions.js +182 -0
- package/dist/relay/relay-types.d.ts +128 -0
- package/dist/relay/relay-types.js +17 -0
- package/dist/relay/relay.d.ts +102 -0
- package/dist/relay/relay.js +426 -0
- package/dist/relay/reservation.d.ts +29 -0
- package/dist/relay/reservation.js +132 -0
- package/dist/relay/sponsorship.d.ts +42 -0
- package/dist/relay/sponsorship.js +184 -0
- package/dist/routes.d.ts +2 -0
- package/dist/routes.js +1 -0
- package/dist/settle-near/codec.d.ts +17 -0
- package/dist/settle-near/codec.js +86 -0
- package/dist/settle-near/identity.d.ts +27 -0
- package/dist/settle-near/identity.js +80 -0
- package/dist/settle-near/purchase.d.ts +40 -0
- package/dist/settle-near/purchase.js +153 -0
- package/dist/settle-near/terms.d.ts +37 -0
- package/dist/settle-near/terms.js +89 -0
- package/dist/shops/discovery.d.ts +95 -0
- package/dist/shops/discovery.js +384 -0
- package/dist/wallet/account-only.d.ts +11 -0
- package/dist/wallet/account-only.js +6 -0
- package/dist/wallet/uncertain.d.ts +8 -0
- package/dist/wallet/uncertain.js +13 -0
- package/dist/wallet/wallet.d.ts +100 -0
- package/dist/wallet/wallet.js +204 -0
- package/package.json +136 -3
package/README.md
CHANGED
|
@@ -1,3 +1,592 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @defdone/annon
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Annon SDK and the `annon` command line tool. A seller encrypts a file into PAY2 and registers
|
|
4
|
+
its terms on the Annon contract on NEAR. A buyer pays in USDC on NEAR, on Base through x402, or from
|
|
5
|
+
another chain through NEAR Intents, and the contract releases the file key to the buyer alone. The
|
|
6
|
+
SDK runs in Node.js and in browsers; the CLI is built on it for developers and agents. People buying
|
|
7
|
+
files in a browser should use the browser purchase flow.
|
|
8
|
+
|
|
9
|
+
Protocol overview: the [repository README](https://github.com/defdone/annon/blob/master/README.md).
|
|
10
|
+
Walkthroughs: [buying](https://github.com/defdone/annon/blob/master/docs/quickstart.md),
|
|
11
|
+
[selling](https://github.com/defdone/annon/blob/master/docs/selling.md) and
|
|
12
|
+
[agents](https://github.com/defdone/annon/blob/master/docs/agents.md). Command reference:
|
|
13
|
+
[CLI.md](CLI.md). Security model and known limits:
|
|
14
|
+
[SECURITY.md](https://github.com/defdone/annon/blob/master/SECURITY.md).
|
|
15
|
+
|
|
16
|
+
The SDK and CLI have no usage fees or daily upload or sales quotas. Shops set their own hosting and
|
|
17
|
+
sponsorship limits ([shops and limits](https://github.com/defdone/annon/blob/master/docs/shops-and-limits.md)).
|
|
18
|
+
|
|
19
|
+
## Contents
|
|
20
|
+
|
|
21
|
+
- [Install](#install)
|
|
22
|
+
- [Quick start](#quick-start)
|
|
23
|
+
- [Browser purchases](#browser-purchases)
|
|
24
|
+
- [Hosted selling](#hosted-selling)
|
|
25
|
+
- [CLI browser wallet login](#cli-browser-wallet-login)
|
|
26
|
+
- [Agent initialization and spending policy](#agent-initialization-and-spending-policy)
|
|
27
|
+
- [Remote signer, HTTP JSON v1](#remote-signer-http-json-v1)
|
|
28
|
+
- [Durable keeper](#durable-keeper)
|
|
29
|
+
- [RPC configuration](#rpc-configuration)
|
|
30
|
+
- [Base splits](#base-splits)
|
|
31
|
+
- [Previews](#previews)
|
|
32
|
+
- [Security notes](#security-notes)
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
Node.js 22 or newer. The package is on the npm `alpha` tag.
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
npm i @defdone/annon@alpha # the SDK
|
|
40
|
+
npm i -g @defdone/annon@alpha # the annon CLI
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Entry point | Use |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `@defdone/annon` | SDK core: `Annon`, wallets, identities, terms, 1Click, encrypted stores and backups, storage funding, sponsorship checks and types, size/retry limits, error classes |
|
|
46
|
+
| `@defdone/annon/browser` | Browser purchase controller, downloads, wallet adapters, sponsorship checks and types, size/retry limits, error classes |
|
|
47
|
+
| `@defdone/annon/browser/purchase-bundle.js` | The same browser entry as one standalone ESM bundle |
|
|
48
|
+
| `@defdone/annon/node` | Node only: `createAdapter` (the engine behind the CLI and the MCP server), `publicError`, `AdapterError`, `BudgetError` |
|
|
49
|
+
| `@defdone/annon/relay` | Relayer admission, durable sponsorship and settlement: `relay`, `RelayLedger`, `keep` and their types |
|
|
50
|
+
| `@defdone/annon/pay/store-node` | `fileStore`, a durable file-backed record store for Node |
|
|
51
|
+
| `@defdone/annon/node/signer-mock` | `signerMock`, the reference signing handler for tests |
|
|
52
|
+
| `@defdone/annon/pay/seller`, `pay/settle`, `pay/store`, `pay/split`, `download/host`, `download/http`, `envelope/pay2`, `near/rpc` | Building blocks for a server that sells files: x402 offers and payment checks, settlement, record stores, revenue splits, grant downloads, the PAY2 format and NEAR RPC reads |
|
|
53
|
+
| `@defdone/annon/node/adapter`, `node/cli`, `node/errors` | Deprecated: use `@defdone/annon/node` |
|
|
54
|
+
|
|
55
|
+
### From source
|
|
56
|
+
|
|
57
|
+
Build with `pnpm build`. In a checkout, run `node packages/annon/dist/node/main.js` in place of
|
|
58
|
+
`annon`. Running the source with a TypeScript runner reads the generated
|
|
59
|
+
`dist/browser/login-bundle.js`, so run `pnpm --filter @defdone/annon build` first; without the bundle,
|
|
60
|
+
`annon login` prints that build instruction.
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
The shortest path is the CLI: [docs/quickstart.md](https://github.com/defdone/annon/blob/master/docs/quickstart.md)
|
|
65
|
+
buys, opens and recovers a demo file in a few commands, and [CLI.md](CLI.md) covers configuration,
|
|
66
|
+
encrypted purchase backups and every command.
|
|
67
|
+
|
|
68
|
+
In code, an `Annon` instance needs a deployment, a durable record store, a persisted backup secret, a
|
|
69
|
+
place to write encrypted backups, and a transport for contract calls. In Node, with a NEAR account
|
|
70
|
+
that signs its own calls:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
74
|
+
import { join } from 'node:path';
|
|
75
|
+
import { Annon, nearKeyPairWallet, storageDeposit } from '@defdone/annon';
|
|
76
|
+
import { fileStore } from '@defdone/annon/pay/store-node';
|
|
77
|
+
|
|
78
|
+
const contractId = 'v1.annon.defdone.near';
|
|
79
|
+
const stateDir = '/absolute/private/annon-state';
|
|
80
|
+
const backups = join(stateDir, 'backups');
|
|
81
|
+
for (const dir of [join(stateDir, 'records'), backups])
|
|
82
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
83
|
+
|
|
84
|
+
// 64 hex characters (32 random bytes), created once with a cryptographic generator and kept
|
|
85
|
+
// outside the repository. Records and backups are unreadable without it.
|
|
86
|
+
const backupSecret = Buffer.from(
|
|
87
|
+
(await readFile('/absolute/private/annon-backup-secret', 'utf8')).trim(),
|
|
88
|
+
'hex',
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// `account` is a near-api-js Account for your NEAR account.
|
|
92
|
+
const near = nearKeyPairWallet(account);
|
|
93
|
+
|
|
94
|
+
const annon = new Annon({
|
|
95
|
+
contractId,
|
|
96
|
+
network: 'near:mainnet',
|
|
97
|
+
nearRpc: 'https://rpc.mainnet.fastnear.com',
|
|
98
|
+
baseRpc: process.env.BASE_RPC!, // an endpoint with an API key, see RPC configuration
|
|
99
|
+
rawStore: fileStore(join(stateDir, 'records')),
|
|
100
|
+
backupSecret,
|
|
101
|
+
writeBackup: (name, encrypted) =>
|
|
102
|
+
writeFile(join(backups, encodeURIComponent(name)), encrypted, { mode: 0o600 }),
|
|
103
|
+
// Contract calls (register, claim_key, reopen and others) are signed by the call's NEAR owner,
|
|
104
|
+
// or by your account when the caller is not a NEAR wallet.
|
|
105
|
+
send: (method, args, owner) => (owner ?? near).call({ receiverId: contractId, method, args }),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Direct calls use your prepaid storage balance on the contract. Fund it once, with an amount you
|
|
109
|
+
// choose (yoctoNEAR); insufficient storage is reported as `storage_required`.
|
|
110
|
+
await storageDeposit(near, contractId, 100_000_000_000_000_000_000_000n);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`annon.sell`, `annon.buy`, `annon.open`, `annon.openTo` and `annon.recover` then drive listings and
|
|
114
|
+
purchases with the wallet you pass in (`evmWallet`, `solanaWallet`, `nearKeyPairWallet`,
|
|
115
|
+
`nearSelectorWallet`). Without `send`, a call whose signer is not a NEAR wallet fails with
|
|
116
|
+
"configure a transaction transport for relayed calls".
|
|
117
|
+
|
|
118
|
+
For sponsored calls through a relayer instead of your own NEAR account, `send` posts to the
|
|
119
|
+
relayer's `/v1/relay` endpoint and waits for its result. The CLI implements that transport in
|
|
120
|
+
[`packages/annon/src/node/relay-transport.ts`](https://github.com/defdone/annon/blob/master/packages/annon/src/node/relay-transport.ts).
|
|
121
|
+
Its adapter,
|
|
122
|
+
[`packages/annon/src/node/core-adapter.ts`](https://github.com/defdone/annon/blob/master/packages/annon/src/node/core-adapter.ts),
|
|
123
|
+
and the command modules it calls are the complete reference integration, including trust, prompts
|
|
124
|
+
and backups.
|
|
125
|
+
|
|
126
|
+
## Browser purchases
|
|
127
|
+
|
|
128
|
+
Import the headless purchase controller from `@defdone/annon/browser`. A seller or marketplace
|
|
129
|
+
supplies its own screen, a verified offer and wallet adapters. There is no hosted checkout.
|
|
130
|
+
`dist/browser/purchase-bundle.js` is built with the same CSP checks as the CLI approval bridge.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import {
|
|
134
|
+
browserAnnon,
|
|
135
|
+
browserEvmWallet,
|
|
136
|
+
browserPurchaseStorage,
|
|
137
|
+
BrowserPurchaseController,
|
|
138
|
+
} from '@defdone/annon/browser';
|
|
139
|
+
|
|
140
|
+
const storage = await browserPurchaseStorage();
|
|
141
|
+
const sdk = browserAnnon(storage, trustedSdkOptions);
|
|
142
|
+
const controller = await BrowserPurchaseController.create({ storage, sdk }, {
|
|
143
|
+
id: stableCheckoutId,
|
|
144
|
+
fileId,
|
|
145
|
+
header: checkedHeader,
|
|
146
|
+
resourceUrl,
|
|
147
|
+
route: '1click',
|
|
148
|
+
maxPrice: approvedUsdcCap,
|
|
149
|
+
});
|
|
150
|
+
controller.subscribe(renderProgress);
|
|
151
|
+
await controller.start({ recovery: recoveryWallet, refund: refundIdentity, origin });
|
|
152
|
+
const instructions = await controller.instructions();
|
|
153
|
+
|
|
154
|
+
// Later, once the purchase is accepted:
|
|
155
|
+
const { meta, data } = await controller.open({ onProgress: renderDownload });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Checkouts, reloads and tabs
|
|
159
|
+
|
|
160
|
+
- Use a stable, caller-owned checkout ID for each terms review. Creation refuses an existing ID.
|
|
161
|
+
- `route` is `'near'`, `'base'` or `'1click'`, the same names as in CLI and MCP results. Offer
|
|
162
|
+
`'base'` only when the file's final on-chain terms include a Base payee; otherwise the SDK refuses
|
|
163
|
+
it with `route_unavailable` before any wallet step. For a file without Base terms, a buyer holding
|
|
164
|
+
USDC on Base uses `'1click'` with Base as the source chain.
|
|
165
|
+
- After a reload, construct `new BrowserPurchaseController({ storage, sdk, id })` and call
|
|
166
|
+
`resume()`. Never call `buy()` or create another checkout to resume an uncertain payment.
|
|
167
|
+
- `storage.list()` supplies the purchases list.
|
|
168
|
+
- A renewable two-minute lease protects an active checkout across tabs. `poll`, `recover` and `open`
|
|
169
|
+
refuse a live lease held by another tab with `PurchaseLeaseBusy` (code `lease_busy`, with
|
|
170
|
+
`waitUntil`); `resume` returns without writing. `snapshot()` and progress events carry
|
|
171
|
+
`leaseWaitUntil` (Unix seconds) so the page can say why it is waiting, for example after a crashed
|
|
172
|
+
tab.
|
|
173
|
+
- After an expired lease, an unsigned preparation that never reached the payment boundary becomes
|
|
174
|
+
`abandoned`; the UI may then explicitly create a fresh checkout ID. Signed Base payloads and
|
|
175
|
+
purchases that crossed the payment boundary remain resumable and must never be replaced.
|
|
176
|
+
- Base starts require `submitBase`. Signed payloads become uncertain before the SDK dry run.
|
|
177
|
+
- `snapshot()` and progress events contain public metadata only; persistence records contain private
|
|
178
|
+
recovery material.
|
|
179
|
+
- `instructions()` releases only a saved, proved 1Click quote with the required time margin.
|
|
180
|
+
|
|
181
|
+
### Paying from another chain (1Click)
|
|
182
|
+
|
|
183
|
+
- Use `OneClick.tokens()` and `originFromToken(tokens, assetId, blockchain, refundTo)` for exact
|
|
184
|
+
source selection. Supported sources are the explicitly listed EVM chains, Solana and NEAR.
|
|
185
|
+
- Catalog selection refuses unsupported families and slow chains. Legacy caller-specified origins in
|
|
186
|
+
the other family keep the existing nonempty, whitespace-free refund validation.
|
|
187
|
+
- NEAR catalog selections explicitly request `ORIGIN_CHAIN` refunds. Legacy direct `quote()` callers
|
|
188
|
+
keep their existing NEAR `INTENTS` default.
|
|
189
|
+
- A 1Click source refund and an Annon rejection refund are different obligations. The latter stays
|
|
190
|
+
in NEAR Intents.
|
|
191
|
+
- `OneClick.preview()` returns amounts and timing without a deposit address.
|
|
192
|
+
- `poll()` awaits progress callbacks, saves memo-aware checkpoints and retries transport errors and
|
|
193
|
+
rate limits with backoff. The controller persists checkpoints automatically.
|
|
194
|
+
- Polling remains available after expiry to track late deposits and refunds. Expiry never extends
|
|
195
|
+
the purchase or permits a new deposit. `SUCCESS` still requires contract acceptance.
|
|
196
|
+
|
|
197
|
+
### Storage and receipts
|
|
198
|
+
|
|
199
|
+
- Each checkout is stored in its own encrypted IndexedDB record, with a small metadata index
|
|
200
|
+
committed atomically alongside it. Index contention retries without weakening purchase revision
|
|
201
|
+
checks.
|
|
202
|
+
- Latest purchase and recovery snapshots are kept alongside every recovery revision archive created
|
|
203
|
+
before an expired key is replaced. Portable receipts carry those archives too.
|
|
204
|
+
- The local random backup secret is persisted in IndexedDB for reload recovery. It protects
|
|
205
|
+
encrypted storage from accidental disclosure and shares the embedding page's trust boundary.
|
|
206
|
+
- Export with `controller.exportReceipt(portableSecret)`, using a separate random 32-byte
|
|
207
|
+
`newBackupSecret()` that you keep separately. Import with
|
|
208
|
+
`storage.importReceipt(file, portableSecret)`. Import restores the header, resource URL and
|
|
209
|
+
recovery records, and requires a fresh recovery proof before releasing deposit instructions.
|
|
210
|
+
- IndexedDB failures and stale writes reject before any payment effect.
|
|
211
|
+
|
|
212
|
+
### Downloading and opening
|
|
213
|
+
|
|
214
|
+
- `recover(wallet)` resumes recovery of an accepted purchase on a new device.
|
|
215
|
+
- `open()` downloads the PAY2 file from the saved resource URL with the buyer's grant proof. It
|
|
216
|
+
checks the header against the one pinned before payment and each chunk against that header while
|
|
217
|
+
downloading; the SDK then checks the on-chain listing and grant before it decrypts in memory.
|
|
218
|
+
`open(bytes)` opens a PAY2 file the page already has.
|
|
219
|
+
- The result is `{ meta, data }`. Plaintext and file keys are never written to storage; the record
|
|
220
|
+
only moves to `file_ready`.
|
|
221
|
+
- Every path caps a file at 50 MB of plaintext (`MAX_PLAINTEXT_BYTES`, 50,000,000 bytes). The
|
|
222
|
+
download cap `BROWSER_DOWNLOAD_LIMIT` equals `MAX_PAY2_BYTES`: those 50 MB plus the largest PAY2
|
|
223
|
+
header, metadata and tags. A known larger size is refused before downloading, and a stream that
|
|
224
|
+
passes the cap is aborted. Both raise `DownloadError` with code `too_large`.
|
|
225
|
+
- Pass `signal` to cancel and `onProgress` to follow the `downloading`, `decrypting` and `ready`
|
|
226
|
+
phases. A failed or cancelled download leaves the purchase accepted, so `open` can be retried
|
|
227
|
+
without paying again.
|
|
228
|
+
- `browserDownload` is the same bounded, proof-authorized download without the controller.
|
|
229
|
+
|
|
230
|
+
### Wallet adapters
|
|
231
|
+
|
|
232
|
+
The adapters wrap the provider object a page already has. The SDK imports no wallet library.
|
|
233
|
+
|
|
234
|
+
| Adapter | Provider | Used for |
|
|
235
|
+
| --- | --- | --- |
|
|
236
|
+
| `browserEvmWallet(provider, address)` | EIP-1193 `request` (MetaMask, Coinbase Wallet and others) | Recovery signatures with `personal_sign` on any chain; Base x402 payments with `eth_signTypedData_v4`, after checking or requesting a switch to Base |
|
|
237
|
+
| `browserSolanaWallet(provider, address)` | Injected `publicKey` and `signMessage` | Recovery signatures |
|
|
238
|
+
| `browserNearWallet(provider, accountId)` | NEAR Wallet Selector style: required `getAccounts`, `signMessage` (NEP-413) and `signAndSendTransaction` | Recovery signatures and USDC payments on the NEAR route |
|
|
239
|
+
|
|
240
|
+
Each adapter checks what the wallet returns: the signature is verified against the expected
|
|
241
|
+
identity, the account is still the selected one, and Base payments are signed for chain 8453.
|
|
242
|
+
Failures raise `BrowserWalletError` with a code a page can show: `rejected`, `wrong_chain`,
|
|
243
|
+
`wrong_identity`, `invalid_signature`, `missing_method`, `invalid_response` or `wallet_error`.
|
|
244
|
+
|
|
245
|
+
## Hosted selling
|
|
246
|
+
|
|
247
|
+
`annon.share(options)` sells a file through a shop's host: it quotes the final terms without writing
|
|
248
|
+
anything, calls `onSplit` with the split and fee for approval, saves the pending encrypted backup
|
|
249
|
+
(`confirmBackup` must resolve only after you stored it and its separate secret), then uploads,
|
|
250
|
+
creates the shop's Base split for an EVM seller, registers, funds a NEAR seller's storage and
|
|
251
|
+
publishes. It returns `{ fileId, url, header, expiresAt }`. `expiresAt` is the hosting expiry the
|
|
252
|
+
shop states, in Unix seconds, or `null` when the shop plans no expiry; the SDK sets no maximum of its
|
|
253
|
+
own and refuses a time that has already passed with `file_expired`. A saved completed sale stays
|
|
254
|
+
readable after that time. `hosting.origin` is the host of a shop you approved; there is no default host. Each step is checkpointed in the encrypted sell record, and
|
|
255
|
+
`annon.resumeShare({ fileId, data, seller, hosting })` continues a stopped sale with the original
|
|
256
|
+
file and terms, reconciling any uncertain transaction before it sends another.
|
|
257
|
+
|
|
258
|
+
- Without `near.recipients` the split pays the seller; sponsored EVM and Solana sellers get the
|
|
259
|
+
relayer's fee share added, as with `sell`.
|
|
260
|
+
- Only EVM sellers get Base terms. NEAR and Solana sellers list with `base: null`.
|
|
261
|
+
- A NEAR seller signs the upload with NEP-413 (`hosting.signUpload`; `signNearUpload` in the browser
|
|
262
|
+
entry), sends its own transactions, and pays `listingStorage` and `storageBudget` in yoctoNEAR.
|
|
263
|
+
- `UploadClient` and `uploadText` are exported for shops that drive the upload protocol themselves.
|
|
264
|
+
|
|
265
|
+
In a page, `createBrowserSeller(options)` from `@defdone/annon/browser` returns a `SellerFacade`:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const seller = createBrowserSeller({ ...trustedSdkOptions, host, indexStore, wallet });
|
|
269
|
+
const quote = await seller.quote(connection, { file, title, description, priceUsdc: '1.00' });
|
|
270
|
+
const sale = await seller.begin(quote.quoteId); // after the person approved the quote
|
|
271
|
+
const backup = await sale.exportBackup(); // let the person save it, then:
|
|
272
|
+
await sale.confirmBackup();
|
|
273
|
+
let view = await sale.view();
|
|
274
|
+
while (view.next) view = await sale.next(); // the only method that signs or writes
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- `quote` signs and writes nothing. It shows the shares, fee, NEAR funding, routes and steps.
|
|
278
|
+
- `next()` runs one step. Each step is persisted before its effect, and a lease keeps two tabs from
|
|
279
|
+
running the same sale.
|
|
280
|
+
- After a reload, `active()` returns the open sale. `attachWallet`, `attachFile` and `unlock` supply
|
|
281
|
+
what `view().needs` lists; `discard()` drops a sale.
|
|
282
|
+
- `view().result` holds `{ fileId, url, expiresAt }` once the file is published; `expiresAt` may be
|
|
283
|
+
`null` (no planned expiry). `quote.hostedDays` shows the optional `fileLifetimeDays` option, which
|
|
284
|
+
a page fills from the selected shop's rules; it is absent when the page does not pass it.
|
|
285
|
+
|
|
286
|
+
### Shop discovery
|
|
287
|
+
|
|
288
|
+
`listShops(config, query)` and `catalog(config, query)`, from both entries, read shop manifests and
|
|
289
|
+
catalogs with client-side search and paging and return `{ items, total, nextOffset }`. `config` is the
|
|
290
|
+
public discovery configuration (`shops`, `defaultShop`, `shopDirectoryUrl`); there is no built-in
|
|
291
|
+
shop or directory. A manifest's optional `rules` (`ShopRules`) is validated strictly and returned as
|
|
292
|
+
shop-provided data: on each shop's `info` from `listShops`, and as `rules` with `shop` (`manifest`,
|
|
293
|
+
`name`) from `catalog`. Show it as text from that shop; it grants no trust. Limits and fields:
|
|
294
|
+
[CLI.md](CLI.md#shop-discovery).
|
|
295
|
+
|
|
296
|
+
## CLI browser wallet login
|
|
297
|
+
|
|
298
|
+
```sh
|
|
299
|
+
annon login --wallet evm
|
|
300
|
+
annon login --wallet near --network near:testnet
|
|
301
|
+
annon login --wallet near --no-open
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Login proves control of a browser wallet you already use. It installs no wallet key and grants no
|
|
305
|
+
ongoing signing authority. Every later signing action opens a new local session and needs approval
|
|
306
|
+
in the wallet.
|
|
307
|
+
|
|
308
|
+
| Wallet | How | Signing supported |
|
|
309
|
+
| --- | --- | --- |
|
|
310
|
+
| EVM | Injected EIP-1193 wallet on Base | `personal_sign` for the SDK's authorization texts, `eth_signTypedData_v4` for the exact EIP-3009/x402 payload |
|
|
311
|
+
| NEAR | Meteor Wallet (extension or web popup; mainnet and testnet) or HOT Wallet (mainnet only) through Wallet Selector, picked on the page; account-only sign-in (no `contractId`, no method list) | NEP-413 and `signAndSendTransaction`, including the required deposit `"1"` on `ft_transfer_call` |
|
|
312
|
+
|
|
313
|
+
Browser and remote EVM split-deployment transactions and browser and remote Solana signing are not
|
|
314
|
+
included; the local-key adapters remain available for those workflows.
|
|
315
|
+
|
|
316
|
+
**The local page.** The listener binds `127.0.0.1` on an OS-assigned port before opening the browser.
|
|
317
|
+
Match the short pairing code with the terminal, and review the action, amount, recipients, file and
|
|
318
|
+
chain on the page before approving in the wallet.
|
|
319
|
+
|
|
320
|
+
- Host (including port), Origin and an unpredictable RAM-only capability are checked on action and
|
|
321
|
+
result API calls.
|
|
322
|
+
- In open mode, a same-origin POST bootstraps the capability once into the opened page, then the
|
|
323
|
+
bootstrap route closes. With `--no-open`, bootstrap is disabled and the capability exists only in
|
|
324
|
+
the printed URL fragment. It is never passed to the browser launcher in argv.
|
|
325
|
+
- Bundled scripts, a restrictive CSP, no framing, `no-store` and `no-referrer` are enforced. The page
|
|
326
|
+
connects only to itself, the Wallet Selector's NEAR RPC (`free.rpc.fastnear.com` on mainnet,
|
|
327
|
+
`test.rpc.fastnear.com` on testnet, fixed in the page and independent of your configured RPC) and
|
|
328
|
+
HOT's relay `h4n.app`, and frames only `hot-labs.org`.
|
|
329
|
+
- Actions expire after two minutes and are verified once. Identical popup and opener result
|
|
330
|
+
submissions return the same outcome.
|
|
331
|
+
- Sessions close after the command or after five idle minutes. Active approvals keep their own
|
|
332
|
+
expiry, and each completed approval restarts the idle timer.
|
|
333
|
+
- A rejected or uncertain action must be reviewed at the terminal before another session starts.
|
|
334
|
+
- No wallet result is accepted without a local signature check or an independent finalized RPC
|
|
335
|
+
outcome check.
|
|
336
|
+
|
|
337
|
+
**Remote machines.** `--no-open` prints the full URL and pairing code to stderr. Over SSH, forward
|
|
338
|
+
the printed port with `ssh -L PORT:127.0.0.1:PORT host` and open that exact URL locally. Keep the
|
|
339
|
+
capability URL private.
|
|
340
|
+
|
|
341
|
+
**NEAR wallets.** MyNearWallet is retired and no longer connects to apps, so the page offers Meteor
|
|
342
|
+
Wallet and HOT Wallet. HOT's Wallet Selector module sends no network with its requests, so the page
|
|
343
|
+
offers it for `near:mainnet` only and refuses it, before loading it, for any other network; requests
|
|
344
|
+
for chains other than `near:mainnet` and `near:testnet` are refused. Both return results to the page itself: Meteor through its extension or a
|
|
345
|
+
popup on `wallet.meteorwallet.app`, HOT through its extension or the HOT widget, which the page
|
|
346
|
+
frames from `hot-labs.org` (the only `frame-src` the CSP allows) and which exchanges the request
|
|
347
|
+
through HOT's relay at `h4n.app`. The page removes the capability from browser history immediately,
|
|
348
|
+
and no capability is sent to a wallet. NEP-413 requests carry no callback URL, so the signed hash
|
|
349
|
+
covers the message, nonce and recipient, as [NEP-413](https://github.com/near/NEPs/blob/master/neps/nep-0413.md)
|
|
350
|
+
specifies. Meteor's SDK creates a local key pair during sign-in and sends its public key to the
|
|
351
|
+
wallet; the page never names a contract or methods. The secret stays in the localStorage of the
|
|
352
|
+
session's own `127.0.0.1` origin, is never logged (the bundled SDK's key-pair log is removed) and
|
|
353
|
+
is removed when the page signs out of Meteor before a new sign-in.
|
|
354
|
+
|
|
355
|
+
**What is saved.** Only public identity, network and configuration go into the config file. Login
|
|
356
|
+
works before you choose a contract; NEAR ownership checks use a public mainnet or testnet RPC by
|
|
357
|
+
default, for login only. Configure and trust your reviewed deployment before other commands. Wallet
|
|
358
|
+
and browser internals have their own storage behaviour. Browser approval is still required with
|
|
359
|
+
`--json`; terminal prompts are disabled in that mode.
|
|
360
|
+
|
|
361
|
+
## Agent initialization and spending policy
|
|
362
|
+
|
|
363
|
+
Choose one signer source. Secrets are referenced, never copied into configuration:
|
|
364
|
+
|
|
365
|
+
```sh
|
|
366
|
+
annon init --agent --wallet evm --recovery evm:0x1111111111111111111111111111111111111111 --key-env BOT_EVM_KEY --daily-budget 2
|
|
367
|
+
annon init --agent --wallet near --near-account bot.near --near-key-file /absolute/credentials.json --daily-budget 2
|
|
368
|
+
annon init --agent --wallet near --near-account bot.near --keychain-entry 'near-mainnet-bot.near/bot.near:ed25519:PUBLIC_KEY' --daily-budget 2
|
|
369
|
+
annon init --agent --wallet near --near-account bot.near --new-key --keychain-entry annon-bot --daily-budget 2
|
|
370
|
+
annon init --agent --signer https://signer.example --recovery evm:0x1111111111111111111111111111111111111111 --credential-env SIGNER_TOKEN --daily-budget 2
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Signer sources
|
|
374
|
+
|
|
375
|
+
- Saved file and environment signers require a public identity (`--recovery` or `--near-account`),
|
|
376
|
+
which is checked against the loaded key.
|
|
377
|
+
- When a signer is configured, per-call `--evm-key-file`, `--near-key-file` and `--solana-key-file`
|
|
378
|
+
overrides are refused.
|
|
379
|
+
- `--key-env` stores only an environment variable's name. EVM variables and files contain hex
|
|
380
|
+
private keys; NEAR variables and files contain credential JSON, as in CLI.md.
|
|
381
|
+
- `--new-key` generates directly into an injectable OS keychain interface and returns only public
|
|
382
|
+
identity and key metadata. The shipped OS adapter supports macOS; other platforms can inject an
|
|
383
|
+
adapter or use file, environment or remote references. Tests always inject a fake keychain and
|
|
384
|
+
process.
|
|
385
|
+
- A new NEAR key is **not installed on-chain** by this command: the owner must authorize its public
|
|
386
|
+
key separately. A function-call key cannot attach the one yoctoNEAR required for an FT purchase.
|
|
387
|
+
Agent payment keys must have the authority the action requires.
|
|
388
|
+
|
|
389
|
+
**macOS Keychain.** New entries use `security`'s `-T ""` ACL, which removes automatic access for the
|
|
390
|
+
creator executable, so every agent key read prompts for macOS user approval. **Never click Always
|
|
391
|
+
Allow for agent keys**: doing so restores automatic access. A generic `security` executable cannot
|
|
392
|
+
isolate trusted callers. Review the ACL of existing entries in Keychain Access. For unattended use,
|
|
393
|
+
choose a policy-enforcing remote signer rather than granting `security` automatic access.
|
|
394
|
+
|
|
395
|
+
### Spending policy
|
|
396
|
+
|
|
397
|
+
Agent configuration includes `agent: true`, `dailyBudget` (decimal USDC), `signer` and `workDir`, a
|
|
398
|
+
directory the owner creates and every agent file path must stay inside (`work_dir_required` without
|
|
399
|
+
it). Keep the deployment settings, `maxPrice`, `allowedResources`, named `origins` and trusted
|
|
400
|
+
`moderators` in that same public config.
|
|
401
|
+
|
|
402
|
+
Order matters, because agent mode refuses `init`, `login` and per-call trust or deployment flags:
|
|
403
|
+
|
|
404
|
+
1. Create the working directory.
|
|
405
|
+
2. Write the deployment, `maxPrice`, `workDir` and, after reviewing the contract,
|
|
406
|
+
`trustContract: true` (or trust it once with a human command and `--trust-contract`). Do not set
|
|
407
|
+
`agent: true` yourself.
|
|
408
|
+
3. Run one `annon init --agent ...` from the examples above. It saves the signer, the daily budget
|
|
409
|
+
and `agent: true`.
|
|
410
|
+
|
|
411
|
+
- Agents never prompt. Missing trust, price cap or daily budget causes a refusal.
|
|
412
|
+
- `allowedResources` is optional. Without it, any secure resource URL is allowed within the
|
|
413
|
+
spending limits; an empty list denies every purchase.
|
|
414
|
+
- The full price is reserved atomically before any payment or deposit instructions are prepared.
|
|
415
|
+
- UTC daily usage lives in `stateDir/budgets`, survives restarts, and is shared across chains and
|
|
416
|
+
identities that use that state directory. Concurrent writers use the file store's CAS and locking.
|
|
417
|
+
- Over-budget requests return `budget_exceeded`; missing policy returns `budget_required`.
|
|
418
|
+
- NEAR and Base reservations stay charged after failures and ambiguous outcomes. A 1Click failure
|
|
419
|
+
before deposit instructions are returned releases its reservation; returned instructions keep it
|
|
420
|
+
reserved. There is no other automatic refund.
|
|
421
|
+
|
|
422
|
+
### Pinning the configuration
|
|
423
|
+
|
|
424
|
+
Protect the config and state directory from changes by untrusted tools. The trusted launcher pins
|
|
425
|
+
`ANNON_CONFIG` (or the default config path) and `ANNON_STATE_DIR` (or the configured or default state
|
|
426
|
+
directory).
|
|
427
|
+
|
|
428
|
+
- Once that config has `agent: true`, per-call `--config` changes, `--state-dir`, `init` and `login`
|
|
429
|
+
are refused, and TTY prompts stay disabled.
|
|
430
|
+
- Launch with `ANNON_AGENT=true` to force the policy even if the config lacks `agent: true`. In this
|
|
431
|
+
mode the pinned config is `~/.config/annon/config.json`, and its saved `stateDir` (or
|
|
432
|
+
`~/.local/share/annon`) pins the ledger. A differing `ANNON_CONFIG` is an error
|
|
433
|
+
(`config_conflict`); `ANNON_STATE_DIR` is ignored, and the saved configuration or the default
|
|
434
|
+
owns the ledger location.
|
|
435
|
+
- Custom agent config paths must be set through `ANNON_CONFIG` before initialization; alternate
|
|
436
|
+
`init --config` paths are rejected. Human initialization must happen before handing off this
|
|
437
|
+
launcher environment.
|
|
438
|
+
|
|
439
|
+
**Limit.** An agent with shell access as the same OS user can edit config and ledgers, change
|
|
440
|
+
environment variables, call the SDK directly or use accessible signing credentials. It is outside
|
|
441
|
+
this protection, and CLI budgets are not a security boundary against it. Use OS isolation and a
|
|
442
|
+
remote signer with its own spending policy for that boundary.
|
|
443
|
+
|
|
444
|
+
### Human payments
|
|
445
|
+
|
|
446
|
+
Before a human payment the CLI confirms the current price, split recipients and remainder, resource
|
|
447
|
+
and file, token and network. Browser mode shows these exact terms with each signing request for
|
|
448
|
+
approval in the page and the wallet. Local and remote human signing require an explicit terminal
|
|
449
|
+
confirmation. Purchase and recovery secrets still need the independent backup secret described in
|
|
450
|
+
CLI.md; login does not derive one from a signature.
|
|
451
|
+
|
|
452
|
+
## Remote signer, HTTP JSON v1
|
|
453
|
+
|
|
454
|
+
`init --agent --signer` persists an HTTPS origin and either `--credential-env NAME` or
|
|
455
|
+
`--credential-keychain SERVICE/ACCOUNT`.
|
|
456
|
+
|
|
457
|
+
- Signer origins and credential references can only be set during `init`. Every per-command
|
|
458
|
+
override is rejected before credential lookup.
|
|
459
|
+
- URL credentials, query or fragment tokens, plaintext credential fields and HTTP are rejected.
|
|
460
|
+
Redirects are refused.
|
|
461
|
+
- The bearer value is read at use time and never saved or printed.
|
|
462
|
+
|
|
463
|
+
A provider adapter implements:
|
|
464
|
+
|
|
465
|
+
| Request | Response |
|
|
466
|
+
| --- | --- |
|
|
467
|
+
| `GET /v1/capabilities` | `{version:1, chain, account, operations:[...]}` |
|
|
468
|
+
| `POST /v1/requests` | `{version:1, id, status:"pending"\|"approved"\|"rejected", result?}` |
|
|
469
|
+
| `GET /v1/requests/{id}` | Same status response; idempotent polling, no new signing |
|
|
470
|
+
|
|
471
|
+
Every request uses `Authorization: Bearer <credential>` over HTTPS. JSON request fields are
|
|
472
|
+
`version:1`, a unique `id`, `operation`, `chain`, `account`, `expiresAt` (Unix milliseconds),
|
|
473
|
+
`payload`, and optional `terms` (display and policy context). Integers too large for JSON numbers are
|
|
474
|
+
decimal strings; nonces are byte arrays.
|
|
475
|
+
|
|
476
|
+
- An ID is never reused for a different exact request. Repeated POSTs must return the same job,
|
|
477
|
+
including during approval.
|
|
478
|
+
- Expired, rejected and mismatched responses are refused locally. A transport failure does not
|
|
479
|
+
authorize a new signing request.
|
|
480
|
+
- The client discovers capabilities before posting, retries a lost create response with the same
|
|
481
|
+
immutable ID and payload (up to three attempts), and polls that ID every second until its
|
|
482
|
+
deadline. HTTP refusals are not retried.
|
|
483
|
+
|
|
484
|
+
| Operation | Exact payload | Approved result |
|
|
485
|
+
| --- | --- | --- |
|
|
486
|
+
| `evm.personalSign` | `{message}`: literal SDK authorization text | `{signature}`: 65-byte hex EIP-191 signature |
|
|
487
|
+
| `evm.signTypedData` | EIP-712 `{domain,types,primaryType,message}` | `{signature}`: checked against the domain, chain and selected account |
|
|
488
|
+
| `near.nep413` | `{message,recipient,nonce,callbackUrl?}` | `{accountId,publicKey,signature}`: base64 Ed25519 signature, checked with final full-access membership |
|
|
489
|
+
| `near.signTransaction` | `{receiverId,method,args,gas,deposit,transaction}`; `transaction` is the exact unsigned Borsh bytes in base64 | `{signedTransaction}`: signed Borsh bytes in base64; exact bytes, fields, signature and final full-access key membership are checked before submission |
|
|
490
|
+
|
|
491
|
+
Chains are `eip155:8453` for the supported EVM payment route and `near:mainnet` or `near:testnet` for
|
|
492
|
+
NEAR. Remote NEAR transaction configuration also needs `signer.publicKey` (`--near-public-key`
|
|
493
|
+
during init), so the CLI builds the exact nonce, block and actions before approval. Providers must
|
|
494
|
+
apply their own independent policy to these semantic operations; discovery is not permission to
|
|
495
|
+
sign arbitrary bytes.
|
|
496
|
+
|
|
497
|
+
**Transaction outcomes.** The browser bridge returns `{txHash}` for wallet-submitted NEAR calls, and
|
|
498
|
+
the CLI independently checks the final signer, receiver, single action, args, gas and deposit through
|
|
499
|
+
RPC. A returned transaction hash alone never proves success.
|
|
500
|
+
|
|
501
|
+
- Definite mismatches and failed finalized transactions are rejected without saving the hash as
|
|
502
|
+
`nearSubmission` evidence. Only RPC unavailability or a non-final status keeps the outcome
|
|
503
|
+
uncertain.
|
|
504
|
+
- If submission or finalized verification is ambiguous, the CLI returns `state: "uncertain"`,
|
|
505
|
+
`purchaseId` and `txHash`, and keeps `nearSubmission: {state:"uncertain",txHash}` in the encrypted
|
|
506
|
+
purchase backup. Check status for that saved purchase before buying again.
|
|
507
|
+
- Signing errors with a known hash also expose safe hash and purchase evidence, with exit code 4.
|
|
508
|
+
|
|
509
|
+
**Reference handler.** `node/signer-mock` exports `signerMock`, an authenticated HTTP
|
|
510
|
+
`Request -> Response` handler with injected approval and signing, duplicate-ID claims, immutable
|
|
511
|
+
request checks, and `decide(id, result)` for pending approvals. Use it in tests or wrap it in your
|
|
512
|
+
provider's HTTPS server. It keeps jobs in RAM and is not a durable production signer: production
|
|
513
|
+
adapters must persist idempotency state and provide an approval and status UI as appropriate. See
|
|
514
|
+
[`node/signing.test.ts`](https://github.com/defdone/annon/blob/master/packages/annon/src/node/signing.test.ts) for fake-key examples; tests need no provider credentials or real signing
|
|
515
|
+
service.
|
|
516
|
+
|
|
517
|
+
## Durable keeper
|
|
518
|
+
|
|
519
|
+
`keep` is exported from `@defdone/annon/relay`. Supply a stable `network` (`near:mainnet`,
|
|
520
|
+
`near:testnet` or `near:sandbox`), a dedicated account and key, durable `store` and `budgetStore`,
|
|
521
|
+
hourly and daily yoctoNEAR budgets, and a maximum gas price. In Node, create private directories and
|
|
522
|
+
use `fileStore<KeeperRecord>` and `fileStore<BudgetRecord>`. Environment configuration is in the
|
|
523
|
+
[relayer README](https://github.com/defdone/annon/blob/master/apps/relayer/README.md).
|
|
524
|
+
|
|
525
|
+
### Discovery and settlement
|
|
526
|
+
|
|
527
|
+
- Discovery reads `open_delivery_count` and `list_open_deliveries` against one finalized block. The
|
|
528
|
+
block, count and cursor are persisted across bounded passes (default 20 pages of 50).
|
|
529
|
+
- A new finalized snapshot begins after each complete sweep. This covers swap-removals and later
|
|
530
|
+
materializations without a historical-ID eligibility cap.
|
|
531
|
+
- Current finalized state is re-read before sending. `settle` uses 600 Tgas while unfunded and 450
|
|
532
|
+
Tgas when funded.
|
|
533
|
+
- InFlight funding and slots are never reset or newly submitted. Calls for other pending slots can
|
|
534
|
+
proceed; the contract guards InFlight slots.
|
|
535
|
+
|
|
536
|
+
### Running the keeper
|
|
537
|
+
|
|
538
|
+
Run the keeper with a dedicated funded account and durable state. Configure its hourly and daily
|
|
539
|
+
spending limits, retain its state after a timeout, and check the saved transaction before retrying.
|
|
540
|
+
The relayer README lists the service settings. A `RefundSource` integration supplies refund leaves
|
|
541
|
+
and current proofs for `materializeRefund`. Run one keeper process per signing key.
|
|
542
|
+
|
|
543
|
+
## RPC configuration
|
|
544
|
+
|
|
545
|
+
| Who | Needs | Setting |
|
|
546
|
+
| --- | --- | --- |
|
|
547
|
+
| A file host, the CLI for Base purchases, the relayer | A Base RPC endpoint with an API key | `baseRpc` in SDK and host options or CLI config; `ANNON_BASE_RPC` for the CLI; `BASE_RPC` for the relayer |
|
|
548
|
+
| A keeper polling every few seconds | A NEAR RPC endpoint with an API key | `nearRpc` in SDK and CLI config; `ANNON_NEAR_RPC` for the CLI; `NEAR_RPC` for the relayer |
|
|
549
|
+
|
|
550
|
+
The public `https://mainnet.base.org` rate-limits the burst of reads made during settlement and
|
|
551
|
+
unlocking. Keep API keys out of public files.
|
|
552
|
+
|
|
553
|
+
## Base splits
|
|
554
|
+
|
|
555
|
+
When a file has Base terms, `sell` results include `baseSplit: { address, kind, salt }` beside
|
|
556
|
+
`terms`. Public results omit `baseSplit` for a file without Base terms; its encrypted sell record
|
|
557
|
+
stores `null`. This metadata stays with the sell record through resume and publication and is
|
|
558
|
+
outside the on-chain terms and their hash.
|
|
559
|
+
|
|
560
|
+
To call `readSplit` later, pass `address` and `{ salt }`, and rebuild the split from
|
|
561
|
+
`terms.near.recipients` in their saved order:
|
|
562
|
+
|
|
563
|
+
| Split field | Value |
|
|
564
|
+
| --- | --- |
|
|
565
|
+
| `recipients` | The recipient identity IDs |
|
|
566
|
+
| `allocations` | Each recipient's `bps` |
|
|
567
|
+
| `totalAllocation` | `10000n` |
|
|
568
|
+
| `distributionIncentive` | `0` |
|
|
569
|
+
|
|
570
|
+
## Previews
|
|
571
|
+
|
|
572
|
+
A seller can build a PAY2 file with a preview by passing `previewLen` to `sell` (format:
|
|
573
|
+
[specs/pay2.md](https://github.com/defdone/annon/blob/master/specs/pay2.md) section 7). The SDK does
|
|
574
|
+
not verify preview packages for buyers in this release, so a page must not present a preview as
|
|
575
|
+
checked.
|
|
576
|
+
|
|
577
|
+
## Security notes
|
|
578
|
+
|
|
579
|
+
- The browser build fails if a post-build scan of the emitted JavaScript finds `eval`, `Function`
|
|
580
|
+
construction or calls, or string-argument timers.
|
|
581
|
+
- CSP compatibility is also checked statically against the actual built, minified bundle and the HTTP
|
|
582
|
+
page and CSP header: no dynamic `eval` or `Function` calls, string timers, inline scripts or HTML
|
|
583
|
+
event handlers.
|
|
584
|
+
- The browser build removes legacy dynamic-code fallbacks from the pinned Wallet Selector and
|
|
585
|
+
js-sha256 copies, with guards that require review when their source changes.
|
|
586
|
+
- VM tests disable string and Wasm code generation and exercise fake wallet flows.
|
|
587
|
+
- **Real-browser execution is not covered by the test suite.** Tests never launch browsers or GUI
|
|
588
|
+
applications and always stub the login opener; no real wallet, RPC, transaction or Keychain is
|
|
589
|
+
used.
|
|
590
|
+
|
|
591
|
+
The trust model, the upgrade key during the alpha and the known protocol limits are in
|
|
592
|
+
[SECURITY.md](https://github.com/defdone/annon/blob/master/SECURITY.md).
|