@opengeni/contracts 3.1.2 → 3.2.2
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/dist/atlassian.js +5 -4
- package/dist/{chunk-OF6OKOBF.js → chunk-B2G6NXLZ.js} +8486 -9303
- package/dist/chunk-B2G6NXLZ.js.map +1 -0
- package/dist/chunk-C6HESWDB.js +1384 -0
- package/dist/chunk-C6HESWDB.js.map +1 -0
- package/dist/{chunk-6C2HKFTU.js → chunk-KLG2MBAU.js} +2 -2
- package/dist/connect.js +8 -7
- package/dist/connect.js.map +1 -1
- package/dist/connection-authority.js +6 -5
- package/dist/google-drive.js +6 -5
- package/dist/google-drive.js.map +1 -1
- package/dist/host-mcp-bindings.js +6 -5
- package/dist/host-mcp-bindings.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +191 -101
- package/dist/personal-github.js +5 -4
- package/dist/personal-github.js.map +1 -1
- package/dist/vault-provider-wire.d.ts +856 -0
- package/dist/vault-provider-wire.js +72 -0
- package/dist/vault-provider-wire.js.map +1 -0
- package/dist/vaults.d.ts +669 -0
- package/package.json +5 -1
- package/src/index.ts +1 -0
- package/src/vault-provider-wire.ts +584 -0
- package/src/vaults.ts +188 -3
- package/dist/chunk-OF6OKOBF.js.map +0 -1
- /package/dist/{chunk-6C2HKFTU.js.map → chunk-KLG2MBAU.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/contracts",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "Shared zod schemas and wire-contract types for the Opengeni API.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -198,6 +198,10 @@
|
|
|
198
198
|
"types": "./dist/workspace-integration-wire.d.ts",
|
|
199
199
|
"import": "./dist/workspace-integration-wire.js"
|
|
200
200
|
},
|
|
201
|
+
"./vault-provider-wire": {
|
|
202
|
+
"types": "./dist/vault-provider-wire.d.ts",
|
|
203
|
+
"import": "./dist/vault-provider-wire.js"
|
|
204
|
+
},
|
|
201
205
|
"./code-search": {
|
|
202
206
|
"types": "./dist/code-search.d.ts",
|
|
203
207
|
"import": "./dist/code-search.js"
|
package/src/index.ts
CHANGED
|
@@ -145,6 +145,7 @@ export * from "./agent-topology";
|
|
|
145
145
|
export * from "./work-claims";
|
|
146
146
|
export * from "./product-analytics";
|
|
147
147
|
export * from "./vaults";
|
|
148
|
+
export * from "./vault-provider-wire";
|
|
148
149
|
export * from "./secret-references";
|
|
149
150
|
export * from "./secret-resolution";
|
|
150
151
|
export * from "./secret-generator";
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `opengeni.vault.v1`: the signed remote vault-provider protocol.
|
|
3
|
+
*
|
|
4
|
+
* Opengeni POSTs one JSON request per operation to a registered endpoint,
|
|
5
|
+
* signed over the exact body with the existing `OpenGeni-Signature` scheme
|
|
6
|
+
* (`signOpenGeniPayload`). The receiver verifies the signature, then maps the
|
|
7
|
+
* trusted native authority tuple to its own tenant and user and rechecks
|
|
8
|
+
* access on every operation: a valid MAC authenticates Opengeni, it is not an
|
|
9
|
+
* authorization decision. Responses are signed with a response-only derived
|
|
10
|
+
* key (`vaultProviderWireResponseKey`) so a response can never be replayed as
|
|
11
|
+
* a request.
|
|
12
|
+
*
|
|
13
|
+
* Every shape is strict and bounded. Metadata shapes have no value fields.
|
|
14
|
+
* Mutations carry an idempotency key; Opengeni never retries a mutation
|
|
15
|
+
* automatically and reports an ambiguous outcome instead.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
import { defineSchema } from "./define-schema";
|
|
19
|
+
import {
|
|
20
|
+
VAULT_LIMITS,
|
|
21
|
+
VaultFieldKind,
|
|
22
|
+
VaultItemType,
|
|
23
|
+
VaultProviderOperations,
|
|
24
|
+
isReservedVaultFieldName,
|
|
25
|
+
isValidSecretName,
|
|
26
|
+
} from "./vaults";
|
|
27
|
+
|
|
28
|
+
export const VAULT_PROVIDER_WIRE_VERSION = "opengeni.vault.v1" as const;
|
|
29
|
+
|
|
30
|
+
/** Response signature header. Requests use the shared `OpenGeni-Signature` header. */
|
|
31
|
+
export const VAULT_PROVIDER_WIRE_RESPONSE_SIGNATURE_HEADER = "OpenGeni-Vault-Response-Signature";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The response signing key is derived from the connection secret with a fixed
|
|
35
|
+
* domain label, so request and response MACs are never interchangeable.
|
|
36
|
+
*/
|
|
37
|
+
export function vaultProviderWireResponseKey(signingSecret: string): string {
|
|
38
|
+
return `${signingSecret}\u0000${VAULT_PROVIDER_WIRE_VERSION}.response`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const VAULT_PROVIDER_WIRE_LIMITS = {
|
|
42
|
+
maxRequestBytes: 4 * 1024 * 1024,
|
|
43
|
+
maxResponseBytes: 4 * 1024 * 1024,
|
|
44
|
+
/** Sum of returned secret value bytes in one read. */
|
|
45
|
+
maxReadValueBytes: 3 * 1024 * 1024,
|
|
46
|
+
pageSizeMax: 100,
|
|
47
|
+
cursorMaxChars: 1_024,
|
|
48
|
+
snapshotMaxChars: 1_024,
|
|
49
|
+
externalIdMaxChars: 300,
|
|
50
|
+
vaultNameMaxChars: 300,
|
|
51
|
+
nativeIdMaxChars: 200,
|
|
52
|
+
readFieldsMax: VAULT_LIMITS.fieldsPerItem,
|
|
53
|
+
idempotencyKeyMaxChars: 200,
|
|
54
|
+
accountIdMaxChars: 300,
|
|
55
|
+
accountNameMaxChars: 300,
|
|
56
|
+
versionMaxChars: 300,
|
|
57
|
+
cacheEpochMaxChars: 200,
|
|
58
|
+
/** Per request deadline enforced by Opengeni's transport. */
|
|
59
|
+
defaultTimeoutMs: 15_000,
|
|
60
|
+
maxTimeoutMs: 60_000,
|
|
61
|
+
/** Accepted request age; matches `OPENGENI_SIGNATURE_TOLERANCE_SECONDS`. */
|
|
62
|
+
signatureToleranceSeconds: 300,
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
/** Mirrors `VAULT_PROVIDER_ERROR_CODES` in `@opengeni/vaults` (parity tested there). */
|
|
66
|
+
export const VAULT_PROVIDER_WIRE_ERROR_CODES = [
|
|
67
|
+
"provider_auth_invalid",
|
|
68
|
+
"provider_auth_expired",
|
|
69
|
+
"provider_rate_limited",
|
|
70
|
+
"provider_unavailable",
|
|
71
|
+
"provider_conflict",
|
|
72
|
+
"provider_not_found",
|
|
73
|
+
"provider_permission_denied",
|
|
74
|
+
"provider_locked",
|
|
75
|
+
"provider_inventory_incomplete",
|
|
76
|
+
"provider_outcome_unknown",
|
|
77
|
+
] as const;
|
|
78
|
+
export type VaultProviderWireErrorCode = (typeof VAULT_PROVIDER_WIRE_ERROR_CODES)[number];
|
|
79
|
+
|
|
80
|
+
export const VAULT_PROVIDER_WIRE_OPERATIONS = [
|
|
81
|
+
"describe",
|
|
82
|
+
"refresh",
|
|
83
|
+
"list_vaults",
|
|
84
|
+
"list_items",
|
|
85
|
+
"read_fields",
|
|
86
|
+
"write_item",
|
|
87
|
+
"archive_item",
|
|
88
|
+
] as const;
|
|
89
|
+
export type VaultProviderWireOperationName = (typeof VAULT_PROVIDER_WIRE_OPERATIONS)[number];
|
|
90
|
+
|
|
91
|
+
export type VaultProviderWireMutationName = "write_item" | "archive_item";
|
|
92
|
+
export function isVaultProviderWireMutation(
|
|
93
|
+
op: VaultProviderWireOperationName,
|
|
94
|
+
): op is VaultProviderWireMutationName {
|
|
95
|
+
return op === "write_item" || op === "archive_item";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Remote providers that use this protocol. */
|
|
99
|
+
export const VaultProviderWireProvider = /* @__PURE__ */ defineSchema(() =>
|
|
100
|
+
z.enum(["external", "bitwarden"]),
|
|
101
|
+
);
|
|
102
|
+
export type VaultProviderWireProvider = z.infer<typeof VaultProviderWireProvider>;
|
|
103
|
+
|
|
104
|
+
// oxlint-disable-next-line no-control-regex
|
|
105
|
+
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/;
|
|
106
|
+
const encoder = /* @__PURE__ */ new TextEncoder();
|
|
107
|
+
|
|
108
|
+
export function vaultProviderWireUtf8Bytes(value: string): number {
|
|
109
|
+
return encoder.encode(value).byteLength;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function boundedText(max: number) {
|
|
113
|
+
return z
|
|
114
|
+
.string()
|
|
115
|
+
.min(1)
|
|
116
|
+
.max(max)
|
|
117
|
+
.refine((value) => !CONTROL_CHARACTERS.test(value), "control characters are not allowed");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const ExternalId = /* @__PURE__ */ defineSchema(() =>
|
|
121
|
+
boundedText(VAULT_PROVIDER_WIRE_LIMITS.externalIdMaxChars),
|
|
122
|
+
);
|
|
123
|
+
const NativeId = /* @__PURE__ */ defineSchema(() =>
|
|
124
|
+
boundedText(VAULT_PROVIDER_WIRE_LIMITS.nativeIdMaxChars),
|
|
125
|
+
);
|
|
126
|
+
const ItemName = /* @__PURE__ */ defineSchema(() =>
|
|
127
|
+
z.string().refine(isValidSecretName, "invalid_secret_name"),
|
|
128
|
+
);
|
|
129
|
+
const FieldName = /* @__PURE__ */ defineSchema(() =>
|
|
130
|
+
z
|
|
131
|
+
.string()
|
|
132
|
+
.refine(
|
|
133
|
+
(name) => isValidSecretName(name) && !isReservedVaultFieldName(name),
|
|
134
|
+
"invalid_secret_name",
|
|
135
|
+
),
|
|
136
|
+
);
|
|
137
|
+
const Website = /* @__PURE__ */ defineSchema(() => boundedText(VAULT_LIMITS.websiteMaxChars));
|
|
138
|
+
const Version = /* @__PURE__ */ defineSchema(() =>
|
|
139
|
+
boundedText(VAULT_PROVIDER_WIRE_LIMITS.versionMaxChars),
|
|
140
|
+
);
|
|
141
|
+
const Cursor = /* @__PURE__ */ defineSchema(() =>
|
|
142
|
+
boundedText(VAULT_PROVIDER_WIRE_LIMITS.cursorMaxChars),
|
|
143
|
+
);
|
|
144
|
+
const Snapshot = /* @__PURE__ */ defineSchema(() =>
|
|
145
|
+
boundedText(VAULT_PROVIDER_WIRE_LIMITS.snapshotMaxChars),
|
|
146
|
+
);
|
|
147
|
+
const IdempotencyKey = /* @__PURE__ */ defineSchema(() =>
|
|
148
|
+
boundedText(VAULT_PROVIDER_WIRE_LIMITS.idempotencyKeyMaxChars),
|
|
149
|
+
);
|
|
150
|
+
const CacheEpoch = /* @__PURE__ */ defineSchema(() =>
|
|
151
|
+
boundedText(VAULT_PROVIDER_WIRE_LIMITS.cacheEpochMaxChars).nullable(),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
function uniqueBy<T>(values: readonly T[], key: (value: T) => string): boolean {
|
|
155
|
+
const seen = new Set<string>();
|
|
156
|
+
for (const value of values) {
|
|
157
|
+
const k = key(value);
|
|
158
|
+
if (seen.has(k)) return false;
|
|
159
|
+
seen.add(k);
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Case-insensitive name identity used for field-name uniqueness. */
|
|
165
|
+
export function vaultProviderWireNameKey(name: string): string {
|
|
166
|
+
return name.normalize("NFC").trim().toLowerCase();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The trusted native caller. Opengeni fills it from the authorized registry,
|
|
171
|
+
* never from model or body-supplied arguments.
|
|
172
|
+
*/
|
|
173
|
+
export const VaultProviderWireAuthority = /* @__PURE__ */ defineSchema(() =>
|
|
174
|
+
z
|
|
175
|
+
.object({
|
|
176
|
+
accountId: NativeId,
|
|
177
|
+
workspaceId: NativeId,
|
|
178
|
+
/** Null for workspace-level background work with no acting human. */
|
|
179
|
+
subjectId: NativeId.nullable(),
|
|
180
|
+
/** Workspace that owns the encrypted connection (may differ from the caller). */
|
|
181
|
+
connectionWorkspaceId: NativeId,
|
|
182
|
+
sessionId: NativeId.optional(),
|
|
183
|
+
turnId: NativeId.optional(),
|
|
184
|
+
attemptId: NativeId.optional(),
|
|
185
|
+
siteId: NativeId.optional(),
|
|
186
|
+
})
|
|
187
|
+
.strict(),
|
|
188
|
+
);
|
|
189
|
+
export type VaultProviderWireAuthority = z.infer<typeof VaultProviderWireAuthority>;
|
|
190
|
+
|
|
191
|
+
export const VaultProviderWirePage = /* @__PURE__ */ defineSchema(() =>
|
|
192
|
+
z
|
|
193
|
+
.object({
|
|
194
|
+
limit: z.number().int().min(1).max(VAULT_PROVIDER_WIRE_LIMITS.pageSizeMax),
|
|
195
|
+
cursor: Cursor.optional(),
|
|
196
|
+
snapshot: Snapshot.optional(),
|
|
197
|
+
})
|
|
198
|
+
.strict(),
|
|
199
|
+
);
|
|
200
|
+
export type VaultProviderWirePage = z.infer<typeof VaultProviderWirePage>;
|
|
201
|
+
|
|
202
|
+
export const VaultProviderWireWriteField = /* @__PURE__ */ defineSchema(() =>
|
|
203
|
+
z
|
|
204
|
+
.object({
|
|
205
|
+
externalFieldId: ExternalId.optional(),
|
|
206
|
+
name: FieldName,
|
|
207
|
+
kind: VaultFieldKind,
|
|
208
|
+
/** Omitted on an update: keep the current value. */
|
|
209
|
+
value: z
|
|
210
|
+
.string()
|
|
211
|
+
.refine(
|
|
212
|
+
(value) => vaultProviderWireUtf8Bytes(value) <= VAULT_LIMITS.textValueMaxBytes,
|
|
213
|
+
"secret_value_too_large",
|
|
214
|
+
)
|
|
215
|
+
.optional(),
|
|
216
|
+
remove: z.literal(true).optional(),
|
|
217
|
+
})
|
|
218
|
+
.strict()
|
|
219
|
+
.refine((field) => !(field.remove && field.value !== undefined), "remove carries no value")
|
|
220
|
+
.refine((field) => !(field.remove && !field.externalFieldId), "remove needs a field id")
|
|
221
|
+
.refine(
|
|
222
|
+
(field) => !(field.kind === "file" && field.value !== undefined),
|
|
223
|
+
"file writes are not supported",
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
export type VaultProviderWireWriteField = z.infer<typeof VaultProviderWireWriteField>;
|
|
227
|
+
|
|
228
|
+
export const VaultProviderWireWriteItem = /* @__PURE__ */ defineSchema(() =>
|
|
229
|
+
z
|
|
230
|
+
.object({
|
|
231
|
+
externalItemId: ExternalId.optional(),
|
|
232
|
+
expectedVersion: Version.nullable().optional(),
|
|
233
|
+
name: ItemName,
|
|
234
|
+
type: VaultItemType.refine((type) => type !== "passkey", "passkeys are hosted only"),
|
|
235
|
+
websites: z.array(Website).max(VAULT_LIMITS.websitesPerItem),
|
|
236
|
+
fields: z.array(VaultProviderWireWriteField).max(VAULT_LIMITS.fieldsPerItem),
|
|
237
|
+
})
|
|
238
|
+
.strict()
|
|
239
|
+
.refine(
|
|
240
|
+
(item) =>
|
|
241
|
+
uniqueBy(
|
|
242
|
+
item.fields.filter((field) => !field.remove),
|
|
243
|
+
(field) => vaultProviderWireNameKey(field.name),
|
|
244
|
+
) &&
|
|
245
|
+
uniqueBy(
|
|
246
|
+
item.fields.filter((field) => field.externalFieldId !== undefined),
|
|
247
|
+
(field) => field.externalFieldId!,
|
|
248
|
+
),
|
|
249
|
+
"duplicate fields",
|
|
250
|
+
),
|
|
251
|
+
);
|
|
252
|
+
export type VaultProviderWireWriteItem = z.infer<typeof VaultProviderWireWriteItem>;
|
|
253
|
+
|
|
254
|
+
const ReadContext = {
|
|
255
|
+
/** Recheck upstream; never bypasses authorization or upstream rate limits. */
|
|
256
|
+
forceRefresh: z.boolean(),
|
|
257
|
+
/** Opengeni's mirror freshness epoch, for receiver-side cache invalidation. */
|
|
258
|
+
cacheEpoch: CacheEpoch,
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export const VaultProviderWireOperation = /* @__PURE__ */ defineSchema(() =>
|
|
262
|
+
z.discriminatedUnion("op", [
|
|
263
|
+
z.object({ op: z.literal("describe") }).strict(),
|
|
264
|
+
z.object({ op: z.literal("refresh") }).strict(),
|
|
265
|
+
z
|
|
266
|
+
.object({ op: z.literal("list_vaults"), page: VaultProviderWirePage, ...ReadContext })
|
|
267
|
+
.strict(),
|
|
268
|
+
z
|
|
269
|
+
.object({
|
|
270
|
+
op: z.literal("list_items"),
|
|
271
|
+
externalVaultId: ExternalId,
|
|
272
|
+
page: VaultProviderWirePage,
|
|
273
|
+
...ReadContext,
|
|
274
|
+
})
|
|
275
|
+
.strict(),
|
|
276
|
+
z
|
|
277
|
+
.object({
|
|
278
|
+
op: z.literal("read_fields"),
|
|
279
|
+
externalVaultId: ExternalId,
|
|
280
|
+
externalItemId: ExternalId,
|
|
281
|
+
externalFieldIds: z
|
|
282
|
+
.array(ExternalId)
|
|
283
|
+
.min(1)
|
|
284
|
+
.max(VAULT_PROVIDER_WIRE_LIMITS.readFieldsMax)
|
|
285
|
+
.refine((ids) => new Set(ids).size === ids.length, "duplicate field ids"),
|
|
286
|
+
...ReadContext,
|
|
287
|
+
})
|
|
288
|
+
.strict(),
|
|
289
|
+
z
|
|
290
|
+
.object({
|
|
291
|
+
op: z.literal("write_item"),
|
|
292
|
+
externalVaultId: ExternalId,
|
|
293
|
+
item: VaultProviderWireWriteItem,
|
|
294
|
+
idempotencyKey: IdempotencyKey,
|
|
295
|
+
})
|
|
296
|
+
.strict(),
|
|
297
|
+
z
|
|
298
|
+
.object({
|
|
299
|
+
op: z.literal("archive_item"),
|
|
300
|
+
externalVaultId: ExternalId,
|
|
301
|
+
externalItemId: ExternalId,
|
|
302
|
+
idempotencyKey: IdempotencyKey,
|
|
303
|
+
})
|
|
304
|
+
.strict(),
|
|
305
|
+
]),
|
|
306
|
+
);
|
|
307
|
+
export type VaultProviderWireOperation = z.infer<typeof VaultProviderWireOperation>;
|
|
308
|
+
export type VaultProviderWireOperationOf<Op extends VaultProviderWireOperationName> = Extract<
|
|
309
|
+
VaultProviderWireOperation,
|
|
310
|
+
{ op: Op }
|
|
311
|
+
>;
|
|
312
|
+
|
|
313
|
+
export const VaultProviderWireRequest = /* @__PURE__ */ defineSchema(() =>
|
|
314
|
+
z
|
|
315
|
+
.object({
|
|
316
|
+
version: z.literal(VAULT_PROVIDER_WIRE_VERSION),
|
|
317
|
+
type: z.literal("request"),
|
|
318
|
+
requestId: z.string().uuid(),
|
|
319
|
+
sentAt: z.string().datetime(),
|
|
320
|
+
provider: VaultProviderWireProvider,
|
|
321
|
+
connectionId: z.string().uuid(),
|
|
322
|
+
authority: VaultProviderWireAuthority,
|
|
323
|
+
operation: VaultProviderWireOperation,
|
|
324
|
+
})
|
|
325
|
+
.strict(),
|
|
326
|
+
);
|
|
327
|
+
export type VaultProviderWireRequest = z.infer<typeof VaultProviderWireRequest>;
|
|
328
|
+
|
|
329
|
+
export const VaultProviderWireErrorCode = /* @__PURE__ */ defineSchema(() =>
|
|
330
|
+
z.enum(VAULT_PROVIDER_WIRE_ERROR_CODES),
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
/** Receiver freshness. Opengeni adds its own `lastCheckedAt`. */
|
|
334
|
+
export const VaultProviderWireFreshness = /* @__PURE__ */ defineSchema(() =>
|
|
335
|
+
z
|
|
336
|
+
.object({
|
|
337
|
+
state: z.enum(["current", "stale", "refreshing", "unavailable"]),
|
|
338
|
+
/** When the receiver last synchronized with its upstream store. */
|
|
339
|
+
sourceSyncedAt: z.string().datetime().nullable(),
|
|
340
|
+
source: z.enum(["live", "cache", "unknown"]),
|
|
341
|
+
errorCode: VaultProviderWireErrorCode.nullable(),
|
|
342
|
+
retryAt: z.string().datetime().nullable(),
|
|
343
|
+
})
|
|
344
|
+
.strict(),
|
|
345
|
+
);
|
|
346
|
+
export type VaultProviderWireFreshness = z.infer<typeof VaultProviderWireFreshness>;
|
|
347
|
+
|
|
348
|
+
export const VaultProviderWireVault = /* @__PURE__ */ defineSchema(() =>
|
|
349
|
+
z
|
|
350
|
+
.object({
|
|
351
|
+
externalVaultId: ExternalId,
|
|
352
|
+
name: boundedText(VAULT_PROVIDER_WIRE_LIMITS.vaultNameMaxChars),
|
|
353
|
+
contentVersion: Version.nullable(),
|
|
354
|
+
itemCount: z.number().int().nonnegative().nullable(),
|
|
355
|
+
writeAccess: z.enum(["read_only", "read_write", "unknown"]),
|
|
356
|
+
/** Narrows the connection operations for this vault; never widens them. */
|
|
357
|
+
operations: VaultProviderOperations.optional(),
|
|
358
|
+
})
|
|
359
|
+
.strict(),
|
|
360
|
+
);
|
|
361
|
+
export type VaultProviderWireVault = z.infer<typeof VaultProviderWireVault>;
|
|
362
|
+
|
|
363
|
+
export const VaultProviderWireFieldMeta = /* @__PURE__ */ defineSchema(() =>
|
|
364
|
+
z.object({ externalFieldId: ExternalId, name: FieldName, kind: VaultFieldKind }).strict(),
|
|
365
|
+
);
|
|
366
|
+
export type VaultProviderWireFieldMeta = z.infer<typeof VaultProviderWireFieldMeta>;
|
|
367
|
+
|
|
368
|
+
/** Item metadata. There is deliberately no value field. */
|
|
369
|
+
export const VaultProviderWireItemMeta = /* @__PURE__ */ defineSchema(() =>
|
|
370
|
+
z
|
|
371
|
+
.object({
|
|
372
|
+
externalItemId: ExternalId,
|
|
373
|
+
name: ItemName,
|
|
374
|
+
type: VaultItemType,
|
|
375
|
+
websites: z.array(Website).max(VAULT_LIMITS.websitesPerItem),
|
|
376
|
+
fields: z
|
|
377
|
+
.array(VaultProviderWireFieldMeta)
|
|
378
|
+
.max(VAULT_LIMITS.fieldsPerItem)
|
|
379
|
+
.refine(
|
|
380
|
+
(fields) =>
|
|
381
|
+
uniqueBy(fields, (field) => field.externalFieldId) &&
|
|
382
|
+
uniqueBy(fields, (field) => vaultProviderWireNameKey(field.name)),
|
|
383
|
+
"duplicate fields",
|
|
384
|
+
),
|
|
385
|
+
version: Version.nullable(),
|
|
386
|
+
updatedAt: z.string().datetime().nullable(),
|
|
387
|
+
})
|
|
388
|
+
.strict(),
|
|
389
|
+
);
|
|
390
|
+
export type VaultProviderWireItemMeta = z.infer<typeof VaultProviderWireItemMeta>;
|
|
391
|
+
|
|
392
|
+
const BASE64 = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
393
|
+
export const VaultProviderWireFieldValue = /* @__PURE__ */ defineSchema(() =>
|
|
394
|
+
z
|
|
395
|
+
.object({
|
|
396
|
+
externalFieldId: ExternalId,
|
|
397
|
+
value: z.discriminatedUnion("type", [
|
|
398
|
+
z
|
|
399
|
+
.object({
|
|
400
|
+
type: z.literal("text"),
|
|
401
|
+
value: z
|
|
402
|
+
.string()
|
|
403
|
+
.refine(
|
|
404
|
+
(value) => vaultProviderWireUtf8Bytes(value) <= VAULT_LIMITS.textValueMaxBytes,
|
|
405
|
+
),
|
|
406
|
+
})
|
|
407
|
+
.strict(),
|
|
408
|
+
/** An `otpauth://` URI or Base32 seed; Opengeni computes codes at use. */
|
|
409
|
+
z
|
|
410
|
+
.object({ type: z.literal("totp"), value: boundedText(VAULT_LIMITS.websiteMaxChars) })
|
|
411
|
+
.strict(),
|
|
412
|
+
z
|
|
413
|
+
.object({
|
|
414
|
+
type: z.literal("bytes"),
|
|
415
|
+
base64: z
|
|
416
|
+
.string()
|
|
417
|
+
.max(Math.ceil(VAULT_LIMITS.fileValueMaxBytes / 3) * 4)
|
|
418
|
+
.regex(BASE64),
|
|
419
|
+
})
|
|
420
|
+
.strict(),
|
|
421
|
+
]),
|
|
422
|
+
})
|
|
423
|
+
.strict(),
|
|
424
|
+
);
|
|
425
|
+
export type VaultProviderWireFieldValue = z.infer<typeof VaultProviderWireFieldValue>;
|
|
426
|
+
|
|
427
|
+
function valueBytes(field: VaultProviderWireFieldValue): number {
|
|
428
|
+
if (field.value.type === "bytes") return Math.floor((field.value.base64.length * 3) / 4);
|
|
429
|
+
return vaultProviderWireUtf8Bytes(field.value.value);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export const VaultProviderWireDescribeResult = /* @__PURE__ */ defineSchema(() =>
|
|
433
|
+
z
|
|
434
|
+
.object({
|
|
435
|
+
/** Stable upstream account or bridge identity; reconnect rejects a change. */
|
|
436
|
+
accountId: boundedText(VAULT_PROVIDER_WIRE_LIMITS.accountIdMaxChars),
|
|
437
|
+
accountName: boundedText(VAULT_PROVIDER_WIRE_LIMITS.accountNameMaxChars).nullable(),
|
|
438
|
+
operations: VaultProviderOperations,
|
|
439
|
+
capabilities: z.object({ totp: z.boolean(), files: z.boolean() }).strict(),
|
|
440
|
+
/** One fixed inventory per connection, independent of the acting subject. */
|
|
441
|
+
inventoryScope: z.literal("connection"),
|
|
442
|
+
})
|
|
443
|
+
.strict(),
|
|
444
|
+
);
|
|
445
|
+
export type VaultProviderWireDescribeResult = z.infer<typeof VaultProviderWireDescribeResult>;
|
|
446
|
+
|
|
447
|
+
function pageOf<T extends z.ZodType>(item: T) {
|
|
448
|
+
return z
|
|
449
|
+
.object({
|
|
450
|
+
items: z.array(item).max(VAULT_PROVIDER_WIRE_LIMITS.pageSizeMax),
|
|
451
|
+
nextCursor: Cursor.nullable(),
|
|
452
|
+
/** Null only when the receiver cannot supply snapshot consistency. */
|
|
453
|
+
snapshot: Snapshot.nullable(),
|
|
454
|
+
})
|
|
455
|
+
.strict();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export const VaultProviderWireListVaultsResult = /* @__PURE__ */ defineSchema(() =>
|
|
459
|
+
pageOf(VaultProviderWireVault),
|
|
460
|
+
);
|
|
461
|
+
export const VaultProviderWireListItemsResult = /* @__PURE__ */ defineSchema(() =>
|
|
462
|
+
pageOf(VaultProviderWireItemMeta),
|
|
463
|
+
);
|
|
464
|
+
export const VaultProviderWireReadFieldsResult = /* @__PURE__ */ defineSchema(() =>
|
|
465
|
+
z
|
|
466
|
+
.object({
|
|
467
|
+
fields: z
|
|
468
|
+
.array(VaultProviderWireFieldValue)
|
|
469
|
+
.max(VAULT_PROVIDER_WIRE_LIMITS.readFieldsMax)
|
|
470
|
+
.refine((fields) => uniqueBy(fields, (field) => field.externalFieldId), "duplicate fields")
|
|
471
|
+
.refine(
|
|
472
|
+
(fields) =>
|
|
473
|
+
fields.reduce((total, field) => total + valueBytes(field), 0) <=
|
|
474
|
+
VAULT_PROVIDER_WIRE_LIMITS.maxReadValueBytes,
|
|
475
|
+
"secret values too large",
|
|
476
|
+
),
|
|
477
|
+
})
|
|
478
|
+
.strict(),
|
|
479
|
+
);
|
|
480
|
+
export const VaultProviderWireRefreshResult = /* @__PURE__ */ defineSchema(() =>
|
|
481
|
+
z.object({ refreshed: z.literal(true) }).strict(),
|
|
482
|
+
);
|
|
483
|
+
export const VaultProviderWireWriteItemResult = /* @__PURE__ */ defineSchema(() =>
|
|
484
|
+
z.object({ item: VaultProviderWireItemMeta }).strict(),
|
|
485
|
+
);
|
|
486
|
+
export const VaultProviderWireArchiveItemResult = /* @__PURE__ */ defineSchema(() =>
|
|
487
|
+
z.object({ archived: z.literal(true) }).strict(),
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
export const VaultProviderWireResults = {
|
|
491
|
+
describe: VaultProviderWireDescribeResult,
|
|
492
|
+
refresh: VaultProviderWireRefreshResult,
|
|
493
|
+
list_vaults: VaultProviderWireListVaultsResult,
|
|
494
|
+
list_items: VaultProviderWireListItemsResult,
|
|
495
|
+
read_fields: VaultProviderWireReadFieldsResult,
|
|
496
|
+
write_item: VaultProviderWireWriteItemResult,
|
|
497
|
+
archive_item: VaultProviderWireArchiveItemResult,
|
|
498
|
+
} as const;
|
|
499
|
+
|
|
500
|
+
export type VaultProviderWireResult<Op extends VaultProviderWireOperationName> = z.infer<
|
|
501
|
+
(typeof VaultProviderWireResults)[Op]
|
|
502
|
+
>;
|
|
503
|
+
|
|
504
|
+
export const VaultProviderWireError = /* @__PURE__ */ defineSchema(() =>
|
|
505
|
+
z
|
|
506
|
+
.object({
|
|
507
|
+
code: VaultProviderWireErrorCode,
|
|
508
|
+
retryAfterMs: z.number().int().min(0).max(86_400_000).nullable(),
|
|
509
|
+
})
|
|
510
|
+
.strict(),
|
|
511
|
+
);
|
|
512
|
+
export type VaultProviderWireError = z.infer<typeof VaultProviderWireError>;
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Response envelope. `result` is validated separately against
|
|
516
|
+
* `VaultProviderWireResults[op]` for the operation that was requested.
|
|
517
|
+
*/
|
|
518
|
+
export const VaultProviderWireResponseEnvelope = /* @__PURE__ */ defineSchema(() =>
|
|
519
|
+
z.discriminatedUnion("ok", [
|
|
520
|
+
z
|
|
521
|
+
.object({
|
|
522
|
+
version: z.literal(VAULT_PROVIDER_WIRE_VERSION),
|
|
523
|
+
type: z.literal("response"),
|
|
524
|
+
requestId: z.string().uuid(),
|
|
525
|
+
ok: z.literal(true),
|
|
526
|
+
op: z.enum(VAULT_PROVIDER_WIRE_OPERATIONS),
|
|
527
|
+
result: z.unknown(),
|
|
528
|
+
freshness: VaultProviderWireFreshness.nullable(),
|
|
529
|
+
})
|
|
530
|
+
.strict(),
|
|
531
|
+
z
|
|
532
|
+
.object({
|
|
533
|
+
version: z.literal(VAULT_PROVIDER_WIRE_VERSION),
|
|
534
|
+
type: z.literal("response"),
|
|
535
|
+
requestId: z.string().uuid(),
|
|
536
|
+
ok: z.literal(false),
|
|
537
|
+
op: z.enum(VAULT_PROVIDER_WIRE_OPERATIONS),
|
|
538
|
+
error: VaultProviderWireError,
|
|
539
|
+
freshness: VaultProviderWireFreshness.nullable(),
|
|
540
|
+
})
|
|
541
|
+
.strict(),
|
|
542
|
+
]),
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
export type VaultProviderWireSuccess<
|
|
546
|
+
Op extends VaultProviderWireOperationName = VaultProviderWireOperationName,
|
|
547
|
+
> = {
|
|
548
|
+
[K in Op]: {
|
|
549
|
+
version: typeof VAULT_PROVIDER_WIRE_VERSION;
|
|
550
|
+
type: "response";
|
|
551
|
+
requestId: string;
|
|
552
|
+
ok: true;
|
|
553
|
+
op: K;
|
|
554
|
+
result: VaultProviderWireResult<K>;
|
|
555
|
+
freshness: VaultProviderWireFreshness | null;
|
|
556
|
+
};
|
|
557
|
+
}[Op];
|
|
558
|
+
|
|
559
|
+
export type VaultProviderWireFailure = {
|
|
560
|
+
version: typeof VAULT_PROVIDER_WIRE_VERSION;
|
|
561
|
+
type: "response";
|
|
562
|
+
requestId: string;
|
|
563
|
+
ok: false;
|
|
564
|
+
op: VaultProviderWireOperationName;
|
|
565
|
+
error: VaultProviderWireError;
|
|
566
|
+
freshness: VaultProviderWireFreshness | null;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
export type VaultProviderWireResponse = VaultProviderWireSuccess | VaultProviderWireFailure;
|
|
570
|
+
|
|
571
|
+
/** Parse one response envelope and its operation-specific result. */
|
|
572
|
+
export function parseVaultProviderWireResponse<Op extends VaultProviderWireOperationName>(
|
|
573
|
+
value: unknown,
|
|
574
|
+
expected: { requestId: string; op: Op },
|
|
575
|
+
): VaultProviderWireSuccess<Op> | VaultProviderWireFailure | null {
|
|
576
|
+
const envelope = VaultProviderWireResponseEnvelope.safeParse(value);
|
|
577
|
+
if (!envelope.success) return null;
|
|
578
|
+
const response = envelope.data;
|
|
579
|
+
if (response.requestId !== expected.requestId || response.op !== expected.op) return null;
|
|
580
|
+
if (!response.ok) return response as VaultProviderWireFailure;
|
|
581
|
+
const result = VaultProviderWireResults[expected.op].safeParse(response.result);
|
|
582
|
+
if (!result.success) return null;
|
|
583
|
+
return { ...response, op: expected.op, result: result.data } as VaultProviderWireSuccess<Op>;
|
|
584
|
+
}
|