@prampta/sdk 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -31
- package/dist/index.d.mts +52 -18
- package/dist/index.d.ts +52 -18
- package/dist/index.js +298 -91
- package/dist/index.mjs +292 -87
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ support is not claimed: CommonJS loads the ESM-only crypto dependency through
|
|
|
13
13
|
[`require(esm)`](https://nodejs.org/en/blog/release/v20.19.0). Local ESM/CommonJS
|
|
14
14
|
smoke tests run on Node 22.20; a wider runtime/browser matrix is still a release gate.
|
|
15
15
|
|
|
16
|
-
This README describes SDK 0.
|
|
16
|
+
This README describes SDK 0.11.0; sections name the version that added them.
|
|
17
17
|
In older 0.4.0, `assertAllowed` throws on
|
|
18
18
|
personal-use `not_blocked` and `PramptaReporter` is unavailable. Use `verify()`
|
|
19
19
|
and inspect `disposition` when supporting older installations;
|
|
@@ -36,47 +36,53 @@ production PRAMPTA deployment running the v1.0.0-default trust posture
|
|
|
36
36
|
refuses it with `PG_NO_PAIR`. Don't build a new integration against it; it
|
|
37
37
|
exists solely so an already-connected provider keeps working mid-migration.
|
|
38
38
|
|
|
39
|
-
## PRE-GEN v5: one setup, one call (0.
|
|
39
|
+
## PRE-GEN v5: one setup, one call (0.11.0, `/v2`)
|
|
40
40
|
|
|
41
41
|
`createPregen` speaks the simplified PRE-GEN v5 formats at `/v2`: signed
|
|
42
42
|
answers bound to your request, signed reports, signed acknowledgements. Set it
|
|
43
|
-
up once; wrap each generation in one call.
|
|
43
|
+
up once; wrap each generation in one call. Use 0.11.1 or later: 0.10.0 and
|
|
44
|
+
0.11.0 had defects found by external reviews (see CHANGELOG). A complete
|
|
45
|
+
program you can run as it stands: https://prampta.com/get-started-v2.md.
|
|
44
46
|
|
|
45
47
|
```typescript
|
|
46
48
|
import pg from "pg";
|
|
47
|
-
import { createPregen, postgresStore } from "@prampta/sdk";
|
|
49
|
+
import { createPregen, postgresStore, loadPregenDirectory, NotGeneratedError } from "@prampta/sdk";
|
|
48
50
|
|
|
49
51
|
const pregen = createPregen({
|
|
50
52
|
baseUrl: "https://api2.prampta.com",
|
|
51
53
|
providerId: "my-ai",
|
|
52
54
|
token: process.env.PRAMPTA_TOKEN!, // your exchange_secret, server-side only
|
|
53
55
|
providerSigningKeyHex: process.env.PREGEN_SIGNING_KEY!, // your Ed25519 private key (32 bytes hex): signs your reports
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
pregenDirectory: await loadPregenDirectory({ previous: lastAccepted }), // PRAMPTA's keys, namespaces, revocations;
|
|
57
|
+
// lastAccepted: the directory you saved last time (toJSON()), or undefined
|
|
58
|
+
store: postgresStore(new pg.Pool()), // shared by all your workers; table pregen_jobs
|
|
56
59
|
});
|
|
57
60
|
|
|
58
|
-
await pregen.registerKey(); // once: registers your public key
|
|
61
|
+
await pregen.registerKey(); // once (again is harmless): registers your public key
|
|
59
62
|
setInterval(() => pregen.flush(), 60_000); // and at startup: resends reports still waiting
|
|
60
63
|
|
|
61
64
|
const result = await pregen.generate(
|
|
62
65
|
{ jobId: task.id, // YOUR stable task id: a retry reuses it, a new generation gets a new one
|
|
63
|
-
subjects: ["
|
|
66
|
+
subjects: ["PG-..."], // everyone in the output
|
|
64
67
|
prompt, model: "my-model", modality: "image", use: { purpose: "commercial" },
|
|
65
68
|
licenseeId }, // the connected user, or omit for a provider-level request
|
|
66
69
|
async ({ obligations }) => {
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
if (queueFull) throw new NotGeneratedError("queue full"); // ONLY when you are sure nothing was produced
|
|
71
|
+
const image = await render(prompt, obligations); // YOU apply the obligations
|
|
72
|
+
return { output: image, outputBytes: image.bytes, obligationsApplied: obligations }; // the ones you carried out
|
|
69
73
|
},
|
|
70
74
|
);
|
|
71
75
|
```
|
|
72
76
|
|
|
73
|
-
**What one call does.**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
**What one call does.** Checks and copies your input before anything else
|
|
78
|
+
(changing your object afterwards changes nothing). Asks the registry about
|
|
79
|
+
every subject under one job hash; verifies every answer: your pinned key, the
|
|
80
|
+
directory if given, every field of the body, bound to your request, times. In
|
|
81
|
+
`enforce` mode generates only when **all** subjects allow, re-checking right
|
|
82
|
+
before your function runs. Stores the outcome and the signed reports
|
|
83
|
+
**before** sending them, and counts a report as filed only when the
|
|
84
|
+
registry's signed acknowledgement matches it: the report, the answer, its
|
|
85
|
+
status, your key.
|
|
80
86
|
|
|
81
87
|
**The result tells you where the job stands:**
|
|
82
88
|
|
|
@@ -84,37 +90,66 @@ threw: nothing is generated and every allow is given back.
|
|
|
84
90
|
|---|---|---|
|
|
85
91
|
| `confirmed` | Finished; every report acknowledged. `generated` says whether it generated. | Nothing. |
|
|
86
92
|
| `report_pending` | Finished; a report has not been acknowledged yet (network, outage). | Nothing: `flush()` resends it, byte for byte. |
|
|
87
|
-
| `needs_check` | The outcome is unknown: the job was interrupted after it may have started generating, or is running in another worker; or the registry refused a report for good. |
|
|
93
|
+
| `needs_check` | The outcome is unknown: your function threw, the job was interrupted after it may have started generating, or it is running in another worker; or the registry refused a report for good. | Find this `jobId` in your generator, then `pregen.resolve(jobId, { generated: true, outputBytes })` or `{ generated: false }`. |
|
|
94
|
+
|
|
95
|
+
**When your function throws.** An error does not prove that nothing was
|
|
96
|
+
produced: a connection reset can arrive after the generator accepted the job.
|
|
97
|
+
So any error leaves the job `needs_check`, nothing is reported, and the use
|
|
98
|
+
stays reserved until you `resolve()` it. Throw `NotGeneratedError` only when
|
|
99
|
+
you are sure the generator never started; then the use is given back at once.
|
|
88
100
|
|
|
89
101
|
The same `jobId` never generates twice: calling again returns where the job
|
|
90
102
|
stands. The same `jobId` with a different generation is refused
|
|
91
|
-
(`PG_JOB_CONFLICT`).
|
|
92
|
-
|
|
103
|
+
(`PG_JOB_CONFLICT`). Several workers may call `resolve()` or `flush()` for the
|
|
104
|
+
same job at once: the first recorded outcome stands and everyone sends that
|
|
105
|
+
one report.
|
|
93
106
|
|
|
94
|
-
**Obligations** are handed to your function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
**Obligations** are handed to your function per subject (`{ subject, id,
|
|
108
|
+
params }`). Return the ones you carried out in `obligationsApplied`; the
|
|
109
|
+
report lists them, and any you did not apply come back in `unmetObligations`.
|
|
110
|
+
How to apply a watermark or a disclosure depends on your medium (image,
|
|
111
|
+
video, audio) and is yours to implement.
|
|
98
112
|
|
|
99
113
|
**Which subjects.** You name them. Matching names in a prompt
|
|
100
114
|
(`matchSubjects`) only suggests candidates; faces, voices and uploaded
|
|
101
115
|
references are for your own pipeline to identify.
|
|
102
116
|
|
|
103
|
-
**`
|
|
117
|
+
**`mode`** is `enforce` (default) or `observe`; anything else is refused when
|
|
118
|
+
you create the client. `observe` generates whatever the answers say and
|
|
104
119
|
reports every generation. Nothing is blocked and nothing is granted: use it
|
|
105
120
|
to trial the integration, then switch to `enforce`; nothing else changes.
|
|
106
121
|
|
|
122
|
+
**Registry keys.** With `pregenDirectory` the SDK takes the registry's keys
|
|
123
|
+
from its key set, keeping only those the steward-signed directory lists and
|
|
124
|
+
has not revoked (the steward key is built into `@pregen/verify`); answers and
|
|
125
|
+
acknowledgements must be signed by one of them. Without a directory, pin them
|
|
126
|
+
yourself with `operatorPublicKeyHex`, obtained independently.
|
|
127
|
+
|
|
107
128
|
**Your signing key.** Reports must be signed. `registerKey()` sends a `first`
|
|
108
129
|
keyset signed by the key it names. After that, the registry also requires
|
|
109
130
|
your `/v1` receipts to be signed with the same key (`providerSigningKeyHex`
|
|
110
|
-
in `submitReceipt`).
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
131
|
+
in `submitReceipt`). To replace the key, `rotateKey(newKeyHex)` sends a
|
|
132
|
+
`rotation` keyset signed by both keys; then create the client again with the
|
|
133
|
+
new key in every worker. Acknowledged reports stand; reports still waiting,
|
|
134
|
+
or sent by a worker that still has the old key, are signed again with the
|
|
135
|
+
new key by the next `flush()`. Recovering a lost key through the registry is
|
|
136
|
+
not available yet. A provider's keysets are public: `GET /v2/keys/{provider_id}`.
|
|
137
|
+
|
|
138
|
+
**Limits.** Per IP address, 200 requests and 300 reports a minute. Above
|
|
139
|
+
them a job is not generated (`error: "HTTP_429"` in its answers) and waiting
|
|
140
|
+
reports go out with the next `flush()`.
|
|
141
|
+
|
|
142
|
+
**Stores.** `postgresStore(pool)` takes any node-postgres `Pool`, `Client` or
|
|
143
|
+
pooled client (the `pg` package is yours; the SDK does not depend on it). It
|
|
144
|
+
creates its table on first use under a lock, so many workers may start at
|
|
145
|
+
once; if your database role may not create tables, run `postgresSchema` in
|
|
146
|
+
your migrations. `memoryStore()` is for tests and a single process: it is
|
|
147
|
+
lost on restart. A custom store must make `claim` and `update` atomic.
|
|
115
148
|
|
|
116
149
|
Not in this release: Python, other stores, several registries in one job,
|
|
117
|
-
key
|
|
150
|
+
key recovery through the registry. The directory is checked in the
|
|
151
|
+
`pregen.registries.v2` format of `@pregen/verify` 0.7.0. From zero, with the
|
|
152
|
+
failures to handle before production: https://prampta.com/get-started-v2.md.
|
|
118
153
|
|
|
119
154
|
## Quick Start: the safe setup (0.9.0)
|
|
120
155
|
|
package/dist/index.d.mts
CHANGED
|
@@ -140,11 +140,16 @@ interface PregenConfig {
|
|
|
140
140
|
token: string;
|
|
141
141
|
/** Your Ed25519 private key, 32 bytes hex. Signs reports; register its public key once with `registerKey()`. */
|
|
142
142
|
providerSigningKeyHex: string;
|
|
143
|
-
/** The registry's public key(s), hex, obtained
|
|
144
|
-
|
|
143
|
+
/** The registry's public key(s), hex, obtained independently, not from its API. Optional with `pregenDirectory`:
|
|
144
|
+
* the keys are then taken from the registry's key set, keeping only those the signed directory lists. */
|
|
145
|
+
operatorPublicKeyHex?: string | string[];
|
|
145
146
|
store: PregenStore;
|
|
146
|
-
/** `enforce` (default): generate only when every subject allows. `observe`: generate anyway and report; nothing is
|
|
147
|
+
/** `enforce` (default): generate only when every subject allows. `observe`: generate anyway and report; nothing is
|
|
148
|
+
* blocked or granted. Any other value is refused. */
|
|
147
149
|
mode?: "enforce" | "observe";
|
|
150
|
+
/** The PRE-GEN directory (`loadPregenDirectory()`): the registry must own each subject's namespace, and every answer
|
|
151
|
+
* and acknowledgement must be signed by a key the directory lists for it and has not revoked. Recommended. */
|
|
152
|
+
pregenDirectory?: Directory;
|
|
148
153
|
timeoutMs?: number;
|
|
149
154
|
}
|
|
150
155
|
interface PregenJobInput {
|
|
@@ -174,21 +179,30 @@ type GenerateFn<T> = (ctx: {
|
|
|
174
179
|
/** One of these is required: the SHA-256 of the output, or the bytes to hash. */
|
|
175
180
|
outputHash?: string;
|
|
176
181
|
outputBytes?: Uint8Array | string;
|
|
177
|
-
/**
|
|
178
|
-
obligationsApplied?:
|
|
182
|
+
/** The obligations you applied, per subject: return the ones from `obligations` you carried out. */
|
|
183
|
+
obligationsApplied?: {
|
|
184
|
+
subject: string;
|
|
185
|
+
id: string;
|
|
186
|
+
}[];
|
|
179
187
|
}>;
|
|
188
|
+
/** Throw this from your generator only when you are sure it produced nothing (it never started). Any other error
|
|
189
|
+
* leaves the outcome unknown: the job becomes `needs_check` and nothing is reported until you `resolve()` it. */
|
|
190
|
+
declare class NotGeneratedError extends Error {
|
|
191
|
+
readonly notGenerated = true;
|
|
192
|
+
constructor(message?: string);
|
|
193
|
+
}
|
|
180
194
|
interface AskResult {
|
|
181
195
|
subject: string;
|
|
182
196
|
status: "allow" | "deny" | "review" | "not_blocked" | null;
|
|
183
197
|
reason?: string;
|
|
184
|
-
/** Why the answer is missing or unusable: PG_UNREACHABLE, PG_SIGNATURE, PG_TIME, PG_BINDING... */
|
|
198
|
+
/** Why the answer is missing or unusable: PG_UNREACHABLE, PG_SIGNATURE, PG_MALFORMED, PG_TIME, PG_BINDING... */
|
|
185
199
|
error?: string;
|
|
186
200
|
}
|
|
187
201
|
interface PregenResult<T = unknown> {
|
|
188
202
|
jobId: string;
|
|
189
203
|
/** confirmed: finished, every report acknowledged. report_pending: finished, a report waits
|
|
190
|
-
* for acknowledgement; `flush()` resends it. needs_check: the outcome is unknown (interrupted,
|
|
191
|
-
* or running in another worker) or the registry
|
|
204
|
+
* for acknowledgement; `flush()` resends it. needs_check: the outcome is unknown (interrupted, the generator threw,
|
|
205
|
+
* or running in another worker) or the registry refused a report; check, then `resolve()`. */
|
|
192
206
|
status: "confirmed" | "report_pending" | "needs_check";
|
|
193
207
|
/** null while the outcome is unknown. */
|
|
194
208
|
generated: boolean | null;
|
|
@@ -200,6 +214,7 @@ interface PregenResult<T = unknown> {
|
|
|
200
214
|
unmetObligations: Obligation[];
|
|
201
215
|
detail?: string;
|
|
202
216
|
}
|
|
217
|
+
type Stage = "claimed" | "asked" | "finished" | "done";
|
|
203
218
|
/** One job as the store keeps it. */
|
|
204
219
|
interface PregenJob {
|
|
205
220
|
providerId: string;
|
|
@@ -208,7 +223,7 @@ interface PregenJob {
|
|
|
208
223
|
jobHash: string;
|
|
209
224
|
/** claimed: one worker owns it. asked: answers stored, generation may have started.
|
|
210
225
|
* finished: outcome and signed reports stored, some not acknowledged. done: nothing left to send. */
|
|
211
|
-
stage:
|
|
226
|
+
stage: Stage;
|
|
212
227
|
asks: {
|
|
213
228
|
request: Record<string, any>;
|
|
214
229
|
answer: Envelope | null;
|
|
@@ -219,6 +234,8 @@ interface PregenJob {
|
|
|
219
234
|
unmetObligations?: Obligation[];
|
|
220
235
|
/** `rejected`: the registry refused this report for good (it holds a different one, or found it malformed). */
|
|
221
236
|
reports: {
|
|
237
|
+
subject: string;
|
|
238
|
+
answerStatus: string;
|
|
222
239
|
report: Envelope;
|
|
223
240
|
ack?: Envelope;
|
|
224
241
|
rejected?: boolean;
|
|
@@ -229,8 +246,9 @@ interface PregenStore {
|
|
|
229
246
|
/** ATOMIC across every worker: store `job` if none exists for (providerId, jobId) and return
|
|
230
247
|
* null; otherwise change nothing and return the existing job. */
|
|
231
248
|
claim(job: PregenJob): Promise<PregenJob | null>;
|
|
232
|
-
/**
|
|
233
|
-
|
|
249
|
+
/** ATOMIC: replace the job only if its stored stage is still `from`; true if it was replaced.
|
|
250
|
+
* Resolve only after a durable write. */
|
|
251
|
+
update(job: PregenJob, from: Stage): Promise<boolean>;
|
|
234
252
|
get(providerId: string, jobId: string): Promise<PregenJob | null>;
|
|
235
253
|
/** Jobs in stage `finished`, oldest first. */
|
|
236
254
|
pending(providerId: string): Promise<PregenJob[]>;
|
|
@@ -246,33 +264,49 @@ declare function createPregen(config: PregenConfig): {
|
|
|
246
264
|
/** Ask, verify, generate (only when permitted), report. See the module comment. */
|
|
247
265
|
generate<T>(input: PregenJobInput, fn: GenerateFn<T>): Promise<PregenResult<T>>;
|
|
248
266
|
/** Resend every report still waiting (after a restart, an outage). Run at startup and every minute or so. */
|
|
249
|
-
flush()
|
|
267
|
+
flush: () => Promise<PregenResult[]>;
|
|
250
268
|
/** Where a job stands, or null if this store never saw it. */
|
|
251
269
|
status(jobId: string): Promise<PregenResult | null>;
|
|
252
|
-
/** After `needs_check`: record what your generator actually did with this job.
|
|
270
|
+
/** After `needs_check`: record what your generator actually did with this job. Safe to call from several workers:
|
|
271
|
+
* the first recorded outcome stands. */
|
|
253
272
|
resolve(jobId: string, outcome: {
|
|
254
273
|
generated: false;
|
|
255
274
|
} | {
|
|
256
275
|
generated: true;
|
|
257
276
|
outputHash?: string;
|
|
258
277
|
outputBytes?: Uint8Array | string;
|
|
259
|
-
obligationsApplied?:
|
|
278
|
+
obligationsApplied?: {
|
|
279
|
+
subject: string;
|
|
280
|
+
id: string;
|
|
281
|
+
}[];
|
|
260
282
|
}): Promise<PregenResult>;
|
|
261
|
-
/** Register this provider's signing key with the registry, once (a `first` keyset, §4.9).
|
|
283
|
+
/** Register this provider's signing key with the registry, once (a `first` keyset, §4.9). Calling it again
|
|
284
|
+
* with the same key is harmless. */
|
|
262
285
|
registerKey(): Promise<{
|
|
263
286
|
keyId: string;
|
|
287
|
+
keysetId: string;
|
|
288
|
+
}>;
|
|
289
|
+
/** Replace this client's signing key with `newSigningKeyHex` (a `rotation` keyset signed by both keys).
|
|
290
|
+
* Then create the client again with the new key, in every worker. Reports already acknowledged keep their
|
|
291
|
+
* acknowledgements; reports still waiting, or sent by a worker that still has the old key, are signed again
|
|
292
|
+
* with the new key by the next `flush()` of a client that has it. */
|
|
293
|
+
rotateKey(newSigningKeyHex: string): Promise<{
|
|
294
|
+
keyId: string;
|
|
295
|
+
keysetId: string;
|
|
264
296
|
}>;
|
|
265
297
|
};
|
|
266
298
|
type Pregen = ReturnType<typeof createPregen>;
|
|
267
299
|
/** In memory. Correct for ONE process only, and lost on restart: tests and trials. */
|
|
268
300
|
declare function memoryStore(): PregenStore;
|
|
269
|
-
/** Anything with node-postgres' `query(text, values)`: a `pg` Pool or
|
|
301
|
+
/** Anything with node-postgres' `query(text, values)`: a `pg` Pool, Client or PoolClient. */
|
|
270
302
|
interface PgQueryable {
|
|
271
303
|
query(text: string, values?: unknown[]): Promise<{
|
|
272
304
|
rows: any[];
|
|
273
305
|
}>;
|
|
274
306
|
}
|
|
275
|
-
/**
|
|
307
|
+
/** The table `postgresStore` uses. Run it in your migrations if your database role may not create tables. */
|
|
308
|
+
declare const postgresSchema = "CREATE TABLE IF NOT EXISTS pregen_jobs (\n provider_id text NOT NULL, job_id text NOT NULL, stage text NOT NULL, job jsonb NOT NULL,\n updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (provider_id, job_id))";
|
|
309
|
+
/** Postgres, shared by every worker. Creates its table on first use, under a lock, so many workers may start at once. */
|
|
276
310
|
declare function postgresStore(pool: PgQueryable): PregenStore;
|
|
277
311
|
|
|
278
312
|
/**
|
|
@@ -858,4 +892,4 @@ declare class Prampta {
|
|
|
858
892
|
private getOnce;
|
|
859
893
|
}
|
|
860
894
|
|
|
861
|
-
export { type AskResult, type AuthorizedGeneration, type AuthorizedRequest, type DeliveryResult, type Envelope, type GenerateFn, type GenerationJournal, type GenerationJournalEntry, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, type Obligation, type Observation, type ObservationOutbox, type OutputMetadata, type PgQueryable, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaJobConflictError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, type Pregen, type PregenConfig, PregenError, type PregenJob, type PregenJobInput, type PregenResult, type PregenStore, type Purpose, REFUSAL_DESCRIPTIONS, type ReceiptOptions, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, createPregen, hashPrompt, matchSubjects, memoryStore, messageId, normalizeForMatch, postgresStore, signingInput, singleProcessJournal, verifyLicenseSignatures, verifyMerkleProof };
|
|
895
|
+
export { type AskResult, type AuthorizedGeneration, type AuthorizedRequest, type DeliveryResult, type Envelope, type GenerateFn, type GenerationJournal, type GenerationJournalEntry, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, NotGeneratedError, type Obligation, type Observation, type ObservationOutbox, type OutputMetadata, type PgQueryable, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaJobConflictError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, type Pregen, type PregenConfig, PregenError, type PregenJob, type PregenJobInput, type PregenResult, type PregenStore, type Purpose, REFUSAL_DESCRIPTIONS, type ReceiptOptions, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, createPregen, hashPrompt, matchSubjects, memoryStore, messageId, normalizeForMatch, postgresSchema, postgresStore, signingInput, singleProcessJournal, verifyLicenseSignatures, verifyMerkleProof };
|
package/dist/index.d.ts
CHANGED
|
@@ -140,11 +140,16 @@ interface PregenConfig {
|
|
|
140
140
|
token: string;
|
|
141
141
|
/** Your Ed25519 private key, 32 bytes hex. Signs reports; register its public key once with `registerKey()`. */
|
|
142
142
|
providerSigningKeyHex: string;
|
|
143
|
-
/** The registry's public key(s), hex, obtained
|
|
144
|
-
|
|
143
|
+
/** The registry's public key(s), hex, obtained independently, not from its API. Optional with `pregenDirectory`:
|
|
144
|
+
* the keys are then taken from the registry's key set, keeping only those the signed directory lists. */
|
|
145
|
+
operatorPublicKeyHex?: string | string[];
|
|
145
146
|
store: PregenStore;
|
|
146
|
-
/** `enforce` (default): generate only when every subject allows. `observe`: generate anyway and report; nothing is
|
|
147
|
+
/** `enforce` (default): generate only when every subject allows. `observe`: generate anyway and report; nothing is
|
|
148
|
+
* blocked or granted. Any other value is refused. */
|
|
147
149
|
mode?: "enforce" | "observe";
|
|
150
|
+
/** The PRE-GEN directory (`loadPregenDirectory()`): the registry must own each subject's namespace, and every answer
|
|
151
|
+
* and acknowledgement must be signed by a key the directory lists for it and has not revoked. Recommended. */
|
|
152
|
+
pregenDirectory?: Directory;
|
|
148
153
|
timeoutMs?: number;
|
|
149
154
|
}
|
|
150
155
|
interface PregenJobInput {
|
|
@@ -174,21 +179,30 @@ type GenerateFn<T> = (ctx: {
|
|
|
174
179
|
/** One of these is required: the SHA-256 of the output, or the bytes to hash. */
|
|
175
180
|
outputHash?: string;
|
|
176
181
|
outputBytes?: Uint8Array | string;
|
|
177
|
-
/**
|
|
178
|
-
obligationsApplied?:
|
|
182
|
+
/** The obligations you applied, per subject: return the ones from `obligations` you carried out. */
|
|
183
|
+
obligationsApplied?: {
|
|
184
|
+
subject: string;
|
|
185
|
+
id: string;
|
|
186
|
+
}[];
|
|
179
187
|
}>;
|
|
188
|
+
/** Throw this from your generator only when you are sure it produced nothing (it never started). Any other error
|
|
189
|
+
* leaves the outcome unknown: the job becomes `needs_check` and nothing is reported until you `resolve()` it. */
|
|
190
|
+
declare class NotGeneratedError extends Error {
|
|
191
|
+
readonly notGenerated = true;
|
|
192
|
+
constructor(message?: string);
|
|
193
|
+
}
|
|
180
194
|
interface AskResult {
|
|
181
195
|
subject: string;
|
|
182
196
|
status: "allow" | "deny" | "review" | "not_blocked" | null;
|
|
183
197
|
reason?: string;
|
|
184
|
-
/** Why the answer is missing or unusable: PG_UNREACHABLE, PG_SIGNATURE, PG_TIME, PG_BINDING... */
|
|
198
|
+
/** Why the answer is missing or unusable: PG_UNREACHABLE, PG_SIGNATURE, PG_MALFORMED, PG_TIME, PG_BINDING... */
|
|
185
199
|
error?: string;
|
|
186
200
|
}
|
|
187
201
|
interface PregenResult<T = unknown> {
|
|
188
202
|
jobId: string;
|
|
189
203
|
/** confirmed: finished, every report acknowledged. report_pending: finished, a report waits
|
|
190
|
-
* for acknowledgement; `flush()` resends it. needs_check: the outcome is unknown (interrupted,
|
|
191
|
-
* or running in another worker) or the registry
|
|
204
|
+
* for acknowledgement; `flush()` resends it. needs_check: the outcome is unknown (interrupted, the generator threw,
|
|
205
|
+
* or running in another worker) or the registry refused a report; check, then `resolve()`. */
|
|
192
206
|
status: "confirmed" | "report_pending" | "needs_check";
|
|
193
207
|
/** null while the outcome is unknown. */
|
|
194
208
|
generated: boolean | null;
|
|
@@ -200,6 +214,7 @@ interface PregenResult<T = unknown> {
|
|
|
200
214
|
unmetObligations: Obligation[];
|
|
201
215
|
detail?: string;
|
|
202
216
|
}
|
|
217
|
+
type Stage = "claimed" | "asked" | "finished" | "done";
|
|
203
218
|
/** One job as the store keeps it. */
|
|
204
219
|
interface PregenJob {
|
|
205
220
|
providerId: string;
|
|
@@ -208,7 +223,7 @@ interface PregenJob {
|
|
|
208
223
|
jobHash: string;
|
|
209
224
|
/** claimed: one worker owns it. asked: answers stored, generation may have started.
|
|
210
225
|
* finished: outcome and signed reports stored, some not acknowledged. done: nothing left to send. */
|
|
211
|
-
stage:
|
|
226
|
+
stage: Stage;
|
|
212
227
|
asks: {
|
|
213
228
|
request: Record<string, any>;
|
|
214
229
|
answer: Envelope | null;
|
|
@@ -219,6 +234,8 @@ interface PregenJob {
|
|
|
219
234
|
unmetObligations?: Obligation[];
|
|
220
235
|
/** `rejected`: the registry refused this report for good (it holds a different one, or found it malformed). */
|
|
221
236
|
reports: {
|
|
237
|
+
subject: string;
|
|
238
|
+
answerStatus: string;
|
|
222
239
|
report: Envelope;
|
|
223
240
|
ack?: Envelope;
|
|
224
241
|
rejected?: boolean;
|
|
@@ -229,8 +246,9 @@ interface PregenStore {
|
|
|
229
246
|
/** ATOMIC across every worker: store `job` if none exists for (providerId, jobId) and return
|
|
230
247
|
* null; otherwise change nothing and return the existing job. */
|
|
231
248
|
claim(job: PregenJob): Promise<PregenJob | null>;
|
|
232
|
-
/**
|
|
233
|
-
|
|
249
|
+
/** ATOMIC: replace the job only if its stored stage is still `from`; true if it was replaced.
|
|
250
|
+
* Resolve only after a durable write. */
|
|
251
|
+
update(job: PregenJob, from: Stage): Promise<boolean>;
|
|
234
252
|
get(providerId: string, jobId: string): Promise<PregenJob | null>;
|
|
235
253
|
/** Jobs in stage `finished`, oldest first. */
|
|
236
254
|
pending(providerId: string): Promise<PregenJob[]>;
|
|
@@ -246,33 +264,49 @@ declare function createPregen(config: PregenConfig): {
|
|
|
246
264
|
/** Ask, verify, generate (only when permitted), report. See the module comment. */
|
|
247
265
|
generate<T>(input: PregenJobInput, fn: GenerateFn<T>): Promise<PregenResult<T>>;
|
|
248
266
|
/** Resend every report still waiting (after a restart, an outage). Run at startup and every minute or so. */
|
|
249
|
-
flush()
|
|
267
|
+
flush: () => Promise<PregenResult[]>;
|
|
250
268
|
/** Where a job stands, or null if this store never saw it. */
|
|
251
269
|
status(jobId: string): Promise<PregenResult | null>;
|
|
252
|
-
/** After `needs_check`: record what your generator actually did with this job.
|
|
270
|
+
/** After `needs_check`: record what your generator actually did with this job. Safe to call from several workers:
|
|
271
|
+
* the first recorded outcome stands. */
|
|
253
272
|
resolve(jobId: string, outcome: {
|
|
254
273
|
generated: false;
|
|
255
274
|
} | {
|
|
256
275
|
generated: true;
|
|
257
276
|
outputHash?: string;
|
|
258
277
|
outputBytes?: Uint8Array | string;
|
|
259
|
-
obligationsApplied?:
|
|
278
|
+
obligationsApplied?: {
|
|
279
|
+
subject: string;
|
|
280
|
+
id: string;
|
|
281
|
+
}[];
|
|
260
282
|
}): Promise<PregenResult>;
|
|
261
|
-
/** Register this provider's signing key with the registry, once (a `first` keyset, §4.9).
|
|
283
|
+
/** Register this provider's signing key with the registry, once (a `first` keyset, §4.9). Calling it again
|
|
284
|
+
* with the same key is harmless. */
|
|
262
285
|
registerKey(): Promise<{
|
|
263
286
|
keyId: string;
|
|
287
|
+
keysetId: string;
|
|
288
|
+
}>;
|
|
289
|
+
/** Replace this client's signing key with `newSigningKeyHex` (a `rotation` keyset signed by both keys).
|
|
290
|
+
* Then create the client again with the new key, in every worker. Reports already acknowledged keep their
|
|
291
|
+
* acknowledgements; reports still waiting, or sent by a worker that still has the old key, are signed again
|
|
292
|
+
* with the new key by the next `flush()` of a client that has it. */
|
|
293
|
+
rotateKey(newSigningKeyHex: string): Promise<{
|
|
294
|
+
keyId: string;
|
|
295
|
+
keysetId: string;
|
|
264
296
|
}>;
|
|
265
297
|
};
|
|
266
298
|
type Pregen = ReturnType<typeof createPregen>;
|
|
267
299
|
/** In memory. Correct for ONE process only, and lost on restart: tests and trials. */
|
|
268
300
|
declare function memoryStore(): PregenStore;
|
|
269
|
-
/** Anything with node-postgres' `query(text, values)`: a `pg` Pool or
|
|
301
|
+
/** Anything with node-postgres' `query(text, values)`: a `pg` Pool, Client or PoolClient. */
|
|
270
302
|
interface PgQueryable {
|
|
271
303
|
query(text: string, values?: unknown[]): Promise<{
|
|
272
304
|
rows: any[];
|
|
273
305
|
}>;
|
|
274
306
|
}
|
|
275
|
-
/**
|
|
307
|
+
/** The table `postgresStore` uses. Run it in your migrations if your database role may not create tables. */
|
|
308
|
+
declare const postgresSchema = "CREATE TABLE IF NOT EXISTS pregen_jobs (\n provider_id text NOT NULL, job_id text NOT NULL, stage text NOT NULL, job jsonb NOT NULL,\n updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (provider_id, job_id))";
|
|
309
|
+
/** Postgres, shared by every worker. Creates its table on first use, under a lock, so many workers may start at once. */
|
|
276
310
|
declare function postgresStore(pool: PgQueryable): PregenStore;
|
|
277
311
|
|
|
278
312
|
/**
|
|
@@ -858,4 +892,4 @@ declare class Prampta {
|
|
|
858
892
|
private getOnce;
|
|
859
893
|
}
|
|
860
894
|
|
|
861
|
-
export { type AskResult, type AuthorizedGeneration, type AuthorizedRequest, type DeliveryResult, type Envelope, type GenerateFn, type GenerationJournal, type GenerationJournalEntry, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, type Obligation, type Observation, type ObservationOutbox, type OutputMetadata, type PgQueryable, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaJobConflictError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, type Pregen, type PregenConfig, PregenError, type PregenJob, type PregenJobInput, type PregenResult, type PregenStore, type Purpose, REFUSAL_DESCRIPTIONS, type ReceiptOptions, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, createPregen, hashPrompt, matchSubjects, memoryStore, messageId, normalizeForMatch, postgresStore, signingInput, singleProcessJournal, verifyLicenseSignatures, verifyMerkleProof };
|
|
895
|
+
export { type AskResult, type AuthorizedGeneration, type AuthorizedRequest, type DeliveryResult, type Envelope, type GenerateFn, type GenerationJournal, type GenerationJournalEntry, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, NotGeneratedError, type Obligation, type Observation, type ObservationOutbox, type OutputMetadata, type PgQueryable, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaJobConflictError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, type Pregen, type PregenConfig, PregenError, type PregenJob, type PregenJobInput, type PregenResult, type PregenStore, type Purpose, REFUSAL_DESCRIPTIONS, type ReceiptOptions, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, createPregen, hashPrompt, matchSubjects, memoryStore, messageId, normalizeForMatch, postgresSchema, postgresStore, signingInput, singleProcessJournal, verifyLicenseSignatures, verifyMerkleProof };
|