@prampta/sdk 0.3.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +286 -204
- package/dist/index.d.mts +615 -0
- package/dist/index.d.ts +615 -3
- package/dist/index.js +1322 -2
- package/dist/index.mjs +1266 -0
- package/package.json +35 -46
- package/dist/client.d.ts +0 -75
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -221
- package/dist/client.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mcp.d.ts +0 -3
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js +0 -324
- package/dist/mcp.js.map +0 -1
- package/dist/types.d.ts +0 -60
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,284 +1,366 @@
|
|
|
1
|
-
#
|
|
1
|
+
# PRAMPTA TypeScript SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **TypeScript-first** with full type definitions
|
|
8
|
-
- **Automatic retries** with exponential backoff
|
|
9
|
-
- **Request cancellation** via AbortSignal
|
|
10
|
-
- **Rate limit handling** with intelligent retry
|
|
11
|
-
- **Zero dependencies** (uses native fetch)
|
|
3
|
+
Pre-generation authorization for AI content. Verifies operator Ed25519 signatures on decisions — not a blind HTTP wrapper.
|
|
12
4
|
|
|
13
5
|
## Installation
|
|
14
6
|
|
|
15
7
|
```bash
|
|
16
8
|
npm install @prampta/sdk
|
|
17
|
-
# or
|
|
18
|
-
pnpm add @prampta/sdk
|
|
19
|
-
# or
|
|
20
|
-
yarn add @prampta/sdk
|
|
21
9
|
```
|
|
22
10
|
|
|
11
|
+
0.7.0 requires Node **20.19.x or newer 20.x, or 22.12+**. Earlier runtime
|
|
12
|
+
support is not claimed: CommonJS loads the ESM-only crypto dependency through
|
|
13
|
+
[`require(esm)`](https://nodejs.org/en/blog/release/v20.19.0). Local ESM/CommonJS
|
|
14
|
+
smoke tests run on Node 22.20; a wider runtime/browser matrix is still a release gate.
|
|
15
|
+
|
|
16
|
+
This README describes SDK 0.7.0.
|
|
17
|
+
In older 0.4.0, `assertAllowed` throws on
|
|
18
|
+
personal-use `not_blocked` and `PramptaReporter` is unavailable. Use `verify()`
|
|
19
|
+
and inspect `disposition` when supporting older installations;
|
|
20
|
+
`not_blocked` is not a licence in any version.
|
|
21
|
+
|
|
22
|
+
## Authentication
|
|
23
|
+
|
|
24
|
+
`token` is your **runtime credential** — a `ProviderCredential` `exchange_secret`,
|
|
25
|
+
issued the moment you complete provider onboarding (inline in the
|
|
26
|
+
`verify-email` response for sandbox, `verify-domain/confirm` for production).
|
|
27
|
+
The `prampta_connection_token` returned by the Connect AI code exchange is not
|
|
28
|
+
accepted by production for runtime calls — use the `exchange_secret`, and pass
|
|
29
|
+
the user's `prampta_licensee_id` as `licenseeId`. See
|
|
30
|
+
[docs/PRAMPTA-Provider-Connect-Guide-2026-08.md](../../docs/PRAMPTA-Provider-Connect-Guide-2026-08.md)
|
|
31
|
+
in the main repo for the full onboarding flow.
|
|
32
|
+
|
|
33
|
+
There used to be a second way in — a shared **pair token** — kept alive only
|
|
34
|
+
for deployments still running in `PILOT_MODE` while a provider migrates. A
|
|
35
|
+
production PRAMPTA deployment running the v1.0.0-default trust posture
|
|
36
|
+
refuses it with `PG_NO_PAIR`. Don't build a new integration against it; it
|
|
37
|
+
exists solely so an already-connected provider keeps working mid-migration.
|
|
38
|
+
|
|
23
39
|
## Quick Start
|
|
24
40
|
|
|
25
41
|
```typescript
|
|
26
|
-
import { Prampta } from
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
42
|
+
import { Prampta } from "@prampta/sdk";
|
|
43
|
+
|
|
44
|
+
const pg = new Prampta({
|
|
45
|
+
baseUrl: "https://api2.prampta.com",
|
|
46
|
+
providerId: "my-ai-service",
|
|
47
|
+
licenseeId: "lic-sandbox", // synthetic fixture only
|
|
48
|
+
token: process.env.PRAMPTA_TOKEN, // sandbox exchange_secret; server-side only
|
|
49
|
+
operatorPublicKeyHex: process.env.PRAMPTA_OPERATOR_PUBLIC_KEY, // obtained independently
|
|
30
50
|
});
|
|
31
51
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
52
|
+
// Option 1: Assert — throws if denied. This sandbox fixture has a research licence.
|
|
53
|
+
await pg.assertAllowed("sbx-allowed", {
|
|
54
|
+
prompt: "A sandbox portrait",
|
|
55
|
+
modality: "image",
|
|
56
|
+
model: "your-model",
|
|
57
|
+
intendedUse: { useCase: "research" },
|
|
36
58
|
});
|
|
37
|
-
console.log('Created:', result.ppCode); // PP-3YGBM5V0HN-4
|
|
38
59
|
|
|
39
|
-
//
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
query: 'typescript',
|
|
46
|
-
category: 'development',
|
|
47
|
-
limit: 10,
|
|
60
|
+
// Option 2: Verify — returns decision object
|
|
61
|
+
const result = await pg.verify("sbx-allowed", {
|
|
62
|
+
prompt: "A sandbox portrait",
|
|
63
|
+
modality: "image",
|
|
64
|
+
model: "your-model",
|
|
65
|
+
intendedUse: { useCase: "research" },
|
|
48
66
|
});
|
|
49
|
-
```
|
|
50
67
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
new Prampta(config: PramptaConfig)
|
|
68
|
+
if (result.allowed) {
|
|
69
|
+
generate({ obligations: result.obligations });
|
|
70
|
+
} else {
|
|
71
|
+
console.log(`Denied: ${result.reason}`);
|
|
72
|
+
}
|
|
57
73
|
```
|
|
58
74
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
### Methods
|
|
75
|
+
For real users, replace `lic-sandbox` with the connected user's
|
|
76
|
+
`prampta_licensee_id` and use a subject and licence that actually cover the
|
|
77
|
+
declared purpose. For unlicensed personal use, omit `licenseeId`, declare
|
|
78
|
+
`useCase: "personal"`, call `verify()` and handle `not_blocked` separately;
|
|
79
|
+
it grants no rights and has no licence receipt. After an `allow` generation,
|
|
80
|
+
file the receipt described in the [Connect Step 2 guide](https://prampta.com/get-started.md).
|
|
67
81
|
|
|
68
|
-
|
|
82
|
+
## Strict license-backed authorization (0.7.0)
|
|
69
83
|
|
|
70
|
-
|
|
84
|
+
`assertAllowed` preserves reporting-only personal use. `assertLicensed` is
|
|
85
|
+
the explicit license-backed path: signature verification and pinned operator
|
|
86
|
+
keys are mandatory, as is your generation id. It rejects `not_blocked`,
|
|
87
|
+
`review`, `deny`, and an internal `allow` without a license, even in monitor
|
|
88
|
+
mode. This verifies a decision, not rights-holder authority or output compliance.
|
|
71
89
|
|
|
72
90
|
```typescript
|
|
73
|
-
const
|
|
74
|
-
|
|
91
|
+
const decision = await pg.assertLicensed(subjectCode, {
|
|
92
|
+
promptHash, generationId: jobId, modality: "video", model: modelName,
|
|
93
|
+
intendedUse: { useCase: "commercial", rights: ["output_generation"] },
|
|
94
|
+
});
|
|
95
|
+
// Apply the decision's obligations, generate, then submit a receipt.
|
|
75
96
|
```
|
|
76
97
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
Get prompt metadata without content (increments usage counter).
|
|
98
|
+
## Check answers against PRE-GEN (recommended)
|
|
80
99
|
|
|
81
100
|
```typescript
|
|
82
|
-
|
|
83
|
-
// Returns: PromptMetadata (no content field)
|
|
84
|
-
```
|
|
101
|
+
import { Prampta, loadPregenDirectory } from "@prampta/sdk";
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
const pregenDirectory = await loadPregenDirectory({ previous: saved }); // steward key built in
|
|
104
|
+
saved = pregenDirectory.toJSON(); // keep it for next start
|
|
87
105
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
const result = await prampta.search({
|
|
92
|
-
query: 'react hooks',
|
|
93
|
-
category: 'development', // development, design, marketing, data, writing, business
|
|
94
|
-
limit: 20,
|
|
95
|
-
page: 1,
|
|
96
|
-
});
|
|
97
|
-
// Returns: { prompts, total, page, totalPages }
|
|
106
|
+
const registry = new Prampta({ baseUrl: "https://api2.prampta.com", providerId, token, licenseeId,
|
|
107
|
+
pregenDirectory });
|
|
98
108
|
```
|
|
99
109
|
|
|
100
|
-
|
|
110
|
+
With `pregenDirectory` the SDK refuses to send a PG code to a registry that
|
|
111
|
+
does not own its namespace, and on every `allow` runs `checkDecision` from
|
|
112
|
+
[`@pregen/verify`](https://www.npmjs.com/package/@pregen/verify): the signing
|
|
113
|
+
key must be one the signed PRE-GEN directory lists for the license's
|
|
114
|
+
namespace, and the origin registry's keys are pinned in that library, so a
|
|
115
|
+
stolen steward key cannot add its own. The directory also refuses an older or
|
|
116
|
+
shrunk copy of itself when you pass the previous one back.
|
|
117
|
+
|
|
118
|
+
For CI without a secret, `createSimulator()` from `@pregen/verify` 0.4.0 is an
|
|
119
|
+
offline sandbox registry: pass `baseUrl: sim.baseUrl`, `operatorPublicKeyHex:
|
|
120
|
+
sim.operatorPublicKeyHex`, `pregenDirectory: sim.directory` and use `sim.fetch`.
|
|
101
121
|
|
|
102
|
-
|
|
122
|
+
## Optional namespace trust with your own root (0.7.0)
|
|
103
123
|
|
|
104
124
|
```typescript
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
125
|
+
import { TrustedRegistryDirectory } from "@prampta/sdk";
|
|
126
|
+
const directory = await TrustedRegistryDirectory.verify(signedDirectory, {
|
|
127
|
+
stewardPublicKeyHex: independentlyObtainedStewardKey,
|
|
128
|
+
previousSnapshot: savedState, // { sequence, hash }, loaded from durable storage
|
|
129
|
+
});
|
|
130
|
+
// Atomically persist { sequence: directory.sequence, hash: directory.snapshotHash }
|
|
131
|
+
// after acceptance, without letting concurrent updates overwrite newer state.
|
|
132
|
+
const registry = new Prampta({
|
|
133
|
+
baseUrl: registryApi, providerId, token: serverSideCredential, licenseeId,
|
|
134
|
+
operatorPublicKeyHex: independentlyObtainedOperatorKey,
|
|
135
|
+
registryDirectory: directory,
|
|
111
136
|
});
|
|
112
|
-
// Returns: { ppCode, title, category, tags }
|
|
113
137
|
```
|
|
114
138
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
139
|
+
This opt-in validates the signed `pregen.registries.v2` directory, requires
|
|
140
|
+
HTTPS registry URLs, rejects ambiguous shared signing keys/endpoints, checks
|
|
141
|
+
the target namespace before sending credentials, and checks a returned
|
|
142
|
+
license's issuer. Origin and prefixed registries get the same checks. Local
|
|
143
|
+
subject ids remain supported at an explicitly listed registry endpoint.
|
|
144
|
+
Authenticated calls do not follow redirects. No automatic multi-registry
|
|
145
|
+
credential sharing or onboarding is provided.
|
|
146
|
+
Persisted `previousSnapshot` detects rollback and signed different-content
|
|
147
|
+
snapshots at the same sequence. `minSequence` is also available for a sequence
|
|
148
|
+
floor alone. Storage and concurrent update serialization belong to the host.
|
|
149
|
+
|
|
150
|
+
The public steward key is still pending: do not obtain a trust anchor from
|
|
151
|
+
the directory itself or invent a production key. An authenticated directory
|
|
152
|
+
snapshot does not prove freshness, key non-revocation or rights-holder consent;
|
|
153
|
+
`minSequence` alone does not prevent stale first-boot snapshots. Full live
|
|
154
|
+
federation needs the separately specified trust-management work.
|
|
155
|
+
|
|
156
|
+
`verifyLicenseSignatures()` checks the subject signature and the operator
|
|
157
|
+
countersignature over body + subject signature + license id offline. Obtain
|
|
158
|
+
keys independently, then verify namespace, authority, status and scope separately.
|
|
159
|
+
Two valid signatures under managed custody are not two independent consents.
|
|
160
|
+
|
|
161
|
+
Security fixes also reject empty request bindings, mismatched generation ids,
|
|
162
|
+
unsupported schemas, contradictory outcomes, missing/invalid expiry and
|
|
163
|
+
non-integer or unsafe signed JSON numbers. Optional advisory fields remain
|
|
164
|
+
signed. An asserted end user cannot silently receive an unbound `allow`.
|
|
165
|
+
Pass a known `providerIdentityLinkId` for exact link matching; resolution of
|
|
166
|
+
`providerUserId` alone still relies on the registry's user-to-link mapping.
|
|
167
|
+
Existing valid signed bodies are unchanged; malformed replies that
|
|
168
|
+
previously passed are intentionally refused. Revalidate against your registry
|
|
169
|
+
before rollout. Python source gets matching decision hardening but not this
|
|
170
|
+
directory API; this release does not restore PyPI distribution.
|
|
171
|
+
|
|
172
|
+
## Signed lifecycle receipts (opt-in)
|
|
173
|
+
|
|
174
|
+
`submitReceipt()` still sends `pg.receipt.v2` by default. A provider with an
|
|
175
|
+
Ed25519 public key registered on its PRAMPTA organization can opt into
|
|
176
|
+
`pg.receipt.v3`, which signs the reported lifecycle moment. Keep the matching
|
|
177
|
+
32-byte private key in a **server-side secret**, never in browser code:
|
|
118
178
|
|
|
119
179
|
```typescript
|
|
120
|
-
|
|
121
|
-
|
|
180
|
+
await pg.submitReceipt(decision, {
|
|
181
|
+
outputHash: outputSha256,
|
|
182
|
+
eventType: "output_accepted", // or preview, output_delivered, output_published
|
|
183
|
+
providerSigningKeyHex: process.env.PRAMPTA_PROVIDER_SIGNING_KEY_HEX,
|
|
184
|
+
});
|
|
122
185
|
```
|
|
123
186
|
|
|
124
|
-
|
|
187
|
+
The SDK signs locally and sends only the signature. A receipt records what the
|
|
188
|
+
provider attests; it does not prove every use was reported, that the output
|
|
189
|
+
complied with rights, or that a payment is due. Do not use an unregistered key:
|
|
190
|
+
the API will reject its signature. Existing v2 integrations are unchanged.
|
|
125
191
|
|
|
126
|
-
|
|
192
|
+
## C2PA Bridge & Anchored Audit Log (PRE-GEN v1.1)
|
|
127
193
|
|
|
128
194
|
```typescript
|
|
129
|
-
|
|
195
|
+
import { verifyMerkleProof } from "@prampta/sdk";
|
|
196
|
+
|
|
197
|
+
// After verifyGeneration() + submitReceipt(), get a signed assertion to
|
|
198
|
+
// embed in your own C2PA manifest — PRAMPTA does not build the manifest.
|
|
199
|
+
const assertion = await pg.createAssertion(decision.decisionId);
|
|
200
|
+
|
|
201
|
+
// Anyone (not just you) can later verify a decision's audit event was
|
|
202
|
+
// included in a published Merkle root, entirely offline:
|
|
203
|
+
const proof = await pg.getInclusionProof(eventId);
|
|
204
|
+
const root = await pg.getMerkleRoot();
|
|
205
|
+
const ok = await verifyMerkleProof(
|
|
206
|
+
proof.event_hash as string,
|
|
207
|
+
proof.proof as any,
|
|
208
|
+
root.root_hash as string,
|
|
209
|
+
);
|
|
130
210
|
```
|
|
131
211
|
|
|
132
|
-
|
|
212
|
+
## Security Features
|
|
133
213
|
|
|
134
|
-
|
|
214
|
+
- **Ed25519 signature verification** on every decision (via `@noble/ed25519`)
|
|
215
|
+
- **Prompt hash binding** — decision is bound to the exact prompt (SHA-256)
|
|
216
|
+
- **Context binding** — decision cannot be replayed for different subject/provider/licensee/modality
|
|
217
|
+
- **Key fingerprint validation** — operator_key_id matches pinned public key
|
|
218
|
+
- **TTL validation** — expired decisions are rejected
|
|
219
|
+
- **Fail-closed** — any error defaults to deny
|
|
135
220
|
|
|
136
|
-
```typescript
|
|
137
|
-
const health = await prampta.healthCheck();
|
|
138
|
-
// Returns: { healthy: boolean, latencyMs: number, authenticated: boolean }
|
|
139
|
-
```
|
|
140
221
|
|
|
141
|
-
|
|
222
|
+
## Key Pinning & Rotation (trust anchor)
|
|
142
223
|
|
|
143
|
-
|
|
224
|
+
Signature verification is only meaningful against a key you obtained out of
|
|
225
|
+
band. **Pin the operator key** — do not rely on the key the API hands you:
|
|
144
226
|
|
|
145
227
|
```typescript
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
signal: controller.signal,
|
|
154
|
-
});
|
|
155
|
-
} catch (err) {
|
|
156
|
-
if (err.message === 'Request was cancelled') {
|
|
157
|
-
console.log('Request cancelled');
|
|
158
|
-
}
|
|
159
|
-
}
|
|
228
|
+
const pg = new Prampta({
|
|
229
|
+
baseUrl: "https://api2.prampta.com",
|
|
230
|
+
providerId: "my-ai-service",
|
|
231
|
+
licenseeId: "acme-corp",
|
|
232
|
+
token: "your-provider-credential-secret", // NOT a pair token — see Authentication
|
|
233
|
+
operatorPublicKeyHex: "<pinned key from PRAMPTA docs>",
|
|
234
|
+
});
|
|
160
235
|
```
|
|
161
236
|
|
|
162
|
-
|
|
237
|
+
- A decision is trusted only when signed by a pinned key. A decision signed by
|
|
238
|
+
an **unpinned** key fails closed with an actionable error (no silent trust).
|
|
239
|
+
- **Rotation without downtime:** pin the current *and* the announced next key
|
|
240
|
+
(comma/space separated). When PRAMPTA rotates, the new key is already trusted.
|
|
241
|
+
- **No pinned key** → trust-on-first-use: the SDK still verifies but logs a
|
|
242
|
+
warning. The signature proves consistency, not authenticity. Never ship
|
|
243
|
+
production this way.
|
|
244
|
+
|
|
245
|
+
## Configuration
|
|
246
|
+
|
|
247
|
+
| Parameter | Env Var | Required | Description |
|
|
248
|
+
|-----------|---------|----------|-------------|
|
|
249
|
+
| `baseUrl` | `PRAMPTA_BASE_URL` | Yes | Registry API URL |
|
|
250
|
+
| `providerId` | `PRAMPTA_PROVIDER_ID` | Yes | Your provider ID |
|
|
251
|
+
| `licenseeId` | `PRAMPTA_LICENSEE_ID` | For licensed use | Connected user's PRAMPTA licensee ID; omit for unlicensed personal use |
|
|
252
|
+
| `token` | `PRAMPTA_TOKEN` | Yes | Runtime credential (`ProviderCredential` secret) — see [Authentication](#authentication) |
|
|
253
|
+
| `operatorPublicKeyHex` | `PRAMPTA_OPERATOR_PUBLIC_KEY` | No | Pinned operator key (recommended for production) |
|
|
254
|
+
| `timeoutMs` | — | No | Default `3000`. Request timeout in ms. |
|
|
255
|
+
| `failClosed` | — | No | Default `true`. Deny on any verification error. |
|
|
256
|
+
| `verifyDecisionSignature` | — | No | Default `true`. Set `false` only for local dev. |
|
|
257
|
+
|
|
258
|
+
## Error Handling
|
|
163
259
|
|
|
164
260
|
```typescript
|
|
165
|
-
import { Prampta,
|
|
261
|
+
import { Prampta, PramptaRefusalError, PramptaSignatureError } from "@prampta/sdk";
|
|
166
262
|
|
|
167
263
|
try {
|
|
168
|
-
|
|
169
|
-
} catch (
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
console.log('Rate limited, try again later');
|
|
177
|
-
} else if (err.status === 401) {
|
|
178
|
-
console.log('Invalid API key');
|
|
179
|
-
}
|
|
264
|
+
await pg.assertAllowed("subject-id", { prompt: "...", modality: "image" });
|
|
265
|
+
} catch (e) {
|
|
266
|
+
if (e instanceof PramptaRefusalError) {
|
|
267
|
+
// License denial — e.reason has the code (PG_NO_LICENSE, PG_SCOPE_VIOLATION, etc.)
|
|
268
|
+
console.log(e.reason);
|
|
269
|
+
} else if (e instanceof PramptaSignatureError) {
|
|
270
|
+
// Operator signature invalid — potential MITM
|
|
271
|
+
alert("Security: tampered decision");
|
|
180
272
|
}
|
|
181
273
|
}
|
|
182
274
|
```
|
|
183
275
|
|
|
184
|
-
##
|
|
276
|
+
## Pre-hashed Prompts
|
|
185
277
|
|
|
186
|
-
|
|
187
|
-
interface Prompt {
|
|
188
|
-
id: string;
|
|
189
|
-
ppCode: string;
|
|
190
|
-
title: string;
|
|
191
|
-
content: string;
|
|
192
|
-
category: string;
|
|
193
|
-
tags: string[];
|
|
194
|
-
authorName: string | null;
|
|
195
|
-
uses: number;
|
|
196
|
-
likes: number;
|
|
197
|
-
visibility: 'public' | 'private';
|
|
198
|
-
createdAt: number;
|
|
199
|
-
media?: Record<string, string>;
|
|
200
|
-
}
|
|
278
|
+
If you hash prompts yourself (e.g., for privacy), pass `promptHash` instead of `prompt`:
|
|
201
279
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
title?: string; // Max 200 characters
|
|
205
|
-
media?: Record<string, string>; // Base64 data URLs
|
|
206
|
-
}
|
|
280
|
+
```typescript
|
|
281
|
+
import { hashPrompt } from "@prampta/sdk";
|
|
207
282
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
283
|
+
const hash = await hashPrompt("Da Vinci in a documentary");
|
|
284
|
+
const result = await pg.verify("leonardo-da-vinci", {
|
|
285
|
+
promptHash: hash,
|
|
286
|
+
modality: "image",
|
|
287
|
+
});
|
|
214
288
|
```
|
|
215
289
|
|
|
216
|
-
##
|
|
290
|
+
## API Reference
|
|
217
291
|
|
|
218
|
-
###
|
|
292
|
+
### `new Prampta(config)`
|
|
219
293
|
|
|
220
|
-
|
|
221
|
-
import express from 'express';
|
|
222
|
-
import { Prampta } from '@prampta/sdk';
|
|
223
|
-
|
|
224
|
-
const app = express();
|
|
225
|
-
const prampta = new Prampta({ apiKey: process.env.PRAMPTA_API_KEY! });
|
|
226
|
-
|
|
227
|
-
app.get('/prompt/:code', async (req, res) => {
|
|
228
|
-
try {
|
|
229
|
-
const prompt = await prampta.decode(req.params.code);
|
|
230
|
-
res.json(prompt);
|
|
231
|
-
} catch (err) {
|
|
232
|
-
res.status(err.status || 500).json({ error: err.message });
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
```
|
|
294
|
+
Creates a client instance.
|
|
236
295
|
|
|
237
|
-
###
|
|
296
|
+
### `pg.verify(subjectId, options)`
|
|
238
297
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
export async function GET(request: Request) {
|
|
247
|
-
const { searchParams } = new URL(request.url);
|
|
248
|
-
const query = searchParams.get('q') || '';
|
|
249
|
-
|
|
250
|
-
const result = await prampta.search({ query });
|
|
251
|
-
return NextResponse.json(result);
|
|
252
|
-
}
|
|
253
|
-
```
|
|
298
|
+
Returns `Promise<SignedDecision>`:
|
|
299
|
+
- `allowed` — generation authorized
|
|
300
|
+
- `reason` — refusal code (`PG_NO_LICENSE`, `PG_SCOPE_VIOLATION`, `PG_SUBJECT_OPTED_OUT`)
|
|
301
|
+
- `licenseId` — the authorizing license
|
|
302
|
+
- `decisionId` — unique decision ID for audit
|
|
303
|
+
- `obligations` — required obligations (attribution, watermark, etc.)
|
|
304
|
+
- `operatorSignature` — Ed25519 signature over decision
|
|
254
305
|
|
|
255
|
-
###
|
|
306
|
+
### `pg.assertAllowed(subjectId, options)`
|
|
256
307
|
|
|
257
|
-
|
|
258
|
-
const codes = ['PP-xxx', 'PP-yyy', 'PP-zzz'];
|
|
308
|
+
Same as `verify()` but throws `PramptaRefusalError` if not allowed.
|
|
259
309
|
|
|
260
|
-
|
|
261
|
-
const results = await Promise.allSettled(
|
|
262
|
-
codes.map(code => prampta.decode(code))
|
|
263
|
-
);
|
|
310
|
+
### `pg.health()` / `pg.version()`
|
|
264
311
|
|
|
265
|
-
|
|
266
|
-
.filter((r): r is PromiseFulfilledResult<Prompt> => r.status === 'fulfilled')
|
|
267
|
-
.map(r => r.value);
|
|
268
|
-
```
|
|
312
|
+
Registry health check and version info.
|
|
269
313
|
|
|
270
|
-
##
|
|
314
|
+
## Connect Step 1: reporting without authorization
|
|
271
315
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
| Requests per day | 5,000 |
|
|
276
|
-
| Max prompt size | 50,000 chars |
|
|
277
|
-
| Max media per prompt | 8 files |
|
|
278
|
-
| Max media size | 2MB per file |
|
|
316
|
+
SDK 0.6.0 adds `PramptaReporter` (Node/server only). Check the installed package
|
|
317
|
+
version before using it. Publishing the SDK and deploying the backend are separate
|
|
318
|
+
steps; this API still requires the `/v1/observations` endpoint on your server.
|
|
279
319
|
|
|
280
|
-
|
|
320
|
+
```ts
|
|
321
|
+
import { PramptaReporter } from '@prampta/sdk';
|
|
281
322
|
|
|
282
|
-
|
|
323
|
+
const reporter = new PramptaReporter({
|
|
324
|
+
baseUrl: process.env.PRAMPTA_BASE_URL!,
|
|
325
|
+
providerId: process.env.PRAMPTA_PROVIDER_ID!,
|
|
326
|
+
token: process.env.PRAMPTA_TOKEN!,
|
|
327
|
+
outbox: durableOutbox, // Your DB/job-queue adapter, not an in-memory array.
|
|
328
|
+
});
|
|
329
|
+
await reporter.report({
|
|
330
|
+
eventKey: 'job-123:created', outputId: 'job-123', subjectId: 'selected-subject',
|
|
331
|
+
occurredAt: '2026-09-21T10:00:00Z', output: outputBytes,
|
|
332
|
+
});
|
|
333
|
+
// Your persistent worker calls reporter.deliver(savedObservation).
|
|
334
|
+
```
|
|
283
335
|
|
|
284
|
-
|
|
336
|
+
`report` persists via `ObservationOutbox.put`, without contacting PRAMPTA. That
|
|
337
|
+
adapter must atomically accept identical replays and reject changed payloads
|
|
338
|
+
under the same event ID, separately for each provider/environment. The original
|
|
339
|
+
eventKey and occurredAt must be stable across retries. Raw output stays local;
|
|
340
|
+
for large video use outputHash computed by your streaming storage pipeline.
|
|
341
|
+
The SDK does not download URLs. Keep credentials server-side.
|
|
342
|
+
|
|
343
|
+
`deliver` performs one bounded attempt. Acknowledge `accepted`; retain and
|
|
344
|
+
reschedule `retry` (respect retryAfterMs, add exponential backoff and jitter);
|
|
345
|
+
quarantine and alert on `rejected`. Never silently discard failed events.
|
|
346
|
+
Storage errors reject `report`; retry the completion job without regenerating.
|
|
347
|
+
No in-memory background delivery guarantee is implied.
|
|
348
|
+
|
|
349
|
+
No licensee account or Pair is required. This does NOT grant permission, record
|
|
350
|
+
payment, verify content or alter `assertAllowed` behavior. Use the existing
|
|
351
|
+
`matchSubjects` export for local candidate discovery, not automatic claims that
|
|
352
|
+
every name mentioned in a prompt appears in the output. Step 2 remains the
|
|
353
|
+
separate authorization/licensing flow described above.
|
|
354
|
+
|
|
355
|
+
## Source-only authority experiment (not shipped)
|
|
356
|
+
|
|
357
|
+
The scoped-authority prototype in `src/experimental*` and its tests remain in
|
|
358
|
+
this repository for review. They are non-normative drafts, not part of the
|
|
359
|
+
`@prampta/sdk` 0.7.0 package. The package builds and exports
|
|
360
|
+
only its stable root API: neither `@prampta/sdk/experimental` nor
|
|
361
|
+
`@prampta/sdk/experimental/node` is available to package consumers.
|
|
362
|
+
|
|
363
|
+
See `spec/drafts/` for the proposed formats and limits. The experimental backend
|
|
364
|
+
bridge lives on branch `experimental/authority-control-plane`, not on `main`.
|
|
365
|
+
No production authority issuance, protected signing provision or certification
|
|
366
|
+
is implied by these sources or tests.
|