@forgezero/runtime 0.1.6 → 0.1.8

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/README.md CHANGED
@@ -1,155 +1,884 @@
1
+ <!--
2
+ GENERATED FILE — do not edit.
3
+
4
+ Change tools/generate-guides.ts or its typed sources, run `bun run guides`,
5
+ and commit the generator and rendered files together.
6
+ -->
7
+
1
8
  # @forgezero/runtime
2
9
 
3
- **The machinery behind a request handler.** Background jobs that never overlap
4
- themselves, keyed queues, a transactional outbox, a hash-chained audit trail,
5
- templated mail, encrypted backups, schema validation, and money that is never a
6
- floating-point number.
10
+ The machinery behind a request handler jobs, queues, an outbox, a hash-chained audit trail, mail, backups, schema validation and exact money. 44 public modules, each imported on its own.
11
+
12
+ ## Package overview
13
+
14
+ Anyone running a service, for the work that happens outside a request. Separate from `access` because a Cloudflare Worker wants the request pipeline and cannot run a backup job. Supported runtimes: bun, node. Package root: @forgezero/runtime. The sections below show the actual named imports emitted by each declaration entry point; wildcard imports are intentionally not used in the documentation.
7
15
 
8
- Thirty-four public modules. Each is its own entry point, so you install one package and
9
- your bundler includes only what you imported.
16
+ ```text
17
+ bun add @forgezero/runtime
18
+ ```
10
19
 
11
- ```bash
20
+ ## ForgeZero package family
21
+
22
+ The five packages are installation boundaries. Choose a package by who installs it; choose a subpath by the capability used in that file.
23
+
24
+ | package | short description | runtimes | documentation |
25
+ |---|---|---|---|
26
+ | @forgezero/vault | Scoped secret access with Agent, API-key and systemd-credential sources. | bun, node, workers, deno | [Open](https://www.forgezero.net/docs/vault-package) |
27
+ | @forgezero/access | Typed route, principal, factor, RBAC and request-pipeline contracts. | bun, node, workers, deno | [Open](https://www.forgezero.net/docs/access) |
28
+ | @forgezero/providers | Typed external providers with priority, health and classified fallback. | bun, node, workers, deno | [Open](https://www.forgezero.net/docs/providers) |
29
+ | @forgezero/runtime | Portable runtime primitives for queries, jobs, events, schemas and finance. | bun, node | [Open](https://www.forgezero.net/docs/runtime) |
30
+ | @forgezero/agent | Operator CLI and managed-node agent for bootstrap, deploy and lifecycle. | bun, node | [Open](https://www.forgezero.net/docs/agent) |
31
+
32
+ ## @forgezero/runtime public imports and commands
33
+
34
+ Every row links to the detailed explanation and named-import/example area below. This table and those details are generated from the package inventory and emitted declarations.
35
+
36
+ | public entry | short description | runtime | details |
37
+ |---|---|---|---|
38
+ | @forgezero/runtime/query | Provider-neutral typed function contracts: decode untrusted input, run with caller-supplied services or storage, and strictly validate the result without coupling business logic to ForgeZero or ArangoDB. | portable | [Details + example](#forgezero-runtime-query) |
39
+ | @forgezero/runtime/jobs | Background work that never overlaps itself, advances a cursor only on success, and can be paused and inspected. | portable | [Details + example](#forgezero-runtime-jobs) |
40
+ | @forgezero/runtime/queue | Memory-only keyed work queue — awaited results, parallel across keys and strictly sequential within one; clustered callers atomically claim ownership in their own business store before submitting. | portable | [Details + example](#forgezero-runtime-queue) |
41
+ | @forgezero/runtime/outbox | Write the event with the record, deliver it after, in order per key with backoff and a dead-letter queue. | portable | [Details + example](#forgezero-runtime-outbox) |
42
+ | @forgezero/runtime/audit | Append-only records chained by hash, with a verifier that names the first altered entry. | portable | [Details + example](#forgezero-runtime-audit) |
43
+ | @forgezero/runtime/backup | Encrypted, chunked, verified snapshots to object storage — and the restore that reads them back. | portable | [Details + example](#forgezero-runtime-backup) |
44
+ | @forgezero/runtime/notify | Render a named template to text and HTML, escaped per part, refusing to send with a blank where a value should be. | portable | [Details + example](#forgezero-runtime-notify) |
45
+ | @forgezero/runtime/notify/templates | The six transactional messages ForgeZero sends. | portable | [Details + example](#forgezero-runtime-notify-templates) |
46
+ | @forgezero/runtime/calendar | Billing periods computed from an anchor, working days, holidays and due dates. | portable | [Details + example](#forgezero-runtime-calendar) |
47
+ | @forgezero/runtime/compliance | Screening as a decision record — tiers, rules and lists, failing closed when a list is unreachable. | portable | [Details + example](#forgezero-runtime-compliance) |
48
+ | @forgezero/runtime/totp | RFC 6238 TOTP on the existing HMAC — base32, an asymmetric window, and replay left to the caller. | portable | [Details + example](#forgezero-runtime-totp) |
49
+ | @forgezero/runtime/passkey | Passkeys for sites that are not us. A vault-held credential is as unphishable as one in a security chip provided the RP ID check never slips — `evil-example.com` ends with `example.com` and is a different site. | portable | [Details + example](#forgezero-runtime-passkey) |
50
+ | @forgezero/runtime/phrase | BIP-39 recovery phrases, and the salted verifier that proves one without being able to reconstruct it. | portable | [Details + example](#forgezero-runtime-phrase) |
51
+ | @forgezero/runtime/snp | Parse an AMD SEV-SNP attestation report at the firmware ABI offsets, and compare a TCB component by component so a microcode bump cannot mask a firmware downgrade. | portable | [Details + example](#forgezero-runtime-snp) |
52
+ | @forgezero/runtime/importers | Read secrets out of a .env, a CSV, or a Bitwarden or 1Password export — skipping what cannot be understood rather than guessing, and never putting a value in an error. | portable | [Details + example](#forgezero-runtime-importers) |
53
+ | @forgezero/runtime/openssh | OpenSSH wire encoding, so a derived ed25519 key becomes a line that pastes into authorized_keys. | portable | [Details + example](#forgezero-runtime-openssh) |
54
+ | @forgezero/runtime/ssh-cert | OpenSSH certificates, so access expires instead of having to be hunted down. Takes a signing FUNCTION rather than a secret key, which is what lets the CA live in a vault that never hands it out. | portable | [Details + example](#forgezero-runtime-ssh-cert) |
55
+ | @forgezero/runtime/slip10 | SLIP-0010 derivation for ed25519, hardened-only — BIP-32 does not work on this curve and produces halves that do not correspond. | portable | [Details + example](#forgezero-runtime-slip10) |
56
+ | @forgezero/runtime/identity | Hybrid Ed25519 + ML-DSA-65 request signing. One canonical string, so the compute agent that signs inside a guest and the API that verifies cannot drift — which two implementations of it certainly would. | portable | [Details + example](#forgezero-runtime-identity) |
57
+ | @forgezero/runtime/schema | Validate against JSON Schema, restrict what a caller may declare, and describe a schema as a form. | portable | [Details + example](#forgezero-runtime-schema) |
58
+ | @forgezero/runtime/schema/typebox | The TypeBox validator behind that interface. | portable | [Details + example](#forgezero-runtime-schema-typebox) |
59
+ | @forgezero/runtime/finance/discounts | Promotions as arithmetic over integer minor units. They never stack — one winner — and a percentage rounds down, because rounding a discount up gives away a unit of currency per invoice forever. | portable | [Details + example](#forgezero-runtime-finance-discounts) |
60
+ | @forgezero/runtime/finance/money | Exact amounts in minor units with the asset attached, so two currencies cannot be added. | portable | [Details + example](#forgezero-runtime-finance-money) |
61
+ | @forgezero/runtime/finance/venues | Trading venues, market types and symbols as data — spot, margin and futures behind one order model. | portable | [Details + example](#forgezero-runtime-finance-venues) |
62
+ | @forgezero/runtime/finance/ledger | Double-entry postings and derived balances. A hold is a posting, not a lock — the queue does the ordering. | portable | [Details + example](#forgezero-runtime-finance-ledger) |
63
+ | @forgezero/runtime/finance/commission | Profit net of flows, a high-water mark, the tier split and the referral share of our income. | portable | [Details + example](#forgezero-runtime-finance-commission) |
64
+ | @forgezero/runtime/finance/rates | What an asset is worth in USD, and how old that answer is. A peg never ages; a quote always does. | portable | [Details + example](#forgezero-runtime-finance-rates) |
65
+ | @forgezero/runtime/finance/transfers | Deposits and withdrawals as ordered pipelines, with screening reserved at position zero. | portable | [Details + example](#forgezero-runtime-finance-transfers) |
66
+ | @forgezero/runtime/finance/chain | Which chains exist, their confirmation depth by value band, assets and explorers — as data an admin edits. | portable | [Details + example](#forgezero-runtime-finance-chain) |
67
+ | @forgezero/runtime/finance/custody | Derive CREATE2 deposit addresses and EIP-712 custody digests — arithmetic over a seed and a salt, needing no credential and no node. | portable | [Details + example](#forgezero-runtime-finance-custody) |
68
+ | @forgezero/runtime/finance/derive | Declare a field ForgeZero generates rather than the tenant supplying: BIP-44 path arithmetic, and the choice of who is capable of generating the key. | portable | [Details + example](#forgezero-runtime-finance-derive) |
69
+ | @forgezero/runtime/finance/tax | Which jurisdiction may tax a sale, who accounts for it, and the three ways to be zero that report differently. Rates are data. | portable | [Details + example](#forgezero-runtime-finance-tax) |
70
+ | @forgezero/runtime/finance/storage | Write an amount so it is both exact and sortable: an authoritative string, and a number that is only an index. | portable | [Details + example](#forgezero-runtime-finance-storage) |
71
+ | @forgezero/runtime/realtime | Provider-neutral realtime audience, shard, event and delivery contracts. | portable | [Details + example](#forgezero-runtime-realtime) |
72
+ | @forgezero/runtime/passkey-hybrid | Versioned WebAuthn PRF plus ML-DSA companion proof construction and verification. | portable | [Details + example](#forgezero-runtime-passkey-hybrid) |
73
+ | @forgezero/runtime/otpauth | Parse and render otpauth URIs without binding enrolment to a UI framework. | portable | [Details + example](#forgezero-runtime-otpauth) |
74
+ | @forgezero/runtime/pipeline | Typed ordered application-pipeline execution with explicit evidence. | portable | [Details + example](#forgezero-runtime-pipeline) |
75
+ | @forgezero/runtime/finance/chain-addresses | Chain-address derivation records and validation independent of a node provider. | portable | [Details + example](#forgezero-runtime-finance-chain-addresses) |
76
+ | @forgezero/runtime/finance/chain-deposits | Provider-neutral deposit observation, confirmation and credit transitions. | portable | [Details + example](#forgezero-runtime-finance-chain-deposits) |
77
+ | @forgezero/runtime/finance/chain-withdrawals | Provider-neutral withdrawal approval, broadcast and finality transitions. | portable | [Details + example](#forgezero-runtime-finance-chain-withdrawals) |
78
+ | @forgezero/runtime/finance/chain-reconcile | Deterministic reconciliation between chain observations and durable transfer state. | portable | [Details + example](#forgezero-runtime-finance-chain-reconcile) |
79
+ | @forgezero/runtime/finance/market | Market order, fill and quote contracts independent of any exchange adapter. | portable | [Details + example](#forgezero-runtime-finance-market) |
80
+ | @forgezero/runtime/custody-share | Threshold-share parsing, validation and reconstruction. | portable | [Details + example](#forgezero-runtime-custody-share) |
81
+ | @forgezero/runtime/custody-crypto | Hybrid ML-KEM-768 plus X25519 custody-share sealing and opening. | portable | [Details + example](#forgezero-runtime-custody-crypto) |
82
+
83
+ ## Commands
84
+
85
+ bun add @forgezero/runtime — Install runtime contracts; import the required subpath so unused capabilities stay out of the bundle.
86
+
87
+ ```text
12
88
  bun add @forgezero/runtime
13
89
  ```
14
90
 
15
- ```ts
91
+ <a id="forgezero-runtime-query"></a>
92
+ ## @forgezero/runtime/query
93
+
94
+ Provider-neutral typed function contracts: decode untrusted input, run with caller-supplied services or storage, and strictly validate the result without coupling business logic to ForgeZero or ArangoDB. Named value imports: QueryContractError, defineQuery, implementQuery. Named type imports: QueryCodec, QueryContract, QueryDecode, QueryExecution, QueryImplementation, QueryInput, QueryIssue, QueryOutput. Import only the names used by this file.
95
+
96
+ ```text
97
+ import { QueryContractError, defineQuery, implementQuery } from '@forgezero/runtime/query';
98
+ import type { QueryCodec, QueryContract, QueryDecode, QueryExecution, QueryImplementation, QueryInput } from '@forgezero/runtime/query';
99
+ import type { QueryIssue, QueryOutput } from '@forgezero/runtime/query';
100
+ ```
101
+
102
+ ## @forgezero/runtime/query — Define and implement a typed query
103
+
104
+ Decode untrusted input and validate output around injected storage/services rather than embedding a database query in the route adapter.
105
+
106
+ ```text
107
+ import { defineQuery, implementQuery } from '@forgezero/runtime/query';
108
+ import { T, type Static, typeboxQueryCodec } from '@forgezero/runtime/schema/typebox';
109
+
110
+ const OrderKey = T.Object({ orderKey: T.String({ minLength: 1 }) });
111
+ const Order = T.Object({ orderKey: T.String(), total: T.String() });
112
+ type OrderRow = Static<typeof Order>;
113
+ type Stores = { orders: { find(key: string): Promise<OrderRow | null> } };
114
+ declare const stores: Stores;
115
+
116
+ const findOrder = defineQuery({
117
+ name: 'orders.find',
118
+ input: typeboxQueryCodec(OrderKey),
119
+ output: typeboxQueryCodec(T.Union([Order, T.Null()]))
120
+ });
121
+ export const runFindOrder = implementQuery(findOrder,
122
+ (context: Stores, input) => context.orders.find(input.orderKey)
123
+ );
124
+
125
+ const order = await runFindOrder.execute(stores, { orderKey: 'ord_123' });
126
+ ```
127
+
128
+ <a id="forgezero-runtime-jobs"></a>
129
+ ## @forgezero/runtime/jobs
130
+
131
+ Background work that never overlaps itself, advances a cursor only on success, and can be paused and inspected. Named value imports: VERSION, createScheduler, cursorJob, defineJob, everyMs, memoryLock, nextWallClockAt, storeLock, systemClock. Named type imports: Clock, CursorBatch, CursorJobSpec, CursorStore, JobContext, JobLock, JobReport, JobResult, JobSpec, Lease, LockStore, Scheduler, SchedulerOptions, WallClockSchedule. Import only the names used by this file.
132
+
133
+ ```text
134
+ import { VERSION, createScheduler, cursorJob, defineJob, everyMs, memoryLock } from '@forgezero/runtime/jobs';
135
+ import { nextWallClockAt, storeLock, systemClock } from '@forgezero/runtime/jobs';
136
+ import type { Clock, CursorBatch, CursorJobSpec, CursorStore, JobContext, JobLock } from '@forgezero/runtime/jobs';
137
+ import type { JobReport, JobResult, JobSpec, Lease, LockStore, Scheduler } from '@forgezero/runtime/jobs';
138
+ import type { SchedulerOptions, WallClockSchedule } from '@forgezero/runtime/jobs';
139
+ ```
140
+
141
+ <a id="forgezero-runtime-queue"></a>
142
+ ## @forgezero/runtime/queue
143
+
144
+ Memory-only keyed work queue — awaited results, parallel across keys and strictly sequential within one; clustered callers atomically claim ownership in their own business store before submitting. Named value imports: QueueKeyStoppedError, QueueStoppedError, TaskCancelledError, createQueue, queueWidthFor. Named type imports: DrainReport, Queue, QueueOptions, QueueResourcePolicy, QueueTask, RetryPolicy. Import only the names used by this file.
145
+
146
+ ```text
147
+ import { QueueKeyStoppedError, QueueStoppedError, TaskCancelledError, createQueue, queueWidthFor } from '@forgezero/runtime/queue';
148
+ import type { DrainReport, Queue, QueueOptions, QueueResourcePolicy, QueueTask, RetryPolicy } from '@forgezero/runtime/queue';
149
+ ```
150
+
151
+ <a id="forgezero-runtime-outbox"></a>
152
+ ## @forgezero/runtime/outbox
153
+
154
+ Write the event with the record, deliver it after, in order per key with backoff and a dead-letter queue. Named value imports: DEFAULT_POLICY, EVENT_STATES, OutboxError, VERSION, backoffMs, createOutbox, memoryStore, outboxJob. Named type imports: DeliveryPolicy, DeliveryResult, DrainReport, EventState, Outbox, OutboxEvent, OutboxOptions, OutboxStore, PublishInput, Transport. Import only the names used by this file.
155
+
156
+ ```text
157
+ import { DEFAULT_POLICY, EVENT_STATES, OutboxError, VERSION, backoffMs, createOutbox } from '@forgezero/runtime/outbox';
158
+ import { memoryStore, outboxJob } from '@forgezero/runtime/outbox';
159
+ import type { DeliveryPolicy, DeliveryResult, DrainReport, EventState, Outbox, OutboxEvent } from '@forgezero/runtime/outbox';
160
+ import type { OutboxOptions, OutboxStore, PublishInput, Transport } from '@forgezero/runtime/outbox';
161
+ ```
162
+
163
+ <a id="forgezero-runtime-audit"></a>
164
+ ## @forgezero/runtime/audit
165
+
166
+ Append-only records chained by hash, with a verifier that names the first altered entry. Named value imports: AuditChainError, GENESIS_DIGEST, VERSION, appendRecord, canonicalise, createAuditChain, exportRange, hashDigester, memoryStore, sealedDigester, verifyChain, verifyExport. Named type imports: AppendOptions, AuditChain, AuditChainOptions, AuditEntry, AuditExport, AuditRecord, AuditStore, ChainVerdict, Digester, EffectAuditRecord. Import only the names used by this file.
167
+
168
+ ```text
169
+ import { AuditChainError, GENESIS_DIGEST, VERSION, appendRecord, canonicalise, createAuditChain } from '@forgezero/runtime/audit';
170
+ import { exportRange, hashDigester, memoryStore, sealedDigester, verifyChain, verifyExport } from '@forgezero/runtime/audit';
171
+ import type { AppendOptions, AuditChain, AuditChainOptions, AuditEntry, AuditExport, AuditRecord } from '@forgezero/runtime/audit';
172
+ import type { AuditStore, ChainVerdict, Digester, EffectAuditRecord } from '@forgezero/runtime/audit';
173
+ ```
174
+
175
+ <a id="forgezero-runtime-backup"></a>
176
+ ## @forgezero/runtime/backup
177
+
178
+ Encrypted, chunked, verified snapshots to object storage — and the restore that reads them back. Named value imports: BackupError, DEFAULT_RETENTION, SNAPSHOT_FORMAT, VERSION, backupJob, listSnapshots, prune, restore, selectForDeletion, snapshot, verifySnapshot. Named type imports: ChunkRecord, ObjectStore, RestoreOptions, RestoreReport, RetentionPolicy, RowSink, RowSource, SnapshotManifest, SnapshotOptions, VerifyReport. Import only the names used by this file.
179
+
180
+ ```text
181
+ import { BackupError, DEFAULT_RETENTION, SNAPSHOT_FORMAT, VERSION, backupJob, listSnapshots } from '@forgezero/runtime/backup';
182
+ import { prune, restore, selectForDeletion, snapshot, verifySnapshot } from '@forgezero/runtime/backup';
183
+ import type { ChunkRecord, ObjectStore, RestoreOptions, RestoreReport, RetentionPolicy, RowSink } from '@forgezero/runtime/backup';
184
+ import type { RowSource, SnapshotManifest, SnapshotOptions, VerifyReport } from '@forgezero/runtime/backup';
185
+ ```
186
+
187
+ <a id="forgezero-runtime-notify"></a>
188
+ ## @forgezero/runtime/notify
189
+
190
+ Render a named template to text and HTML, escaped per part, refusing to send with a blank where a value should be. Named value imports: CHANNELS, NotifyError, VERSION, assertHeaderSafe, button, codeBlock, createNotifier, defineTemplate, escapeHtml, layout, preview, render. Named type imports: Channel, Notification, Notifier, NotifierOptions, Rendered, TemplateSpec, Transport. Import only the names used by this file.
191
+
192
+ ```text
193
+ import { CHANNELS, NotifyError, VERSION, assertHeaderSafe, button, codeBlock } from '@forgezero/runtime/notify';
194
+ import { createNotifier, defineTemplate, escapeHtml, layout, preview, render } from '@forgezero/runtime/notify';
195
+ import type { Channel, Notification, Notifier, NotifierOptions, Rendered, TemplateSpec } from '@forgezero/runtime/notify';
196
+ import type { Transport } from '@forgezero/runtime/notify';
197
+ ```
198
+
199
+ <a id="forgezero-runtime-notify-templates"></a>
200
+ ## @forgezero/runtime/notify/templates
201
+
202
+ The six transactional messages ForgeZero sends. Named value imports: TEMPLATES, ceremonyProposed, custodianEnrolment, escapeHtml, invitation, newDevice, signInCode, vaultLocked. Named type imports: TemplateKey. Import only the names used by this file.
203
+
204
+ ```text
205
+ import { TEMPLATES, ceremonyProposed, custodianEnrolment, escapeHtml, invitation, newDevice } from '@forgezero/runtime/notify/templates';
206
+ import { signInCode, vaultLocked } from '@forgezero/runtime/notify/templates';
207
+ import type { TemplateKey } from '@forgezero/runtime/notify/templates';
208
+ ```
209
+
210
+ <a id="forgezero-runtime-calendar"></a>
211
+ ## @forgezero/runtime/calendar
212
+
213
+ Billing periods computed from an anchor, working days, holidays and due dates. Named value imports: CalendarError, DAY_MS, DEFAULT_WORKING_DAYS, closedPeriods, dayOfWeek, daysOverdue, fromDay, isOverdue, isWorkingDay, nextWorkingDay, periodAt, periodIndexOn, periodOn, toDay, workingDaysBetween. Named type imports: BillingSchedule, Calendar, IsoDate, Period. Import only the names used by this file.
214
+
215
+ ```text
216
+ import { CalendarError, DAY_MS, DEFAULT_WORKING_DAYS, closedPeriods, dayOfWeek, daysOverdue } from '@forgezero/runtime/calendar';
217
+ import { fromDay, isOverdue, isWorkingDay, nextWorkingDay, periodAt, periodIndexOn } from '@forgezero/runtime/calendar';
218
+ import { periodOn, toDay, workingDaysBetween } from '@forgezero/runtime/calendar';
219
+ import type { BillingSchedule, Calendar, IsoDate, Period } from '@forgezero/runtime/calendar';
220
+ ```
221
+
222
+ <a id="forgezero-runtime-compliance"></a>
223
+ ## @forgezero/runtime/compliance
224
+
225
+ Screening as a decision record — tiers, rules and lists, failing closed when a list is unreachable. Named value imports: ComplianceError, RISK_LEVELS, VERIFICATION_TIERS, combineLists, countryRule, newCounterpartyRule, screen, screeningStage, staticList, tierLimitRule, unavailableList. Named type imports: ListEntry, RiskLevel, Rule, RuleHit, ScreenOptions, ScreeningList, StageOptions, Subject, Verdict, VerificationTier. Import only the names used by this file.
226
+
227
+ ```text
228
+ import { ComplianceError, RISK_LEVELS, VERIFICATION_TIERS, combineLists, countryRule, newCounterpartyRule } from '@forgezero/runtime/compliance';
229
+ import { screen, screeningStage, staticList, tierLimitRule, unavailableList } from '@forgezero/runtime/compliance';
230
+ import type { ListEntry, RiskLevel, Rule, RuleHit, ScreenOptions, ScreeningList } from '@forgezero/runtime/compliance';
231
+ import type { StageOptions, Subject, Verdict, VerificationTier } from '@forgezero/runtime/compliance';
232
+ ```
233
+
234
+ <a id="forgezero-runtime-totp"></a>
235
+ ## @forgezero/runtime/totp
236
+
237
+ RFC 6238 TOTP on the existing HMAC — base32, an asymmetric window, and replay left to the caller. Named value imports: DEFAULT_DIGITS, DEFAULT_STEP_SECONDS, TotpError, assertCode, codeAt, codeFor, counterAt, enrolmentUri, fromBase32, generateSecret, secondsRemaining, toBase32, verifyCode. Named type imports: TotpOptions, VerifyOptions, VerifyResult. Import only the names used by this file.
238
+
239
+ ```text
240
+ import { DEFAULT_DIGITS, DEFAULT_STEP_SECONDS, TotpError, assertCode, codeAt, codeFor } from '@forgezero/runtime/totp';
241
+ import { counterAt, enrolmentUri, fromBase32, generateSecret, secondsRemaining, toBase32 } from '@forgezero/runtime/totp';
242
+ import { verifyCode } from '@forgezero/runtime/totp';
243
+ import type { TotpOptions, VerifyOptions, VerifyResult } from '@forgezero/runtime/totp';
244
+ ```
245
+
246
+ <a id="forgezero-runtime-passkey"></a>
247
+ ## @forgezero/runtime/passkey
248
+
249
+ Passkeys for sites that are not us. A vault-held credential is as unphishable as one in a security chip provided the RP ID check never slips — `evil-example.com` ends with `example.com` and is a different site. Named value imports: FLAG_BE, FLAG_BS, FLAG_UP, FLAG_UV, PasskeyError, assertPasskey, authenticatorData, credentialIdFor, passkeysFor, rpIdMatches, verifyAssertion. Named type imports: Assertion, AssertionRequest, StoredPasskey. Import only the names used by this file.
250
+
251
+ ```text
252
+ import { FLAG_BE, FLAG_BS, FLAG_UP, FLAG_UV, PasskeyError, assertPasskey } from '@forgezero/runtime/passkey';
253
+ import { authenticatorData, credentialIdFor, passkeysFor, rpIdMatches, verifyAssertion } from '@forgezero/runtime/passkey';
254
+ import type { Assertion, AssertionRequest, StoredPasskey } from '@forgezero/runtime/passkey';
255
+ ```
256
+
257
+ <a id="forgezero-runtime-phrase"></a>
258
+ ## @forgezero/runtime/phrase
259
+
260
+ BIP-39 recovery phrases, and the salted verifier that proves one without being able to reconstruct it. Named value imports: PHRASE_SALT_BYTES, PHRASE_WORDS, generatePhrase, newSalt, phraseToKey, phraseVerifier, validatePhrase. Named type imports: none. Import only the names used by this file.
261
+
262
+ ```text
263
+ import { PHRASE_SALT_BYTES, PHRASE_WORDS, generatePhrase, newSalt, phraseToKey, phraseVerifier } from '@forgezero/runtime/phrase';
264
+ import { validatePhrase } from '@forgezero/runtime/phrase';
265
+ ```
266
+
267
+ <a id="forgezero-runtime-snp"></a>
268
+ ## @forgezero/runtime/snp
269
+
270
+ Parse an AMD SEV-SNP attestation report at the firmware ABI offsets, and compare a TCB component by component so a microcode bump cannot mask a firmware downgrade. Named value imports: REPORT_BYTES, SnpError, bindsNonce, parseSnpReport, tcbAtLeast. Named type imports: GuestPolicy, SnpReport, TcbVersion. Import only the names used by this file.
271
+
272
+ ```text
273
+ import { REPORT_BYTES, SnpError, bindsNonce, parseSnpReport, tcbAtLeast } from '@forgezero/runtime/snp';
274
+ import type { GuestPolicy, SnpReport, TcbVersion } from '@forgezero/runtime/snp';
275
+ ```
276
+
277
+ <a id="forgezero-runtime-importers"></a>
278
+ ## @forgezero/runtime/importers
279
+
280
+ Read secrets out of a .env, a CSV, or a Bitwarden or 1Password export — skipping what cannot be understood rather than guessing, and never putting a value in an error. Named value imports: IMPORT_FORMATS, normaliseName, parseBitwarden, parseCsv, parseEnv, parseImport, parseOnePassword. Named type imports: ImportFormat, ImportResult, ImportedSecret, Skipped. Import only the names used by this file.
281
+
282
+ ```text
283
+ import { IMPORT_FORMATS, normaliseName, parseBitwarden, parseCsv, parseEnv, parseImport } from '@forgezero/runtime/importers';
284
+ import { parseOnePassword } from '@forgezero/runtime/importers';
285
+ import type { ImportFormat, ImportResult, ImportedSecret, Skipped } from '@forgezero/runtime/importers';
286
+ ```
287
+
288
+ <a id="forgezero-runtime-openssh"></a>
289
+ ## @forgezero/runtime/openssh
290
+
291
+ OpenSSH wire encoding, so a derived ed25519 key becomes a line that pastes into authorized_keys. Named value imports: OpenSshError, authorizedKey, fingerprint, parseAuthorizedKey, publicKeyBlob, signatureBlob. Named type imports: none. Import only the names used by this file.
292
+
293
+ ```text
294
+ import { OpenSshError, authorizedKey, fingerprint, parseAuthorizedKey, publicKeyBlob, signatureBlob } from '@forgezero/runtime/openssh';
295
+ ```
296
+
297
+ <a id="forgezero-runtime-ssh-cert"></a>
298
+ ## @forgezero/runtime/ssh-cert
299
+
300
+ OpenSSH certificates, so access expires instead of having to be hunted down. Takes a signing FUNCTION rather than a secret key, which is what lets the CA live in a vault that never hands it out. Named value imports: CERT_TYPE_HOST, CERT_TYPE_USER, DEFAULT_EXTENSIONS, SshCertError, caFingerprint, caFromSecretKey, caPublicKeyLine, isCurrentlyValid, signCertificate. Named type imports: CertificateAuthority, CertificateRequest. Import only the names used by this file.
301
+
302
+ ```text
303
+ import { CERT_TYPE_HOST, CERT_TYPE_USER, DEFAULT_EXTENSIONS, SshCertError, caFingerprint, caFromSecretKey } from '@forgezero/runtime/ssh-cert';
304
+ import { caPublicKeyLine, isCurrentlyValid, signCertificate } from '@forgezero/runtime/ssh-cert';
305
+ import type { CertificateAuthority, CertificateRequest } from '@forgezero/runtime/ssh-cert';
306
+ ```
307
+
308
+ <a id="forgezero-runtime-slip10"></a>
309
+ ## @forgezero/runtime/slip10
310
+
311
+ SLIP-0010 derivation for ed25519, hardened-only — BIP-32 does not work on this curve and produces halves that do not correspond. Named value imports: HARDENED_OFFSET, Slip10Error, deriveChild, derivePath, masterFromSeed. Named type imports: Slip10Node. Import only the names used by this file.
312
+
313
+ ```text
314
+ import { HARDENED_OFFSET, Slip10Error, deriveChild, derivePath, masterFromSeed } from '@forgezero/runtime/slip10';
315
+ import type { Slip10Node } from '@forgezero/runtime/slip10';
316
+ ```
317
+
318
+ <a id="forgezero-runtime-identity"></a>
319
+ ## @forgezero/runtime/identity
320
+
321
+ Hybrid Ed25519 + ML-DSA-65 request signing. One canonical string, so the compute agent that signs inside a guest and the API that verifies cannot drift — which two implementations of it certainly would. Named value imports: CLOCK_SKEW_SECONDS, REQUEST_SIGNATURE_SUITE, RESPONSE_KEY_HEADER, RESPONSE_SEALING_SUITE, canonicalString, decodeSignatureHeader, deriveKeysFromSeed, derivePublicKeysFromSeed, encodeSignatureHeader, generateNodeKeys, generateResponseRecipient, openResponse, sealResponse, signRequest, validResponsePublicKey, verifyRequest. Named type imports: NodeKeyPair, NodePublicKeys, ResponseRecipient, SealedResponse, SignedEnvelope, VerifyFailure. Import only the names used by this file.
322
+
323
+ ```text
324
+ import { CLOCK_SKEW_SECONDS, REQUEST_SIGNATURE_SUITE, RESPONSE_KEY_HEADER, RESPONSE_SEALING_SUITE, canonicalString, decodeSignatureHeader } from '@forgezero/runtime/identity';
325
+ import { deriveKeysFromSeed, derivePublicKeysFromSeed, encodeSignatureHeader, generateNodeKeys, generateResponseRecipient, openResponse } from '@forgezero/runtime/identity';
326
+ import { sealResponse, signRequest, validResponsePublicKey, verifyRequest } from '@forgezero/runtime/identity';
327
+ import type { NodeKeyPair, NodePublicKeys, ResponseRecipient, SealedResponse, SignedEnvelope, VerifyFailure } from '@forgezero/runtime/identity';
328
+ ```
329
+
330
+ <a id="forgezero-runtime-schema"></a>
331
+ ## @forgezero/runtime/schema
332
+
333
+ Validate against JSON Schema, restrict what a caller may declare, and describe a schema as a form. Named value imports: DEFAULT_RESTRICTIONS, SCHEMA_VERSION, SchemaError, VERSION, describeJsonSchema, readableFields, restrictJsonSchema, writeOnlyPaths. Named type imports: FieldKind, FormField, Restrictions, SchemaValidator, ValidationResult. Import only the names used by this file.
334
+
335
+ ```text
336
+ import { DEFAULT_RESTRICTIONS, SCHEMA_VERSION, SchemaError, VERSION, describeJsonSchema, readableFields } from '@forgezero/runtime/schema';
337
+ import { restrictJsonSchema, writeOnlyPaths } from '@forgezero/runtime/schema';
338
+ import type { FieldKind, FormField, Restrictions, SchemaValidator, ValidationResult } from '@forgezero/runtime/schema';
339
+ ```
340
+
341
+ ## @forgezero/runtime/schema — Restrict an untrusted JSON Schema before storing it
342
+
343
+ Tenant-supplied schemas are data, not executable code. Restriction rejects references, remote identifiers, unsupported keywords, excessive depth/size, and arrays without a bounded maxItems. Form metadata is derived from the accepted schema; write-only fields remain identifiable for secret handling.
344
+
345
+ ```text
346
+ import { restrictJsonSchema, describeJsonSchema, writeOnlyPaths } from '@forgezero/runtime/schema';
347
+
348
+ const schema = restrictJsonSchema({
349
+ type: 'object', additionalProperties: false,
350
+ properties: {
351
+ recipients: { type: 'array', maxItems: 100, items: { type: 'string' } },
352
+ apiKey: { type: 'string', title: 'API key', writeOnly: true }
353
+ },
354
+ required: ['apiKey']
355
+ });
356
+ const fields = describeJsonSchema(schema);
357
+ const secretFields = writeOnlyPaths(fields);
358
+ ```
359
+
360
+ <a id="forgezero-runtime-schema-typebox"></a>
361
+ ## @forgezero/runtime/schema/typebox
362
+
363
+ The TypeBox validator behind that interface. Named value imports: T, parse, typebox, typeboxQueryCodec. Named type imports: Static, TSchema. Import only the names used by this file.
364
+
365
+ ```text
366
+ import { T, parse, typebox, typeboxQueryCodec } from '@forgezero/runtime/schema/typebox';
367
+ import type { Static, TSchema } from '@forgezero/runtime/schema/typebox';
368
+ ```
369
+
370
+ ## @forgezero/runtime/schema/typebox — One TypeBox shape for runtime validation and static types
371
+
372
+ TypeBox is an optional peer. Its schema is JSON Schema, so the same restriction and form layer applies. The query codec validates both untrusted input and returned output; malformed values fail instead of being coerced.
373
+
374
+ ```text
375
+ import { T, type Static, parse, typeboxQueryCodec } from '@forgezero/runtime/schema/typebox';
376
+
377
+ const User = T.Object({
378
+ userKey: T.String({ minLength: 1 }),
379
+ roles: T.Array(T.String(), { maxItems: 32 })
380
+ }, { additionalProperties: false });
381
+ type User = Static<typeof User>;
382
+ const user: User = parse(User, unknownInput);
383
+ const codec = typeboxQueryCodec(User);
384
+ ```
385
+
386
+ <a id="forgezero-runtime-finance-discounts"></a>
387
+ ## @forgezero/runtime/finance/discounts
388
+
389
+ Promotions as arithmetic over integer minor units. They never stack — one winner — and a percentage rounds down, because rounding a discount up gives away a unit of currency per invoice forever. Named value imports: DiscountError, assertCode, assertWindow, bestDiscount, discountFor, ineligibility, normaliseCode. Named type imports: AppliedDiscount, DiscountKind, Ineligibility, Promotion. Import only the names used by this file.
390
+
391
+ ```text
392
+ import { DiscountError, assertCode, assertWindow, bestDiscount, discountFor, ineligibility } from '@forgezero/runtime/finance/discounts';
393
+ import { normaliseCode } from '@forgezero/runtime/finance/discounts';
394
+ import type { AppliedDiscount, DiscountKind, Ineligibility, Promotion } from '@forgezero/runtime/finance/discounts';
395
+ ```
396
+
397
+ <a id="forgezero-runtime-finance-money"></a>
398
+ ## @forgezero/runtime/finance/money
399
+
400
+ Exact amounts in minor units with the asset attached, so two currencies cannot be added. Named value imports: ASSETS, MoneyError, ROUNDING, VERSION, abs, add, allocate, assetSpec, compare, convert, defineAsset, equals, formatAmount, isNegative, isZero, money, mulRate, negate, parseAmount, subtract, toStep, zero. Named type imports: AssetSpec, Money, Rounding. Import only the names used by this file.
401
+
402
+ ```text
403
+ import { ASSETS, MoneyError, ROUNDING, VERSION, abs, add } from '@forgezero/runtime/finance/money';
404
+ import { allocate, assetSpec, compare, convert, defineAsset, equals } from '@forgezero/runtime/finance/money';
405
+ import { formatAmount, isNegative, isZero, money, mulRate, negate } from '@forgezero/runtime/finance/money';
406
+ import { parseAmount, subtract, toStep, zero } from '@forgezero/runtime/finance/money';
407
+ import type { AssetSpec, Money, Rounding } from '@forgezero/runtime/finance/money';
408
+ ```
409
+
410
+ <a id="forgezero-runtime-finance-venues"></a>
411
+ ## @forgezero/runtime/finance/venues
412
+
413
+ Trading venues, market types and symbols as data — spot, margin and futures behind one order model. Named value imports: MARKET_TYPES, ORDER_SIDES, ORDER_TYPES, TIME_IN_FORCE, VENUES, VERSION, VenueError, notionalOf, parseSymbol, submitOrder, symbolOf, validateOrder, venue, venuesFor. Named type imports: MarketSpec, MarketType, OrderRequest, OrderResult, OrderSide, OrderStatus, OrderType, TimeInForce, VenueAdapter, VenueSpec. Import only the names used by this file.
414
+
415
+ ```text
416
+ import { MARKET_TYPES, ORDER_SIDES, ORDER_TYPES, TIME_IN_FORCE, VENUES, VERSION } from '@forgezero/runtime/finance/venues';
417
+ import { VenueError, notionalOf, parseSymbol, submitOrder, symbolOf, validateOrder } from '@forgezero/runtime/finance/venues';
418
+ import { venue, venuesFor } from '@forgezero/runtime/finance/venues';
419
+ import type { MarketSpec, MarketType, OrderRequest, OrderResult, OrderSide, OrderStatus } from '@forgezero/runtime/finance/venues';
420
+ import type { OrderType, TimeInForce, VenueAdapter, VenueSpec } from '@forgezero/runtime/finance/venues';
421
+ ```
422
+
423
+ <a id="forgezero-runtime-finance-ledger"></a>
424
+ ## @forgezero/runtime/finance/ledger
425
+
426
+ Double-entry postings and derived balances. A hold is a posting, not a lock — the queue does the ordering. Named value imports: ACCOUNT_KINDS, BUCKETS, LedgerError, MAX_LEDGER_ENTRIES, VERSION, accountId, assertAvailable, assertBalanced, availableOf, balanceOf, balancesFrom, captureHold, heldOf, parseAccount, placeHold, queueKeyFor, releaseHold, statement, totalOf, transfer, trialBalance. Named type imports: AccountKind, AccountRef, Bucket, Entry, Transaction, TrialBalance. Import only the names used by this file.
427
+
428
+ ```text
429
+ import { ACCOUNT_KINDS, BUCKETS, LedgerError, MAX_LEDGER_ENTRIES, VERSION, accountId } from '@forgezero/runtime/finance/ledger';
430
+ import { assertAvailable, assertBalanced, availableOf, balanceOf, balancesFrom, captureHold } from '@forgezero/runtime/finance/ledger';
431
+ import { heldOf, parseAccount, placeHold, queueKeyFor, releaseHold, statement } from '@forgezero/runtime/finance/ledger';
432
+ import { totalOf, transfer, trialBalance } from '@forgezero/runtime/finance/ledger';
433
+ import type { AccountKind, AccountRef, Bucket, Entry, Transaction, TrialBalance } from '@forgezero/runtime/finance/ledger';
434
+ ```
435
+
436
+ <a id="forgezero-runtime-finance-commission"></a>
437
+ ## @forgezero/runtime/finance/commission
438
+
439
+ Profit net of flows, a high-water mark, the tier split and the referral share of our income. Named value imports: CommissionError, allocate, chargeableProfit, commissionTransaction, nextHighWaterMark, periodProfit, projectPrepay, splitCommission, splitReferrals. Named type imports: CommissionInput, CommissionSplit, PeriodPerformance, Prepay, PrepayInput, ReferralShare. Import only the names used by this file.
440
+
441
+ ```text
442
+ import { CommissionError, allocate, chargeableProfit, commissionTransaction, nextHighWaterMark, periodProfit } from '@forgezero/runtime/finance/commission';
443
+ import { projectPrepay, splitCommission, splitReferrals } from '@forgezero/runtime/finance/commission';
444
+ import type { CommissionInput, CommissionSplit, PeriodPerformance, Prepay, PrepayInput, ReferralShare } from '@forgezero/runtime/finance/commission';
445
+ ```
446
+
447
+ <a id="forgezero-runtime-finance-rates"></a>
448
+ ## @forgezero/runtime/finance/rates
449
+
450
+ What an asset is worth in USD, and how old that answer is. A peg never ages; a quote always does. Named value imports: BASE_ASSET, RATE_SOURCES, RateError, assertRate, createRateTable, describeRate, isRegistered, peg, rateRefreshJob, refreshRates, registerAsset. Named type imports: AssetEntry, AssetRate, RateFetcher, RateSource, RateTable, RateTableOptions, RefreshReport. Import only the names used by this file.
451
+
452
+ ```text
453
+ import { BASE_ASSET, RATE_SOURCES, RateError, assertRate, createRateTable, describeRate } from '@forgezero/runtime/finance/rates';
454
+ import { isRegistered, peg, rateRefreshJob, refreshRates, registerAsset } from '@forgezero/runtime/finance/rates';
455
+ import type { AssetEntry, AssetRate, RateFetcher, RateSource, RateTable, RateTableOptions } from '@forgezero/runtime/finance/rates';
456
+ import type { RefreshReport } from '@forgezero/runtime/finance/rates';
457
+ ```
458
+
459
+ <a id="forgezero-runtime-finance-transfers"></a>
460
+ ## @forgezero/runtime/finance/transfers
461
+
462
+ Deposits and withdrawals as ordered pipelines, with screening reserved at position zero. Named value imports: DIRECTIONS, PipelineError, SCREENING_ORDER, approvalStage, balanceStage, createTransferPipeline, limitsStage, notFrozenStage, screeningStage. Named type imports: Direction, PipelineResult, Stage, StageOutcome, Transfer, TransferPipeline. Import only the names used by this file.
463
+
464
+ ```text
465
+ import { DIRECTIONS, PipelineError, SCREENING_ORDER, approvalStage, balanceStage, createTransferPipeline } from '@forgezero/runtime/finance/transfers';
466
+ import { limitsStage, notFrozenStage, screeningStage } from '@forgezero/runtime/finance/transfers';
467
+ import type { Direction, PipelineResult, Stage, StageOutcome, Transfer, TransferPipeline } from '@forgezero/runtime/finance/transfers';
468
+ ```
469
+
470
+ <a id="forgezero-runtime-finance-chain"></a>
471
+ ## @forgezero/runtime/finance/chain
472
+
473
+ Which chains exist, their confirmation depth by value band, assets and explorers — as data an admin edits. Named value imports: ADDRESS_SCHEMES, ChainError, SEED_CHAINS, SEED_CHAIN_ASSETS, assertChain, confirmationsFor, createChainRegistry, estimatedSeconds, validateAddress. Named type imports: AddressScheme, ChainAsset, ChainRegistry, ChainSpec, ConfirmationBand. Import only the names used by this file.
474
+
475
+ ```text
476
+ import { ADDRESS_SCHEMES, ChainError, SEED_CHAINS, SEED_CHAIN_ASSETS, assertChain, confirmationsFor } from '@forgezero/runtime/finance/chain';
477
+ import { createChainRegistry, estimatedSeconds, validateAddress } from '@forgezero/runtime/finance/chain';
478
+ import type { AddressScheme, ChainAsset, ChainRegistry, ChainSpec, ConfirmationBand } from '@forgezero/runtime/finance/chain';
479
+ ```
480
+
481
+ <a id="forgezero-runtime-finance-custody"></a>
482
+ ## @forgezero/runtime/finance/custody
483
+
484
+ Derive CREATE2 deposit addresses and EIP-712 custody digests — arithmetic over a seed and a salt, needing no credential and no node. Named value imports: CustodyError, VERSION, depositAddress, domainSeparator, orderSignatures, saltFor, toChecksumAddress, withdrawDigest. Named type imports: none. Import only the names used by this file.
485
+
486
+ ```text
487
+ import { CustodyError, VERSION, depositAddress, domainSeparator, orderSignatures, saltFor } from '@forgezero/runtime/finance/custody';
488
+ import { toChecksumAddress, withdrawDigest } from '@forgezero/runtime/finance/custody';
489
+ ```
490
+
491
+ <a id="forgezero-runtime-finance-derive"></a>
492
+ ## @forgezero/runtime/finance/derive
493
+
494
+ Declare a field ForgeZero generates rather than the tenant supplying: BIP-44 path arithmetic, and the choice of who is capable of generating the key. Named value imports: CUSTODY, DERIVE_KEYWORD, DeriveError, SCHEMES, assertDeriveSpec, custodyOf, deriveSpecOf, derivedFields, derivedPaths, pathFor, suppliedPaths. Named type imports: Custody, DeriveSpec, DerivedField, Scheme. Import only the names used by this file.
495
+
496
+ ```text
497
+ import { CUSTODY, DERIVE_KEYWORD, DeriveError, SCHEMES, assertDeriveSpec, custodyOf } from '@forgezero/runtime/finance/derive';
498
+ import { deriveSpecOf, derivedFields, derivedPaths, pathFor, suppliedPaths } from '@forgezero/runtime/finance/derive';
499
+ import type { Custody, DeriveSpec, DerivedField, Scheme } from '@forgezero/runtime/finance/derive';
500
+ ```
501
+
502
+ <a id="forgezero-runtime-finance-tax"></a>
503
+ ## @forgezero/runtime/finance/tax
504
+
505
+ Which jurisdiction may tax a sale, who accounts for it, and the three ways to be zero that report differently. Rates are data. Named value imports: TREATMENTS, TaxError, applyTax, assertTaxRate, decideTax, describeTax, parseRate, percent, totalWithTax. Named type imports: Customer, Supplier, TaxAmounts, TaxDecision, TaxRate, Treatment. Import only the names used by this file.
506
+
507
+ ```text
508
+ import { TREATMENTS, TaxError, applyTax, assertTaxRate, decideTax, describeTax } from '@forgezero/runtime/finance/tax';
509
+ import { parseRate, percent, totalWithTax } from '@forgezero/runtime/finance/tax';
510
+ import type { Customer, Supplier, TaxAmounts, TaxDecision, TaxRate, Treatment } from '@forgezero/runtime/finance/tax';
511
+ ```
512
+
513
+ <a id="forgezero-runtime-finance-storage"></a>
514
+ ## @forgezero/runtime/finance/storage
515
+
516
+ Write an amount so it is both exact and sortable: an authoritative string, and a number that is only an index. Named value imports: SORT_FIELD, fromStored, rangeBounds, sortExact, sortKey, storeAmount, toStored, withinRange. Named type imports: StoredMoney. Import only the names used by this file.
517
+
518
+ ```text
519
+ import { SORT_FIELD, fromStored, rangeBounds, sortExact, sortKey, storeAmount } from '@forgezero/runtime/finance/storage';
520
+ import { toStored, withinRange } from '@forgezero/runtime/finance/storage';
521
+ import type { StoredMoney } from '@forgezero/runtime/finance/storage';
522
+ ```
523
+
524
+ <a id="forgezero-runtime-realtime"></a>
525
+ ## @forgezero/runtime/realtime
526
+
527
+ Provider-neutral realtime audience, shard, event and delivery contracts. Named value imports: REALTIME_MAX_BATCH_BYTES, REALTIME_MAX_EVENTS, REALTIME_MAX_EVENT_BYTES, REALTIME_MAX_SHARDS_PER_TOPIC, REALTIME_MAX_SOCKETS_PER_SHARD, canReceiveRealtimeAudience, issueRealtimeSubscriptionTicket, realtimeBatchBytes, realtimeHmac, realtimeShardKey, validateRealtimeAudience, validateRealtimeBatch, validateRealtimeSubscriptionTicket, verifyRealtimeHmac, verifyRealtimeSubscriptionToken. Named type imports: RealtimeAudience, RealtimeBatch, RealtimeEvent, RealtimePrincipalKind, RealtimeSubscriptionTicket. Import only the names used by this file.
528
+
529
+ ```text
530
+ import { REALTIME_MAX_BATCH_BYTES, REALTIME_MAX_EVENTS, REALTIME_MAX_EVENT_BYTES, REALTIME_MAX_SHARDS_PER_TOPIC, REALTIME_MAX_SOCKETS_PER_SHARD, canReceiveRealtimeAudience } from '@forgezero/runtime/realtime';
531
+ import { issueRealtimeSubscriptionTicket, realtimeBatchBytes, realtimeHmac, realtimeShardKey, validateRealtimeAudience, validateRealtimeBatch } from '@forgezero/runtime/realtime';
532
+ import { validateRealtimeSubscriptionTicket, verifyRealtimeHmac, verifyRealtimeSubscriptionToken } from '@forgezero/runtime/realtime';
533
+ import type { RealtimeAudience, RealtimeBatch, RealtimeEvent, RealtimePrincipalKind, RealtimeSubscriptionTicket } from '@forgezero/runtime/realtime';
534
+ ```
535
+
536
+ <a id="forgezero-runtime-passkey-hybrid"></a>
537
+ ## @forgezero/runtime/passkey-hybrid
538
+
539
+ Versioned WebAuthn PRF plus ML-DSA companion proof construction and verification. Named value imports: PASSKEY_HYBRID_SUITE, PASSKEY_HYBRID_VERSION, PASSKEY_ML_DSA_PUBLIC_KEY_BYTES, PASSKEY_ML_DSA_SIGNATURE_BYTES, PASSKEY_PRF_SALT, createPasskeyHybridProof, passkeyHybridMessage, verifyPasskeyHybridProof. Named type imports: PasskeyHybridBinding, PasskeyHybridProof, PasskeyHybridPurpose, PasskeyHybridRegistrationProof. Import only the names used by this file.
540
+
541
+ ```text
542
+ import { PASSKEY_HYBRID_SUITE, PASSKEY_HYBRID_VERSION, PASSKEY_ML_DSA_PUBLIC_KEY_BYTES, PASSKEY_ML_DSA_SIGNATURE_BYTES, PASSKEY_PRF_SALT, createPasskeyHybridProof } from '@forgezero/runtime/passkey-hybrid';
543
+ import { passkeyHybridMessage, verifyPasskeyHybridProof } from '@forgezero/runtime/passkey-hybrid';
544
+ import type { PasskeyHybridBinding, PasskeyHybridProof, PasskeyHybridPurpose, PasskeyHybridRegistrationProof } from '@forgezero/runtime/passkey-hybrid';
545
+ ```
546
+
547
+ <a id="forgezero-runtime-otpauth"></a>
548
+ ## @forgezero/runtime/otpauth
549
+
550
+ Parse and render otpauth URIs without binding enrolment to a UI framework. Named value imports: formatOtpAuth, fromManualSecret, newOtpAuth, parseOtpAuth. Named type imports: OtpAuth. Import only the names used by this file.
551
+
552
+ ```text
553
+ import { formatOtpAuth, fromManualSecret, newOtpAuth, parseOtpAuth } from '@forgezero/runtime/otpauth';
554
+ import type { OtpAuth } from '@forgezero/runtime/otpauth';
555
+ ```
556
+
557
+ <a id="forgezero-runtime-pipeline"></a>
558
+ ## @forgezero/runtime/pipeline
559
+
560
+ Typed ordered application-pipeline execution with explicit evidence. Named value imports: GIT_PROVIDERS, PipelineError, parsePush, shouldDeploy, verifyWebhook, webhookPath. Named type imports: DeployTrigger, GitProvider, PushEvent. Import only the names used by this file.
561
+
562
+ ```text
563
+ import { GIT_PROVIDERS, PipelineError, parsePush, shouldDeploy, verifyWebhook, webhookPath } from '@forgezero/runtime/pipeline';
564
+ import type { DeployTrigger, GitProvider, PushEvent } from '@forgezero/runtime/pipeline';
565
+ ```
566
+
567
+ <a id="forgezero-runtime-finance-chain-addresses"></a>
568
+ ## @forgezero/runtime/finance/chain-addresses
569
+
570
+ Chain-address derivation records and validation independent of a node provider. Named value imports: AddressError, COUNTERFACTUAL_SCHEMES, assertDerivationMatches, create2Address, depositAddressBook, depositSaltFor, deriveDepositAddress, ownerOfAddress, toChecksumAddress, validateAddress. Named type imports: AddressScheme, ChainDeployment. Import only the names used by this file.
571
+
572
+ ```text
573
+ import { AddressError, COUNTERFACTUAL_SCHEMES, assertDerivationMatches, create2Address, depositAddressBook, depositSaltFor } from '@forgezero/runtime/finance/chain-addresses';
574
+ import { deriveDepositAddress, ownerOfAddress, toChecksumAddress, validateAddress } from '@forgezero/runtime/finance/chain-addresses';
575
+ import type { AddressScheme, ChainDeployment } from '@forgezero/runtime/finance/chain-addresses';
576
+ ```
577
+
578
+ <a id="forgezero-runtime-finance-chain-deposits"></a>
579
+ ## @forgezero/runtime/finance/chain-deposits
580
+
581
+ Provider-neutral deposit observation, confirmation and credit transitions. Named value imports: DepositError, creditDeposit, cursorFrom, depositReference, describeScan, isConfirmed, rescanRange, scanOnce. Named type imports: ChainReader, ChainTransfer, DepositCursor, ScanContext, ScanReport. Import only the names used by this file.
582
+
583
+ ```text
584
+ import { DepositError, creditDeposit, cursorFrom, depositReference, describeScan, isConfirmed } from '@forgezero/runtime/finance/chain-deposits';
585
+ import { rescanRange, scanOnce } from '@forgezero/runtime/finance/chain-deposits';
586
+ import type { ChainReader, ChainTransfer, DepositCursor, ScanContext, ScanReport } from '@forgezero/runtime/finance/chain-deposits';
587
+ ```
588
+
589
+ <a id="forgezero-runtime-finance-chain-withdrawals"></a>
590
+ ## @forgezero/runtime/finance/chain-withdrawals
591
+
592
+ Provider-neutral withdrawal approval, broadcast and finality transitions. Named value imports: WITHDRAWAL_STATES, WithdrawalError, approveWithdrawal, broadcastKeyFor, canTransition, confirmWithdrawal, describeSweep, holdReference, markFailed, processOnce, refundFailed, rejectWithdrawal, requestWithdrawal, sweepForPayout. Named type imports: Broadcaster, SweepCandidate, SweepPlan, Withdrawal, WithdrawalState. Import only the names used by this file.
593
+
594
+ ```text
595
+ import { WITHDRAWAL_STATES, WithdrawalError, approveWithdrawal, broadcastKeyFor, canTransition, confirmWithdrawal } from '@forgezero/runtime/finance/chain-withdrawals';
596
+ import { describeSweep, holdReference, markFailed, processOnce, refundFailed, rejectWithdrawal } from '@forgezero/runtime/finance/chain-withdrawals';
597
+ import { requestWithdrawal, sweepForPayout } from '@forgezero/runtime/finance/chain-withdrawals';
598
+ import type { Broadcaster, SweepCandidate, SweepPlan, Withdrawal, WithdrawalState } from '@forgezero/runtime/finance/chain-withdrawals';
599
+ ```
600
+
601
+ <a id="forgezero-runtime-finance-chain-reconcile"></a>
602
+ ## @forgezero/runtime/finance/chain-reconcile
603
+
604
+ Deterministic reconciliation between chain observations and durable transfer state. Named value imports: DISCREPANCY_KINDS, ReconcileError, reconcileOnce, reconcileReport, shortfallUsd. Named type imports: Discrepancy, DiscrepancyKind, LedgerLiability, OnChainHolding, ReconcileReport. Import only the names used by this file.
605
+
606
+ ```text
607
+ import { DISCREPANCY_KINDS, ReconcileError, reconcileOnce, reconcileReport, shortfallUsd } from '@forgezero/runtime/finance/chain-reconcile';
608
+ import type { Discrepancy, DiscrepancyKind, LedgerLiability, OnChainHolding, ReconcileReport } from '@forgezero/runtime/finance/chain-reconcile';
609
+ ```
610
+
611
+ <a id="forgezero-runtime-finance-market"></a>
612
+ ## @forgezero/runtime/finance/market
613
+
614
+ Market order, fill and quote contracts independent of any exchange adapter. Named value imports: MarketError, createFeed, disagreementOf, isStale, lastPrice, limitFor, stalenessOf, subscribe. Named type imports: Feed, FeedEvent, FeedOptions, PriceResult, Quote, StalenessPolicy, VenueSource. Import only the names used by this file.
615
+
616
+ ```text
617
+ import { MarketError, createFeed, disagreementOf, isStale, lastPrice, limitFor } from '@forgezero/runtime/finance/market';
618
+ import { stalenessOf, subscribe } from '@forgezero/runtime/finance/market';
619
+ import type { Feed, FeedEvent, FeedOptions, PriceResult, Quote, StalenessPolicy } from '@forgezero/runtime/finance/market';
620
+ import type { VenueSource } from '@forgezero/runtime/finance/market';
621
+ ```
622
+
623
+ <a id="forgezero-runtime-custody-share"></a>
624
+ ## @forgezero/runtime/custody-share
625
+
626
+ Threshold-share parsing, validation and reconstruction. Named value imports: PROBE_BYTES, factorWrapAad, joinProbeAndShare, openFactorEnvelope, openSealedToFactor, openShareWithPasskey, openShareWithPhrase, passkeyWrappingKey, phraseWrappingKey, sealShare, shareIndexOf, splitProbeAndShare, wrappingKeysFor. Named type imports: SealedShare, WrappingKeys. Import only the names used by this file.
627
+
628
+ ```text
629
+ import { PROBE_BYTES, factorWrapAad, joinProbeAndShare, openFactorEnvelope, openSealedToFactor, openShareWithPasskey } from '@forgezero/runtime/custody-share';
630
+ import { openShareWithPhrase, passkeyWrappingKey, phraseWrappingKey, sealShare, shareIndexOf, splitProbeAndShare } from '@forgezero/runtime/custody-share';
631
+ import { wrappingKeysFor } from '@forgezero/runtime/custody-share';
632
+ import type { SealedShare, WrappingKeys } from '@forgezero/runtime/custody-share';
633
+ ```
634
+
635
+ <a id="forgezero-runtime-custody-crypto"></a>
636
+ ## @forgezero/runtime/custody-crypto
637
+
638
+ Hybrid ML-KEM-768 plus X25519 custody-share sealing and opening. Named value imports: deriveKey, openFromKey, openWithKey, sealToKey, sealWithKey, wrappingKeyPair. Named type imports: CipherBox, SealedToKey. Import only the names used by this file.
639
+
640
+ ```text
641
+ import { deriveKey, openFromKey, openWithKey, sealToKey, sealWithKey, wrappingKeyPair } from '@forgezero/runtime/custody-crypto';
642
+ import type { CipherBox, SealedToKey } from '@forgezero/runtime/custody-crypto';
643
+ ```
644
+
645
+ ## 1. Install, then import a subpath
646
+
647
+ There is no root export, and that is deliberate. `import from "@forgezero/runtime"` is meant to fail rather than resolve to whichever module happened to be listed first — a bare import that silently works is how a project ends up depending on the whole package to use one function. Every module is its own entry point, so a bundler includes what you imported and nothing else.
648
+
649
+ ```text
650
+ bun add @forgezero/runtime
651
+
16
652
  import { parseAmount } from '@forgezero/runtime/finance/money';
17
653
  import { createScheduler } from '@forgezero/runtime/jobs';
18
654
 
19
- // There is no root export. `import from '@forgezero/runtime'` is meant to fail
20
- // rather than resolve to whichever module happened to be listed first.
655
+ // This throws. It is supposed to.
656
+ // import { anything } from '@forgezero/runtime';
21
657
  ```
22
658
 
23
659
  ## What is in it
24
660
 
25
- | | |
26
- |---|---|
27
- | **Spine** | `query` `jobs` `queue` `outbox` |
28
- | **Record** | `audit` `backup` `compliance` `calendar` |
29
- | **Identity** | `identity` `totp` `notify` `notify/templates` `schema` `schema/typebox` |
30
- | **Finance** | `finance/money` `finance/storage` `finance/ledger` `finance/commission` `finance/rates` `finance/transfers` `finance/tax` `finance/chain` `finance/custody` `finance/derive` `finance/venues` `finance/binance` |
661
+ Four groups. The service spine is what a process needs to keep working after the response is sent; the record keeps evidence; the finance modules are the ones that must never be approximated. Nothing here knows what your product does — a tenant binds its own business rules to these, it does not fork them.
662
+
663
+ ```text
664
+ SPINE jobs scheduled work submitted through the common queue
665
+ queue awaited result; parallel keys, sequential within one
666
+ outbox business-owned durability, drained through handlers
667
+
668
+ RECORD audit append-only, hash-chained, names the first altered row
669
+ backup encrypted, chunked, verified snapshots + the restore
670
+ compliance screening as a decision record, failing closed
671
+ calendar billing periods, working days, holidays, due dates
672
+
673
+ IDENTITY identity hybrid Ed25519 + ML-DSA-65 request signing
674
+ totp RFC 6238, asymmetric window, replay rejected
675
+ notify named templates to text and HTML, escaped per part
676
+ schema JSON Schema validation, restricted and describable
677
+
678
+ FINANCE money minor units with the asset attached
679
+ storage the same amount, exact AND sortable in a database
680
+ ledger double-entry postings and derived balances
681
+ commission profit net of flows, high-water mark, referral split
682
+ rates what an asset is worth, and how old that answer is
683
+ transfers deposits and withdrawals as ordered pipelines
684
+ tax who may tax a sale, and who accounts for it
685
+ chain chains, assets, confirmation depth by value band
686
+ custody CREATE2 deposit addresses and EIP-712 digests
687
+ derive declare a key ForgeZero generates, not the tenant
688
+ venues trading venues, market types and symbols as data
689
+ binance Binance behind the venue adapter
690
+ ```
691
+
692
+ ## Optional peers — install only what your subpath needs
31
693
 
32
- None of it knows what your product does. You bind your own rules to these; you
33
- do not fork them.
694
+ Nothing third-party is bundled. Vendoring a crypto library means a fix for it never reaches you until this package is republished, so the imports stay real and the dependency stays yours. Most subpaths need nothing; four of them do, and the failure is a plain module-not-found rather than a silently wrong result.
34
695
 
35
- ## Typed function queries
696
+ ```text
697
+ # most subpaths: nothing to add
36
698
 
37
- `query` binds typed input/output codecs to an ordinary function and any context
38
- you choose. It has no database or ForgeZero realm dependency. Inputs may decode
39
- wire values; outputs are checked strictly so a wrong handler result cannot be
40
- coerced into looking correct.
699
+ # finance/custody — CREATE2 and EIP-712 digests
700
+ bun add @noble/hashes
41
701
 
42
- ```ts
43
- import { defineQuery, implementQuery } from '@forgezero/runtime/query';
44
- import { typeboxQueryCodec, T } from '@forgezero/runtime/schema/typebox';
702
+ # identity — hybrid post-quantum signing
703
+ bun add @noble/curves @noble/post-quantum
45
704
 
46
- const contract = defineQuery({
47
- name: 'invoice.by-reference',
48
- input: typeboxQueryCodec(T.Object({ reference: T.String() })),
49
- output: typeboxQueryCodec(T.Object({ total: T.String() }))
50
- });
705
+ # schema/typebox — only if you validate with TypeBox
706
+ bun add @sinclair/typebox
707
+ ```
51
708
 
52
- const findInvoice = implementQuery(contract, async (services: MyServices, input, { signal }) =>
53
- services.invoices.find(input.reference, { signal })
54
- );
709
+ ## Money is never a number
710
+
711
+ An amount is minor units as a bigint with its asset attached, so two currencies cannot be added by accident and a rounding mode is always stated. A database needs a second thing the bigint cannot give it — an ORDER BY that works — so `finance/storage` writes both: the authoritative string, and a lossy double used for sorting and range filters only. One ETH is 10^18 minor units and a signed 64-bit column overflows at about nine ETH, which is why the exact value is never the sortable one.
712
+
713
+ ```text
714
+ import { parseAmount, mulRate, formatAmount } from '@forgezero/runtime/finance/money';
715
+ import { toStored, rangeBounds } from '@forgezero/runtime/finance/storage';
716
+
717
+ const fee = mulRate(parseAmount('1250.00', 'USD'), '0.015', 'down');
718
+ formatAmount(fee); // '18.75' — never 18.749999999
719
+
720
+ const row = toStored(fee);
721
+ // { units: '1875', value: '18.75', asset: 'USD', sort: 18.75 }
722
+ // units → what you pay out. sort → what you ORDER BY.
723
+
724
+ rangeBounds(parseAmount('10', 'USD'), null); // { gte: 10 }
55
725
  ```
56
726
 
57
- ## One reusable async queue
727
+ ## The contracts ship with it
728
+
729
+ The Solidity behind `finance/custody` is in the package rather than in a repository you have to go find. `finance/custody` computes the counterfactual address; `DepositFactory` is what makes that address real on first sweep, and `ColdVault` is the M-of-N EIP-712 approval the digests are built for. Address arithmetic without the contract that honours it is half an answer.
58
730
 
59
- `queue` is deliberately memory-only: it accepts a function, returns that
60
- function's value through a Promise, runs one key sequentially, and runs
61
- different keys in parallel. Persistence and cluster ownership remain in the
62
- business layer that knows what a pending request means; ForgeZero uses an
63
- ArangoDB unique claim, while another caller may use any database or no database.
731
+ ```text
732
+ node_modules/@forgezero/runtime/contracts/src/
733
+ DepositProxy.sol lazily deployed at first sweep no key, no gas per address
734
+ DepositFactory.sol CREATE2, salt opaque to the contract
735
+ ColdVault.sol M-of-N, signatures ordered by ascending signer
736
+ SafeTransferLib.sol tokens that do not return a bool
737
+ ```
64
738
 
65
- ```ts
66
- import { createQueue } from '@forgezero/runtime/queue';
739
+ ## Jobs — background work that never overlaps
67
740
 
68
- // Default admission is 60% of reported logical CPUs (with one reserved).
69
- // Use `width` for an exact ceiling, or an explicit dynamic resource policy:
70
- const queue = createQueue({ resources: { percent: 60, reserve: 1, max: 32 } });
71
- const task = queue.run('tenant-a:wallet-7', transfer, amount, destination);
72
- const receipt = await task.result;
741
+ The lock and the cursor store are interfaces, so this runs against a database, Redis, or nothing at all in a test.
73
742
 
74
- queue.pauseKey('tenant-a:wallet-7');
75
- queue.resumeKey('tenant-a:wallet-7');
76
- queue.stopKey('tenant-a:wallet-7'); // running finishes; pending is rejected
77
- queue.startKey('tenant-a:wallet-7');
78
- queue.cancel(task.id); // pending task only
79
- await queue.stop(30_000); // close intake and drain all work
743
+ ```text
744
+ import { createScheduler, defineJob, cursorJob } from '@forgezero/runtime/jobs';
80
745
  ```
81
746
 
82
- Different async keys overlap immediately. CPU-heavy JavaScript does not become
83
- multi-core merely by entering a queue: put that handler in Bun/standard Workers
84
- and await the Worker result from the queue.
747
+ ## Never setInterval
85
748
 
86
- Jobs accept intervals down to seconds or a local wall-clock schedule with an
87
- IANA timezone and weekday filter. `overlap: 'wait'` (the default) schedules the
88
- next run after completion; `overlap: 'skip'` keeps clock cadence and drops a tick
89
- when the same key is still busy. Same-key overlap is never allowed.
749
+ An interval fires whether or not the previous run finished, so work that takes longer than its period ends up running twice over the same data. The next run is scheduled after the current one completes, which makes the overlap impossible rather than unlikely.
90
750
 
91
- ```ts
92
- defineJob({
93
- key: 'tenant:acme:invoice',
94
- label: 'Monthly invoice preparation',
95
- schedule: { timezone: 'Asia/Kolkata', time: '00:00:15', weekdays: [1] },
96
- overlap: 'skip',
97
- run: async ({ signal }) => generateInvoices({ signal })
751
+ ```text
752
+ const scheduler = createScheduler({
753
+ jobs: [
754
+ defineJob({ key: 'reap-sessions', label: 'Reap sessions', every: '5m', run: reap }),
755
+ defineJob({ key: 'probe-providers', label: 'Probe providers', every: '30s', run: probe })
756
+ ],
757
+ lock: storeLock(lockStore)
98
758
  });
759
+
760
+ scheduler.start();
99
761
  ```
100
762
 
101
- ## Three things worth knowing before you use it
763
+ ## A lease, not a mutex
102
764
 
103
- **Money is never a number.** An amount is minor units as a `bigint` with its
104
- asset attached, so two currencies cannot be added by accident and a rounding
105
- mode is always stated. `finance/storage` writes the same amount twice — the
106
- authoritative string, and a lossy double used for `ORDER BY` only. One ETH is
107
- 10^18 minor units and a signed 64-bit column overflows around nine ETH, which is
108
- why the exact value is never the sortable one.
765
+ A process that dies holding a mutex blocks its job for ever, and somebody clears it by hand at three in the morning. A lease expires on its own. The fence number rises each time the lock is granted, so a run that stalled past its lease and woke up finds its fence stale and stops before writing.
109
766
 
110
- ```ts
111
- const fee = mulRate(parseAmount('1250.00', 'USD'), '0.015', 'down');
112
- formatAmount(fee); // '18.75' never 18.749999999
113
- toStored(fee); // { units: '1875', value: '18.75', asset: 'USD', sort: 18.75 }
767
+ ```text
768
+ const lease = await lock.acquire('scan', 60_000); // undefined if held
769
+ await lock.renew('scan', lease.fence, 60_000); // false once superseded
114
770
  ```
115
771
 
116
- **A scheduler, never `setInterval`.** An interval fires whether or not the last
117
- run finished, so work slower than its period runs twice over the same data. The
118
- next run is scheduled after the current one completes. The lock is a lease with
119
- a fence, not a mutex — a process that dies does not block its job for ever.
772
+ ## The cursor advances only on a complete batch
120
773
 
121
- **The cursor advances only on a complete batch.** Fetch, process every item,
122
- then write the cursor — never per item, never before. Item three of ten throwing
123
- retries the whole batch from the same position, which means `process` must be
124
- idempotent, and idempotent retries beat records nobody ever looks at again.
774
+ Fetch a batch, process every item, then write the cursor never per item and never before. If item three of ten throws, the whole batch is retried from the same position. That means process must be idempotent, and idempotent retries are strictly better than the alternative, which is records nobody ever looks at again.
125
775
 
126
- ## Optional peers
776
+ ```text
777
+ cursorJob({
778
+ key: 'scan-deposits',
779
+ label: 'Scan deposits',
780
+ every: '15s',
781
+ store: cursors,
782
+ from: '0',
783
+ fetch: (cursor) => fetchBlocks(cursor),
784
+ process: (transfer) => credit(transfer) // keyed, so a retry is free
785
+ });
786
+ ```
127
787
 
128
- Nothing third-party is bundled — vendoring a crypto library means a fix for it
129
- never reaches you until this package republishes. Most subpaths need nothing:
788
+ ## Stop waits
130
789
 
131
- ```bash
132
- bun add @noble/hashes # finance/custody
133
- bun add @noble/curves @noble/post-quantum # identity
134
- bun add @sinclair/typebox # schema/typebox
790
+ Returning from stop() before in-flight work settles is how a deploy leaves a record half-written and the next boot finds state nothing explains. Timers are cleared, the abort signal fires so long runs can cut themselves short, and then it awaits what is still running.
791
+
792
+ ```text
793
+ await scheduler.stop(); // clears timers, aborts, awaits in-flight
794
+ scheduler.pause(); // stop scheduling, let in-flight finish
795
+ await scheduler.runNow('scan-deposits');
135
796
  ```
136
797
 
137
- ## The Solidity ships with it
798
+ ## Status somebody can read during an incident
138
799
 
139
- `finance/custody` computes counterfactual CREATE2 deposit addresses; the
140
- contracts that make those addresses real are in the tarball, not in a repository
141
- you have to go find.
800
+ Last run, duration, result, error and consecutive failures per job. Without it a job that has been failing for a week looks exactly like a job that has been succeeding.
142
801
 
802
+ ```text
803
+ scheduler.status();
804
+ // [{ key: 'scan-deposits', state: 'idle', runs: 412,
805
+ // lastDurationMs: 840, lastResult: { processed: 17, batches: 2 },
806
+ // consecutiveFailures: 0, skippedLocked: 0 }]
143
807
  ```
144
- node_modules/@forgezero/runtime/contracts/src/
145
- DepositProxy.sol deployed lazily at first sweep no key, no gas per address
146
- DepositFactory.sol CREATE2, salt opaque to the contract
147
- ColdVault.sol M-of-N EIP-712, signatures ordered by ascending signer
808
+
809
+ ## Schemavalidation you can also render
810
+
811
+ TypeBox is a peer dependency and optional. A project on Zod pulls none of it, because the interface is what the other packages depend on.
812
+
813
+ ```text
814
+ import { validate, describeForm } from '@forgezero/runtime/schema';
815
+
816
+ // only if you validate with TypeBox
817
+ bun add @sinclair/typebox
148
818
  ```
149
819
 
150
- Full documentation: **https://www.forgezero.net/docs/runtime**
820
+ ## 2. Validate
821
+
822
+ Query strings are entirely strings, so values are converted before checking — otherwise ?port=587 fails a schema expecting a number on a perfectly well-formed request.
823
+
824
+ ```text
825
+ import { typebox, T } from '@forgezero/runtime/schema/typebox';
826
+
827
+ const Config = T.Object(
828
+ { host: T.String(), port: T.Integer() },
829
+ { additionalProperties: false }
830
+ );
831
+
832
+ typebox.validate(Config, { host: 'mail', port: '587' });
833
+ // { ok: true, value: { host: 'mail', port: 587 } }
834
+ ```
835
+
836
+ ## 3. Render a form from it
837
+
838
+ describeForm turns a schema into flat, framework-agnostic fields. This is what lets an admin screen render provider credentials and security factors with no per-feature UI code.
151
839
 
152
- ## Licence
840
+ ```text
841
+ typebox.describeForm(Config);
842
+ // [{ path: 'host', label: 'Host', kind: 'string', required: true }, ...]
843
+ ```
844
+
845
+ ## The restrictions are a security boundary
846
+
847
+ A schema authored in your own source is trusted. One submitted by a tenant is not, and these limits are the only thing between the two. $ref would let a schema point validation at a document you do not control; unbounded depth is a denial-of-service; and an object accepting unknown properties is one that lets an unvalidated field ride into an envelope.
848
+
849
+ ```text
850
+ typebox.restrict(schema)
851
+
852
+ $ref · $id · $defs refused
853
+ depth > 4 refused
854
+ more than 100 fields refused
855
+ larger than 64 KiB refused
856
+ additionalProperties must be false
857
+ ```
858
+
859
+ ## writeOnly is the vault boundary
860
+
861
+ A field marked writeOnly is never returned to a browser. readableFields strips them at any depth, and writeOnlyPaths lists exactly what must be routed to secret storage instead.
862
+
863
+ ```text
864
+ const fields = typebox.describeForm(schema);
865
+
866
+ readableFields(fields); // safe to serialise
867
+ writeOnlyPaths(fields); // ['apiKey', 'nested.secret']
868
+ ```
869
+
870
+ ## Adding another validator
871
+
872
+ Implement four functions. toJsonSchema is what keeps the ecosystem from fragmenting: stored config, the admin UI and the wire all speak JSON Schema, so a Zod project and a TypeBox project produce identical documents and either can read the other.
873
+
874
+ ```text
875
+ interface SchemaValidator<S> {
876
+ name: string;
877
+ validate(schema: S, value: unknown): ValidationResult;
878
+ describeForm(schema: S): FormField[];
879
+ toJsonSchema(schema: S): Record<string, unknown>;
880
+ restrict(schema: S, limits?: Restrictions): S;
881
+ }
882
+ ```
153
883
 
154
- MIT. Part of [ForgeZero](https://www.forgezero.net) — secrets, attested compute and
155
- deploys — and usable entirely on its own, with no ForgeZero account.
884
+ Full rendered documentation: https://www.forgezero.net/docs/runtime