@opengeni/vaults 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/README.md CHANGED
@@ -12,6 +12,12 @@ This package owns:
12
12
  and the in-memory fake), plus `VaultProviderError` and its codes;
13
13
  - `createVaultProviderRegistry`, which picks the provider for a vault;
14
14
  - `createProviderCallBudget`, the per-attempt provider call budget;
15
+ - `createRemoteVaultProvider`, the adapter for vaults served over Opengeni's
16
+ signed remote protocol (`opengeni.vault.v1`): an embedder's receiver built
17
+ with `@opengeni/sdk/external-vault-receiver`, or the customer-run
18
+ `@opengeni/vaults-bitwarden-bridge`. The caller supplies the bounded HTTP
19
+ transport (`createFetchRemoteVaultTransport` adapts a pinned fetch) and the
20
+ trusted `ctx.authority`;
15
21
  - the shell splice contract (`spliceShellCommand`, `dialectFromShell`) used
16
22
  by `exec_command` and `run_on` to turn references into per-command
17
23
  environment variables;
@@ -20,3 +26,11 @@ This package owns:
20
26
 
21
27
  Providers never see principals: access is decided before a provider is called.
22
28
  Provider errors always map to a `VaultProviderErrorCode`; nothing is swallowed.
29
+
30
+ Listings are read with `listProviderVaults` and `listProviderItems` (bounded
31
+ enumeration for discovery), or with `readProviderVaultInventory` and
32
+ `readProviderItemInventory`, which also report whether the listing is
33
+ `consistent`: one response, or every page under one snapshot. Only a
34
+ consistent inventory may remove items from a mirror. A multi-page listing
35
+ without a snapshot proves only what it contains, so a vault served without
36
+ snapshots is mirrored only while its listing fits in one page.
@@ -11,7 +11,10 @@ var VAULT_PROVIDER_ERROR_CODES = [
11
11
  * write into a 1Password vault the service account can only read). The vault
12
12
  * service reports `vault_read_only` for writes, `vault_permission_denied` otherwise.
13
13
  */
14
- "provider_permission_denied"
14
+ "provider_permission_denied",
15
+ "provider_locked",
16
+ "provider_inventory_incomplete",
17
+ "provider_outcome_unknown"
15
18
  ];
16
19
  var VaultProviderError = class extends Error {
17
20
  code;
@@ -33,6 +36,10 @@ function createVaultProviderRegistry(factories) {
33
36
  return factories.onepassword(vault.providerConnectionId);
34
37
  }
35
38
  if (vault.source === "host" && factories.host) return factories.host();
39
+ if (vault.source !== "opengeni" && vault.source !== "host" && vault.source !== "onepassword") {
40
+ const factory = factories[vault.source];
41
+ if (factory) return factory(vault.providerConnectionId);
42
+ }
36
43
  throw new VaultProviderError(
37
44
  "provider_unavailable",
38
45
  vault.source === "opengeni" ? "hosted vaults are read from the database, not a provider" : `no ${vault.source} vault provider is configured`
@@ -65,4 +72,4 @@ export {
65
72
  EMPTY_VAULT_PROVIDER_REGISTRY,
66
73
  createProviderCallBudget
67
74
  };
68
- //# sourceMappingURL=chunk-7S23LHTK.js.map
75
+ //# sourceMappingURL=chunk-BD43JBPC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.ts"],"sourcesContent":["import type {\n ConnectionCredentialsPort,\n SecretMaterial,\n VaultFieldKind,\n VaultItemType,\n VaultSource,\n VaultProviderOperations,\n VaultProviderFreshness,\n} from \"@opengeni/contracts\";\n\nexport interface VaultProviderCapabilities {\n write: boolean;\n createVault: boolean;\n totp: boolean;\n files: boolean;\n groupGrants: boolean;\n /** Passkeys live only in Opengeni-hosted vaults (D20). */\n passkeys: false;\n /** Additive: older in-process providers retain the legacy capability flags. */\n operations?: VaultProviderOperations;\n}\nexport interface ProviderVault {\n externalVaultId: string;\n name: string;\n contentVersion: string | null;\n itemCount: number | null;\n writeAccess?: \"read_only\" | \"read_write\" | \"unknown\";\n operations?: VaultProviderOperations;\n}\nexport interface ProviderFieldMeta {\n externalFieldId: string;\n name: string;\n kind: VaultFieldKind;\n}\nexport interface ProviderItemMeta {\n externalItemId: string;\n name: string;\n type: VaultItemType;\n websites: string[];\n fields: ProviderFieldMeta[];\n version: string | null;\n updatedAt: string | null;\n}\nexport interface ProviderFieldValue {\n externalFieldId: string;\n material: SecretMaterial;\n}\nexport interface ProviderWriteField {\n externalFieldId?: string;\n name: string;\n kind: VaultFieldKind;\n value?: string;\n remove?: true;\n}\nexport interface ProviderWriteItem {\n externalItemId?: string;\n expectedVersion?: string | null;\n name: string;\n type: VaultItemType;\n websites: string[];\n fields: ProviderWriteField[];\n}\nexport type ProviderCallOperation = \"list\" | \"read\" | \"write\" | \"admin\";\nexport interface ProviderCallBudget {\n /** Throws VaultProviderError(\"provider_rate_limited\") when exhausted. */\n take(op: ProviderCallOperation): void;\n}\nexport interface VaultProviderContext {\n connectionId: string | null;\n budget: ProviderCallBudget;\n signal?: AbortSignal;\n /** Trusted native scope, supplied by the authorized registry, never model arguments. */\n authority?: VaultProviderAuthority;\n /** Last successful mirror-check identity; changes invalidate attempt value caches. */\n cacheEpoch?: string | null;\n /** Recheck upstream, without retrying a mutation or bypassing provider throttling. */\n forceRefresh?: boolean;\n /** Stable caller operation identity when available. No automatic replay is implied. */\n idempotencyKey?: string;\n /** Runtime-only observation; never supplied by an agent or persisted with values. */\n onFreshness?: (freshness: VaultProviderFreshness) => void;\n}\n\nexport interface VaultProviderAuthority {\n accountId: string;\n workspaceId: string;\n subjectId: string | null;\n /** Where the encrypted connection is owned, not a replacement for the calling workspace. */\n connectionWorkspaceId: string;\n sessionId?: string;\n turnId?: string;\n attemptId?: string;\n siteId?: string;\n}\n\nexport interface ProviderPageRequest {\n cursor?: string;\n limit: number;\n snapshot?: string;\n}\n\nexport interface ProviderPage<T> {\n items: T[];\n nextCursor: string | null;\n /**\n * Null only when upstream cannot supply snapshot consistency. A single page\n * needs none, but a multi-page listing without a snapshot proves only what\n * it contains: Opengeni lists it for discovery, yet never replaces a mirror\n * from it (that would drop items skipped while the pages were read). Every\n * page of one listing carries the same snapshot.\n */\n snapshot: string | null;\n freshness?: VaultProviderFreshness;\n}\n\nexport interface VaultProvider {\n readonly id: Exclude<VaultSource, \"opengeni\"> | \"fake\";\n capabilities(): VaultProviderCapabilities;\n validate(ctx: VaultProviderContext): Promise<{\n accountName: string | null;\n /** Stable identity, when supplied by the upstream account or bridge. */\n accountId?: string;\n }>;\n /**\n * One complete response, trusted as a consistent inventory. An adapter\n * that pages upstream should implement `listVaultPage`/`listItemPage`, so\n * page consistency is checked, rather than join pages here.\n */\n listVaults(ctx: VaultProviderContext): Promise<ProviderVault[]>;\n listItems(ctx: VaultProviderContext, externalVaultId: string): Promise<ProviderItemMeta[]>;\n listVaultPage?(\n ctx: VaultProviderContext,\n page: ProviderPageRequest,\n ): Promise<ProviderPage<ProviderVault>>;\n listItemPage?(\n ctx: VaultProviderContext,\n externalVaultId: string,\n page: ProviderPageRequest,\n ): Promise<ProviderPage<ProviderItemMeta>>;\n readFields(\n ctx: VaultProviderContext,\n externalVaultId: string,\n externalItemId: string,\n externalFieldIds: readonly string[],\n ): Promise<ProviderFieldValue[]>;\n writeItem(\n ctx: VaultProviderContext,\n externalVaultId: string,\n item: ProviderWriteItem,\n ): Promise<ProviderItemMeta>;\n archiveItem(\n ctx: VaultProviderContext,\n externalVaultId: string,\n externalItemId: string,\n ): Promise<void>;\n createVault?(\n ctx: VaultProviderContext,\n input: { name: string; description?: string; allowAdminsAccess: boolean; groupId?: string },\n ): Promise<ProviderVault>;\n}\n\nexport const VAULT_PROVIDER_ERROR_CODES = [\n \"provider_auth_invalid\",\n \"provider_auth_expired\",\n \"provider_rate_limited\",\n \"provider_unavailable\",\n \"provider_conflict\",\n \"provider_not_found\",\n /**\n * The provider refused the operation for this credential (for example a\n * write into a 1Password vault the service account can only read). The vault\n * service reports `vault_read_only` for writes, `vault_permission_denied` otherwise.\n */\n \"provider_permission_denied\",\n \"provider_locked\",\n \"provider_inventory_incomplete\",\n \"provider_outcome_unknown\",\n] as const;\nexport type VaultProviderErrorCode = (typeof VAULT_PROVIDER_ERROR_CODES)[number];\n\nexport class VaultProviderError extends Error {\n readonly code: VaultProviderErrorCode;\n readonly retryAfterMs: number | undefined;\n constructor(code: VaultProviderErrorCode, message: string, retryAfterMs?: number) {\n super(message);\n this.name = \"VaultProviderError\";\n this.code = code;\n this.retryAfterMs = retryAfterMs;\n }\n}\n\nexport function isVaultProviderError(error: unknown): error is VaultProviderError {\n return error instanceof VaultProviderError;\n}\n\n/**\n * The embedder's connection-credential port with its password-manager leg\n * (design §10.9, OP-6). `@opengeni/contracts` cannot name `VaultProvider`\n * (this package depends on it), so the leg is added here; core and worker\n * dependencies use this type. A host provider is linked as a vault with\n * `source: \"host\"` and resolved through `createVaultProviderRegistry({ host })`.\n */\nexport type VaultConnectionCredentialsPort = ConnectionCredentialsPort & {\n vaultProvider?: VaultProvider | null;\n};\n\nexport interface VaultProviderRegistry {\n /** onepassword: loads the connection credential and the SDK lazily; host: the embedder port; fake: tests/dev. */\n forVault(vault: {\n source: VaultSource;\n providerConnectionId: string | null;\n }): Promise<VaultProvider>;\n}\n\n/**\n * A registry over fixed provider factories. Missing providers raise\n * `provider_unavailable`; `opengeni` (hosted) vaults never reach a provider.\n */\nexport function createVaultProviderRegistry(factories: {\n onepassword?: (connectionId: string | null) => Promise<VaultProvider>;\n host?: () => Promise<VaultProvider>;\n external?: (connectionId: string | null) => Promise<VaultProvider>;\n protonpass?: (connectionId: string | null) => Promise<VaultProvider>;\n keeper?: (connectionId: string | null) => Promise<VaultProvider>;\n bitwarden?: (connectionId: string | null) => Promise<VaultProvider>;\n}): VaultProviderRegistry {\n return {\n async forVault(vault) {\n if (vault.source === \"onepassword\" && factories.onepassword) {\n return factories.onepassword(vault.providerConnectionId);\n }\n if (vault.source === \"host\" && factories.host) return factories.host();\n if (\n vault.source !== \"opengeni\" &&\n vault.source !== \"host\" &&\n vault.source !== \"onepassword\"\n ) {\n const factory = factories[vault.source];\n if (factory) return factory(vault.providerConnectionId);\n }\n throw new VaultProviderError(\n \"provider_unavailable\",\n vault.source === \"opengeni\"\n ? \"hosted vaults are read from the database, not a provider\"\n : `no ${vault.source} vault provider is configured`,\n );\n },\n };\n}\n\n/** A registry with no providers: every provider vault is unavailable. */\nexport const EMPTY_VAULT_PROVIDER_REGISTRY: VaultProviderRegistry = createVaultProviderRegistry({});\n\n/**\n * A simple counting budget: at most `limit` calls of any kind (Q-18 uses 50\n * reads per attempt). Exhaustion raises `provider_rate_limited`.\n */\nexport function createProviderCallBudget(limit: number): ProviderCallBudget & { used(): number } {\n let used = 0;\n return {\n take(op) {\n if (used >= limit) {\n throw new VaultProviderError(\n \"provider_rate_limited\",\n `this attempt used its ${limit} vault provider calls (last: ${op})`,\n );\n }\n used += 1;\n },\n used: () => used,\n };\n}\n"],"mappings":";AAiKO,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EACnC;AAAA,EACA;AAAA,EACT,YAAY,MAA8B,SAAiB,cAAuB;AAChF,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,eAAe;AAAA,EACtB;AACF;AAEO,SAAS,qBAAqB,OAA6C;AAChF,SAAO,iBAAiB;AAC1B;AAyBO,SAAS,4BAA4B,WAOlB;AACxB,SAAO;AAAA,IACL,MAAM,SAAS,OAAO;AACpB,UAAI,MAAM,WAAW,iBAAiB,UAAU,aAAa;AAC3D,eAAO,UAAU,YAAY,MAAM,oBAAoB;AAAA,MACzD;AACA,UAAI,MAAM,WAAW,UAAU,UAAU,KAAM,QAAO,UAAU,KAAK;AACrE,UACE,MAAM,WAAW,cACjB,MAAM,WAAW,UACjB,MAAM,WAAW,eACjB;AACA,cAAM,UAAU,UAAU,MAAM,MAAM;AACtC,YAAI,QAAS,QAAO,QAAQ,MAAM,oBAAoB;AAAA,MACxD;AACA,YAAM,IAAI;AAAA,QACR;AAAA,QACA,MAAM,WAAW,aACb,6DACA,MAAM,MAAM,MAAM;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,gCAAuD,4BAA4B,CAAC,CAAC;AAM3F,SAAS,yBAAyB,OAAwD;AAC/F,MAAI,OAAO;AACX,SAAO;AAAA,IACL,KAAK,IAAI;AACP,UAAI,QAAQ,OAAO;AACjB,cAAM,IAAI;AAAA,UACR;AAAA,UACA,yBAAyB,KAAK,gCAAgC,EAAE;AAAA,QAClE;AAAA,MACF;AACA,cAAQ;AAAA,IACV;AAAA,IACA,MAAM,MAAM;AAAA,EACd;AACF;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VaultProviderError
3
- } from "./chunk-7S23LHTK.js";
3
+ } from "./chunk-BD43JBPC.js";
4
4
 
5
5
  // src/testing/fake-provider.ts
6
6
  import { parseTotpInput } from "@opengeni/contracts";
@@ -226,4 +226,4 @@ function createFakeVaultProvider(seed = {}) {
226
226
  export {
227
227
  createFakeVaultProvider
228
228
  };
229
- //# sourceMappingURL=chunk-VKPGG2FW.js.map
229
+ //# sourceMappingURL=chunk-BEKN77JO.js.map
package/dist/index.d.ts CHANGED
@@ -1,2 +1,5 @@
1
1
  export * from "./provider.js";
2
+ export * from "./provider-pages.js";
3
+ export * from "./provider-capabilities.js";
4
+ export * from "./remote-provider.js";
2
5
  export * from "./splice/index.js";