@infinitetoken/lumber-kit 0.5.2 → 0.6.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/README.md CHANGED
@@ -50,7 +50,7 @@ import { LogLevel, LogRecord, LogEncrypted, LogPayload, LogsQuery } from '@infin
50
50
  | `PUSH_OFF` | constant | A push threshold one past Fatal (5): log pushes off |
51
51
  | `PushThreshold` | type | A `LogLevel` or `PUSH_OFF`, stored as a preference's `minimumLevel` |
52
52
  | `normalizePushThreshold` | function | A threshold from any stored or sent value: past Fatal is off, not a number is Warn |
53
- | `PushData` | type | The `data` each push type carries (`log`, `note`, `device_link`, `key_check`, `key_waiting`, `keys_shared`) |
53
+ | `PushData` | type | The `data` each push type carries (`log`, `note`, `device_link`, `device_link_answered`, `key_check`, `key_waiting`, `keys_shared`) |
54
54
  | `parsePushData` | function | A push's data as `PushData`, or `null` for data no Lumber push sends |
55
55
 
56
56
  ### Socket events
@@ -100,6 +100,10 @@ import { LogLevel, LogRecord, LogEncrypted, LogPayload, LogsQuery } from '@infin
100
100
  | `AppKeyUpsertBody` | interface | Body of PUT /apps/:id/keys/:userId |
101
101
  | `KeyPin` | interface | An account key a user accepted for someone, with its MAC |
102
102
  | `HolderKeyCheck`, `ClaimantKeyCheck`, `KeyCheckResult` | interfaces | A key check as the holder and the person being confirmed read it |
103
+ | `DeviceLinkCreateBody`, `DeviceLinkCreatedRecord` | interfaces | POST /device-links: a new device asking for the account key, and its answer |
104
+ | `DeviceLinkRecord`, `DeviceLinkApproveBody` | interfaces | A link request as the new device polls it (the exchange's first message once a code is shown, then the account key and the tag proving a device that showed the code sent it), and the approval that carries them |
105
+ | `PendingDeviceLink` | interface | A link request as a device holding the account key reads it, to show its code and finish it |
106
+ | `DeviceLinkStatus`, `DeviceLinkPlatform` | types | `pending`, `approved`, `denied` or `failed` (a code that didn't match); what the request comes from |
103
107
 
104
108
  ### Colors
105
109
 
package/dist/index.d.mts CHANGED
@@ -222,7 +222,24 @@ interface AppsQuery {
222
222
  cursor?: string;
223
223
  }
224
224
 
225
- type DeviceLinkStatus = 'pending' | 'approved' | 'denied';
225
+ /**
226
+ * A device link request: a new device asking for the account key from a device that has it. `failed` is a code that
227
+ * didn't match, which ends the request.
228
+ */
229
+ type DeviceLinkStatus = 'pending' | 'approved' | 'denied' | 'failed';
230
+ /** What a request comes from, so the device answering it can say so and offer a QR code only to a phone's camera. */
231
+ type DeviceLinkPlatform = 'ios' | 'android' | 'web' | 'desktop' | 'cli';
232
+ /**
233
+ * Body of POST /device-links.
234
+ */
235
+ interface DeviceLinkCreateBody {
236
+ deviceName: string;
237
+ platform: DeviceLinkPlatform;
238
+ /** The public half of the new device's one-off X25519 keypair, which the account key is wrapped to. */
239
+ publicKey: string;
240
+ /** The new device's push device id, so the push asking the account's devices to link it skips it. */
241
+ deviceId?: string;
242
+ }
226
243
  /**
227
244
  * Response from POST /device-links.
228
245
  */
@@ -231,14 +248,48 @@ interface DeviceLinkCreatedRecord {
231
248
  expiresAt: string;
232
249
  }
233
250
  /**
234
- * Response from GET /device-links/:id.
235
- * encryptedKey is present only when status is 'approved'.
251
+ * Body of POST /device-links/:id/approve: the account key wrapped to the new device, and a tag over it made from the
252
+ * code's secret, which the new device checks before storing the key, so the server can't hand it a key of its own.
253
+ */
254
+ interface DeviceLinkApproveBody {
255
+ encryptedKey: string;
256
+ deliveryTag: string;
257
+ }
258
+ /**
259
+ * Response from GET /device-links/:id, as the new device reads it.
260
+ * encryptedKey and deliveryTag are present only when status is 'approved'. sid and holderMessage are set once a device
261
+ * holding the account key shows the request's code, and answeredAt once the new device has answered it.
236
262
  */
237
263
  interface DeviceLinkRecord {
238
264
  id: string;
239
265
  status: DeviceLinkStatus;
240
266
  encryptedKey?: string;
267
+ deliveryTag?: string;
241
268
  expiresAt: string;
269
+ sid?: string | null;
270
+ holderMessage?: string | null;
271
+ answeredAt?: string | null;
272
+ }
273
+ /**
274
+ * A request waiting on the caller's account key (GET /device-links/pending), as a device holding it reads it: enough
275
+ * to show its code, and to finish it once answered. The messages are the same password-authenticated exchange as a key
276
+ * check's, so the server can't read or test codes against them. holderState is the code's side of the exchange, sealed
277
+ * under the account key, so any of the account's devices can show the same code or finish the request.
278
+ */
279
+ interface PendingDeviceLink {
280
+ id: string;
281
+ deviceName: string;
282
+ platform: DeviceLinkPlatform;
283
+ /** The new device's one-off public key, which an answer's tag has to cover before anything is wrapped to it. */
284
+ publicKey: string;
285
+ sid: string | null;
286
+ holderMessage: string | null;
287
+ holderState: string | null;
288
+ claimantMessage: string | null;
289
+ tag: string | null;
290
+ answeredAt: string | null;
291
+ expiresAt: string;
292
+ createdAt: string;
242
293
  }
243
294
 
244
295
  /**
@@ -471,6 +522,12 @@ type PushData =
471
522
  deviceLinkId: string;
472
523
  deviceName: string;
473
524
  }
525
+ /** The new device entered its link code, which a device holding the account key now checks (Lumber-API). */
526
+ | {
527
+ type: 'device_link_answered';
528
+ deviceLinkId: string;
529
+ deviceName: string;
530
+ }
474
531
  /** The person the reader gave a code entered it (Lumber-API). */
475
532
  | {
476
533
  type: 'key_check';
@@ -657,4 +714,4 @@ declare const apiErrorCodeOf: (body: unknown) => ApiErrorCode | null;
657
714
  /** A push notification's data as PushData, or null for data no Lumber push sends. */
658
715
  declare const parsePushData: (data: unknown) => PushData | null;
659
716
 
660
- export { APP_COLORS, type ApiErrorBody, type ApiErrorCode, type ApiKeyRecord, type ApiResponse, type AppCreatedRecord, type AppEncryptedKey, type AppKeyUpsertBody, type AppPermission, type AppPersonRecord, type AppRecord, type AppTeamAccess, type Appearance, type AppsQuery, type ClaimantKeyCheck, type ClientToServerEvents, DEFAULT_ACCENT, DEFAULT_PUSH_THRESHOLD, type DeviceLinkCreatedRecord, type DeviceLinkRecord, type DeviceLinkStatus, type FlagRecord, type FlagsQuery, type GuestRecord, type HolderKeyCheck, type InvitationRecord, type KeyCheckResult, type KeyHolder, type KeyPin, type LogContext, type LogEncrypted, LogLevel, type LogLevelName, type LogPayload, type LogRecord, type LogTimeseriesQuery, type LogsQuery, type NoteEncrypted, type NoteRecord, type NotesQuery, type NotificationPreference, type NotificationPreferences, PUSH_OFF, type PendingAppKey, type PushData, type PushThreshold, type PushToken, type PushType, type ResponseMetadata, type ServerToClientEvents, type SocketAppPayload, SubscriptionLevel, type SubscriptionLevelName, type SubscriptionLimits, type SubscriptionPlan, type SubscriptionPlanRecord, type TeamMemberRecord, type TeamRecord, type TeamsQuery, type TimeseriesBucket, type TimeseriesResponse, type UserRecord, apiErrorCodeOf, apiErrorCodes, isApiErrorCode, isValidAppearance, isValidColor, isValidDeviceLinkStatus, isValidImageUrl, isValidLogLevel, isValidSubscriptionLevel, isValidTeamMemberRole, joinApiKey, logLevelColors, logLevelNameToLevel, logLevelToName, logLevels, nameColor, normalizePushThreshold, parsePushData, personColor, resolveAppColor, splitApiKey, subscriptionLevelToName, subscriptionLevels, subscriptionLimits, subscriptionPlans };
717
+ export { APP_COLORS, type ApiErrorBody, type ApiErrorCode, type ApiKeyRecord, type ApiResponse, type AppCreatedRecord, type AppEncryptedKey, type AppKeyUpsertBody, type AppPermission, type AppPersonRecord, type AppRecord, type AppTeamAccess, type Appearance, type AppsQuery, type ClaimantKeyCheck, type ClientToServerEvents, DEFAULT_ACCENT, DEFAULT_PUSH_THRESHOLD, type DeviceLinkApproveBody, type DeviceLinkCreateBody, type DeviceLinkCreatedRecord, type DeviceLinkPlatform, type DeviceLinkRecord, type DeviceLinkStatus, type FlagRecord, type FlagsQuery, type GuestRecord, type HolderKeyCheck, type InvitationRecord, type KeyCheckResult, type KeyHolder, type KeyPin, type LogContext, type LogEncrypted, LogLevel, type LogLevelName, type LogPayload, type LogRecord, type LogTimeseriesQuery, type LogsQuery, type NoteEncrypted, type NoteRecord, type NotesQuery, type NotificationPreference, type NotificationPreferences, PUSH_OFF, type PendingAppKey, type PendingDeviceLink, type PushData, type PushThreshold, type PushToken, type PushType, type ResponseMetadata, type ServerToClientEvents, type SocketAppPayload, SubscriptionLevel, type SubscriptionLevelName, type SubscriptionLimits, type SubscriptionPlan, type SubscriptionPlanRecord, type TeamMemberRecord, type TeamRecord, type TeamsQuery, type TimeseriesBucket, type TimeseriesResponse, type UserRecord, apiErrorCodeOf, apiErrorCodes, isApiErrorCode, isValidAppearance, isValidColor, isValidDeviceLinkStatus, isValidImageUrl, isValidLogLevel, isValidSubscriptionLevel, isValidTeamMemberRole, joinApiKey, logLevelColors, logLevelNameToLevel, logLevelToName, logLevels, nameColor, normalizePushThreshold, parsePushData, personColor, resolveAppColor, splitApiKey, subscriptionLevelToName, subscriptionLevels, subscriptionLimits, subscriptionPlans };
package/dist/index.d.ts CHANGED
@@ -222,7 +222,24 @@ interface AppsQuery {
222
222
  cursor?: string;
223
223
  }
224
224
 
225
- type DeviceLinkStatus = 'pending' | 'approved' | 'denied';
225
+ /**
226
+ * A device link request: a new device asking for the account key from a device that has it. `failed` is a code that
227
+ * didn't match, which ends the request.
228
+ */
229
+ type DeviceLinkStatus = 'pending' | 'approved' | 'denied' | 'failed';
230
+ /** What a request comes from, so the device answering it can say so and offer a QR code only to a phone's camera. */
231
+ type DeviceLinkPlatform = 'ios' | 'android' | 'web' | 'desktop' | 'cli';
232
+ /**
233
+ * Body of POST /device-links.
234
+ */
235
+ interface DeviceLinkCreateBody {
236
+ deviceName: string;
237
+ platform: DeviceLinkPlatform;
238
+ /** The public half of the new device's one-off X25519 keypair, which the account key is wrapped to. */
239
+ publicKey: string;
240
+ /** The new device's push device id, so the push asking the account's devices to link it skips it. */
241
+ deviceId?: string;
242
+ }
226
243
  /**
227
244
  * Response from POST /device-links.
228
245
  */
@@ -231,14 +248,48 @@ interface DeviceLinkCreatedRecord {
231
248
  expiresAt: string;
232
249
  }
233
250
  /**
234
- * Response from GET /device-links/:id.
235
- * encryptedKey is present only when status is 'approved'.
251
+ * Body of POST /device-links/:id/approve: the account key wrapped to the new device, and a tag over it made from the
252
+ * code's secret, which the new device checks before storing the key, so the server can't hand it a key of its own.
253
+ */
254
+ interface DeviceLinkApproveBody {
255
+ encryptedKey: string;
256
+ deliveryTag: string;
257
+ }
258
+ /**
259
+ * Response from GET /device-links/:id, as the new device reads it.
260
+ * encryptedKey and deliveryTag are present only when status is 'approved'. sid and holderMessage are set once a device
261
+ * holding the account key shows the request's code, and answeredAt once the new device has answered it.
236
262
  */
237
263
  interface DeviceLinkRecord {
238
264
  id: string;
239
265
  status: DeviceLinkStatus;
240
266
  encryptedKey?: string;
267
+ deliveryTag?: string;
241
268
  expiresAt: string;
269
+ sid?: string | null;
270
+ holderMessage?: string | null;
271
+ answeredAt?: string | null;
272
+ }
273
+ /**
274
+ * A request waiting on the caller's account key (GET /device-links/pending), as a device holding it reads it: enough
275
+ * to show its code, and to finish it once answered. The messages are the same password-authenticated exchange as a key
276
+ * check's, so the server can't read or test codes against them. holderState is the code's side of the exchange, sealed
277
+ * under the account key, so any of the account's devices can show the same code or finish the request.
278
+ */
279
+ interface PendingDeviceLink {
280
+ id: string;
281
+ deviceName: string;
282
+ platform: DeviceLinkPlatform;
283
+ /** The new device's one-off public key, which an answer's tag has to cover before anything is wrapped to it. */
284
+ publicKey: string;
285
+ sid: string | null;
286
+ holderMessage: string | null;
287
+ holderState: string | null;
288
+ claimantMessage: string | null;
289
+ tag: string | null;
290
+ answeredAt: string | null;
291
+ expiresAt: string;
292
+ createdAt: string;
242
293
  }
243
294
 
244
295
  /**
@@ -471,6 +522,12 @@ type PushData =
471
522
  deviceLinkId: string;
472
523
  deviceName: string;
473
524
  }
525
+ /** The new device entered its link code, which a device holding the account key now checks (Lumber-API). */
526
+ | {
527
+ type: 'device_link_answered';
528
+ deviceLinkId: string;
529
+ deviceName: string;
530
+ }
474
531
  /** The person the reader gave a code entered it (Lumber-API). */
475
532
  | {
476
533
  type: 'key_check';
@@ -657,4 +714,4 @@ declare const apiErrorCodeOf: (body: unknown) => ApiErrorCode | null;
657
714
  /** A push notification's data as PushData, or null for data no Lumber push sends. */
658
715
  declare const parsePushData: (data: unknown) => PushData | null;
659
716
 
660
- export { APP_COLORS, type ApiErrorBody, type ApiErrorCode, type ApiKeyRecord, type ApiResponse, type AppCreatedRecord, type AppEncryptedKey, type AppKeyUpsertBody, type AppPermission, type AppPersonRecord, type AppRecord, type AppTeamAccess, type Appearance, type AppsQuery, type ClaimantKeyCheck, type ClientToServerEvents, DEFAULT_ACCENT, DEFAULT_PUSH_THRESHOLD, type DeviceLinkCreatedRecord, type DeviceLinkRecord, type DeviceLinkStatus, type FlagRecord, type FlagsQuery, type GuestRecord, type HolderKeyCheck, type InvitationRecord, type KeyCheckResult, type KeyHolder, type KeyPin, type LogContext, type LogEncrypted, LogLevel, type LogLevelName, type LogPayload, type LogRecord, type LogTimeseriesQuery, type LogsQuery, type NoteEncrypted, type NoteRecord, type NotesQuery, type NotificationPreference, type NotificationPreferences, PUSH_OFF, type PendingAppKey, type PushData, type PushThreshold, type PushToken, type PushType, type ResponseMetadata, type ServerToClientEvents, type SocketAppPayload, SubscriptionLevel, type SubscriptionLevelName, type SubscriptionLimits, type SubscriptionPlan, type SubscriptionPlanRecord, type TeamMemberRecord, type TeamRecord, type TeamsQuery, type TimeseriesBucket, type TimeseriesResponse, type UserRecord, apiErrorCodeOf, apiErrorCodes, isApiErrorCode, isValidAppearance, isValidColor, isValidDeviceLinkStatus, isValidImageUrl, isValidLogLevel, isValidSubscriptionLevel, isValidTeamMemberRole, joinApiKey, logLevelColors, logLevelNameToLevel, logLevelToName, logLevels, nameColor, normalizePushThreshold, parsePushData, personColor, resolveAppColor, splitApiKey, subscriptionLevelToName, subscriptionLevels, subscriptionLimits, subscriptionPlans };
717
+ export { APP_COLORS, type ApiErrorBody, type ApiErrorCode, type ApiKeyRecord, type ApiResponse, type AppCreatedRecord, type AppEncryptedKey, type AppKeyUpsertBody, type AppPermission, type AppPersonRecord, type AppRecord, type AppTeamAccess, type Appearance, type AppsQuery, type ClaimantKeyCheck, type ClientToServerEvents, DEFAULT_ACCENT, DEFAULT_PUSH_THRESHOLD, type DeviceLinkApproveBody, type DeviceLinkCreateBody, type DeviceLinkCreatedRecord, type DeviceLinkPlatform, type DeviceLinkRecord, type DeviceLinkStatus, type FlagRecord, type FlagsQuery, type GuestRecord, type HolderKeyCheck, type InvitationRecord, type KeyCheckResult, type KeyHolder, type KeyPin, type LogContext, type LogEncrypted, LogLevel, type LogLevelName, type LogPayload, type LogRecord, type LogTimeseriesQuery, type LogsQuery, type NoteEncrypted, type NoteRecord, type NotesQuery, type NotificationPreference, type NotificationPreferences, PUSH_OFF, type PendingAppKey, type PendingDeviceLink, type PushData, type PushThreshold, type PushToken, type PushType, type ResponseMetadata, type ServerToClientEvents, type SocketAppPayload, SubscriptionLevel, type SubscriptionLevelName, type SubscriptionLimits, type SubscriptionPlan, type SubscriptionPlanRecord, type TeamMemberRecord, type TeamRecord, type TeamsQuery, type TimeseriesBucket, type TimeseriesResponse, type UserRecord, apiErrorCodeOf, apiErrorCodes, isApiErrorCode, isValidAppearance, isValidColor, isValidDeviceLinkStatus, isValidImageUrl, isValidLogLevel, isValidSubscriptionLevel, isValidTeamMemberRole, joinApiKey, logLevelColors, logLevelNameToLevel, logLevelToName, logLevels, nameColor, normalizePushThreshold, parsePushData, personColor, resolveAppColor, splitApiKey, subscriptionLevelToName, subscriptionLevels, subscriptionLimits, subscriptionPlans };
package/dist/index.js CHANGED
@@ -157,7 +157,7 @@ var resolveAppColor = (name, color) => color && isValidColor(color) ? color : na
157
157
  var personColor = (userId) => nameColor(userId);
158
158
  var isValidLogLevel = (value) => logLevels.includes(value);
159
159
  var isValidSubscriptionLevel = (value) => subscriptionLevels.includes(value);
160
- var deviceLinkStatuses = ["pending", "approved", "denied"];
160
+ var deviceLinkStatuses = ["pending", "approved", "denied", "failed"];
161
161
  var isValidDeviceLinkStatus = (value) => deviceLinkStatuses.includes(value);
162
162
  var teamMemberRoles = ["owner", "admin", "member"];
163
163
  var isValidTeamMemberRole = (value) => teamMemberRoles.includes(value);
@@ -199,7 +199,8 @@ var parsePushData = (data) => {
199
199
  case "note":
200
200
  return isId(d.appId) && isId(d.noteId) ? { type: "note", appId: d.appId, noteId: d.noteId } : null;
201
201
  case "device_link":
202
- return isId(d.deviceLinkId) ? { type: "device_link", deviceLinkId: d.deviceLinkId, deviceName: typeof d.deviceName === "string" ? d.deviceName : "" } : null;
202
+ case "device_link_answered":
203
+ return isId(d.deviceLinkId) ? { type: d.type, deviceLinkId: d.deviceLinkId, deviceName: typeof d.deviceName === "string" ? d.deviceName : "" } : null;
203
204
  case "key_check":
204
205
  return isId(d.checkId) ? { type: "key_check", checkId: d.checkId } : null;
205
206
  case "key_waiting":
package/dist/index.mjs CHANGED
@@ -101,7 +101,7 @@ var resolveAppColor = (name, color) => color && isValidColor(color) ? color : na
101
101
  var personColor = (userId) => nameColor(userId);
102
102
  var isValidLogLevel = (value) => logLevels.includes(value);
103
103
  var isValidSubscriptionLevel = (value) => subscriptionLevels.includes(value);
104
- var deviceLinkStatuses = ["pending", "approved", "denied"];
104
+ var deviceLinkStatuses = ["pending", "approved", "denied", "failed"];
105
105
  var isValidDeviceLinkStatus = (value) => deviceLinkStatuses.includes(value);
106
106
  var teamMemberRoles = ["owner", "admin", "member"];
107
107
  var isValidTeamMemberRole = (value) => teamMemberRoles.includes(value);
@@ -143,7 +143,8 @@ var parsePushData = (data) => {
143
143
  case "note":
144
144
  return isId(d.appId) && isId(d.noteId) ? { type: "note", appId: d.appId, noteId: d.noteId } : null;
145
145
  case "device_link":
146
- return isId(d.deviceLinkId) ? { type: "device_link", deviceLinkId: d.deviceLinkId, deviceName: typeof d.deviceName === "string" ? d.deviceName : "" } : null;
146
+ case "device_link_answered":
147
+ return isId(d.deviceLinkId) ? { type: d.type, deviceLinkId: d.deviceLinkId, deviceName: typeof d.deviceName === "string" ? d.deviceName : "" } : null;
147
148
  case "key_check":
148
149
  return isId(d.checkId) ? { type: "key_check", checkId: d.checkId } : null;
149
150
  case "key_waiting":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infinitetoken/lumber-kit",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Shared TypeScript types and wire format contracts for the Lumber cloud logging suite",
5
5
  "keywords": [
6
6
  "lumber",