@fluxpointstudios/orynq-sdk-quickstart 0.2.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/dist/index.cjs ADDED
@@ -0,0 +1,392 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+ var os = require('os');
6
+ var utilCrypto = require('@polkadot/util-crypto');
7
+ var keyring = require('@polkadot/keyring');
8
+ var crypto = require('crypto');
9
+ var orynqSdkProcessTrace = require('@fluxpointstudios/orynq-sdk-process-trace');
10
+ var orynqSdkAnchorsMaterios = require('@fluxpointstudios/orynq-sdk-anchors-materios');
11
+
12
+ // src/identity.ts
13
+ function defaultConfigPath() {
14
+ return `${os.homedir()}/.orynq/config.json`;
15
+ }
16
+ async function loadOrCreateIdentity(opts = {}) {
17
+ await utilCrypto.cryptoWaitReady();
18
+ const configPath = opts.configPath ?? defaultConfigPath();
19
+ const ss58Format = opts.ss58Format ?? 42;
20
+ const warnings = [];
21
+ if (fs.existsSync(configPath)) {
22
+ let parsed;
23
+ try {
24
+ const raw = fs.readFileSync(configPath, "utf-8");
25
+ parsed = JSON.parse(raw);
26
+ } catch (err) {
27
+ const msg = err instanceof Error ? err.message : String(err);
28
+ throw new Error(
29
+ `orynq identity config at ${configPath} is corrupt and cannot be parsed: ${msg}. Inspect the file by hand \u2014 do NOT delete it without first checking whether the mnemonic inside is still recoverable. If you want a fresh identity, move the file aside (e.g. mv ${configPath} ${configPath}.broken) and rerun.`
30
+ );
31
+ }
32
+ if (!parsed || typeof parsed !== "object" || typeof parsed.mnemonic !== "string" || typeof parsed.address !== "string") {
33
+ throw new Error(
34
+ `orynq identity config at ${configPath} is missing required fields (mnemonic, address). File contents may be from an older or unrelated tool. Move it aside and rerun.`
35
+ );
36
+ }
37
+ if (!utilCrypto.mnemonicValidate(parsed.mnemonic)) {
38
+ throw new Error(
39
+ `orynq identity config at ${configPath} has an invalid mnemonic. Move it aside and rerun, or restore from your secure backup.`
40
+ );
41
+ }
42
+ const keyring2 = new keyring.Keyring({ type: "sr25519", ss58Format });
43
+ const pair2 = keyring2.addFromUri(parsed.mnemonic);
44
+ if (pair2.address !== parsed.address) {
45
+ warnings.push(
46
+ `address re-encoded under ss58Format=${ss58Format} (config had ${parsed.address})`
47
+ );
48
+ }
49
+ return {
50
+ mnemonic: parsed.mnemonic,
51
+ address: pair2.address,
52
+ generatedAt: parsed.generatedAt,
53
+ configPath,
54
+ freshlyGenerated: false,
55
+ warnings
56
+ };
57
+ }
58
+ const mnemonic = utilCrypto.mnemonicGenerate(12);
59
+ const keyring$1 = new keyring.Keyring({ type: "sr25519", ss58Format });
60
+ const pair = keyring$1.addFromUri(mnemonic);
61
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
62
+ const config = {
63
+ version: 1,
64
+ mnemonic,
65
+ address: pair.address,
66
+ generatedAt
67
+ };
68
+ fs.mkdirSync(path.dirname(configPath), { recursive: true, mode: 448 });
69
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
70
+ if (process.platform !== "win32") {
71
+ fs.chmodSync(configPath, 384);
72
+ }
73
+ return {
74
+ mnemonic,
75
+ address: pair.address,
76
+ generatedAt,
77
+ configPath,
78
+ freshlyGenerated: true,
79
+ warnings
80
+ };
81
+ }
82
+ async function firstTraceBundle(opts) {
83
+ const restore = installDeterministicHooks(opts);
84
+ try {
85
+ const run = await orynqSdkProcessTrace.createTrace({ agentId: opts.agentId });
86
+ const span = orynqSdkProcessTrace.addSpan(run, { name: "first-trace", visibility: "public" });
87
+ await orynqSdkProcessTrace.addEvent(run, span.id, {
88
+ kind: "observation",
89
+ observation: opts.summary,
90
+ visibility: "public"
91
+ });
92
+ await orynqSdkProcessTrace.closeSpan(run, span.id);
93
+ const bundle = await orynqSdkProcessTrace.finalizeTrace(run);
94
+ const content = canonicalJson(bundle.publicView);
95
+ const manifestHash = sha256Hex(content);
96
+ return {
97
+ runId: bundle.publicView.runId,
98
+ agentId: bundle.publicView.agentId,
99
+ rootHash: bundle.rootHash,
100
+ merkleRoot: bundle.merkleRoot,
101
+ manifestHash,
102
+ content,
103
+ bundle
104
+ };
105
+ } finally {
106
+ restore();
107
+ }
108
+ }
109
+ function installDeterministicHooks(hooks) {
110
+ const originalRandomUuid = globalThis.crypto?.randomUUID?.bind(globalThis.crypto);
111
+ const originalDate = globalThis.Date;
112
+ let didPatchUuid = false;
113
+ let didPatchDate = false;
114
+ if (hooks.runId || hooks.spanId || hooks.eventId) {
115
+ const queue = [];
116
+ if (hooks.runId) queue.push(hooks.runId);
117
+ if (hooks.spanId) queue.push(hooks.spanId);
118
+ if (hooks.eventId) queue.push(hooks.eventId);
119
+ globalThis.crypto.randomUUID = () => {
120
+ const next = queue.shift();
121
+ if (next) return next;
122
+ return originalRandomUuid ? originalRandomUuid() : "00000000-0000-4000-8000-000000000000";
123
+ };
124
+ didPatchUuid = true;
125
+ }
126
+ if (hooks.now) {
127
+ const fixed = hooks.now();
128
+ const fixedMs = fixed.getTime();
129
+ const Wrapped = new Proxy(originalDate, {
130
+ construct(target, args) {
131
+ if (args.length === 0) {
132
+ return new target(fixedMs);
133
+ }
134
+ return new target(
135
+ ...args
136
+ );
137
+ },
138
+ get(target, prop, receiver) {
139
+ if (prop === "now") return () => fixedMs;
140
+ return Reflect.get(target, prop, receiver);
141
+ }
142
+ });
143
+ globalThis.Date = Wrapped;
144
+ didPatchDate = true;
145
+ }
146
+ return function restore() {
147
+ if (didPatchUuid && originalRandomUuid) {
148
+ globalThis.crypto.randomUUID = originalRandomUuid;
149
+ }
150
+ if (didPatchDate) {
151
+ globalThis.Date = originalDate;
152
+ }
153
+ };
154
+ }
155
+ function canonicalJson(value) {
156
+ return JSON.stringify(sortValue(value));
157
+ }
158
+ function sortValue(value) {
159
+ if (Array.isArray(value)) {
160
+ return value.map((v) => sortValue(v));
161
+ }
162
+ if (value !== null && typeof value === "object") {
163
+ const obj = value;
164
+ const sorted = {};
165
+ for (const key of Object.keys(obj).sort()) {
166
+ const v = obj[key];
167
+ if (v === void 0 || v === null) continue;
168
+ sorted[key] = sortValue(v);
169
+ }
170
+ return sorted;
171
+ }
172
+ return value;
173
+ }
174
+ function sha256Hex(s) {
175
+ return crypto.createHash("sha256").update(s, "utf-8").digest("hex");
176
+ }
177
+
178
+ // src/faucet.ts
179
+ function normaliseToRoot(base) {
180
+ let s = base.trim();
181
+ if (s.endsWith("/")) s = s.slice(0, -1);
182
+ if (s.endsWith("/blobs")) s = s.slice(0, -"/blobs".length);
183
+ return s;
184
+ }
185
+ async function requestFaucet(opts) {
186
+ const f = opts.fetchImpl ?? fetch;
187
+ const rootBase = normaliseToRoot(opts.gatewayBaseUrl);
188
+ const url = `${rootBase}/blobs/faucet/drip`;
189
+ const fetchOpts = {
190
+ method: "POST",
191
+ headers: { "content-type": "application/json" },
192
+ body: JSON.stringify({ address: opts.address })
193
+ };
194
+ if (opts.signal) {
195
+ fetchOpts.signal = opts.signal;
196
+ }
197
+ const res = await f(url, fetchOpts);
198
+ const text = await res.text();
199
+ let json = {};
200
+ try {
201
+ json = text ? JSON.parse(text) : {};
202
+ } catch {
203
+ return {
204
+ kind: "error",
205
+ status: res.status,
206
+ message: text.slice(0, 256)
207
+ };
208
+ }
209
+ if (res.ok && json["success"] === true) {
210
+ return {
211
+ kind: "success",
212
+ txHash: String(json["tx_hash"] ?? ""),
213
+ amount: String(json["amount"] ?? ""),
214
+ message: String(json["message"] ?? "MATRA dripped")
215
+ };
216
+ }
217
+ if (res.status === 409 && typeof json["dripped_at"] === "number") {
218
+ return { kind: "already-funded", drippedAtMs: Number(json["dripped_at"]) };
219
+ }
220
+ if (res.status === 429 || /cooldown/i.test(String(json["error"] ?? ""))) {
221
+ const retryAfterMs = typeof json["cooldown_ms"] === "number" ? Number(json["cooldown_ms"]) : typeof json["retry_after_seconds"] === "number" ? Number(json["retry_after_seconds"]) * 1e3 : 0;
222
+ return {
223
+ kind: "cooldown",
224
+ retryAfterMs,
225
+ message: String(json["error"] ?? "Faucet cooldown active")
226
+ };
227
+ }
228
+ return {
229
+ kind: "error",
230
+ status: res.status,
231
+ message: String(json["error"] ?? text.slice(0, 256) ?? "Unknown faucet error")
232
+ };
233
+ }
234
+
235
+ // src/explorer.ts
236
+ function strip0x(hex) {
237
+ return hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
238
+ }
239
+ function normaliseGatewayBase(base) {
240
+ let s = base.trim();
241
+ if (s.endsWith("/")) s = s.slice(0, -1);
242
+ if (s.endsWith("/blobs")) {
243
+ const rootBase = s.slice(0, -"/blobs".length);
244
+ return { blobsBase: s, rootBase };
245
+ }
246
+ return { blobsBase: s, rootBase: s };
247
+ }
248
+ function buildExplorerUrls(input) {
249
+ const contentHash = strip0x(input.contentHash);
250
+ const blockHash = strip0x(input.blockHash);
251
+ const { blobsBase, rootBase } = normaliseGatewayBase(input.gatewayBaseUrl);
252
+ const encodedRpc = encodeURIComponent(input.rpcUrl);
253
+ const explorer = `https://polkadot.js.org/apps/?rpc=${encodedRpc}#/explorer/query/0x${blockHash}`;
254
+ return {
255
+ blobStatus: `${blobsBase}/blobs/${contentHash}/status`,
256
+ explorer,
257
+ chainInfo: `${rootBase}/chain-info`,
258
+ gatewayHealth: `${rootBase}/health`
259
+ };
260
+ }
261
+ var DEFAULT_RPC_URL = "wss://materios.fluxpointstudios.com/rpc";
262
+ var DEFAULT_GATEWAY_URL = "https://materios.fluxpointstudios.com/blobs";
263
+ var DEFAULT_AGENT_ID = "orynq-quickstart";
264
+ async function bootstrapAndTrace(opts = {}) {
265
+ await utilCrypto.cryptoWaitReady();
266
+ const start = Date.now();
267
+ const onProgress = opts.onProgress ?? (() => {
268
+ });
269
+ const identity = await loadOrCreateIdentity({
270
+ ...opts.configPath !== void 0 ? { configPath: opts.configPath } : {}
271
+ });
272
+ onProgress({ kind: "identity-loaded", identity });
273
+ const gateway = opts.gatewayBaseUrl ?? DEFAULT_GATEWAY_URL;
274
+ const rpcUrl = opts.rpcUrl ?? DEFAULT_RPC_URL;
275
+ if (!opts.skipFaucet) {
276
+ const faucetResult = await requestFaucet({ address: identity.address, gatewayBaseUrl: gateway });
277
+ onProgress({ kind: "faucet-result", result: faucetResult });
278
+ if (faucetResult.kind === "error" || faucetResult.kind === "cooldown") {
279
+ throw new Error(
280
+ `faucet drip failed for ${identity.address}: ${faucetResult.kind} \u2014 ${faucetResult.message ?? "unknown"}. Workarounds: (1) skipFaucet:true if you've already funded ${identity.address} elsewhere; (2) retry in a few minutes if cooldown; (3) ask in Discord (#materios) for a top-up.`
281
+ );
282
+ }
283
+ }
284
+ const provider = new orynqSdkAnchorsMaterios.MateriosProvider({ rpcUrl, signerUri: identity.mnemonic });
285
+ await provider.connect();
286
+ try {
287
+ onProgress({ kind: "waiting-for-motra" });
288
+ const balance = await orynqSdkAnchorsMaterios.waitForMotra(provider, void 0, { timeoutMs: 9e4 });
289
+ onProgress({ kind: "motra-ready", balance });
290
+ const bundle = await firstTraceBundle({
291
+ agentId: opts.agentId ?? DEFAULT_AGENT_ID,
292
+ summary: opts.summary ?? `first trace via orynq-sdk-quickstart at ${(/* @__PURE__ */ new Date()).toISOString()}`
293
+ });
294
+ onProgress({ kind: "trace-built", bundle });
295
+ const keypair = provider.getKeypair();
296
+ const contentBuf = Buffer.from(bundle.content, "utf-8");
297
+ const contentHash = bundle.manifestHash;
298
+ const certTimeoutMs = opts.certTimeoutMs ?? 12e4;
299
+ const treatCertTimeoutAsSuccess = opts.treatCertTimeoutAsSuccess !== false;
300
+ const contentHashHex = contentHash.startsWith("0x") ? contentHash.slice(2) : contentHash;
301
+ const receiptIdHex = "0x" + crypto.createHash("sha256").update(Buffer.from(contentHashHex, "hex")).digest("hex");
302
+ const { manifest, chunks } = orynqSdkAnchorsMaterios.prepareBlobData(receiptIdHex, contentBuf);
303
+ const uploadResult = await orynqSdkAnchorsMaterios.uploadBlobs(
304
+ receiptIdHex,
305
+ manifest,
306
+ chunks,
307
+ {
308
+ baseUrl: gateway,
309
+ signerKeypair: {
310
+ address: keypair.address,
311
+ sign: (msg) => keypair.sign(msg)
312
+ }
313
+ }
314
+ );
315
+ if (!uploadResult.success) {
316
+ throw new Error(`blob upload failed: ${uploadResult.error ?? "unknown"}`);
317
+ }
318
+ const submitResult = await orynqSdkAnchorsMaterios.submitReceipt(provider, {
319
+ receiptId: receiptIdHex,
320
+ contentHash,
321
+ rootHash: bundle.rootHash,
322
+ manifestHash: uploadResult.storageLocatorHash ?? bundle.manifestHash
323
+ });
324
+ onProgress({
325
+ kind: "receipt-submitted",
326
+ receiptId: submitResult.receiptId,
327
+ blockHash: submitResult.blockHash
328
+ });
329
+ let certHash;
330
+ if (certTimeoutMs > 0) {
331
+ try {
332
+ const certResult = await orynqSdkAnchorsMaterios.waitForCertification(
333
+ provider,
334
+ submitResult.receiptId,
335
+ { timeoutMs: certTimeoutMs }
336
+ );
337
+ certHash = certResult.certHash;
338
+ onProgress({ kind: "certified", certHash });
339
+ } catch (err) {
340
+ const msg = err instanceof Error ? err.message : String(err);
341
+ const isCertTimeout = /Certification timeout/i.test(msg);
342
+ if (!(isCertTimeout && treatCertTimeoutAsSuccess)) {
343
+ throw err;
344
+ }
345
+ }
346
+ }
347
+ const urls = buildExplorerUrls({
348
+ contentHash: receiptIdHex,
349
+ blockHash: submitResult.blockHash,
350
+ gatewayBaseUrl: gateway,
351
+ rpcUrl
352
+ });
353
+ onProgress({ kind: "explorer-ready", urls });
354
+ const result = {
355
+ identity,
356
+ bundle,
357
+ receiptId: submitResult.receiptId,
358
+ blockHash: submitResult.blockHash,
359
+ urls,
360
+ elapsedMs: Date.now() - start
361
+ };
362
+ if (certHash) {
363
+ result.certHash = certHash;
364
+ }
365
+ return result;
366
+ } finally {
367
+ await provider.disconnect().catch(() => {
368
+ });
369
+ }
370
+ }
371
+ async function deriveAddress(mnemonic, ss58Format = 42) {
372
+ await utilCrypto.cryptoWaitReady();
373
+ const keyring$1 = new keyring.Keyring({ type: "sr25519", ss58Format });
374
+ return keyring$1.addFromUri(mnemonic).address;
375
+ }
376
+
377
+ // src/index.ts
378
+ var VERSION = "0.1.0";
379
+
380
+ exports.DEFAULT_AGENT_ID = DEFAULT_AGENT_ID;
381
+ exports.DEFAULT_GATEWAY_URL = DEFAULT_GATEWAY_URL;
382
+ exports.DEFAULT_RPC_URL = DEFAULT_RPC_URL;
383
+ exports.VERSION = VERSION;
384
+ exports.bootstrapAndTrace = bootstrapAndTrace;
385
+ exports.buildExplorerUrls = buildExplorerUrls;
386
+ exports.defaultConfigPath = defaultConfigPath;
387
+ exports.deriveAddress = deriveAddress;
388
+ exports.firstTraceBundle = firstTraceBundle;
389
+ exports.loadOrCreateIdentity = loadOrCreateIdentity;
390
+ exports.requestFaucet = requestFaucet;
391
+ //# sourceMappingURL=index.cjs.map
392
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/identity.ts","../src/trace.ts","../src/faucet.ts","../src/explorer.ts","../src/bootstrap.ts","../src/index.ts"],"names":["homedir","cryptoWaitReady","existsSync","readFileSync","mnemonicValidate","keyring","Keyring","pair","mnemonicGenerate","mkdirSync","dirname","writeFileSync","chmodSync","createTrace","addSpan","addEvent","closeSpan","finalizeTrace","createHash","MateriosProvider","waitForMotra","prepareBlobData","uploadBlobs","submitReceipt","waitForCertification"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @summary Local sr25519 identity bootstrap for solo-dev quickstart.\n *\n * Generates a fresh BIP39 mnemonic on first run, derives an sr25519 keypair,\n * and persists the mnemonic to `~/.orynq/config.json` (or any caller-supplied\n * path) with 0600 permissions on POSIX systems. Subsequent calls reload the\n * same identity so the address stays stable across processes.\n *\n * This is intentionally pure-local: no network, no chain RPC, no faucet.\n * Anchoring + faucet drip belong in `bootstrap.ts` so callers who already\n * have an identity can skip identity generation entirely.\n *\n * Trust model: the mnemonic on disk is treated like any other developer\n * secret. The config file is created with 0600 perms; an explicit warning is\n * emitted via the returned `OrynqIdentity.warnings` array when the env\n * suggests a shared filesystem (which is reserved for a follow-up — kept\n * as `warnings: []` today so the public shape stays stable).\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from \"fs\";\nimport { dirname } from \"path\";\nimport { homedir } from \"os\";\nimport {\n cryptoWaitReady,\n mnemonicGenerate,\n mnemonicValidate,\n} from \"@polkadot/util-crypto\";\nimport { Keyring } from \"@polkadot/keyring\";\n\n/**\n * Solo-dev identity loaded from disk or freshly generated.\n *\n * Fields:\n * - `mnemonic` BIP39 12-word seed phrase. Required to sign on-chain\n * txs and blob-gateway uploads. Treat as a secret.\n * - `address` sr25519 SS58 address derived from `mnemonic`. Safe to\n * log; this is the public chain identity.\n * - `generatedAt` ISO timestamp of original generation.\n * - `configPath` Where the identity is persisted.\n * - `freshlyGenerated` True iff this call generated a new mnemonic (vs\n * reloading an existing one). Lets the CLI print \"saved\n * new identity to...\" only on the first run.\n * - `warnings` Non-fatal advisories from the loader. Empty today;\n * reserved for shared-FS / world-readable-perm checks.\n */\nexport interface OrynqIdentity {\n mnemonic: string;\n address: string;\n generatedAt: string;\n configPath: string;\n freshlyGenerated: boolean;\n warnings: string[];\n}\n\nexport interface LoadOrCreateIdentityOptions {\n /**\n * Path to the persistent identity file. Defaults to\n * `${HOME}/.orynq/config.json`. The parent directory is created\n * recursively if it does not exist.\n */\n configPath?: string | undefined;\n\n /**\n * SS58 prefix for the encoded address. Defaults to 42 (generic Substrate).\n * Materios uses 42 in v6 preprod; pass a different value here if you're\n * targeting a chain with a custom prefix.\n */\n ss58Format?: number | undefined;\n}\n\n/**\n * Default config-file location: `~/.orynq/config.json`.\n *\n * Exposed so other code (`bootstrap.ts`, the CLI) can reference the same\n * default without duplicating the homedir join.\n */\nexport function defaultConfigPath(): string {\n return `${homedir()}/.orynq/config.json`;\n}\n\ninterface OnDiskConfig {\n version: 1;\n mnemonic: string;\n address: string;\n generatedAt: string;\n}\n\n/**\n * Load an existing identity from `configPath`, or generate + persist a new\n * one if the file does not exist.\n *\n * Throws if the config file exists but cannot be parsed — better to fail\n * loudly than silently regenerate and orphan whatever identity used to be\n * there (and any MATRA balance on it).\n */\nexport async function loadOrCreateIdentity(\n opts: LoadOrCreateIdentityOptions = {},\n): Promise<OrynqIdentity> {\n await cryptoWaitReady();\n\n const configPath = opts.configPath ?? defaultConfigPath();\n const ss58Format = opts.ss58Format ?? 42;\n const warnings: string[] = [];\n\n if (existsSync(configPath)) {\n let parsed: OnDiskConfig;\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n parsed = JSON.parse(raw) as OnDiskConfig;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(\n `orynq identity config at ${configPath} is corrupt and cannot be parsed: ${msg}. ` +\n `Inspect the file by hand — do NOT delete it without first checking whether the ` +\n `mnemonic inside is still recoverable. If you want a fresh identity, move the file ` +\n `aside (e.g. mv ${configPath} ${configPath}.broken) and rerun.`,\n );\n }\n if (\n !parsed ||\n typeof parsed !== \"object\" ||\n typeof parsed.mnemonic !== \"string\" ||\n typeof parsed.address !== \"string\"\n ) {\n throw new Error(\n `orynq identity config at ${configPath} is missing required fields (mnemonic, address). ` +\n `File contents may be from an older or unrelated tool. Move it aside and rerun.`,\n );\n }\n if (!mnemonicValidate(parsed.mnemonic)) {\n throw new Error(\n `orynq identity config at ${configPath} has an invalid mnemonic. ` +\n `Move it aside and rerun, or restore from your secure backup.`,\n );\n }\n const keyring = new Keyring({ type: \"sr25519\", ss58Format });\n const pair = keyring.addFromUri(parsed.mnemonic);\n if (pair.address !== parsed.address) {\n // Likely an SS58 prefix mismatch between when it was written and now.\n // Re-encode at the caller-specified prefix and continue, but warn.\n warnings.push(\n `address re-encoded under ss58Format=${ss58Format} (config had ${parsed.address})`,\n );\n }\n return {\n mnemonic: parsed.mnemonic,\n address: pair.address,\n generatedAt: parsed.generatedAt,\n configPath,\n freshlyGenerated: false,\n warnings,\n };\n }\n\n // No config — generate fresh.\n const mnemonic = mnemonicGenerate(12);\n const keyring = new Keyring({ type: \"sr25519\", ss58Format });\n const pair = keyring.addFromUri(mnemonic);\n const generatedAt = new Date().toISOString();\n\n const config: OnDiskConfig = {\n version: 1,\n mnemonic,\n address: pair.address,\n generatedAt,\n };\n\n mkdirSync(dirname(configPath), { recursive: true, mode: 0o700 });\n // Set the file mode at creation time via the `mode` option (POSIX\n // O_CREAT honors it). The previous post-create `chmodSync` left the\n // file briefly world-readable in the window between write and chmod —\n // for the default `~/.orynq/config.json` path the 0o700 parent dir\n // mitigates, but for env-var paths whose parent dir pre-exists at\n // 0o755 (e.g. /tmp/devkey.json) a co-tenant could race the chmod\n // and read the mnemonic. Setting mode at open(2) closes that window.\n // Sec-review finding: PR #54, MEDIUM, 9/10 confidence.\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\", { encoding: \"utf-8\", mode: 0o600 });\n if (process.platform !== \"win32\") {\n // Defense-in-depth — handles the case where the file already existed\n // and `writeFileSync` overwrote without re-applying mode. (Per Node\n // docs, `mode` is ignored when the file already exists.)\n chmodSync(configPath, 0o600);\n }\n\n return {\n mnemonic,\n address: pair.address,\n generatedAt,\n configPath,\n freshlyGenerated: true,\n warnings,\n };\n}\n","/**\n * @summary Minimal trace-bundle factory used by `orynq init` / `orynq trace`.\n *\n * Wraps `@fluxpointstudios/orynq-sdk-process-trace` with a one-call helper\n * that produces a finalised bundle from a single observation event. The\n * heavyweight builder (multi-span, multi-event, custom kinds) lives in\n * the underlying package — quickstart deliberately ships only the\n * \"hello world\" path so a fresh dev sees a trace land before they're\n * forced to learn span semantics.\n */\n\nimport { createHash } from \"crypto\";\nimport {\n createTrace,\n addSpan,\n addEvent,\n closeSpan,\n finalizeTrace,\n} from \"@fluxpointstudios/orynq-sdk-process-trace\";\nimport type { TraceBundle } from \"@fluxpointstudios/orynq-sdk-process-trace\";\n\n/**\n * Slimmed-down public view of a `TraceBundle` — exposes only the fields\n * `orynq init` / `orynq trace` needs to print + the raw `content` JSON the\n * caller will upload as a blob. The full `bundle` is preserved on the\n * returned object so power-users can still walk events/spans.\n */\nexport interface TraceBundleLite {\n runId: string;\n agentId: string;\n rootHash: string;\n merkleRoot: string;\n /**\n * SHA-256 of the canonical JSON content payload as a hex string. Set by\n * `firstTraceBundle()` so the same hash that ends up in the on-chain\n * receipt is available without re-canonicalising downstream.\n */\n manifestHash: string;\n /**\n * Canonical JSON serialisation of `bundle.publicView`. This is what we\n * upload to the blob gateway under `contentHash = sha256(content)`.\n */\n content: string;\n /** Original full bundle, in case callers want spans/events. */\n bundle: TraceBundle;\n}\n\n/**\n * Optional deterministic-clock + identifier hooks. Used by the\n * documentation tests + recipes that need stable hashes across runs.\n *\n * In normal use (production), callers pass nothing here and let the\n * trace-builder pick wall-clock timestamps + random UUIDs.\n */\nexport interface DeterministicHooks {\n /** Pin `new Date()`/`Date.now()` for the duration of this call. */\n now?: () => Date;\n /** Pin the run UUID returned by `createTrace`. */\n runId?: string;\n /** Pin the span UUID returned by `addSpan`. */\n spanId?: string;\n /** Pin the event UUID returned by `addEvent`. */\n eventId?: string;\n}\n\nexport interface FirstTraceBundleOptions extends DeterministicHooks {\n agentId: string;\n /** Free-form one-liner appended as the public observation event. */\n summary: string;\n}\n\n/**\n * Build, finalise, and serialise a one-event, one-span trace bundle.\n *\n * The optional `now`/`runId`/`spanId`/`eventId` hooks are useful for tests\n * that need byte-stable hashes; they patch the globals only for the\n * duration of this single call and restore them in a `finally` block so\n * we never leak the patch into surrounding code.\n */\nexport async function firstTraceBundle(\n opts: FirstTraceBundleOptions,\n): Promise<TraceBundleLite> {\n const restore = installDeterministicHooks(opts);\n try {\n const run = await createTrace({ agentId: opts.agentId });\n const span = addSpan(run, { name: \"first-trace\", visibility: \"public\" });\n await addEvent<\"observation\">(run, span.id, {\n kind: \"observation\",\n observation: opts.summary,\n visibility: \"public\",\n });\n await closeSpan(run, span.id);\n const bundle = await finalizeTrace(run);\n\n // Canonicalise the public view (the part we publish) — this is the\n // exact bytes the blob-gateway will checksum at upload time.\n const content = canonicalJson(bundle.publicView);\n const manifestHash = sha256Hex(content);\n\n return {\n runId: bundle.publicView.runId,\n agentId: bundle.publicView.agentId,\n rootHash: bundle.rootHash,\n merkleRoot: bundle.merkleRoot,\n manifestHash,\n content,\n bundle,\n };\n } finally {\n restore();\n }\n}\n\n/**\n * Apply the deterministic-clock + UUID hooks and return a function that\n * undoes them. No-op when no hooks are supplied — the production hot path\n * pays zero cost.\n */\nfunction installDeterministicHooks(hooks: DeterministicHooks): () => void {\n // Capture all originals up front so multiple-restore is a no-op.\n const originalRandomUuid = globalThis.crypto?.randomUUID?.bind(globalThis.crypto);\n const originalDate = globalThis.Date;\n\n let didPatchUuid = false;\n let didPatchDate = false;\n\n if (hooks.runId || hooks.spanId || hooks.eventId) {\n const queue: string[] = [];\n if (hooks.runId) queue.push(hooks.runId);\n if (hooks.spanId) queue.push(hooks.spanId);\n if (hooks.eventId) queue.push(hooks.eventId);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (globalThis.crypto as any).randomUUID = (): string => {\n const next = queue.shift();\n if (next) return next;\n return originalRandomUuid\n ? originalRandomUuid()\n : \"00000000-0000-4000-8000-000000000000\";\n };\n didPatchUuid = true;\n }\n\n if (hooks.now) {\n const fixed = hooks.now();\n const fixedMs = fixed.getTime();\n // Wrap the Date constructor so `new Date()` (no args) returns the\n // pinned moment; `new Date(ms)` and `new Date(str)` still work.\n const Wrapped = new Proxy(originalDate, {\n construct(target, args) {\n if (args.length === 0) {\n return new (target as DateConstructor)(fixedMs);\n }\n return new (target as DateConstructor)(\n ...(args as ConstructorParameters<DateConstructor>),\n );\n },\n get(target, prop, receiver) {\n if (prop === \"now\") return () => fixedMs;\n return Reflect.get(target, prop, receiver);\n },\n });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (globalThis as any).Date = Wrapped;\n didPatchDate = true;\n }\n\n return function restore() {\n if (didPatchUuid && originalRandomUuid) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (globalThis.crypto as any).randomUUID = originalRandomUuid;\n }\n if (didPatchDate) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (globalThis as any).Date = originalDate;\n }\n };\n}\n\n/**\n * Minimal RFC 8785-ish canonical JSON.\n *\n * Sorts keys, strips nulls/undefined. The full RFC 8785 implementation\n * lives in `@fluxpointstudios/orynq-sdk-core/utils` but we deliberately\n * avoid that dependency here so quickstart stays tiny + has zero\n * transitive deps beyond polkadot.\n */\nfunction canonicalJson(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((v) => sortValue(v));\n }\n if (value !== null && typeof value === \"object\") {\n const obj = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n const v = obj[key];\n if (v === undefined || v === null) continue;\n sorted[key] = sortValue(v);\n }\n return sorted;\n }\n return value;\n}\n\nfunction sha256Hex(s: string): string {\n return createHash(\"sha256\").update(s, \"utf-8\").digest(\"hex\");\n}\n","/**\n * @summary Free-tier MATRA faucet client.\n *\n * The Materios preprod blob-gateway exposes `POST /blobs/faucet/drip` (and\n * `POST /faucet/drip` mounted at the same handler). One-shot per SS58\n * address, IP-cooldown 5 min on the un-prefixed path. The dripped MATRA\n * generates MOTRA (fee currency) over the next few blocks — that's how a\n * fresh dev pays for their first `submit_receipt` extrinsic without an\n * out-of-band funding step.\n *\n * Returns a discriminated union so callers can branch on `kind` without\n * sniffing error messages.\n */\n\nexport interface FaucetDripSuccess {\n kind: \"success\";\n txHash: string;\n amount: string;\n message: string;\n}\n\nexport interface FaucetDripAlreadyFunded {\n kind: \"already-funded\";\n drippedAtMs: number;\n}\n\nexport interface FaucetDripCooldown {\n kind: \"cooldown\";\n retryAfterMs: number;\n message: string;\n}\n\nexport interface FaucetDripError {\n kind: \"error\";\n status: number;\n message: string;\n}\n\nexport type FaucetDripResult =\n | FaucetDripSuccess\n | FaucetDripAlreadyFunded\n | FaucetDripCooldown\n | FaucetDripError;\n\nexport interface RequestFaucetOptions {\n /** SS58 address to drip MATRA into. */\n address: string;\n /**\n * Gateway base URL. Accepts either `https://host` or `https://host/blobs`.\n * The /blobs/-prefixed faucet path is preferred (per-address ledger);\n * the bare /faucet path adds an IP-level 5-min cooldown so we leave it\n * alone here.\n */\n gatewayBaseUrl: string;\n /**\n * Optional fetch impl injection (for tests + Cloudflare Workers).\n * Defaults to the global `fetch`.\n */\n fetchImpl?: typeof fetch | undefined;\n /**\n * Optional AbortSignal — propagated to the underlying fetch so callers\n * can wire up a Ctrl-C handler.\n */\n signal?: AbortSignal | undefined;\n}\n\n/**\n * Strip a trailing slash + trailing /blobs from a gateway base URL,\n * leaving the bare origin. The faucet route is mounted on the express\n * root (not the /blobs router) but is reachable via the nginx\n * reverse-proxy that prefixes /blobs — so the publicly-working URL\n * needs the /blobs segment exactly once.\n */\nfunction normaliseToRoot(base: string): string {\n let s = base.trim();\n if (s.endsWith(\"/\")) s = s.slice(0, -1);\n if (s.endsWith(\"/blobs\")) s = s.slice(0, -\"/blobs\".length);\n return s;\n}\n\n/**\n * Drip MATRA to a fresh SS58 address.\n *\n * Idempotent at the caller's level: if the address has already been\n * dripped (per-address ledger), returns `kind: \"already-funded\"` instead\n * of throwing — the caller can treat both `success` and `already-funded`\n * as \"we have MATRA, proceed\".\n */\nexport async function requestFaucet(\n opts: RequestFaucetOptions,\n): Promise<FaucetDripResult> {\n const f = opts.fetchImpl ?? fetch;\n const rootBase = normaliseToRoot(opts.gatewayBaseUrl);\n // /blobs/faucet/drip == the per-address-ledger faucet (preferred).\n // /faucet/drip == the IP-cooldown variant (we avoid it).\n const url = `${rootBase}/blobs/faucet/drip`;\n\n const fetchOpts: RequestInit = {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ address: opts.address }),\n };\n if (opts.signal) {\n fetchOpts.signal = opts.signal;\n }\n const res = await f(url, fetchOpts);\n\n const text = await res.text();\n let json: Record<string, unknown> = {};\n try {\n json = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {\n // Non-JSON body — surface the raw text as the error message.\n return {\n kind: \"error\",\n status: res.status,\n message: text.slice(0, 256),\n };\n }\n\n if (res.ok && json[\"success\"] === true) {\n return {\n kind: \"success\",\n txHash: String(json[\"tx_hash\"] ?? \"\"),\n amount: String(json[\"amount\"] ?? \"\"),\n message: String(json[\"message\"] ?? \"MATRA dripped\"),\n };\n }\n\n // Per-address dedup: 409 + \"Address already received a drip\" + dripped_at.\n if (res.status === 409 && typeof json[\"dripped_at\"] === \"number\") {\n return { kind: \"already-funded\", drippedAtMs: Number(json[\"dripped_at\"]) };\n }\n\n // IP-level cooldown (the un-prefixed /faucet/drip path uses this).\n if (\n res.status === 429 ||\n /cooldown/i.test(String(json[\"error\"] ?? \"\"))\n ) {\n const retryAfterMs =\n typeof json[\"cooldown_ms\"] === \"number\"\n ? Number(json[\"cooldown_ms\"])\n : typeof json[\"retry_after_seconds\"] === \"number\"\n ? Number(json[\"retry_after_seconds\"]) * 1000\n : 0;\n return {\n kind: \"cooldown\",\n retryAfterMs,\n message: String(json[\"error\"] ?? \"Faucet cooldown active\"),\n };\n }\n\n return {\n kind: \"error\",\n status: res.status,\n message: String(json[\"error\"] ?? text.slice(0, 256) ?? \"Unknown faucet error\"),\n };\n}\n","/**\n * @summary Compose the user-facing URLs that close the loop on \"first trace\".\n *\n * The DX requirement (#175) is: after submission, the SDK MUST print a URL\n * the developer can click and see something. Materios doesn't yet ship a\n * native trace-detail explorer page, so we compose three known-good URLs:\n *\n * 1. `blobStatus` — `${gateway}/blobs/<contentHash>/status` — gateway-\n * side status of the receipt (HTTP 200 + JSON,\n * browser-renderable).\n * 2. `explorer` — Polkadot.js apps explorer pre-pointed at the\n * submission block. Shows the on-chain extrinsic\n * with full SCALE-decoded args.\n * 3. `chainInfo` — `${gateway}/chain-info` — JSON with the live\n * genesis hash + best block, useful as a sanity\n * check that the gateway is the chain you think\n * it is.\n * 4. `gatewayHealth` — `${gateway}/health` — cluster-health summary\n * (cert-daemon, anchor-worker, storage usage).\n *\n * A follow-up will replace `explorer` with a first-party\n * `https://materios.fluxpointstudios.com/trace/<contentHash>` page (filed\n * separately) — at which point the field swaps and the rest of the SDK\n * surface keeps working.\n */\n\nexport interface BuildExplorerUrlsInput {\n /**\n * Hex content hash, with or without `0x` prefix. Used to build the\n * gateway status URL.\n */\n contentHash: string;\n /**\n * Hex block hash from the on-chain submission, with or without `0x`.\n * Used to build the Polkadot.js apps query URL.\n */\n blockHash: string;\n /**\n * Gateway base URL. Accepts either `https://host` or `https://host/blobs`\n * — the function normalises so callers don't have to remember which\n * variant the env exports.\n */\n gatewayBaseUrl: string;\n /**\n * Substrate websocket RPC URL. Used to build the Polkadot.js apps\n * pre-pointed-at-this-chain URL.\n */\n rpcUrl: string;\n}\n\nexport interface ExplorerUrls {\n /** Gateway blob status JSON. */\n blobStatus: string;\n /** Polkadot.js apps pre-pointed at this chain's submission block. */\n explorer: string;\n /** Gateway chain-info endpoint (genesis + best block). */\n chainInfo: string;\n /** Gateway top-level health roll-up. */\n gatewayHealth: string;\n}\n\n/**\n * Strip an optional `0x` prefix from a hex string. Returns the cleaned\n * hex if present, otherwise the original string unchanged.\n */\nfunction strip0x(hex: string): string {\n return hex.startsWith(\"0x\") || hex.startsWith(\"0X\") ? hex.slice(2) : hex;\n}\n\n/**\n * Normalise the gateway base URL.\n *\n * The blob-gateway's express routes are mounted at `/blobs/:contentHash/...`,\n * and the gateway is exposed both directly AND via an nginx reverse-proxy\n * that also prefixes `/blobs`. In production, the SDK is configured with\n * `baseUrl=\"https://host/blobs\"`, and constructs upload URLs like\n * `${baseUrl}/blobs/<hash>/manifest` — i.e. the **upload path keeps both\n * \"/blobs\" segments**. To produce a working *human-facing* status URL\n * here, we must preserve the same shape.\n *\n * Accepts:\n * - `https://host` — `originBase = host`\n * - `https://host/blobs` — `originBase = host` (the /blobs is the\n * nginx prefix; we keep it for blob URLs but\n * strip it for the top-level /chain-info,\n * /health endpoints which mount on the\n * gateway's root express app, not the blobs\n * router).\n *\n * Returns both the normalised forms callers need:\n * - `blobsBase` the URL prefix the SDK already uses for /blobs/<h>/...\n * uploads. Status URLs share this prefix.\n * - `rootBase` the bare origin for /chain-info, /health.\n */\nfunction normaliseGatewayBase(base: string): { blobsBase: string; rootBase: string } {\n let s = base.trim();\n if (s.endsWith(\"/\")) s = s.slice(0, -1);\n if (s.endsWith(\"/blobs\")) {\n const rootBase = s.slice(0, -\"/blobs\".length);\n return { blobsBase: s, rootBase };\n }\n // No /blobs in baseUrl — assume nginx mounts gateway at the root.\n // Blob URLs and root URLs share the same origin.\n return { blobsBase: s, rootBase: s };\n}\n\nexport function buildExplorerUrls(input: BuildExplorerUrlsInput): ExplorerUrls {\n const contentHash = strip0x(input.contentHash);\n const blockHash = strip0x(input.blockHash);\n const { blobsBase, rootBase } = normaliseGatewayBase(input.gatewayBaseUrl);\n\n // Polkadot.js apps URL format:\n // https://polkadot.js.org/apps/?rpc=<encoded-ws-url>#/explorer/query/<blockHash>\n // The leading `?rpc=` lives BEFORE the hash because the apps router\n // reads the query string ahead of the hash route.\n const encodedRpc = encodeURIComponent(input.rpcUrl);\n const explorer = `https://polkadot.js.org/apps/?rpc=${encodedRpc}#/explorer/query/0x${blockHash}`;\n\n // Match the upload-side path shape: ${blobsBase}/blobs/<hash>/...\n // (The SDK's `uploadBlobs()` does `${baseUrl}/blobs/<hash>/manifest`.)\n return {\n blobStatus: `${blobsBase}/blobs/${contentHash}/status`,\n explorer,\n chainInfo: `${rootBase}/chain-info`,\n gatewayHealth: `${rootBase}/health`,\n };\n}\n","/**\n * @summary One-call solo-dev bootstrap.\n *\n * `bootstrapAndTrace()` does ALL of:\n *\n * 1. `loadOrCreateIdentity()` — fresh sr25519 keypair on first run.\n * 2. `requestFaucet()` — free-tier MATRA drip on the gateway.\n * 3. `firstTraceBundle()` — build + finalise a \"hello\" trace.\n * 4. `submitCertifiedReceipt()` — upload blob + submit receipt on chain.\n * 5. `buildExplorerUrls()` — compose the URLs the dev needs to click.\n *\n * Compared to the manual flow (e2e-flow.ts), this collapses ~10 lines of\n * MateriosProvider config + 30 lines of error handling into a single\n * `await bootstrapAndTrace({})`. Defaults target Materios preprod; pass\n * env-driven overrides to point at a different chain.\n *\n * Designed to surface, not paper over, real failures. Faucet cooldown is\n * NOT retried; certification timeout is NOT swallowed. The expectation is\n * that a fresh dev sees a green path on first run and a clear error\n * message otherwise — never a hung \"loading...\".\n */\n\nimport { createHash } from \"crypto\";\nimport { Keyring } from \"@polkadot/keyring\";\nimport { cryptoWaitReady } from \"@polkadot/util-crypto\";\nimport {\n MateriosProvider,\n submitReceipt,\n uploadBlobs,\n prepareBlobData,\n waitForCertification,\n waitForMotra,\n} from \"@fluxpointstudios/orynq-sdk-anchors-materios\";\n\nimport { loadOrCreateIdentity } from \"./identity.js\";\nimport type { OrynqIdentity } from \"./identity.js\";\nimport { firstTraceBundle } from \"./trace.js\";\nimport type { TraceBundleLite } from \"./trace.js\";\nimport { requestFaucet } from \"./faucet.js\";\nimport type { FaucetDripResult } from \"./faucet.js\";\nimport { buildExplorerUrls } from \"./explorer.js\";\nimport type { ExplorerUrls } from \"./explorer.js\";\n\nexport const DEFAULT_RPC_URL = \"wss://materios.fluxpointstudios.com/rpc\";\nexport const DEFAULT_GATEWAY_URL = \"https://materios.fluxpointstudios.com/blobs\";\nexport const DEFAULT_AGENT_ID = \"orynq-quickstart\";\n\nexport interface BootstrapAndTraceOptions {\n /** Path to the on-disk identity (defaults to `~/.orynq/config.json`). */\n configPath?: string | undefined;\n /** Substrate WS RPC URL. Defaults to preprod. */\n rpcUrl?: string | undefined;\n /** Blob-gateway base URL (with or without /blobs suffix). */\n gatewayBaseUrl?: string | undefined;\n /** AgentId stamped on the trace bundle. */\n agentId?: string | undefined;\n /**\n * One-liner observation stamped as the public event of the first trace.\n * Defaults to a self-describing message that includes the env's wall\n * clock, so the trace is identifiable on the explorer.\n */\n summary?: string | undefined;\n /**\n * Wait this long for the cert-daemon committee to certify the receipt.\n * Defaults to 120 s. Set to 0 to skip the cert wait entirely (returns\n * as soon as the receipt is on chain). On preprod the cert window is\n * ~30-90 s depending on attestor load + finality gap; 120 s gives the\n * happy path a comfortable margin without keeping the dev hanging.\n */\n certTimeoutMs?: number | undefined;\n /**\n * If true, a cert timeout is treated as success — the receipt is on\n * chain, the explorer URL is printed, and the cert is just \"still\n * pending\". Defaults to true so a fresh dev sees a working trace URL\n * even when the committee is mid-vote.\n */\n treatCertTimeoutAsSuccess?: boolean | undefined;\n /**\n * Hook fired after each major step. Lets the CLI render a live status\n * line without coupling business logic to console.log.\n */\n onProgress?: ((step: BootstrapStep) => void) | undefined;\n /**\n * If true, the faucet step is skipped — useful when the dev has\n * pre-funded their address via Discord faucet, an existing wallet, etc.\n * Defaults to false.\n */\n skipFaucet?: boolean | undefined;\n}\n\nexport type BootstrapStep =\n | { kind: \"identity-loaded\"; identity: OrynqIdentity }\n | { kind: \"faucet-result\"; result: FaucetDripResult }\n | { kind: \"waiting-for-motra\" }\n | { kind: \"motra-ready\"; balance: bigint }\n | { kind: \"trace-built\"; bundle: TraceBundleLite }\n | { kind: \"blob-uploaded\"; contentHash: string }\n | { kind: \"receipt-submitted\"; receiptId: string; blockHash: string }\n | { kind: \"certified\"; certHash: string }\n | { kind: \"explorer-ready\"; urls: ExplorerUrls };\n\nexport interface BootstrapAndTraceResult {\n identity: OrynqIdentity;\n bundle: TraceBundleLite;\n receiptId: string;\n blockHash: string;\n certHash?: string;\n urls: ExplorerUrls;\n /** Wall-clock duration from start of bootstrap to URLs available. */\n elapsedMs: number;\n}\n\n/**\n * Bootstrap a fresh dev to a chain-anchored first trace.\n *\n * Steps (each emits a progress event via `onProgress`):\n * 1. load-or-create identity ~50 ms\n * 2. faucet drip (skipped if funded) ~2 s\n * 3. wait for MOTRA to generate ~10-30 s (chain block production)\n * 4. build local trace bundle ~10 ms\n * 5. upload blob + submit receipt ~6 s (1 block)\n * 6. await certification (optional) ~10-30 s (committee voting)\n * 7. compose explorer URLs instant\n *\n * Total budget on a fresh address: 30-60 s wall-clock. With faucet skipped\n * and MOTRA already in hand: <10 s.\n */\nexport async function bootstrapAndTrace(\n opts: BootstrapAndTraceOptions = {},\n): Promise<BootstrapAndTraceResult> {\n await cryptoWaitReady();\n const start = Date.now();\n const onProgress = opts.onProgress ?? (() => {});\n\n // Step 1: identity\n const identity = await loadOrCreateIdentity({\n ...(opts.configPath !== undefined ? { configPath: opts.configPath } : {}),\n });\n onProgress({ kind: \"identity-loaded\", identity });\n\n const gateway = opts.gatewayBaseUrl ?? DEFAULT_GATEWAY_URL;\n const rpcUrl = opts.rpcUrl ?? DEFAULT_RPC_URL;\n\n // Step 2: faucet (best-effort; idempotent under success + already-funded)\n if (!opts.skipFaucet) {\n const faucetResult = await requestFaucet({ address: identity.address, gatewayBaseUrl: gateway });\n onProgress({ kind: \"faucet-result\", result: faucetResult });\n if (faucetResult.kind === \"error\" || faucetResult.kind === \"cooldown\") {\n throw new Error(\n `faucet drip failed for ${identity.address}: ${faucetResult.kind} — ${faucetResult.message ?? \"unknown\"}. ` +\n `Workarounds: (1) skipFaucet:true if you've already funded ${identity.address} elsewhere; ` +\n `(2) retry in a few minutes if cooldown; (3) ask in Discord (#materios) for a top-up.`,\n );\n }\n }\n\n // Step 3: connect to chain, wait for MOTRA\n const provider = new MateriosProvider({ rpcUrl, signerUri: identity.mnemonic });\n await provider.connect();\n try {\n onProgress({ kind: \"waiting-for-motra\" });\n // 1 MATRA at 6-dec = 1_000_000 units. We need enough MOTRA (the fee\n // currency, auto-generated from MATRA at ~6.94e-12 MOTRA per MATRA-block)\n // to cover one submit_receipt. The default min in waitForMotra\n // (1.5e12) is empirically the floor that covers one extrinsic + a\n // chain-tx + a chunk upload.\n const balance = await waitForMotra(provider, undefined, { timeoutMs: 90_000 });\n onProgress({ kind: \"motra-ready\", balance });\n\n // Step 4: build the trace\n const bundle = await firstTraceBundle({\n agentId: opts.agentId ?? DEFAULT_AGENT_ID,\n summary:\n opts.summary ??\n `first trace via orynq-sdk-quickstart at ${new Date().toISOString()}`,\n });\n onProgress({ kind: \"trace-built\", bundle });\n\n // Step 5: upload blob + submit receipt + (optionally) wait for cert.\n //\n // We call the three SDK primitives explicitly instead of\n // `submitCertifiedReceipt()` so a cert-poll timeout doesn't lose the\n // submit result. The on-chain receipt + blockHash are already known\n // by then — we just want to surface \"submitted, pending cert\" cleanly.\n const keypair = provider.getKeypair();\n const contentBuf = Buffer.from(bundle.content, \"utf-8\");\n const contentHash = bundle.manifestHash; // canonical content == addressable blob\n const certTimeoutMs = opts.certTimeoutMs ?? 120_000;\n const treatCertTimeoutAsSuccess = opts.treatCertTimeoutAsSuccess !== false;\n\n // 5a. Derive the receiptId the same way submit_receipt does: it's\n // sha256 of the (binary) contentHash. The blob-gateway routes\n // all chunk + manifest paths under this receiptId so they must\n // match the on-chain id byte-for-byte.\n const contentHashHex = contentHash.startsWith(\"0x\") ? contentHash.slice(2) : contentHash;\n const receiptIdHex = \"0x\" + createHash(\"sha256\")\n .update(Buffer.from(contentHashHex, \"hex\"))\n .digest(\"hex\");\n\n // 5b. Upload the blob via sig-only auth (no API key required).\n const { manifest, chunks } = prepareBlobData(receiptIdHex, contentBuf);\n const uploadResult = await uploadBlobs(\n receiptIdHex,\n manifest,\n chunks,\n {\n baseUrl: gateway,\n signerKeypair: {\n address: keypair.address,\n sign: (msg: Uint8Array) => keypair.sign(msg),\n },\n },\n );\n if (!uploadResult.success) {\n throw new Error(`blob upload failed: ${uploadResult.error ?? \"unknown\"}`);\n }\n\n // 5c. Submit the on-chain receipt. Pass receiptId explicitly so the\n // gateway-side blob path + the on-chain receipt agree on the key\n // (the SDK's default derivation matches what we computed above).\n const submitResult = await submitReceipt(provider, {\n receiptId: receiptIdHex,\n contentHash,\n rootHash: bundle.rootHash,\n manifestHash: uploadResult.storageLocatorHash ?? bundle.manifestHash,\n });\n onProgress({\n kind: \"receipt-submitted\",\n receiptId: submitResult.receiptId,\n blockHash: submitResult.blockHash,\n });\n\n // 5c. Optionally wait for cert. Timeouts are surfaced as \"submitted,\n // pending cert\" rather than a hard failure so the dev still sees\n // a usable URL on a slow committee.\n let certHash: string | undefined;\n if (certTimeoutMs > 0) {\n try {\n const certResult = await waitForCertification(\n provider,\n submitResult.receiptId,\n { timeoutMs: certTimeoutMs },\n );\n certHash = certResult.certHash;\n onProgress({ kind: \"certified\", certHash });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const isCertTimeout = /Certification timeout/i.test(msg);\n if (!(isCertTimeout && treatCertTimeoutAsSuccess)) {\n throw err;\n }\n // Cert pending — proceed with the URLs we have.\n }\n }\n\n // The gateway routes blob status by the same key the SDK uploaded\n // under — that's the receiptId, NOT the content sha256. Pass it as\n // `contentHash` to buildExplorerUrls (the parameter name carries the\n // legacy meaning from the gateway route).\n const urls = buildExplorerUrls({\n contentHash: receiptIdHex,\n blockHash: submitResult.blockHash,\n gatewayBaseUrl: gateway,\n rpcUrl,\n });\n onProgress({ kind: \"explorer-ready\", urls });\n\n const result: BootstrapAndTraceResult = {\n identity,\n bundle,\n receiptId: submitResult.receiptId,\n blockHash: submitResult.blockHash,\n urls,\n elapsedMs: Date.now() - start,\n };\n if (certHash) {\n result.certHash = certHash;\n }\n return result;\n } finally {\n await provider.disconnect().catch(() => {\n // Swallow disconnect errors — we already have the result the caller\n // wanted. Surfacing this would mask the real (successful) outcome.\n // The provider's WS will tear itself down on process exit anyway.\n });\n }\n}\n\n/**\n * Standalone helper: spin up a `Keyring` from a mnemonic. Exposed so the\n * CLI can re-derive an address from the saved config without pulling in\n * the full bootstrap path.\n */\nexport async function deriveAddress(mnemonic: string, ss58Format = 42): Promise<string> {\n await cryptoWaitReady();\n const keyring = new Keyring({ type: \"sr25519\", ss58Format });\n return keyring.addFromUri(mnemonic).address;\n}\n\n","/**\n * @fluxpointstudios/orynq-sdk-quickstart\n *\n * Solo-developer DX surface. Get from `npm install` to a chain-anchored\n * first trace in under 5 minutes — no signer URI to manage, no wallet to\n * seed, no Cardano addresses to look up.\n *\n * Three layers:\n *\n * - **CLI** (`bin/orynq.mjs`): `orynq init`, `orynq trace`,\n * `orynq whoami`, `orynq status`.\n * - **One-call API**: `bootstrapAndTrace()` — identity,\n * faucet, submit, certify, URL.\n * - **Primitives**: `loadOrCreateIdentity`,\n * `firstTraceBundle`, `requestFaucet`,\n * `buildExplorerUrls`. Mix and match\n * when you're past the hello-world tier.\n *\n * All primitives are pure ESM, zero side-effects on import. The first\n * filesystem write happens only when you call into `loadOrCreateIdentity`\n * (or any helper that wraps it), so this package is safe to require()\n * from a Cloudflare Worker or a Vite client bundle.\n */\n\nexport {\n loadOrCreateIdentity,\n defaultConfigPath,\n} from \"./identity.js\";\nexport type {\n OrynqIdentity,\n LoadOrCreateIdentityOptions,\n} from \"./identity.js\";\n\nexport { firstTraceBundle } from \"./trace.js\";\nexport type {\n TraceBundleLite,\n FirstTraceBundleOptions,\n DeterministicHooks,\n} from \"./trace.js\";\n\nexport { requestFaucet } from \"./faucet.js\";\nexport type {\n FaucetDripResult,\n FaucetDripSuccess,\n FaucetDripAlreadyFunded,\n FaucetDripCooldown,\n FaucetDripError,\n RequestFaucetOptions,\n} from \"./faucet.js\";\n\nexport { buildExplorerUrls } from \"./explorer.js\";\nexport type { ExplorerUrls, BuildExplorerUrlsInput } from \"./explorer.js\";\n\nexport {\n bootstrapAndTrace,\n deriveAddress,\n DEFAULT_RPC_URL,\n DEFAULT_GATEWAY_URL,\n DEFAULT_AGENT_ID,\n} from \"./bootstrap.js\";\nexport type {\n BootstrapAndTraceOptions,\n BootstrapAndTraceResult,\n BootstrapStep,\n} from \"./bootstrap.js\";\n\nexport const VERSION = \"0.1.0\";\n"]}