@inixiative/signet 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@ This package is the one implementation of that protocol, shared by Kingdom (whic
10
10
  - `proof`: create and verify DPoP proofs (ES256, nonce-bound, access-token-bound).
11
11
  - `files`: private 0600 JSON files for keys and credentials.
12
12
  - `client`:
13
- - an **Installation** (a local Foundry, Archive or Oracle) is an actor known to Kingdom by its key: `registerInstallation`, then `requestRegistration` returns a review code a person claims in Kingdom (a `registerIntegration` Inquiry), then `collectSignet` and `saveCollectedSignet`. `installationInquiries`, `installationSignets`, `cancelInstallationInquiry`, `enrollInstallationSignet` and `rotateInstallationKey` act as the installation;
13
+ - an **Installation** (a local Foundry, Archive or Oracle) is an actor known to Kingdom by its key: `registerInstallation`, then `requestRegistration` asks for what it needs (the owner it expects, by organization id or slug, and resources by id or by kind and name, with their operations) and returns a review code a person claims in Kingdom (a `registerIntegration` Inquiry), where the review opens with that owner and those resources selected, then `collectSignet` and `saveCollectedSignet`. `installationInquiries`, `installationSignets`, `cancelInstallationInquiry`, `enrollInstallationSignet` and `rotateInstallationKey` act as the installation;
14
14
  - `pairInstallation` does the whole pairing (register, request, wait on the socket, confirm the owner, collect) and `holdInstallationSignets` keeps one credential file per Signet the installation's integrations hold, enrolling later grants and dropping revoked ones;
15
15
  - `InstallationSocket`, Kingdom's live line to an installation: it proves the key, receives a snapshot of the installation's inquiries and Signets on connect and on every change, keeps the installation's integrations marked online, advertises its viewer URL, and polls the same reads while the socket is down;
16
16
  - `SignetClient`, which renews its access token and signs each call: `describe`, `execute`, task settlement, and the run actions a Foundry uses for inference (`resolveRun`, `delegateRun`, `renewRun`, `revokeRun`), and for a subscription run `releaseSubscription` and `reportSubscriptionLimits`.
@@ -20,7 +20,11 @@ import { holdInstallationSignets, InstallationSocket, pairInstallation, SignetCl
20
20
 
21
21
  const paired = await pairInstallation({
22
22
  url: kingdom, root: dataDir, kind: 'archive', name: 'Laptop Archive', sourceId,
23
- terms: { name: 'Laptop Archive', lifecycle: 'ongoing', resources: [], expiresAt: null, maxRequests: null, maxConcurrent: 4 },
23
+ terms: {
24
+ name: 'Laptop Archive', lifecycle: 'ongoing', expiresAt: null, maxRequests: null, maxConcurrent: 4,
25
+ owner: { ownerModel: 'Organization', organization: 'inixiative' },
26
+ resources: [{ resourceKind: 'archiveLibrary', operations: ['sessions.write'] }],
27
+ },
24
28
  onReview: ({ review }) => console.log(`Approve in Kingdom: ${review}`),
25
29
  confirmOwner: async ({ ownerName }) => confirm(`Pair with ${ownerName}?`),
26
30
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inixiative/signet",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Present Kingdom Signets: the wire contracts, DPoP proofs, device pairing and a renewing client shared by Kingdom, Archive, Foundry and Oracle.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "devDependencies": {
36
36
  "@biomejs/biome": "2.5.2",
37
- "@inixiative/config": "^0.5.74",
37
+ "@inixiative/config": "^0.5.86",
38
38
  "@types/bun": "1.4.2",
39
39
  "lefthook": "2.1.9",
40
40
  "typescript": "6.0.3",
package/src/client.ts CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  nonceSchema,
13
13
  ownerRefSchema,
14
14
  publicClientKeySchema,
15
- type RegistrationTerms,
15
+ type RegistrationRequest,
16
16
  registerInstallationResponseSchema,
17
17
  renewalCredentialPattern,
18
18
  requestRegistrationResponseSchema,
@@ -111,17 +111,16 @@ function abortable<T>(pending: Promise<T>, signal: AbortSignal): Promise<T> {
111
111
  });
112
112
  }
113
113
 
114
+ /** Kingdom's JSON reason, else the start of the raw body (a proxy's page). */
114
115
  const errorDetail = async (response: Response) => {
116
+ const text = (await response.text().catch(() => '')).slice(0, 65_536).trim();
115
117
  try {
116
- const body = JSON.parse((await response.text()).slice(0, 4096)) as {
117
- message?: unknown;
118
- error?: { message?: unknown };
119
- };
120
- const message = body.error?.message ?? body.message;
121
- return typeof message === 'string' ? message.slice(0, 500) : undefined;
122
- } catch {
123
- return undefined;
124
- }
118
+ const body = JSON.parse(text) as { message?: unknown; error?: unknown };
119
+ const error = body.error as { message?: unknown } | string | undefined;
120
+ const message = typeof error === 'string' ? error : (error?.message ?? body.message);
121
+ if (typeof message === 'string') return message.slice(0, 500);
122
+ } catch {}
123
+ return text.replace(/\s+/g, ' ').slice(0, 500) || undefined;
125
124
  };
126
125
 
127
126
  /** POST to Kingdom's access API and return the response's `data`. */
@@ -249,7 +248,7 @@ export const registerInstallation = (
249
248
  export const requestRegistration = (
250
249
  url: string,
251
250
  keyFile: string,
252
- terms: RegistrationTerms,
251
+ terms: RegistrationRequest,
253
252
  options: SignetRequestOptions = {},
254
253
  ) =>
255
254
  installationPost(
package/src/contracts.ts CHANGED
@@ -104,12 +104,33 @@ export const signetReviewProposalSchema = z.strictObject({
104
104
  ...signetTerms,
105
105
  resources: z.array(signetResourceSchema).max(100),
106
106
  });
107
- /** What an Installation asks for when it asks to be registered as an owner's integration. */
107
+ /** The terms a person approves when registering an Installation as an owner's integration. */
108
108
  export const registrationTermsSchema = z.strictObject({
109
109
  ...signetTerms,
110
110
  lifecycle: z.literal('ongoing'),
111
111
  resources: z.array(signetResourceSchema).max(100),
112
112
  });
113
+ /** The owner an Installation asks to belong to; `organization` is the organization's id or slug. */
114
+ export const requestedOwnerSchema = z.strictObject({
115
+ ownerModel: ownerModelSchema,
116
+ organization: z.string().trim().min(1).max(120).optional(),
117
+ spaceId: z.uuid().optional(),
118
+ });
119
+ /** A resource asked for by kind, and optionally name, when the Installation does not know its id. */
120
+ export const requestedResourceKindSchema = z.strictObject({
121
+ resourceKind: z.string().min(1).max(80),
122
+ resourceName: z.string().trim().min(1).max(120).optional(),
123
+ operations: z.array(z.string().min(1).max(80)).min(1).max(100),
124
+ lens: signetLensSchema.default({}),
125
+ });
126
+ export const requestedResourceSchema = z.union([signetResourceSchema, requestedResourceKindSchema]);
127
+ /** What an Installation asks for: the owner it expects and the resources and operations it needs. */
128
+ export const registrationRequestSchema = z.strictObject({
129
+ ...signetTerms,
130
+ lifecycle: z.literal('ongoing'),
131
+ owner: requestedOwnerSchema.optional(),
132
+ resources: z.array(requestedResourceSchema).max(100),
133
+ });
113
134
  /** A person asks for a Signet on an integration, to be presented with `publicKey`. */
114
135
  export const signetRequestSchema = z.strictObject({
115
136
  ...signetTerms,
@@ -124,6 +145,9 @@ export const signetRequestResponseSchema = z.object({
124
145
  });
125
146
  export type SignetProposal = z.infer<typeof signetProposalSchema>;
126
147
  export type RegistrationTerms = z.infer<typeof registrationTermsSchema>;
148
+ export type RequestedOwner = z.infer<typeof requestedOwnerSchema>;
149
+ export type RequestedResource = z.input<typeof requestedResourceSchema>;
150
+ export type RegistrationRequest = z.input<typeof registrationRequestSchema>;
127
151
  export type SignetRequest = z.infer<typeof signetRequestSchema>;
128
152
  export type SignetRequestResponse = z.infer<typeof signetRequestResponseSchema>;
129
153
 
@@ -139,7 +163,7 @@ export const registerInstallationSchema = z
139
163
  message: 'A local Archive registers with its sourceId',
140
164
  });
141
165
  export const registerInstallationResponseSchema = z.object({ installationId: z.uuid() });
142
- export const requestRegistrationSchema = z.strictObject({ terms: registrationTermsSchema });
166
+ export const requestRegistrationSchema = z.strictObject({ terms: registrationRequestSchema });
143
167
  export const requestRegistrationResponseSchema = z.object({
144
168
  reviewCode: z.string(),
145
169
  expiresAt: z.iso.datetime(),
@@ -12,7 +12,7 @@ import {
12
12
  saveCollectedSignet,
13
13
  signetCredentialSchema,
14
14
  } from './client';
15
- import type { InstallationKind, OwnerRef, RegistrationTerms } from './contracts';
15
+ import type { InstallationKind, OwnerRef, RegistrationRequest } from './contracts';
16
16
  import { readPrivateJson, writePrivateJson } from './files';
17
17
  import { type InstallationSnapshot, InstallationSocket } from './installationSocket';
18
18
 
@@ -109,7 +109,7 @@ export type InstallationPairing = {
109
109
  kind: InstallationKind;
110
110
  name: string;
111
111
  sourceId?: string;
112
- terms: RegistrationTerms;
112
+ terms: RegistrationRequest;
113
113
  onReview: (review: { reviewCode: string; review: string; expiresAt: string }) => void;
114
114
  confirmOwner?: (owner: { ownerName: string | null; owner: OwnerRef | null }) => Promise<boolean>;
115
115
  /** Failures while waiting (a poll Kingdom refused, a dropped socket); waiting continues. */