@naulon/shared 0.0.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.
Files changed (57) hide show
  1. package/dist/attribution.d.ts +92 -0
  2. package/dist/attribution.d.ts.map +1 -0
  3. package/dist/attribution.js +145 -0
  4. package/dist/attribution.js.map +1 -0
  5. package/dist/botAuthSign.d.ts +74 -0
  6. package/dist/botAuthSign.d.ts.map +1 -0
  7. package/dist/botAuthSign.js +90 -0
  8. package/dist/botAuthSign.js.map +1 -0
  9. package/dist/config.d.ts +131 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +400 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/credits.d.ts +7 -0
  14. package/dist/credits.d.ts.map +1 -0
  15. package/dist/credits.js +7 -0
  16. package/dist/credits.js.map +1 -0
  17. package/dist/eip3009.d.ts +69 -0
  18. package/dist/eip3009.d.ts.map +1 -0
  19. package/dist/eip3009.js +34 -0
  20. package/dist/eip3009.js.map +1 -0
  21. package/dist/eventsink.d.ts +19 -0
  22. package/dist/eventsink.d.ts.map +1 -0
  23. package/dist/eventsink.js +110 -0
  24. package/dist/eventsink.js.map +1 -0
  25. package/dist/index.d.ts +14 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +14 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/license.d.ts +178 -0
  30. package/dist/license.d.ts.map +1 -0
  31. package/dist/license.js +252 -0
  32. package/dist/license.js.map +1 -0
  33. package/dist/networks.d.ts +63 -0
  34. package/dist/networks.d.ts.map +1 -0
  35. package/dist/networks.js +84 -0
  36. package/dist/networks.js.map +1 -0
  37. package/dist/observationsink.d.ts +22 -0
  38. package/dist/observationsink.d.ts.map +1 -0
  39. package/dist/observationsink.js +83 -0
  40. package/dist/observationsink.js.map +1 -0
  41. package/dist/publisher.d.ts +209 -0
  42. package/dist/publisher.d.ts.map +1 -0
  43. package/dist/publisher.js +2 -0
  44. package/dist/publisher.js.map +1 -0
  45. package/dist/settlementEmit.d.ts +31 -0
  46. package/dist/settlementEmit.d.ts.map +1 -0
  47. package/dist/settlementEmit.js +65 -0
  48. package/dist/settlementEmit.js.map +1 -0
  49. package/dist/supabase.d.ts +8 -0
  50. package/dist/supabase.d.ts.map +1 -0
  51. package/dist/supabase.js +47 -0
  52. package/dist/supabase.js.map +1 -0
  53. package/dist/types.d.ts +154 -0
  54. package/dist/types.d.ts.map +1 -0
  55. package/dist/types.js +22 -0
  56. package/dist/types.js.map +1 -0
  57. package/package.json +44 -0
package/dist/config.js ADDED
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Env loading + validation. Fail loud at boot if a required var is missing,
3
+ * rather than mid-payment. Secrets never get a default.
4
+ */
5
+ import { existsSync } from "node:fs";
6
+ import { dirname, join } from "node:path";
7
+ import { config as loadDotenv } from "dotenv";
8
+ import { z } from "zod";
9
+ // Find the repo root by walking UP from the cwd until we hit the workspace root
10
+ // (marked by package-lock.json). We anchor on cwd, not import.meta.url, because
11
+ // this package is symlinked into node_modules/@naulon/shared — under
12
+ // `npm run -w` the module URL can resolve to the symlink path, sending an
13
+ // import.meta.url-relative lookup into node_modules/ instead of the repo. Every
14
+ // entrypoint (make, npm -w, tests) runs with cwd inside the repo, so walking up
15
+ // from cwd is reliable and symlink-immune. Falls back to cwd if no marker found.
16
+ function findRepoRoot() {
17
+ let dir = process.cwd();
18
+ for (let i = 0; i < 12; i++) {
19
+ if (existsSync(join(dir, "package-lock.json")))
20
+ return dir;
21
+ const parent = dirname(dir);
22
+ if (parent === dir)
23
+ break;
24
+ dir = parent;
25
+ }
26
+ return process.cwd();
27
+ }
28
+ const REPO_ROOT = findRepoRoot();
29
+ // Anchor .env to the repo root, not cwd — npm workspaces run each package script
30
+ // with cwd set to the package dir, so a cwd-relative lookup would miss the root
31
+ // .env and silently fall back to defaults (mock mode, ephemeral keys).
32
+ // Tests are hermetic — they must not pick up a developer's local .env (which may
33
+ // set PAYMENT_MODE=gateway, real creds, etc.). Everything else loads the root .env.
34
+ if (process.env.NODE_ENV !== "test") {
35
+ loadDotenv({ path: join(REPO_ROOT, ".env") });
36
+ }
37
+ // Anchor the ledger to the repo root, not the current working directory — npm
38
+ // workspaces change cwd per package, so a cwd-relative default would split the
39
+ // ledger across folders. Uses REPO_ROOT (cwd-walked) for the same symlink-immune
40
+ // reason as the .env load above.
41
+ const DEFAULT_EVENTS_PATH = join(REPO_ROOT, "data/events.jsonl");
42
+ const DEFAULT_OBSERVATIONS_PATH = join(REPO_ROOT, "data/observations.jsonl");
43
+ const DEFAULT_PAYOUTS_PATH = join(REPO_ROOT, "data/payouts.jsonl");
44
+ const DEFAULT_LICENSE_STORE = join(REPO_ROOT, "data/wayfarer-licenses.json");
45
+ const DEFAULT_CREDITS_FIXTURES = join(REPO_ROOT, "examples/meridian/credits.json");
46
+ const DEFAULT_SETTLEMENT_OUTBOX = join(REPO_ROOT, "data/settlement-outbox.jsonl");
47
+ // Exported so config validation (e.g. the licensing superRefine) is unit-testable
48
+ // without mutating process.env / the getConfig() singleton.
49
+ export const configSchema = z.object({
50
+ // Payment rail. "mock" settles offline (no creds); "gateway" uses the real
51
+ // Circle Gateway batching SDK (needs a funded BUYER_PRIVATE_KEY).
52
+ PAYMENT_MODE: z.enum(["mock", "gateway"]).default("mock"),
53
+ // Settlement network — which Circle Gateway chain the gate tolls on. Selects the
54
+ // whole rail (x402 quote, settlement body, discovery manifest, buyer client, and
55
+ // the testnet-vs-mainnet facilitator) from one switch. Default arcTestnet so a
56
+ // misconfigured deploy settles on testnet, never silently on mainnet. See
57
+ // shared/networks.ts for the per-network constants.
58
+ SETTLEMENT_NETWORK: z.enum(["arcTestnet", "baseSepolia", "base"]).default("arcTestnet"),
59
+ // Circle Gateway. GATEWAY_API_URL overrides the facilitator endpoint; the
60
+ // testnet facilitator needs no key.
61
+ CIRCLE_API_KEY: z.string().optional(),
62
+ GATEWAY_API_URL: z.string().url().optional(),
63
+ // Relayer key for the Arc self-relay (memo) settlement path. Required ONLY when
64
+ // PAYMENT_MODE=gateway AND the active network ships the Memo predeploy (Arc) —
65
+ // on Base/Base Sepolia the rail is Circle Gateway and this is unused. The relayer
66
+ // is an EOA (the Memo precompile is EOA-only) that signs the OUTER tx and pays gas
67
+ // (native USDC on Arc, an operating cost); it NEVER touches the transferred funds
68
+ // (buyer→author by the buyer's EIP-3009 authorization). Custody-free holds. A
69
+ // settle-time guard (not boot) errors clearly if it's missing on a memo network.
70
+ RELAYER_PRIVATE_KEY: z.string().optional(),
71
+ // Override the Arc USDC EIP-712 domain `name` if it is ever not the standard
72
+ // "USD Coin" (PREFLIGHT: confirm against the on-chain name() before real settle).
73
+ USDC_EIP712_NAME: z.string().optional(),
74
+ // Validity window (seconds) we advertise in the x402 quote (validBefore = now +
75
+ // this). Circle's Gateway facilitator rejects `verify` unless the REMAINING
76
+ // validity at verify time is >= 7 days (604800s). The SDK client clamps short
77
+ // windows up, but a non-SDK buyer that trusts our advertised value verbatim
78
+ // fails `authorization_validity_too_short` if it is below the floor. Default 8
79
+ // days = ~1 day of margin over the floor for signing->verify latency + clock
80
+ // skew. Keep it >= 604900 (floor + the SDK's 100s buffer) — enforced below so a
81
+ // future edit can't silently re-arm the 4d footgun this fix removed.
82
+ X402_MAX_TIMEOUT_SECONDS: z.coerce
83
+ .number()
84
+ .int()
85
+ .positive()
86
+ .default(691_200)
87
+ .refine((v) => v >= 604_900, {
88
+ message: "must be >= 604900 (Circle Gateway's 7-day validity floor 604800 + the SDK's 100s buffer); below it a non-SDK buyer that signs our advertised window verbatim fails authorization_validity_too_short",
89
+ }),
90
+ // Tollgate
91
+ TOLLGATE_PORT: z.coerce.number().int().positive().default(8402),
92
+ // The site the gate sits in front of. Any publisher; not tied to one product.
93
+ ORIGIN_URL: z.string().url().default("http://localhost:3000"),
94
+ DEFAULT_PRICE_USDC: z.coerce.number().positive().default(0.001),
95
+ // Citation tolls cost this multiple of a single read (a citation has downstream
96
+ // reach — it grounds an answer many will see). Both resolve to the same author
97
+ // payees; only the price differs. 1 = price a citation the same as a read.
98
+ CITATION_MULTIPLIER: z.coerce.number().positive().default(5),
99
+ // Which path prefixes count as gateable articles (comma-separated).
100
+ ARTICLE_PATH_PREFIXES: z.string().default("essays,articles,posts"),
101
+ // ── Hardening ──
102
+ // HMAC secret that signs 402 payment nonces. If unset, the gate mints an
103
+ // ephemeral one at boot (fine for a single instance; set it for multi-instance
104
+ // or to keep outstanding nonces valid across restarts).
105
+ TOLLGATE_SECRET: z.string().optional(),
106
+ // How long an issued 402 nonce stays valid (seconds). Also the replay window.
107
+ NONCE_TTL_SECONDS: z.coerce.number().int().positive().default(300),
108
+ // Per-client request ceiling. 0 disables rate limiting. Sustained rate is
109
+ // RATE_LIMIT_RPM/min; short bursts up to RATE_LIMIT_BURST are absorbed.
110
+ RATE_LIMIT_RPM: z.coerce.number().int().nonnegative().default(120),
111
+ RATE_LIMIT_BURST: z.coerce.number().int().positive().default(40),
112
+ // Trust X-Forwarded-For for the client IP. Only enable behind a proxy you
113
+ // control — otherwise clients spoof their rate-limit identity. Default: off.
114
+ TRUST_PROXY: z
115
+ .enum(["true", "false"])
116
+ .default("false")
117
+ .transform((v) => v === "true"),
118
+ // Web Bot Auth: allow http:// + loopback key directories so a LOCAL signer
119
+ // fixture can serve its directory from a loopback port. Test walks only —
120
+ // never enable in production (the directory URL is attacker-supplied).
121
+ BOT_AUTH_ALLOW_HTTP: z
122
+ .enum(["true", "false"])
123
+ .default("false")
124
+ .transform((v) => v === "true"),
125
+ // Web Bot Auth SIGNING identity (slice 3 — the toll's own species marker).
126
+ // A base64url 32-byte Ed25519 seed (generate: scripts/wba-keygen.mjs). When
127
+ // set, the gate serves + self-signs the key directory at
128
+ // /.well-known/http-message-signatures-directory, and the wayfarer signs its
129
+ // outbound requests. Unset ⇒ both surfaces are dark (byte-identical traffic).
130
+ BOT_AUTH_SIGNING_KEY: z.string().optional(),
131
+ // The directory host the wayfarer advertises in Signature-Agent — it must
132
+ // actually serve OUR directory (e.g. "naulon.app"). An http://127.0.0.1:port
133
+ // form is a local-walk fixture (needs the verifying gate's BOT_AUTH_ALLOW_HTTP).
134
+ BOT_AUTH_SIGNATURE_AGENT: z.string().optional(),
135
+ // Credits resolution — how the gate maps an article to its author(s).
136
+ // If CREDITS_API_URL is set, the gate fetches `${url}/credits/:slug`.
137
+ // Otherwise it reads a local JSON fixture (CREDITS_FIXTURES).
138
+ CREDITS_API_URL: z.string().url().optional(),
139
+ CREDITS_API_TOKEN: z.string().optional(),
140
+ CREDITS_FIXTURES: z.string().default(DEFAULT_CREDITS_FIXTURES),
141
+ // Shared HMAC secret for the naulon → publisher settlement emit (POST
142
+ // ${ORIGIN_URL}/api/credits/settlement). Must match the publisher's value. When
143
+ // unset the emit is dark — the gate still tolls and serves; it just doesn't
144
+ // report earnings (keeps the no-creds mock loop working, per hard rule).
145
+ CREDITS_SETTLEMENT_SECRET: z.string().optional(),
146
+ // Crash-safe at-least-once delivery for the settlement emit. The hot path
147
+ // tries once; a background drain re-sends anything not yet acked. The outbox
148
+ // is an append-only log of acked event ids — durable across restarts, and
149
+ // only an optimization (a lost outbox just re-POSTs, which IA dedupes).
150
+ SETTLEMENT_OUTBOX_PATH: z.string().default(DEFAULT_SETTLEMENT_OUTBOX),
151
+ // Bounded retry budget per event inside the drain (the hot path is always 1).
152
+ SETTLEMENT_MAX_ATTEMPTS: z.coerce.number().int().positive().default(5),
153
+ // Per-attempt POST timeout (ms) — a hung IA must never stall the gate.
154
+ SETTLEMENT_TIMEOUT_MS: z.coerce.number().int().positive().default(4000),
155
+ // How often the background drain sweeps for unacked events. 0 disables it
156
+ // (e.g. serverless, where a cron drives the drain instead of a live loop).
157
+ SETTLEMENT_DRAIN_INTERVAL_MS: z.coerce.number().int().nonnegative().default(60_000),
158
+ // Wayfarer
159
+ BUYER_ADDRESS: z.string().optional(),
160
+ BUYER_PRIVATE_KEY: z.string().optional(),
161
+ WAYFARER_BUDGET_USDC: z.coerce.number().positive().default(0.1),
162
+ // BUY-1.4 pay-path hardening. The buyer prices then pays in two separate requests,
163
+ // so the toll can move between them. The MCP re-quotes at pay time and aborts if the
164
+ // live total tops the quoted total by more than this tolerance (basis points). 0 =
165
+ // strict: abort on ANY increase over the quoted total (the budget-safe default; a
166
+ // price DROP is always fine). Raise it only to absorb known small price drift.
167
+ WAYFARER_TOLL_TOLERANCE_BPS: z.coerce.number().int().nonnegative().default(0),
168
+ // Floor (seconds) on the buyer's EIP-3009 validity window, stamped at pay time. If a
169
+ // gate advertises a too-short maxTimeoutSeconds, the signed authorization could expire
170
+ // before the relayer submits it (`authorization_validity_too_short`). The window only
171
+ // widens to this floor, never shrinks. See memory `x402-validity-window-floor`.
172
+ WAYFARER_MIN_VALIDITY_SECONDS: z.coerce.number().int().positive().default(60),
173
+ // BUY-3 policy engine (server-config, never LLM-controlled). All optional — unset
174
+ // ⇒ DEFAULT_POLICY. The MCP folds these into the DecisionPolicy it hands run().
175
+ // *_DOMAINS: comma-separated publisher hosts. ALLOW is an allowlist (deny-by-
176
+ // default for anything not listed); DENY always wins. CAP: max pays per host.
177
+ // APPROVAL_USDC: a toll at/above this becomes an "approve" (human gate), not a
178
+ // pay. KILL_SWITCH: halt all new spend (free re-reads of held licenses still ok).
179
+ // Parse to a non-empty host list, or `undefined` when blank/malformed (e.g. ","):
180
+ // a garbled value must read as "unset" (no restriction), never as an empty
181
+ // allowlist that would silently skip every essay.
182
+ WAYFARER_ALLOW_DOMAINS: z
183
+ .string()
184
+ .optional()
185
+ .transform((v) => {
186
+ const hosts = v ? v.split(",").map((s) => s.trim()).filter(Boolean) : [];
187
+ return hosts.length ? hosts : undefined;
188
+ }),
189
+ WAYFARER_DENY_DOMAINS: z
190
+ .string()
191
+ .optional()
192
+ .transform((v) => {
193
+ const hosts = v ? v.split(",").map((s) => s.trim()).filter(Boolean) : [];
194
+ return hosts.length ? hosts : undefined;
195
+ }),
196
+ WAYFARER_PER_DOMAIN_CAP: z.coerce.number().int().positive().optional(),
197
+ WAYFARER_APPROVAL_USDC: z.coerce.number().positive().optional(),
198
+ WAYFARER_KILL_SWITCH: z
199
+ .string()
200
+ .default("false")
201
+ .transform((v) => v === "true" || v === "1"),
202
+ OPENAI_API_KEY: z.string().optional(),
203
+ // USDC the agent deposits into the Gateway Wallet at the start of a run.
204
+ DEPOSIT_AMOUNT_USDC: z.string().default("1"),
205
+ // The tollgate the agent pays. Defaults to the local tollgate.
206
+ TOLLGATE_URL: z.string().url().optional(),
207
+ // Where the agent discovers candidate essays (a catalog of {slug,title,summary}).
208
+ CATALOG_URL: z.string().url().optional(),
209
+ // RSS/sitemap discovery. If set, the agent discovers from the publisher's live
210
+ // feed instead of a CATALOG_URL. Precedence: RSS_URL > PUBLISHER_URL > CATALOG_URL
211
+ // > bundled demo. rssSource reads ${PUBLISHER_URL}/rss.xml unless RSS_URL overrides.
212
+ PUBLISHER_URL: z.string().url().optional(),
213
+ RSS_URL: z.string().url().optional(),
214
+ // Optional — fills slug coverage RSS truncates (latest-N). Unused until sitemap
215
+ // parsing lands; reserved here so the seam is config-complete.
216
+ SITEMAP_URL: z.string().url().optional(),
217
+ // Where the agent caches Citation Licenses it has been issued, so a live one
218
+ // lets it re-read an essay free instead of paying again (across runs).
219
+ WAYFARER_LICENSE_PATH: z.string().default(DEFAULT_LICENSE_STORE),
220
+ // Dashboard
221
+ DASHBOARD_PORT: z.coerce.number().int().positive().default(8403),
222
+ // Interface the dashboard binds. Defaults to localhost: the earnings view has
223
+ // no built-in auth and exposes author wallets + USD, so it must not face the
224
+ // public internet directly. Set "0.0.0.0" only behind your own auth (reverse
225
+ // proxy, access gateway). See README "Dashboard exposure".
226
+ DASHBOARD_BIND: z.string().default("127.0.0.1"),
227
+ // ── Storage backend ──
228
+ // Where attributed events live. "jsonl" = an append-only local file (default,
229
+ // no creds — great for dev/demo and a single-box deploy). "supabase" = a
230
+ // Postgres table over Supabase's REST API, for serverless / multi-instance
231
+ // hosts (e.g. Vercel) that have no shared disk. Same `EventSink` seam either way.
232
+ EVENTS_BACKEND: z.enum(["jsonl", "supabase"]).default("jsonl"),
233
+ // Where spent 402 nonces are remembered (replay protection). "memory" =
234
+ // in-process (default; correct for a single instance). "supabase" = a shared
235
+ // table, so replay protection holds across many serverless instances.
236
+ NONCE_BACKEND: z.enum(["memory", "supabase"]).default("memory"),
237
+ // Where buyer-authorized EXTRA settlement legs await their deferred on-chain settle
238
+ // (the operator/co-author legs beyond the synchronous author leg). "memory" =
239
+ // in-process (default; correct for a single instance / mock dev). "supabase" = a
240
+ // shared table the deferred-settle drain reads, so pending authorizations survive a
241
+ // restart and settle exactly-once across instances. Same PendingLegSink seam either way.
242
+ PENDING_LEGS_BACKEND: z.enum(["memory", "supabase"]).default("memory"),
243
+ // Supabase project — only needed when a *_BACKEND above is "supabase". The
244
+ // service-role key is a secret; keep it in .env, never in the repo.
245
+ SUPABASE_URL: z.string().url().optional(),
246
+ SUPABASE_SERVICE_KEY: z.string().optional(),
247
+ SUPABASE_EVENTS_TABLE: z.string().default("naulon_events"),
248
+ SUPABASE_NONCES_TABLE: z.string().default("naulon_nonces"),
249
+ SUPABASE_PENDING_LEGS_TABLE: z.string().default("naulon_pending_legs"),
250
+ SUPABASE_REVOCATIONS_TABLE: z.string().default("naulon_revocations"),
251
+ // Where gated-request OBSERVATIONS go (the audit/observability plane — who was
252
+ // served free / denied / paid). "off" (default) records nothing, so the open
253
+ // core keeps its zero-overhead, nothing-stored posture; "jsonl" = a local file
254
+ // (dev); "supabase" = a shared table a multi-tenant embedder reads for its audit
255
+ // UI. Separate from EVENTS_BACKEND on purpose: observations are higher-volume,
256
+ // lower-value, and TTL'd, so a deploy may want them in a different place (or off).
257
+ OBSERVATIONS_BACKEND: z.enum(["off", "jsonl", "supabase"]).default("off"),
258
+ SUPABASE_OBSERVATIONS_TABLE: z.string().default("naulon_observations"),
259
+ // ── Citation License Tokens (CLT) ──
260
+ // Signed receipts handed to a paying agent (see docs/citation-license.md).
261
+ // On by default — they're additive (an extra response header) and the offline
262
+ // path mints with an ephemeral key. A STABLE key is required (below) for real
263
+ // payments or a supabase/multi-instance deploy.
264
+ LICENSES_ENABLED: z
265
+ .enum(["true", "false"])
266
+ .default("true")
267
+ .transform((v) => v === "true"),
268
+ // Ed25519 private key (PKCS8 PEM or base64 DER) that signs licenses. SECRET.
269
+ // Leave unset only for single-instance mock/dev (ephemeral key + boot warning).
270
+ LICENSE_SIGNING_KEY: z.string().optional(),
271
+ // Re-read window for a license, seconds. A CLT is an unrevocable bearer
272
+ // credential on the offline tier, so the TTL is the kill switch — kept short.
273
+ LICENSE_TTL_SECONDS: z.coerce.number().int().positive().default(600),
274
+ // Issuer/audience string; defaults to `naulon:<gate host>` derived at runtime.
275
+ LICENSE_ISSUER: z.string().optional(),
276
+ // Embed the full payees graph (transparent, default) or just a hash + primary.
277
+ LICENSE_PAYEES_MODE: z.enum(["full", "hashed"]).default("full"),
278
+ // Consult the jti revocation seam on the online verify tier (P2). Off = the
279
+ // offline JWKS tier only (exp-bounded). Needs shared state when on.
280
+ LICENSE_ONLINE_CHECK: z
281
+ .enum(["true", "false"])
282
+ .default("false")
283
+ .transform((v) => v === "true"),
284
+ // Holder-of-key (P5): mint licenses bound to the payer wallet (RFC 7800 cnf),
285
+ // and require a proof-of-possession (an EIP-191 wallet signature) on re-read.
286
+ // Off by default — v1 licenses are short-TTL bearer tokens; turn this on to
287
+ // close leak-replay (a captured token is then useless without the wallet key).
288
+ // Needs a signing-capable buyer wallet (BUYER_PRIVATE_KEY, or the dev key on
289
+ // the mock path) — the demo loop is unaffected while this is off.
290
+ LICENSE_POP: z
291
+ .enum(["true", "false"])
292
+ .default("false")
293
+ .transform((v) => v === "true"),
294
+ // Freshness window (seconds) for a proof-of-possession: the gate accepts a
295
+ // proof whose timestamp is within ±this of its clock, and remembers the proof
296
+ // nonce for this long to stop replay. Shorter = tighter replay window; longer
297
+ // = more clock-skew tolerance across instances.
298
+ LICENSE_POP_WINDOW_SECONDS: z.coerce.number().int().positive().default(120),
299
+ // Shared event ledger (jsonl backend). Tollgate appends here; dashboard +
300
+ // attribution read it.
301
+ EVENTS_PATH: z.string().default(DEFAULT_EVENTS_PATH),
302
+ // Observation ledger (jsonl backend). Tollgate appends; the audit plane reads.
303
+ OBSERVATIONS_PATH: z.string().default(DEFAULT_OBSERVATIONS_PATH),
304
+ // Attribution & settlement.
305
+ PAYOUTS_PATH: z.string().default(DEFAULT_PAYOUTS_PATH),
306
+ // Don't settle a wallet until its accrued tolls reach this — amortizes the
307
+ // per-transfer overhead across many sub-cent reads. Below it, carry forward.
308
+ MIN_PAYOUT_USDC: z.coerce.number().positive().default(0.005),
309
+ // How the single on-chain recipient is chosen when two co-authors tie for the
310
+ // top share. "wallet" (default) breaks ties by address, so who gets the on-chain
311
+ // leg is a pure function of who's credited — a reordered credits graph can't move
312
+ // it. "input" keeps the credits-graph order. The full split is recorded either way.
313
+ PRIMARY_PAYEE_TIEBREAK: z.enum(["wallet", "input"]).default("wallet"),
314
+ // Pay co-authors directly on-chain (split-at-source) for multi-author articles
315
+ // instead of routing the whole toll to the primary author. OFF by default → the
316
+ // stock single-recipient toll (the rest of the split is recorded for the publisher
317
+ // to reconcile off-protocol). ON → the primary's content-gating leg drops to its
318
+ // own share and each other co-author gets a direct deferred buyer→author leg
319
+ // (custody-free). Sets `PublisherConfig.coauthorSplit` for the single-tenant gate;
320
+ // a multi-tenant resolver decides this per publisher instead.
321
+ COAUTHOR_ONCHAIN_SPLIT: z
322
+ .enum(["true", "false"])
323
+ .default("false")
324
+ .transform((v) => v === "true"),
325
+ }).superRefine((cfg, ctx) => {
326
+ // A "supabase" backend is useless without the project creds — fail loud at
327
+ // boot, not mid-payment, naming exactly what's missing.
328
+ const usesSupabase = cfg.EVENTS_BACKEND === "supabase" ||
329
+ cfg.NONCE_BACKEND === "supabase" ||
330
+ cfg.PENDING_LEGS_BACKEND === "supabase" ||
331
+ cfg.OBSERVATIONS_BACKEND === "supabase";
332
+ if (usesSupabase) {
333
+ for (const key of ["SUPABASE_URL", "SUPABASE_SERVICE_KEY"]) {
334
+ if (!cfg[key]) {
335
+ ctx.addIssue({
336
+ code: "custom",
337
+ path: [key],
338
+ message: `required when EVENTS_BACKEND, NONCE_BACKEND or OBSERVATIONS_BACKEND is "supabase"`,
339
+ });
340
+ }
341
+ }
342
+ }
343
+ // A CLT is a reusable, externally-verified token, so an ephemeral signing key
344
+ // is only safe on a single-instance mock box. Require a stable key once real
345
+ // money moves or any shared (supabase) backend / multi-instance is in play —
346
+ // otherwise JWKS and the paid re-read go non-deterministic across instances.
347
+ const needsStableKey = cfg.LICENSES_ENABLED && (cfg.PAYMENT_MODE !== "mock" || usesSupabase);
348
+ if (needsStableKey && !cfg.LICENSE_SIGNING_KEY) {
349
+ ctx.addIssue({
350
+ code: "custom",
351
+ path: ["LICENSE_SIGNING_KEY"],
352
+ message: "required (stable Ed25519 key) when licensing is on with real payments or a supabase backend — " +
353
+ "ephemeral keys break verification across instances. See docs/citation-license.md.",
354
+ });
355
+ }
356
+ if (cfg.LICENSE_TTL_SECONDS > 3600) {
357
+ ctx.addIssue({
358
+ code: "custom",
359
+ path: ["LICENSE_TTL_SECONDS"],
360
+ message: "must be <= 3600: a CLT is an unrevocable bearer credential on the offline tier, so a short TTL is the kill switch.",
361
+ });
362
+ }
363
+ if (cfg.LICENSE_POP_WINDOW_SECONDS > 600) {
364
+ ctx.addIssue({
365
+ code: "custom",
366
+ path: ["LICENSE_POP_WINDOW_SECONDS"],
367
+ message: "must be <= 600: the proof-of-possession window is a replay window — keep it tight (it only needs to cover clock skew + one round trip).",
368
+ });
369
+ }
370
+ });
371
+ let cached;
372
+ export function getConfig() {
373
+ if (cached)
374
+ return cached;
375
+ const parsed = configSchema.safeParse(process.env);
376
+ if (!parsed.success) {
377
+ const issues = parsed.error.issues
378
+ .map((i) => ` ${i.path.join(".")}: ${i.message}`)
379
+ .join("\n");
380
+ throw new Error(`Invalid environment:\n${issues}\nSee .env.example.`);
381
+ }
382
+ cached = parsed.data;
383
+ return cached;
384
+ }
385
+ /**
386
+ * Drop the memoized config so the next `getConfig()` re-reads `process.env`.
387
+ * A test seam for the env-dependent branches the singleton otherwise freezes
388
+ * (e.g. the Circle-key settlement guard) — not for production hot-reload.
389
+ */
390
+ export function resetConfig() {
391
+ cached = undefined;
392
+ }
393
+ /** Assert a set of keys are present (call at a component's boot once it knows what it needs). */
394
+ export function requireKeys(cfg, keys) {
395
+ const missing = keys.filter((k) => cfg[k] === undefined || cfg[k] === "");
396
+ if (missing.length) {
397
+ throw new Error(`Missing required env: ${missing.join(", ")}. See .env.example.`);
398
+ }
399
+ }
400
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Credits-graph validation now lives in @naulon/sdk — the one place the
3
+ * money-routing trust boundary's schema is defined. Re-exported here so existing
4
+ * `@naulon/shared` importers (and `shared/index.ts`'s barrel) are unchanged.
5
+ */
6
+ export { creditsSchema, parseCredits, buildCredits } from "@naulon/sdk";
7
+ //# sourceMappingURL=credits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../src/credits.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Credits-graph validation now lives in @naulon/sdk — the one place the
3
+ * money-routing trust boundary's schema is defined. Re-exported here so existing
4
+ * `@naulon/shared` importers (and `shared/index.ts`'s barrel) are unchanged.
5
+ */
6
+ export { creditsSchema, parseCredits, buildCredits } from "@naulon/sdk";
7
+ //# sourceMappingURL=credits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credits.js","sourceRoot":"","sources":["../src/credits.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * EIP-3009 (`transferWithAuthorization`) protocol descriptor — the SDK-free, pure-data
3
+ * definition of a USDC EIP-3009 authorization and the EIP-712 domain it is signed
4
+ * against. Both sides of the memo self-relay rail consume this single source of truth:
5
+ * the BUYER signs against it (wayfarer), the GATE verifies against it (tollgate). The
6
+ * viem signing/recovery act stays in each consumer — each already depends on viem; this
7
+ * file must not, so `shared` stays dependency-light (it is imported by the cloud control
8
+ * plane too).
9
+ *
10
+ * This is the sibling of `networks.ts` `gatewayExtra` (the GatewayWallet EIP-712 domain):
11
+ * same pattern — the protocol descriptor lives in `shared`, the crypto lives in the
12
+ * consumer. Keep them adjacent.
13
+ */
14
+ import type { SettlementNetwork } from "./networks.ts";
15
+ /** The buyer's EIP-3009 authorization — the exact `{from,to,value,validAfter,
16
+ * validBefore,nonce}` message they sign, identical in shape to the Gateway payload's
17
+ * `authorization` (only the EIP-712 DOMAIN they sign it against differs: the USDC
18
+ * token here vs the GatewayWallet contract in batched mode). */
19
+ export interface MemoAuthorization {
20
+ from: `0x${string}`;
21
+ to: `0x${string}`;
22
+ value: string;
23
+ validAfter: string;
24
+ validBefore: string;
25
+ nonce: `0x${string}`;
26
+ }
27
+ /** EIP-712 typed-data type for the EIP-3009 transfer authorization (what the buyer
28
+ * signs against the USDC domain, and what the gate recovers the signer from). */
29
+ export declare const TRANSFER_WITH_AUTHORIZATION_TYPES: {
30
+ readonly TransferWithAuthorization: readonly [{
31
+ readonly name: "from";
32
+ readonly type: "address";
33
+ }, {
34
+ readonly name: "to";
35
+ readonly type: "address";
36
+ }, {
37
+ readonly name: "value";
38
+ readonly type: "uint256";
39
+ }, {
40
+ readonly name: "validAfter";
41
+ readonly type: "uint256";
42
+ }, {
43
+ readonly name: "validBefore";
44
+ readonly type: "uint256";
45
+ }, {
46
+ readonly name: "nonce";
47
+ readonly type: "bytes32";
48
+ }];
49
+ };
50
+ /** The USDC EIP-712 domain name/version — LAST-RESORT fallback only. The real values
51
+ * live per-network on `SettlementNetwork.usdcName`/`usdcVersion` (verified on-chain,
52
+ * e.g. Arc testnet = "USDC"/"2", NOT this mainnet FiatToken default), and the
53
+ * `USDC_EIP712_NAME` env var overrides everything. PREFLIGHT before the first real
54
+ * settle on a new network: confirm `name()` on its USDC contract matches the domain —
55
+ * a mismatch makes both the off-chain pre-verify AND the on-chain
56
+ * `transferWithAuthorization` reject a valid signature, with a confusing "signer != from".
57
+ * (`make arc-preflight` does this check.) */
58
+ export declare const USDC_EIP712_NAME = "USD Coin";
59
+ export declare const USDC_EIP712_VERSION = "2";
60
+ /** Resolve the USDC EIP-712 domain: env override → per-network verified value → the
61
+ * mainnet-FiatToken fallback. Never trusts the fallback for a network that declares
62
+ * its own (Arc), so the default path signs correctly with no env required. */
63
+ export declare function usdcDomain(net: SettlementNetwork, nameOverride?: string): {
64
+ readonly name: string;
65
+ readonly version: string;
66
+ readonly chainId: number;
67
+ readonly verifyingContract: `0x${string}`;
68
+ };
69
+ //# sourceMappingURL=eip3009.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eip3009.d.ts","sourceRoot":"","sources":["../src/eip3009.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;iEAGiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;IACpB,EAAE,EAAE,KAAK,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;CACtB;AAED;kFACkF;AAClF,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;CASpC,CAAC;AAEX;;;;;;;8CAO8C;AAC9C,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAC3C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;+EAE+E;AAC/E,wBAAgB,UAAU,CAAC,GAAG,EAAE,iBAAiB,EAAE,YAAY,CAAC,EAAE,MAAM;;;;;EAOvE"}
@@ -0,0 +1,34 @@
1
+ /** EIP-712 typed-data type for the EIP-3009 transfer authorization (what the buyer
2
+ * signs against the USDC domain, and what the gate recovers the signer from). */
3
+ export const TRANSFER_WITH_AUTHORIZATION_TYPES = {
4
+ TransferWithAuthorization: [
5
+ { name: "from", type: "address" },
6
+ { name: "to", type: "address" },
7
+ { name: "value", type: "uint256" },
8
+ { name: "validAfter", type: "uint256" },
9
+ { name: "validBefore", type: "uint256" },
10
+ { name: "nonce", type: "bytes32" },
11
+ ],
12
+ };
13
+ /** The USDC EIP-712 domain name/version — LAST-RESORT fallback only. The real values
14
+ * live per-network on `SettlementNetwork.usdcName`/`usdcVersion` (verified on-chain,
15
+ * e.g. Arc testnet = "USDC"/"2", NOT this mainnet FiatToken default), and the
16
+ * `USDC_EIP712_NAME` env var overrides everything. PREFLIGHT before the first real
17
+ * settle on a new network: confirm `name()` on its USDC contract matches the domain —
18
+ * a mismatch makes both the off-chain pre-verify AND the on-chain
19
+ * `transferWithAuthorization` reject a valid signature, with a confusing "signer != from".
20
+ * (`make arc-preflight` does this check.) */
21
+ export const USDC_EIP712_NAME = "USD Coin";
22
+ export const USDC_EIP712_VERSION = "2";
23
+ /** Resolve the USDC EIP-712 domain: env override → per-network verified value → the
24
+ * mainnet-FiatToken fallback. Never trusts the fallback for a network that declares
25
+ * its own (Arc), so the default path signs correctly with no env required. */
26
+ export function usdcDomain(net, nameOverride) {
27
+ return {
28
+ name: nameOverride ?? net.usdcName ?? USDC_EIP712_NAME,
29
+ version: net.usdcVersion ?? USDC_EIP712_VERSION,
30
+ chainId: net.chainId,
31
+ verifyingContract: net.usdc,
32
+ };
33
+ }
34
+ //# sourceMappingURL=eip3009.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eip3009.js","sourceRoot":"","sources":["../src/eip3009.ts"],"names":[],"mappings":"AA4BA;kFACkF;AAClF,MAAM,CAAC,MAAM,iCAAiC,GAAG;IAC/C,yBAAyB,EAAE;QACzB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;QACjC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;QAC/B,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;QAClC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE;QACvC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE;QACxC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;KACnC;CACO,CAAC;AAEX;;;;;;;8CAO8C;AAC9C,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;AAC3C,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC;;+EAE+E;AAC/E,MAAM,UAAU,UAAU,CAAC,GAAsB,EAAE,YAAqB;IACtE,OAAO;QACL,IAAI,EAAE,YAAY,IAAI,GAAG,CAAC,QAAQ,IAAI,gBAAgB;QACtD,OAAO,EAAE,GAAG,CAAC,WAAW,IAAI,mBAAmB;QAC/C,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,iBAAiB,EAAE,GAAG,CAAC,IAAI;KACnB,CAAC;AACb,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { AttributedEvent, EventSink } from "./types.ts";
2
+ export declare function jsonlSink(path?: string): EventSink;
3
+ /**
4
+ * Supabase-backed sink. Each event is stored as one row: its `id` (primary key,
5
+ * so a retried write is idempotent), its `at` timestamp (indexed, for ordering),
6
+ * and the whole `AttributedEvent` as a jsonb `data` column — so `readAll` hands
7
+ * back the exact shape the writer stored, with no field-by-field mapping to drift.
8
+ * This is the backend to use on serverless/multi-instance hosts with no shared disk.
9
+ */
10
+ export declare function supabaseSink(): EventSink;
11
+ /**
12
+ * Pick the EventSink the config asks for. JSONL file by default (offline, no
13
+ * creds); Supabase when EVENTS_BACKEND=supabase. Callers use this instead of
14
+ * naming a sink directly, so switching backends is one env var.
15
+ */
16
+ export declare function getSink(): EventSink;
17
+ /** In-memory sink, handy for tests. */
18
+ export declare function memorySink(seed?: AttributedEvent[]): EventSink;
19
+ //# sourceMappingURL=eventsink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventsink.d.ts","sourceRoot":"","sources":["../src/eventsink.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7D,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAqClD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,SAAS,CA4BxC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,SAAS,CAEnC;AAED,uCAAuC;AACvC,wBAAgB,UAAU,CAAC,IAAI,GAAE,eAAe,EAAO,GAAG,SAAS,CAalE"}