@openwop/openwop 1.8.0 → 2.0.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.
Files changed (60) hide show
  1. package/README.md +64 -117
  2. package/dist/client.d.ts +131 -245
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +233 -440
  5. package/dist/client.js.map +1 -1
  6. package/dist/cost-attribution.d.ts +2 -2
  7. package/dist/cost-attribution.js +2 -2
  8. package/dist/envelope-directive.d.ts +1 -1
  9. package/dist/envelope-directive.js +1 -1
  10. package/dist/event-helpers.js +1 -1
  11. package/dist/event-helpers.js.map +1 -1
  12. package/dist/generated.d.ts +17 -0
  13. package/dist/generated.d.ts.map +1 -0
  14. package/dist/generated.js +311 -0
  15. package/dist/generated.js.map +1 -0
  16. package/dist/index.d.ts +16 -16
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +26 -53
  19. package/dist/index.js.map +1 -1
  20. package/dist/run-helpers.d.ts +20 -21
  21. package/dist/run-helpers.d.ts.map +1 -1
  22. package/dist/run-helpers.js +23 -72
  23. package/dist/run-helpers.js.map +1 -1
  24. package/dist/sse.d.ts +33 -15
  25. package/dist/sse.d.ts.map +1 -1
  26. package/dist/sse.js +28 -30
  27. package/dist/sse.js.map +1 -1
  28. package/dist/types.d.ts +253 -559
  29. package/dist/types.d.ts.map +1 -1
  30. package/dist/types.js.map +1 -1
  31. package/dist/webhook-header-families.d.ts +42 -0
  32. package/dist/webhook-header-families.d.ts.map +1 -0
  33. package/dist/webhook-header-families.js +58 -0
  34. package/dist/webhook-header-families.js.map +1 -0
  35. package/dist/webhook-helpers.browser.d.ts +15 -28
  36. package/dist/webhook-helpers.browser.d.ts.map +1 -1
  37. package/dist/webhook-helpers.browser.js +16 -27
  38. package/dist/webhook-helpers.browser.js.map +1 -1
  39. package/dist/webhook-helpers.d.ts +48 -32
  40. package/dist/webhook-helpers.d.ts.map +1 -1
  41. package/dist/webhook-helpers.js +52 -37
  42. package/dist/webhook-helpers.js.map +1 -1
  43. package/package.json +6 -4
  44. package/src/client.ts +255 -454
  45. package/src/cost-attribution.ts +2 -2
  46. package/src/envelope-directive.ts +1 -1
  47. package/src/event-helpers.ts +1 -1
  48. package/src/generated.ts +322 -0
  49. package/src/index.ts +78 -105
  50. package/src/run-helpers.ts +27 -85
  51. package/src/sse.ts +63 -42
  52. package/src/types.ts +268 -603
  53. package/src/webhook-header-families.ts +78 -0
  54. package/src/webhook-helpers.browser.ts +24 -28
  55. package/src/webhook-helpers.ts +87 -39
  56. package/dist/registry-helpers.d.ts +0 -118
  57. package/dist/registry-helpers.d.ts.map +0 -1
  58. package/dist/registry-helpers.js +0 -82
  59. package/dist/registry-helpers.js.map +0 -1
  60. package/src/registry-helpers.ts +0 -173
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Webhook header families and signature-value parsing (webhooks.md §Headers;
3
+ * RFC 0165 §C.1, RFC 0176 §D.2). Pure — no Node builtin — so both the server
4
+ * module (`webhook-helpers.ts`) and the browser stub re-export it.
5
+ * Verification itself stays server-only.
6
+ */
7
+
8
+ /** The one accepted signature-value form: `sha256=<hex>`. The SDK-only `v1=` form was removed in v2 (headers.md §Removed). */
9
+ const SIGNATURE_VALUE_PREFIX = 'sha256=';
10
+
11
+ /** The one signature scheme (`OpenWOP-Signature-Algorithm`): HMAC-SHA256 with the subscription secret. */
12
+ export const WEBHOOK_SIGNATURE_ALGORITHMS = ['v1'] as const;
13
+ export type WebhookSignatureAlgorithm = (typeof WEBHOOK_SIGNATURE_ALGORITHMS)[number];
14
+
15
+ /** `sha256=<hex>` → `<hex>`; anything else → null. */
16
+ export function parseSignatureValue(value: string): string | null {
17
+ if (!value.startsWith(SIGNATURE_VALUE_PREFIX)) return null;
18
+ const hex = value.slice(SIGNATURE_VALUE_PREFIX.length);
19
+ return /^[0-9a-f]+$/i.test(hex) ? hex : null;
20
+ }
21
+
22
+ /**
23
+ * Header-name families a delivery may carry, in the order a receiver SHOULD
24
+ * prefer them: the v2 `OpenWOP-*` family, then the v1 `X-openwop-*` family a
25
+ * dual-major host emits beside it through the overlap (webhooks.md §Dual
26
+ * emission — a v2 receiver MUST accept a delivery carrying only that family).
27
+ * Lookups are case-insensitive.
28
+ */
29
+ export const WEBHOOK_HEADER_FAMILIES: ReadonlyArray<{
30
+ readonly family: 'openwop' | 'x-openwop';
31
+ readonly signature: string;
32
+ readonly timestamp: string;
33
+ readonly algorithm: string;
34
+ }> = [
35
+ { family: 'openwop', signature: 'OpenWOP-Signature', timestamp: 'OpenWOP-Timestamp', algorithm: 'OpenWOP-Signature-Algorithm' },
36
+ { family: 'x-openwop', signature: 'X-openwop-Signature', timestamp: 'X-openwop-Timestamp', algorithm: 'X-openwop-Signature-Algorithm' },
37
+ ];
38
+
39
+ export interface WebhookHeaderRead {
40
+ readonly signatureHeader: string;
41
+ readonly timestampHeader: string;
42
+ /** The `*-Signature-Algorithm` value when the delivery carried one. */
43
+ readonly algorithmHeader?: string;
44
+ /** Which family was read. */
45
+ readonly family: 'openwop' | 'x-openwop';
46
+ }
47
+
48
+ /**
49
+ * Pick the signature + timestamp (+ algorithm) values out of a delivery's
50
+ * headers, first present family wins. Returns null when no family is
51
+ * complete. Pass a plain object (any casing) or a `Headers`-like with a `get`
52
+ * method.
53
+ */
54
+ export function readWebhookHeaders(
55
+ headers: Record<string, string | string[] | undefined> | { get(name: string): string | null },
56
+ ): WebhookHeaderRead | null {
57
+ const get = (name: string): string | undefined => {
58
+ if (typeof (headers as { get?: unknown }).get === 'function') {
59
+ const v = (headers as { get(name: string): string | null }).get(name);
60
+ return v === null ? undefined : v;
61
+ }
62
+ const rec = headers as Record<string, string | string[] | undefined>;
63
+ const key = Object.keys(rec).find((k) => k.toLowerCase() === name.toLowerCase());
64
+ const v = key === undefined ? undefined : rec[key];
65
+ return Array.isArray(v) ? v[0] : v;
66
+ };
67
+ for (const f of WEBHOOK_HEADER_FAMILIES) {
68
+ const sig = get(f.signature);
69
+ const ts = get(f.timestamp);
70
+ if (sig !== undefined && ts !== undefined) {
71
+ const alg = get(f.algorithm);
72
+ return alg === undefined
73
+ ? { signatureHeader: sig, timestampHeader: ts, family: f.family }
74
+ : { signatureHeader: sig, timestampHeader: ts, algorithmHeader: alg, family: f.family };
75
+ }
76
+ }
77
+ return null;
78
+ }
@@ -1,33 +1,18 @@
1
1
  /**
2
- * Browser substitute for `webhook-helpers.ts` (openwop-sdks#30).
3
- *
4
- * ## Why this file exists
2
+ * Browser substitute for `webhook-helpers.ts`.
5
3
  *
6
4
  * `webhook-helpers.ts` imports `node:crypto` for `createHmac` and
7
- * `timingSafeEqual`. The package barrel re-exports it, and the `exports` map
8
- * offers only `"."` — so a browser consumer importing ANYTHING from
9
- * `@openwop/openwop` pulled the barrel, pulled the webhook helpers, and pulled
10
- * `node:crypto`. Vite/Rollup then failed the BUILD with
11
- *
12
- * "createHmac" is not exported by "__vite-browser-external"
13
- *
14
- * which names a bundler-internal shim rather than the real cause, so the error
15
- * points nowhere useful. Reported 2026-05-26 and still reproducible against the
16
- * published 1.7.0 fifteen months later.
5
+ * `timingSafeEqual`. The `browser` field in package.json maps that module to
6
+ * this one so a browser bundle builds. Webhook signature verification is a
7
+ * SERVER concern a browser has no business holding the subscription
8
+ * secret so the honest browser behaviour is to keep the import working and
9
+ * refuse the call, not to ship a second crypto implementation.
17
10
  *
18
- * The `browser` field in package.json maps the Node module to this one, so the
19
- * barrel is importable in a browser again. Webhook signature verification is a
20
- * SERVER concern a browser has no business holding the subscription secret
21
- * so the honest browser behaviour is to keep the import working and refuse the
22
- * call, not to ship a second crypto implementation.
23
- *
24
- * ## Why it throws rather than returning a failure
25
- *
26
- * `verifyWebhookSignature` returning `{ ok: false }` in a browser would be a
27
- * silent security downgrade: a caller that treats "not ok" as "reject the
28
- * delivery" behaves identically whether the signature was forged or the
29
- * platform simply could not check it. Those are different facts and only one of
30
- * them is about the payload. Throwing keeps them distinguishable.
11
+ * It throws rather than returning a failure: `{ valid: false }` in a browser
12
+ * would be a silent security downgrade — a caller that treats "not valid" as
13
+ * "reject the delivery" behaves identically whether the signature was forged
14
+ * or the platform simply could not check it. Throwing keeps them
15
+ * distinguishable.
31
16
  */
32
17
 
33
18
  const REASON =
@@ -35,7 +20,7 @@ const REASON =
35
20
  + 'Webhook verification is a server-side concern — the subscription secret must never reach a browser. '
36
21
  + 'Import them on the server from "@openwop/openwop/webhooks".';
37
22
 
38
- /** @see spec/v1/webhooks.md §"Replay attack protection" */
23
+ /** @see spec/v2/core/webhooks.md §Verification */
39
24
  export const DEFAULT_WEBHOOK_FRESHNESS_WINDOW_SECONDS = 300;
40
25
 
41
26
  export function verifyWebhookSignature(): never {
@@ -46,4 +31,15 @@ export function signWebhookDelivery(): never {
46
31
  throw new Error(REASON);
47
32
  }
48
33
 
49
- export type { VerifyWebhookSignatureOptions, VerifyWebhookOutcome } from './webhook-helpers.js';
34
+ // The header-family readers need no Node builtin, so the browser build
35
+ // carries the real implementations (a browser MAY inspect which family a
36
+ // delivery carries; it still MUST NOT verify — no secret in a browser).
37
+ export {
38
+ WEBHOOK_HEADER_FAMILIES,
39
+ WEBHOOK_SIGNATURE_ALGORITHMS,
40
+ parseSignatureValue,
41
+ readWebhookHeaders,
42
+ } from './webhook-header-families.js';
43
+
44
+ export type { VerifyWebhookSignatureOptions, VerifyWebhookOutcome, SignedWebhookDelivery } from './webhook-helpers.js';
45
+ export type { WebhookHeaderRead, WebhookSignatureAlgorithm } from './webhook-header-families.js';
@@ -1,33 +1,40 @@
1
1
  /**
2
- * Webhook delivery-verification helpers per `spec/v1/webhooks.md`
3
- * §"Signature recipe". Receivers MUST verify both the HMAC AND the
4
- * timestamp freshness before accepting a delivery — verifying HMAC
5
- * alone leaves the receiver open to replay attacks.
2
+ * Webhook delivery-verification helpers per `spec/v2/core/webhooks.md`
3
+ * §Verification. Receivers MUST verify both the HMAC AND the timestamp
4
+ * freshness before accepting a delivery — verifying HMAC alone leaves the
5
+ * receiver open to replay attacks.
6
6
  *
7
- * The canonical signing recipe:
7
+ * The signing recipe (webhooks.md §Headers):
8
8
  *
9
9
  * hmac = HMAC-SHA256(secret, `${timestamp}.${rawBody}`)
10
- * header `openwop-Webhook-Signature: v1=<hmac-hex>`
11
- * header `openwop-Webhook-Timestamp: <unix-seconds>`
10
+ * header `OpenWOP-Signature: sha256=<hmac-hex>`
11
+ * header `OpenWOP-Timestamp: <unix-seconds>`
12
+ * header `OpenWOP-Signature-Algorithm: v1`
13
+ *
14
+ * A host advertising both majors sends the `X-openwop-*` family beside it
15
+ * with identical values through the overlap; `readWebhookHeaders` accepts
16
+ * either family. The SDK-only `openwop-Webhook-*` names and the `v1=<hex>`
17
+ * value form were removed in v2 (headers.md §Removed).
12
18
  *
13
19
  * Verification:
14
20
  *
15
- * 1. Parse the `v1=<hex>` value from the signature header.
16
- * 2. Recompute `expected = HMAC-SHA256(secret, `${timestamp}.${rawBody}`)`.
17
- * 3. Compare using **constant-time** equality (timing-safe).
18
- * 4. Reject when `|now - timestamp|` exceeds the freshness window
19
- * (default 5 minutes per `webhooks.md`'s recommendation).
21
+ * 1. Parse the `sha256=<hex>` value from the signature header.
22
+ * 2. Reject an unrecognized `OpenWOP-Signature-Algorithm` value (MUST).
23
+ * 3. Reject a timestamp more than ±window from the clock (default 5 min).
24
+ * 4. Recompute `HMAC-SHA256(`${timestamp}.${rawBody}`, secret)` and compare
25
+ * in constant time.
20
26
  *
21
- * Implementation note: this helper uses `node:crypto`'s `timingSafeEqual`
22
- * for the comparison. The browser-side equivalent (Web Crypto's
23
- * `subtle.verify`) is not wrapped here — the SDK's runtime is Node.
27
+ * Implementation note: this helper uses `node:crypto`'s `timingSafeEqual`.
28
+ * The browser build substitutes a stub that throws (see
29
+ * `webhook-helpers.browser.ts`).
24
30
  *
25
31
  * @module @openwop/openwop/webhook-helpers
26
32
  */
27
33
 
28
34
  import { createHmac, timingSafeEqual } from 'node:crypto';
35
+ import { parseSignatureValue, WEBHOOK_SIGNATURE_ALGORITHMS } from './webhook-header-families.js';
29
36
 
30
- /** Default freshness window per `spec/v1/webhooks.md` §"Replay attack resistance". */
37
+ /** Default freshness window per webhooks.md §Verification (±5 minutes). */
31
38
  export const DEFAULT_WEBHOOK_FRESHNESS_WINDOW_SECONDS = 300;
32
39
 
33
40
  export interface VerifyWebhookSignatureOptions {
@@ -42,24 +49,38 @@ export interface VerifyWebhookSignatureOptions {
42
49
  * Default `Math.floor(Date.now() / 1000)`.
43
50
  */
44
51
  nowSeconds?: number;
52
+ /**
53
+ * The `OpenWOP-Signature-Algorithm` value the delivery carried, when the
54
+ * caller read one (`readWebhookHeaders().algorithmHeader`). An
55
+ * unrecognized value is rejected as `unsupported_signature_algorithm`.
56
+ */
57
+ algorithmHeader?: string;
45
58
  }
46
59
 
47
60
  export type VerifyWebhookOutcome =
48
61
  | { valid: true }
49
- | { valid: false; reason: 'signature_mismatch' | 'timestamp_expired' | 'timestamp_too_far_in_future' | 'malformed_signature_header' | 'malformed_timestamp_header' };
62
+ | {
63
+ valid: false;
64
+ reason:
65
+ | 'signature_mismatch'
66
+ | 'timestamp_expired'
67
+ | 'timestamp_too_far_in_future'
68
+ | 'malformed_signature_header'
69
+ | 'malformed_timestamp_header'
70
+ | 'unsupported_signature_algorithm';
71
+ };
50
72
 
51
73
  /**
52
- * Verify a webhook delivery per `spec/v1/webhooks.md` §"Signature
53
- * recipe". Returns `{ valid: true }` on success; otherwise
74
+ * Verify a webhook delivery. Returns `{ valid: true }` on success; otherwise
54
75
  * `{ valid: false, reason }` so callers can log + alert appropriately.
55
76
  *
56
- * Callers MUST pass the **raw** body bytes — JSON-parsed-then-
57
- * re-serialized bodies will fail verification because the host
58
- * signs the exact bytes it delivered.
77
+ * Callers MUST pass the **raw** body bytes — JSON-parsed-then-re-serialized
78
+ * bodies fail verification because the host signs the exact bytes it
79
+ * delivered.
59
80
  *
60
- * @param secret The pre-shared secret returned from `webhooks.register`.
61
- * @param signatureHeader The value of the `openwop-Webhook-Signature` header (e.g., `"v1=abc123…"`).
62
- * @param timestampHeader The value of the `openwop-Webhook-Timestamp` header (unix seconds as string).
81
+ * @param secret The subscription secret.
82
+ * @param signatureHeader `OpenWOP-Signature` / `X-openwop-Signature` (`"sha256=abc123…"`); see `readWebhookHeaders`.
83
+ * @param timestampHeader The matching timestamp header (unix seconds as string).
63
84
  * @param rawBody The exact request body bytes the host POSTed.
64
85
  */
65
86
  export function verifyWebhookSignature(
@@ -69,32 +90,36 @@ export function verifyWebhookSignature(
69
90
  rawBody: string | Buffer,
70
91
  options: VerifyWebhookSignatureOptions = {},
71
92
  ): VerifyWebhookOutcome {
72
- // 1. Parse the signature header.
73
- if (!signatureHeader.startsWith('v1=')) {
93
+ // 1. Parse the signature header — `sha256=<hex>` only.
94
+ const providedHex = parseSignatureValue(signatureHeader);
95
+ if (providedHex === null) {
74
96
  return { valid: false, reason: 'malformed_signature_header' };
75
97
  }
76
- const providedHex = signatureHeader.slice(3);
77
- if (!/^[0-9a-f]+$/i.test(providedHex)) {
78
- return { valid: false, reason: 'malformed_signature_header' };
98
+
99
+ // 2. Reject an unrecognized scheme.
100
+ if (
101
+ options.algorithmHeader !== undefined &&
102
+ !(WEBHOOK_SIGNATURE_ALGORITHMS as readonly string[]).includes(options.algorithmHeader)
103
+ ) {
104
+ return { valid: false, reason: 'unsupported_signature_algorithm' };
79
105
  }
80
106
 
81
- // 2. Parse the timestamp.
107
+ // 3. Parse the timestamp.
82
108
  const timestamp = Number(timestampHeader);
83
109
  if (!Number.isInteger(timestamp) || timestamp <= 0) {
84
110
  return { valid: false, reason: 'malformed_timestamp_header' };
85
111
  }
86
112
 
87
- // 3. Freshness check.
113
+ // 4. Freshness check.
88
114
  const window = options.freshnessWindowSeconds ?? DEFAULT_WEBHOOK_FRESHNESS_WINDOW_SECONDS;
89
115
  if (window > 0) {
90
116
  const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
91
117
  const delta = now - timestamp;
92
118
  if (delta > window) return { valid: false, reason: 'timestamp_expired' };
93
- // Allow small future skew (within the window) but reject far-future timestamps.
94
119
  if (delta < -window) return { valid: false, reason: 'timestamp_too_far_in_future' };
95
120
  }
96
121
 
97
- // 4. Recompute + constant-time compare.
122
+ // 5. Recompute + constant-time compare.
98
123
  const bodyStr = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');
99
124
  const signedBytes = `${timestamp}.${bodyStr}`;
100
125
  const expectedHex = createHmac('sha256', secret).update(signedBytes, 'utf8').digest('hex');
@@ -111,21 +136,44 @@ export function verifyWebhookSignature(
111
136
  return { valid: true };
112
137
  }
113
138
 
139
+ export interface SignedWebhookDelivery {
140
+ /** `sha256=<hex>` */
141
+ readonly signatureHeader: string;
142
+ readonly timestampHeader: string;
143
+ /** The five `OpenWOP-*` headers a v2 host MUST send on every delivery, minus the two subscription-specific ones (`OpenWOP-Webhook-Id`, `OpenWOP-Event-Type`), plus the `X-openwop-*` twins a dual-major host emits through the overlap. */
144
+ readonly headers: Readonly<Record<string, string>>;
145
+ }
146
+
114
147
  /**
115
148
  * Compute the canonical webhook signature for a payload — useful when
116
- * implementing a host (forward direction) OR when generating test
117
- * fixtures. Receivers verify via `verifyWebhookSignature`; this is the
118
- * inverse.
149
+ * implementing a host (forward direction) OR when generating test fixtures.
150
+ * Receivers verify via `verifyWebhookSignature`; this is the inverse.
119
151
  */
120
152
  export function signWebhookDelivery(
121
153
  secret: string,
122
154
  timestamp: number,
123
155
  rawBody: string | Buffer,
124
- ): { signatureHeader: string; timestampHeader: string } {
156
+ ): SignedWebhookDelivery {
125
157
  const bodyStr = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');
126
158
  const hex = createHmac('sha256', secret).update(`${timestamp}.${bodyStr}`, 'utf8').digest('hex');
127
159
  return {
128
- signatureHeader: `v1=${hex}`,
160
+ signatureHeader: `sha256=${hex}`,
129
161
  timestampHeader: String(timestamp),
162
+ headers: {
163
+ 'OpenWOP-Signature': `sha256=${hex}`,
164
+ 'OpenWOP-Timestamp': String(timestamp),
165
+ 'OpenWOP-Signature-Algorithm': 'v1',
166
+ 'X-openwop-Signature': `sha256=${hex}`,
167
+ 'X-openwop-Timestamp': String(timestamp),
168
+ 'X-openwop-Signature-Algorithm': 'v1',
169
+ },
130
170
  };
131
171
  }
172
+
173
+ export {
174
+ WEBHOOK_HEADER_FAMILIES,
175
+ WEBHOOK_SIGNATURE_ALGORITHMS,
176
+ parseSignatureValue,
177
+ readWebhookHeaders,
178
+ } from './webhook-header-families.js';
179
+ export type { WebhookHeaderRead, WebhookSignatureAlgorithm } from './webhook-header-families.js';
@@ -1,118 +0,0 @@
1
- /**
2
- * Public-registry read helpers per
3
- * `spec/v1/registry-operations.md`.
4
- *
5
- * The OpenWOP host SDK targets the host wire surface
6
- * (`/.well-known/openwop` + `/v1/runs/*` + `/v1/interrupts/*` etc).
7
- * The **public node-pack registry** at `packs.openwop.dev` is a
8
- * separate wire surface with its own discovery payload and pack-
9
- * versioned read endpoints. This module exposes a thin typed client
10
- * for that surface so adopters fetching pack manifests, indices, or
11
- * signature material don't roll their own HTTP plumbing.
12
- *
13
- * Read-only by design — the public registry uses pull-request-driven
14
- * publishing per `spec/v1/registry-operations.md` §"Submission flow"
15
- * + the `registry-publish.yml` GitHub workflow. There is no write API.
16
- *
17
- * No auth required for public reads.
18
- *
19
- * @module @openwop/openwop/registry-helpers
20
- */
21
- /** Public registry discovery payload per `registry-operations.md` §"Discovery". */
22
- export interface RegistryDiscovery {
23
- registryVersion: string;
24
- protocolVersion: string;
25
- name?: string;
26
- operator?: string;
27
- url?: string;
28
- supportedNamespaces: readonly string[];
29
- supportedSigningMethods: readonly string[];
30
- supportedTrustModes?: readonly string[];
31
- endpoints: {
32
- registryIndex: string;
33
- packMetadata: string;
34
- versionManifest: string;
35
- versionTarball: string;
36
- versionSignature: string;
37
- publicKey: string;
38
- };
39
- signingKeys?: ReadonlyArray<{
40
- keyId: string;
41
- algorithm: string;
42
- publicKeyUrl?: string;
43
- permittedNamespaces?: readonly string[];
44
- operator?: string;
45
- status?: string;
46
- }>;
47
- [key: string]: unknown;
48
- }
49
- /** Registry-wide index entry per `/v1/index.json` rows. */
50
- export interface RegistryIndexEntry {
51
- name: string;
52
- latestVersion: string;
53
- description?: string;
54
- scope?: string;
55
- [key: string]: unknown;
56
- }
57
- export interface RegistryIndex {
58
- packs: ReadonlyArray<RegistryIndexEntry>;
59
- generated?: string;
60
- packCount?: number;
61
- [key: string]: unknown;
62
- }
63
- /** Per-pack metadata document at `/v1/packs/{name}/index.json`. */
64
- export interface RegistryPackMetadata {
65
- name: string;
66
- description?: string;
67
- versions: ReadonlyArray<string>;
68
- latestVersion?: string;
69
- [key: string]: unknown;
70
- }
71
- /** Version manifest at `/v1/packs/{name}/-/{version}.json`. */
72
- export interface RegistryVersionManifest {
73
- name: string;
74
- version: string;
75
- description?: string;
76
- /** SRI hash: `sha256-<43-char-b64>=`. */
77
- integrity?: string;
78
- signing?: {
79
- method: 'ed25519' | 'manual' | string;
80
- keyId: string;
81
- publicKeyUrl?: string;
82
- };
83
- [key: string]: unknown;
84
- }
85
- export interface RegistryClientOptions {
86
- /** Base URL for the registry. Defaults to `https://packs.openwop.dev`. */
87
- baseUrl?: string;
88
- /** Custom fetch implementation; defaults to `globalThis.fetch`. */
89
- fetch?: typeof fetch;
90
- }
91
- /**
92
- * Typed client for the public OpenWOP node-pack registry. Read-only;
93
- * no auth required for the canonical public read surface.
94
- *
95
- * For host-side install-time verification (SRI + Ed25519 + lockfile),
96
- * see `examples/hosts/postgres/src/pack-consumer.ts` — the registry
97
- * client is the fetch surface; the consumer is the security surface.
98
- */
99
- export declare class RegistryClient {
100
- #private;
101
- readonly baseUrl: string;
102
- constructor(options?: RegistryClientOptions);
103
- /** `GET /.well-known/openwop-registry` — discovery + endpoint catalog. */
104
- discovery(): Promise<RegistryDiscovery>;
105
- /** `GET /v1/index.json` — registry-wide pack index. */
106
- index(): Promise<RegistryIndex>;
107
- /** `GET /v1/packs/{name}/index.json` — per-pack metadata. */
108
- pack(name: string): Promise<RegistryPackMetadata>;
109
- /** `GET /v1/packs/{name}/-/{version}.json` — version manifest. */
110
- version(name: string, version: string): Promise<RegistryVersionManifest>;
111
- /** Fetch raw tarball bytes. Caller MUST verify SRI + signature before trust. */
112
- tarball(name: string, version: string): Promise<Buffer>;
113
- /** Fetch raw 64-byte Ed25519 signature bytes. */
114
- signature(name: string, version: string): Promise<Buffer>;
115
- /** Fetch a publisher's public key as PEM text. */
116
- publicKey(keyId: string): Promise<string>;
117
- }
118
- //# sourceMappingURL=registry-helpers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry-helpers.d.ts","sourceRoot":"","sources":["../src/registry-helpers.ts"],"names":[],"mappings":"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"}
@@ -1,82 +0,0 @@
1
- /**
2
- * Public-registry read helpers per
3
- * `spec/v1/registry-operations.md`.
4
- *
5
- * The OpenWOP host SDK targets the host wire surface
6
- * (`/.well-known/openwop` + `/v1/runs/*` + `/v1/interrupts/*` etc).
7
- * The **public node-pack registry** at `packs.openwop.dev` is a
8
- * separate wire surface with its own discovery payload and pack-
9
- * versioned read endpoints. This module exposes a thin typed client
10
- * for that surface so adopters fetching pack manifests, indices, or
11
- * signature material don't roll their own HTTP plumbing.
12
- *
13
- * Read-only by design — the public registry uses pull-request-driven
14
- * publishing per `spec/v1/registry-operations.md` §"Submission flow"
15
- * + the `registry-publish.yml` GitHub workflow. There is no write API.
16
- *
17
- * No auth required for public reads.
18
- *
19
- * @module @openwop/openwop/registry-helpers
20
- */
21
- /**
22
- * Typed client for the public OpenWOP node-pack registry. Read-only;
23
- * no auth required for the canonical public read surface.
24
- *
25
- * For host-side install-time verification (SRI + Ed25519 + lockfile),
26
- * see `examples/hosts/postgres/src/pack-consumer.ts` — the registry
27
- * client is the fetch surface; the consumer is the security surface.
28
- */
29
- export class RegistryClient {
30
- baseUrl;
31
- #fetch;
32
- constructor(options = {}) {
33
- this.baseUrl = (options.baseUrl ?? 'https://packs.openwop.dev').replace(/\/$/, '');
34
- this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
35
- }
36
- /** `GET /.well-known/openwop-registry` — discovery + endpoint catalog. */
37
- async discovery() {
38
- return this.#getJson('/.well-known/openwop-registry');
39
- }
40
- /** `GET /v1/index.json` — registry-wide pack index. */
41
- async index() {
42
- return this.#getJson('/v1/index.json');
43
- }
44
- /** `GET /v1/packs/{name}/index.json` — per-pack metadata. */
45
- async pack(name) {
46
- return this.#getJson(`/v1/packs/${encodeURIComponent(name)}/index.json`);
47
- }
48
- /** `GET /v1/packs/{name}/-/{version}.json` — version manifest. */
49
- async version(name, version) {
50
- return this.#getJson(`/v1/packs/${encodeURIComponent(name)}/-/${encodeURIComponent(version)}.json`);
51
- }
52
- /** Fetch raw tarball bytes. Caller MUST verify SRI + signature before trust. */
53
- async tarball(name, version) {
54
- return this.#getBinary(`/v1/packs/${encodeURIComponent(name)}/-/${encodeURIComponent(version)}.tgz`);
55
- }
56
- /** Fetch raw 64-byte Ed25519 signature bytes. */
57
- async signature(name, version) {
58
- return this.#getBinary(`/v1/packs/${encodeURIComponent(name)}/-/${encodeURIComponent(version)}.sig`);
59
- }
60
- /** Fetch a publisher's public key as PEM text. */
61
- async publicKey(keyId) {
62
- const res = await this.#fetch(`${this.baseUrl}/keys/${encodeURIComponent(keyId)}.pub`);
63
- if (!res.ok)
64
- throw new Error(`registry: GET /keys/${keyId}.pub returned ${res.status}`);
65
- return res.text();
66
- }
67
- async #getJson(path) {
68
- const res = await this.#fetch(`${this.baseUrl}${path}`, {
69
- headers: { Accept: 'application/json' },
70
- });
71
- if (!res.ok)
72
- throw new Error(`registry: GET ${path} returned ${res.status}`);
73
- return (await res.json());
74
- }
75
- async #getBinary(path) {
76
- const res = await this.#fetch(`${this.baseUrl}${path}`);
77
- if (!res.ok)
78
- throw new Error(`registry: GET ${path} returned ${res.status}`);
79
- return Buffer.from(await res.arrayBuffer());
80
- }
81
- }
82
- //# sourceMappingURL=registry-helpers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry-helpers.js","sourceRoot":"","sources":["../src/registry-helpers.ts"],"names":[],"mappings":"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"}