@robono/server 0.8.0 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1
4
+
5
+ - Simplifies the public implementation path while retaining the complete endpoint, authorization, event, and testing contracts.
6
+ - Renames the public client-side API reference and removes unnecessary infrastructure terminology.
7
+
3
8
  ## 0.8.0
4
9
 
5
10
  - Added typed external-user export and deletion operations with status lookup.
package/README.md CHANGED
@@ -1,12 +1,10 @@
1
1
  # `@robono/server`
2
2
 
3
- Headless Node/TypeScript SDK for connecting an existing app or closed ecosystem to Robono Bridge. It provides typed API calls, bounded retries, idempotency, restriction checks, speech tools, webhook verification, and the protected client adapter.
3
+ Headless Node/TypeScript SDK for connecting an existing app or private ecosystem to Robono Bridge.
4
4
 
5
- ## License in plain English
5
+ ## License
6
6
 
7
- You may use this SDK to build, test, and operate an authorized application that connects to Robono. You may not redistribute it as a standalone SDK, use it to bypass Robono, or use it to build a competing bridge service. Service access, pricing, maintenance, and support are separate. If you and Robono sign a written agreement that expressly replaces this SDK license, that agreement controls to the extent it says so. The included `LICENSE` is the complete, controlling text.
8
-
9
- See the [SDK license FAQ](https://robono.com/sdk-license) for procurement guidance and the route for organization terms.
7
+ Licensed for authorized Robono integrations. Redistribution as a standalone SDK, bypassing Robono, and competing bridge services are prohibited. See the included `LICENSE` and [license FAQ](https://robono.com/sdk-license).
10
8
 
11
9
  Robono's own app and third-party connected apps appear as endpoints in the same directory. Your product keeps its existing accounts, screens, storage, safety rules, and notifications.
12
10
 
@@ -16,10 +14,7 @@ Robono's own app and third-party connected apps appear as endpoints in the same
16
14
  npm install @robono/server
17
15
  ```
18
16
 
19
- The package includes `SBOM.spdx.json`, a machine-readable inventory of its
20
- production components.
21
-
22
- The package is ESM-only and supports Node.js 18+ and Deno 2.x through npm compatibility, including Supabase Edge Functions:
17
+ The package is ESM-only, supports Node.js 18+ and Deno 2.x through npm compatibility, and includes TypeScript declarations and `SBOM.spdx.json`.
23
18
 
24
19
  CommonJS applications must use a dynamic import:
25
20
 
@@ -27,13 +22,6 @@ CommonJS applications must use a dynamic import:
27
22
  const { RobonoServer } = await import("@robono/server");
28
23
  ```
29
24
 
30
- ```ts
31
- import {
32
- createRobonoBackendAdapter,
33
- RobonoServer,
34
- } from "npm:@robono/server@0.8.0";
35
- ```
36
-
37
25
  ## Discover endpoints
38
26
 
39
27
  ```ts
@@ -91,129 +79,33 @@ const result = await robono.endpointMessages.send({
91
79
  }, { idempotencyKey: messageOperationId });
92
80
  ```
93
81
 
94
- The normalized `connection.connection_id` is valid for SDK state and subsequent unified calls. Endpoint-specific methods remain available for integrations that need lower-level control.
82
+ The normalized `connection.connection_id` is valid for subsequent unified SDK calls.
95
83
 
96
84
  The unified send validates negotiated message type, text length, media size, duration, MIME type, and attachment count before making the API request. For media composed as one message, give every item the same `attachment_batch.id`, its zero-based `index`, and the common `count`.
97
-
98
- New server-only integrations can use the normalized namespaces for the rest of the lifecycle without branching on endpoint type:
99
-
100
- ```ts
101
- const page = await robono.endpointConnections.list({
102
- external_user_id: user.id,
103
- limit: 50,
104
- });
105
-
106
- const history = await robono.endpointMessages.list({
107
- connection,
108
- external_user_id: user.id,
109
- limit: 50,
110
- });
111
-
112
- const firstMessage = history.messages[0];
113
- if (firstMessage) {
114
- await robono.endpointMessages.mark({
115
- connection,
116
- external_user_id: user.id,
117
- message_id: firstMessage.message_id,
118
- event: "read",
119
- }, { idempotencyKey: receiptOperation.id });
120
- }
121
-
122
- await robono.endpointConnections.update({
123
- connection,
124
- external_user_id: user.id,
125
- external_profile: currentProfile,
126
- }, { idempotencyKey: profileUpdate.operationId });
127
-
128
- await robono.endpointConnections.disconnect({
129
- connection,
130
- external_user_id: user.id,
131
- }, { idempotencyKey: disconnectOperation.id });
132
- ```
133
-
134
- Use `page.next_cursor` and `history.next_before` for older pages. Use `history.sync_cursor` as the next `after` value when checking only for newer messages.
135
- Create and persist each operation ID before its first request, then reuse that ID
136
- after a timeout, retry, or restart.
85
+ The same normalized namespaces list, update, and disconnect connections; load message history; and mark messages delivered, read, or heard. See the [Server SDK reference](https://robono.com/sdk-reference/server/).
137
86
 
138
87
  ## Protect client operations
139
88
 
140
- Keep the API key on your server. Mount the adapter at `/robono/*` behind your existing authentication:
89
+ Keep the API key on your server. Mount the app-facing route at `/robono/*` behind your existing authentication:
141
90
 
142
91
  ```ts
143
92
  const handleRobono = createRobonoBackendAdapter({
144
93
  robono,
145
94
  authenticate: async request =>
146
95
  (await yourAuth.findUser(request))?.id ?? null,
147
- authorize: async ({ action, userId, input }) => {
148
- const user = await accounts.findActiveUser(userId);
149
- if (!user) return false;
150
- if (action === "networks.list") return true;
151
- if (action === "network_connections.respond") {
152
- return guardianApprovals.mayRespond(user, input);
153
- }
154
- if (action.endsWith(".list")) return access.mayRead(user, input);
155
- if (
156
- action === "message_transforms.create" ||
157
- action === "speech_transforms.create"
158
- ) return processing.hasPermissionAndCredits(user, input);
159
- if (
160
- action.endsWith(".request") ||
161
- action.endsWith(".create") ||
162
- action.endsWith(".send") ||
163
- action.endsWith(".mark") ||
164
- action.endsWith(".update") ||
165
- action.endsWith(".update_profile") ||
166
- action.endsWith(".disconnect") ||
167
- action === "push_diagnostics.report"
168
- ) return access.mayChange(user, action, input);
169
- return false;
170
- },
96
+ authorize: context =>
97
+ yourPermissions.allowRobonoAction(context),
171
98
  });
172
99
  ```
173
100
 
174
- `authenticate` proves the user's identity. The required `authorize` callback decides whether that user may perform the exact operation. Enforce relationship state, account rules, guardian approval when applicable, messaging restrictions, and processing allowances. Missing or false decisions are denied.
175
- Return `null` or `undefined` for a normal missing or invalid session. If your
176
- authentication library must throw for that case, throw
177
- `RobonoAuthenticationError`; the adapter safely returns HTTP 401. Unexpected
178
- authentication-system failures remain sanitized HTTP 500 responses.
101
+ `authenticate` proves identity. The required `authorize` callback checks the exact action against your account, relationship, guardian, safety, and processing rules. Missing or false decisions are denied. The route uses standard Web `Request` and `Response` objects, so it works with common Node, serverless, and Deno frameworks.
179
102
 
180
- The adapter uses standard Web `Request` and `Response` objects, so it can be mounted in Express, Fastify, Next.js, serverless functions, Supabase Edge Functions, or another compatible backend. Exact Express, Next.js, Supabase Edge, and Deno mounts are available in the website documentation.
181
-
182
- ## Language and speech tools
183
-
184
- The typed transform methods cover transcription, translation, TTS, translated voice, and existing-message transforms:
185
-
186
- ```ts
187
- const { languages } = await robono.languages();
188
- showAvailableTransformLanguages(languages);
189
-
190
- const result = await robono.transforms.speech({
191
- input: {
192
- type: "audio",
193
- source_url: audioUrl,
194
- mime_type: "audio/mp4",
195
- language: "en",
196
- },
197
- outputs: ["transcript", "translated_voice"],
198
- target_language: "es",
199
- }, { idempotencyKey: transformOperationId });
200
-
201
- console.log(result.status); // "completed"
202
- console.log(result.artifacts); // Typed transcript and generated voice
203
- console.log(result.billing.operations); // Exact transcription/translation/TTS steps
204
- ```
205
-
206
- Transforms complete synchronously. SDK errors report request failures, while signed `transform.completed` and `transform.failed` webhooks provide an auditable server event. Generated media URLs include `source_url_expires_at` and currently expire after ten minutes. Reuse the same idempotency key when retrying the same operation; cached message artifacts are not charged again.
207
-
208
- ## Verify webhooks
103
+ ## Verify signed events
209
104
 
210
105
  Verify the signature against the exact raw request body before parsing JSON:
211
106
 
212
107
  ```ts
213
- import {
214
- toClientPushPayload,
215
- verifyRobonoWebhook,
216
- } from "@robono/server";
108
+ import { verifyRobonoWebhook } from "@robono/server";
217
109
 
218
110
  const webhookSecret = process.env.ROBONO_WEBHOOK_SECRET;
219
111
  if (!webhookSecret) throw new Error("ROBONO_WEBHOOK_SECRET is required");
@@ -229,80 +121,10 @@ await webhookInbox.insertPendingIfAbsent({
229
121
  rawBody,
230
122
  event: verified.event,
231
123
  });
232
-
233
- // Return any HTTP 2xx within 10 seconds; 202 is recommended here.
234
- // Timeouts, network failures, redirects, and non-2xx responses receive up to
235
- // five retries with increasing delay capped at 15 minutes. Duplicate event IDs
236
- // must also return 2xx. A worker polls durable pending rows and runs:
237
- async function processPendingWebhook(pending: typeof verified) {
238
- switch (pending.event.event) {
239
- case "bridge.connection_requested":
240
- await pendingRequests.store(pending.event);
241
- break;
242
- case "connection.status_changed":
243
- case "bridge.connection_status_changed":
244
- await existingMessagingSystem.updateConnection(pending.event);
245
- break;
246
- case "message.created":
247
- await existingMessagingSystem.storeMessage(pending.event);
248
- break;
249
- case "bridge.message_created":
250
- await existingMessagingSystem.storeMessage(pending.event.message);
251
- break;
252
- case "transform.completed":
253
- case "transform.failed":
254
- await existingMessagingSystem.updateTransform(pending.event);
255
- break;
256
- }
257
-
258
- const externalUserId =
259
- await existingMessagingSystem.recipientFor(pending.event);
260
- if (externalUserId) {
261
- await yourPushProvider.send(externalUserId, {
262
- data: toClientPushPayload(pending.event),
263
- });
264
- }
265
- await webhookInbox.markComplete(pending.eventId);
266
- }
124
+ return new Response("accepted", { status: 202 });
267
125
  ```
268
126
 
269
- `verified.event` is a runtime-validated, discriminated `RobonoWebhookEvent` union covering connection, message, receipt, guardian, transform, failure, and diagnostic events. The durable inbox must enforce a unique event ID and keep failed work pending; receiving a duplicate must never cause unfinished work to be marked complete.
270
- Message-created webhooks contain the complete message: direct-endpoint content is
271
- on the event itself and app-to-app content is in `event.message`. Client push
272
- data contains routing IDs only; the authenticated client fetches current state.
273
-
274
- For `bridge.connection_requested`, resolve `event.target.identifier` on your
275
- backend, store the request pending any required guardian approval, and call
276
- `networkConnections.respond()` only from an authorized server action. Use a
277
- generic requester-facing result for `not_found`, rejection, and unauthorized
278
- lookups so account existence cannot be tested.
279
-
280
- ## Automate user data requests
281
-
282
- Create exports and deletions only after your backend authenticates and
283
- authorizes the privacy request:
284
-
285
- ```ts
286
- const request = await robono.dataRequests.deleteUser({
287
- external_user_id: user.id,
288
- external_request_id: privacyCase.id,
289
- }, { idempotencyKey: privacyCase.operationId });
290
-
291
- const status = await robono.dataRequests.status({
292
- data_request_id: request.data_request.id,
293
- });
294
-
295
- if (status.export) {
296
- await yourPrivacySystem.storeCompletedExport(status.export);
297
- }
298
- ```
299
-
300
- Deletion requires a stable idempotency key. Signed `data.export_*` and
301
- `data.deletion_*` events report completion or failure; status lookup remains
302
- available if a webhook is delayed. A completed JSON export remains available
303
- from status responses until `result_expires_at`. Use a `data:read` or
304
- `data:write` API key when the operation should not share the integration's
305
- broader credential.
127
+ Store each event once by event ID, return HTTP 2xx within 10 seconds, and process it asynchronously. Duplicate events must not create duplicate work. Connection requests must be resolved and approved only by an authorized server action.
306
128
 
307
129
  ## Run the sandbox lifecycle
308
130
 
@@ -313,9 +135,7 @@ ROBONO_SANDBOX_NETWORK_B_ID=... \
313
135
  npx --package @robono/server robono-sandbox-test
314
136
  ```
315
137
 
316
- The test runs the stateful network-to-network lifecycle, then validates the same unified connection and send methods against the test-only Robono endpoint. The Robono endpoint check cannot reach real users, SMS, billing, or production delivery.
317
-
318
- The lifecycle runner tests Robono's sandbox API, not your authentication, CORS, or deployed adapter. Test those separately:
138
+ The lifecycle runner tests Robono's isolated Sandbox. Test your own authentication, authorization, and CORS separately:
319
139
 
320
140
  ```bash
321
141
  ROBONO_ADAPTER_URL=https://your-backend.example/robono \
@@ -328,18 +148,14 @@ npx --package @robono/server robono-adapter-test
328
148
 
329
149
  All packages are currently early-access `0.x` releases. Patch releases contain
330
150
  compatible fixes. Before `1.0`, a minor release may contain a breaking change;
331
- read the packaged `CHANGELOG.md` before upgrading. The latest minor line is the
332
- supported line, and public API versions receive at least 90 days' notice before
333
- retirement.
334
-
335
- The exact package and API compatibility matrix is published at
336
- [robono.com/docs#test](https://robono.com/docs#test). Production credentials
337
- start at 300 requests per rolling minute; Sandbox credentials start at 600.
338
- There is no separate published concurrency quota. Honor `Retry-After` on rate
339
- limits and retryable temporary failures.
151
+ read the packaged `CHANGELOG.md` before upgrading. Each minor package line
152
+ receives critical security and Bridge compatibility fixes for at least 12
153
+ months after its successor is published. Public API versions receive at least
154
+ 12 months' notice before retirement unless continued support would create an
155
+ active security risk.
340
156
 
341
157
  Report SDK problems at [robono.com/contact?topic=sdk](https://robono.com/contact?topic=sdk).
342
158
  Include the package version, runtime, safe reproduction steps, and request ID;
343
159
  never include credentials or private message content.
344
160
 
345
- See the complete build flow, REST reference, platform support, capabilities, delivery behavior, and testing guide at [robono.com/docs](https://robono.com/docs).
161
+ See the complete build flow, optional language and privacy operations, platform support, and testing guide at [robono.com/docs](https://robono.com/docs).
package/SBOM.spdx.json CHANGED
@@ -2,22 +2,22 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "@robono/server@0.8.0",
6
- "documentNamespace": "http://spdx.org/spdxdocs/@robono%2fserver-0.8.0-d581d2ee-66b5-4ca6-82e1-b207f45d0a67",
5
+ "name": "@robono/server@0.8.1",
6
+ "documentNamespace": "http://spdx.org/spdxdocs/@robono%2fserver-0.8.1-5363c57b-f29a-4d3f-bcb9-1e07d0a950d0",
7
7
  "creationInfo": {
8
- "created": "2026-07-29T22:21:34.416Z",
8
+ "created": "2026-07-30T22:37:53.518Z",
9
9
  "creators": [
10
10
  "Tool: npm/cli-12.0.2"
11
11
  ]
12
12
  },
13
13
  "documentDescribes": [
14
- "SPDXRef-Package-robono.server-0.8.0"
14
+ "SPDXRef-Package-robono.server-0.8.1"
15
15
  ],
16
16
  "packages": [
17
17
  {
18
18
  "name": "@robono/server",
19
- "SPDXID": "SPDXRef-Package-robono.server-0.8.0",
20
- "versionInfo": "0.8.0",
19
+ "SPDXID": "SPDXRef-Package-robono.server-0.8.1",
20
+ "versionInfo": "0.8.1",
21
21
  "packageFileName": "",
22
22
  "description": "Provider-neutral server SDK for the Robono Bridge API.",
23
23
  "primaryPackagePurpose": "LIBRARY",
@@ -29,7 +29,7 @@
29
29
  {
30
30
  "referenceCategory": "PACKAGE-MANAGER",
31
31
  "referenceType": "purl",
32
- "referenceLocator": "pkg:npm/%40robono/server@0.8.0"
32
+ "referenceLocator": "pkg:npm/%40robono/server@0.8.1"
33
33
  }
34
34
  ]
35
35
  }
@@ -37,7 +37,7 @@
37
37
  "relationships": [
38
38
  {
39
39
  "spdxElementId": "SPDXRef-DOCUMENT",
40
- "relatedSpdxElement": "SPDXRef-Package-robono.server-0.8.0",
40
+ "relatedSpdxElement": "SPDXRef-Package-robono.server-0.8.1",
41
41
  "relationshipType": "DESCRIBES"
42
42
  }
43
43
  ]
package/dist/client.js CHANGED
@@ -45,7 +45,7 @@ export class RobonoServer {
45
45
  code: "fetch_required",
46
46
  });
47
47
  }
48
- this.userAgent = options.userAgent ?? "@robono/server/0.8.0";
48
+ this.userAgent = options.userAgent ?? "@robono/server/0.8.1";
49
49
  this.apiVersion = options.apiVersion?.trim() || DEFAULT_API_VERSION;
50
50
  this.directory = {
51
51
  list: (input = {}, requestOptions) => this.request("/networks", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robono/server",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Provider-neutral server SDK for the Robono Bridge API.",
5
5
  "keywords": [
6
6
  "robono",