@byok-sdk/keys 0.3.8 → 0.3.10

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.
@@ -1,10 +1,13 @@
1
- import { type ModelProviderId, type ModelProviderProfile } from './provider-profile';
1
+ import { type ExactProviderProfileBinding, type ModelProviderProfile, type ProviderProfileRef } from './provider-profile';
2
2
  import { type SecretStore } from './secret-store';
3
3
  export interface PiProviderLauncherOptions {
4
4
  piBin: string;
5
5
  profileDbPath: string;
6
- providerId: ModelProviderId;
6
+ /** Carried by the `--provider` flag: the exact local profile to launch. */
7
+ profileRef: ProviderProfileRef;
7
8
  modelId: string;
9
+ expectedBinding?: ExactProviderProfileBinding;
10
+ validateOnly: boolean;
8
11
  sessionDir: string;
9
12
  secretServicePrefix?: string;
10
13
  macosKeychainPath?: string;
@@ -1,8 +1,17 @@
1
1
  import type { ModelProviderProfile } from './provider-profile';
2
2
  export declare const PI_PROJECTED_KEY_ENV = "PI_PROVIDER_API_KEY";
3
3
  /** Keep projected providers disjoint from Pi built-ins so composition can never fall back to one. */
4
- export declare function piProjectionProviderId(profileProviderId: string): string;
5
- /** Credential-blind Pi configuration derived from one validated local profile. */
4
+ export declare function piProjectionProviderId(profileRef: string): string;
5
+ /**
6
+ * Credential-blind Pi configuration derived from one validated local profile.
7
+ *
8
+ * The projected provider is namespaced by the profile's own ref, not by its
9
+ * provider kind, so two independently configured endpoints of the same kind
10
+ * project to two distinct Pi providers instead of colliding on one. Model
11
+ * `input` modalities are projected from the profile's declared capabilities —
12
+ * declared local configuration is the only authority; nothing is inferred from
13
+ * the model name or base URL.
14
+ */
6
15
  export declare function buildPiProviderProjection(profile: ModelProviderProfile): object;
7
16
  /**
8
17
  * Validate the credential-blind RPC argv the client may delegate, then bind
@@ -1,5 +1,5 @@
1
1
  import { ByokKeysError } from './errors';
2
- import { type ModelProviderId, type ModelProviderProfile } from './provider-profile';
2
+ import { type ModelProviderProfile, type ProviderProfileRef } from './provider-profile';
3
3
  /**
4
4
  * Storage contract for provider profiles — everything needed to address a
5
5
  * provider except the API key, which lives in a {@link SecretStore}.
@@ -23,24 +23,24 @@ import { type ModelProviderId, type ModelProviderProfile } from './provider-prof
23
23
  export interface ProviderProfileStore {
24
24
  /** Release the underlying resource. Safe to call more than once. */
25
25
  close(): Promise<void>;
26
- /** Remove `providerId`; `false` when it was not configured. */
27
- delete(providerId: ModelProviderId): Promise<boolean>;
28
- /** Read one provider's profile, configured or not enabled alike. */
29
- get(providerId: ModelProviderId): Promise<ModelProviderProfile | undefined>;
26
+ /** Remove `profileRef`; `false` when it was not configured. */
27
+ delete(profileRef: ProviderProfileRef): Promise<boolean>;
28
+ /** Read one profile, configured or not enabled alike. */
29
+ get(profileRef: ProviderProfileRef): Promise<ModelProviderProfile | undefined>;
30
30
  /** The single enabled profile, or `undefined` when none is enabled. */
31
31
  getEnabled(): Promise<ModelProviderProfile | undefined>;
32
- /** Every configured profile, ordered by provider id. */
32
+ /** Every configured profile, ordered by profile ref. */
33
33
  list(): Promise<ModelProviderProfile[]>;
34
34
  /** Insert or update `profile`, enforcing both invariants above. */
35
35
  save(profile: ModelProviderProfile): Promise<ModelProviderProfile>;
36
36
  /**
37
- * Make `providerId` the enabled profile. Throws `PROVIDER_NOT_CONFIGURED`
37
+ * Make `profileRef` the enabled profile. Throws `PROVIDER_NOT_CONFIGURED`
38
38
  * when it has no profile — a fail-closed port of `providers.ts:1243-1250`.
39
39
  */
40
- setEnabled(providerId: ModelProviderId): Promise<ModelProviderProfile>;
40
+ setEnabled(profileRef: ProviderProfileRef): Promise<ModelProviderProfile>;
41
41
  }
42
42
  /** Shared by both implementations so their error text cannot drift apart. */
43
- export declare function providerNotConfigured(providerId: ModelProviderId): ByokKeysError;
43
+ export declare function providerNotConfigured(profileRef: ProviderProfileRef): ByokKeysError;
44
44
  /**
45
45
  * Profile store held in process memory: the default, and the one every test
46
46
  * that does not specifically exercise SQLite should use.
@@ -52,10 +52,10 @@ export declare function providerNotConfigured(providerId: ModelProviderId): Byok
52
52
  export declare class InMemoryProviderProfileStore implements ProviderProfileStore {
53
53
  #private;
54
54
  close(): Promise<void>;
55
- delete(providerId: ModelProviderId): Promise<boolean>;
56
- get(providerId: ModelProviderId): Promise<ModelProviderProfile | undefined>;
55
+ delete(profileRef: ProviderProfileRef): Promise<boolean>;
56
+ get(profileRef: ProviderProfileRef): Promise<ModelProviderProfile | undefined>;
57
57
  getEnabled(): Promise<ModelProviderProfile | undefined>;
58
58
  list(): Promise<ModelProviderProfile[]>;
59
59
  save(profile: ModelProviderProfile): Promise<ModelProviderProfile>;
60
- setEnabled(providerId: ModelProviderId): Promise<ModelProviderProfile>;
60
+ setEnabled(profileRef: ProviderProfileRef): Promise<ModelProviderProfile>;
61
61
  }
@@ -1,10 +1,42 @@
1
1
  import { z } from 'zod';
2
2
  /**
3
- * Model providers this package knows how to address. Ported from
4
- * `aip-main-open@c6a5385` `providers.ts:30-36` (`LOCAL_MODEL_PROVIDER_IDS`).
3
+ * Opaque, portable identity of one locally configured provider profile.
4
+ *
5
+ * This is the profile's primary key: it is a local logical id, never a path,
6
+ * and several profiles may share one {@link MODEL_PROVIDER_KINDS} kind (two
7
+ * independent `custom` endpoints, for example). It replaces the former
8
+ * `MODEL_PROVIDER_IDS` primary key, which conflated instance identity with
9
+ * provider kind and therefore allowed exactly one profile per kind.
10
+ *
11
+ * `@byok-sdk/protocol` declares the same lexical rule for the wire form in
12
+ * `provider-profile-binding.ts`. The two definitions are deliberately parallel
13
+ * rather than shared: the release graph (`scripts/release/check-package-graph.mjs`)
14
+ * forbids `@byok-sdk/keys` from depending on any dispatch package, so the
15
+ * device-local authority cannot import the wire authority. This mirrors how
16
+ * `packages/protocol/src/blob.ts`'s `CONTENT_HASH_RE` restates
17
+ * `packages/core/src/blob.ts`'s content-hash format across the same boundary.
18
+ */
19
+ export declare const PROVIDER_PROFILE_REF_PATTERN: RegExp;
20
+ export declare const ProviderProfileRefSchema: z.ZodString;
21
+ export type ProviderProfileRef = z.infer<typeof ProviderProfileRefSchema>;
22
+ /**
23
+ * Provider kinds this package knows how to address — the surviving half of the
24
+ * former `MODEL_PROVIDER_IDS`. A kind says *what dialect family and vendor
25
+ * shape* a profile is; {@link ProviderProfileRefSchema} says *which* profile.
26
+ * Ported from `aip-main-open@c6a5385` `providers.ts:30-36`
27
+ * (`LOCAL_MODEL_PROVIDER_IDS`).
5
28
  */
6
- export declare const MODEL_PROVIDER_IDS: readonly ['openai', 'deepseek', 'anthropic', 'custom'];
7
- export type ModelProviderId = (typeof MODEL_PROVIDER_IDS)[number];
29
+ export declare const MODEL_PROVIDER_KINDS: readonly ['openai', 'deepseek', 'anthropic', 'custom'];
30
+ export type ModelProviderKind = (typeof MODEL_PROVIDER_KINDS)[number];
31
+ /**
32
+ * Bounded model capabilities a profile may declare. A capability is explicit
33
+ * local configuration, never inferred from the model name or the base URL.
34
+ */
35
+ export declare const PROVIDER_MODEL_CAPABILITIES: readonly ['image-input'];
36
+ export declare const ProviderModelCapabilitySchema: z.ZodEnum<{
37
+ "image-input": "image-input";
38
+ }>;
39
+ export type ProviderModelCapability = z.infer<typeof ProviderModelCapabilitySchema>;
8
40
  /** Auth modes a provider profile can request (`providers.ts:29`). */
9
41
  export declare const PROVIDER_AUTH_MODES: readonly ['bearer', 'x_api_key', 'none'];
10
42
  export type ProviderAuthMode = (typeof PROVIDER_AUTH_MODES)[number];
@@ -34,12 +66,16 @@ export declare const ModelProviderProfileSchema: z.ZodObject<{
34
66
  x_api_key: "x_api_key";
35
67
  }>;
36
68
  base_url: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
69
+ capabilities: z.ZodArray<z.ZodEnum<{
70
+ "image-input": "image-input";
71
+ }>>;
37
72
  created_at: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
38
73
  display_name: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
39
74
  enabled: z.ZodBoolean;
40
75
  kind: z.ZodLiteral<"model">;
41
76
  model: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
42
- provider_id: z.ZodEnum<{
77
+ profile_ref: z.ZodString;
78
+ provider_kind: z.ZodEnum<{
43
79
  anthropic: "anthropic";
44
80
  custom: "custom";
45
81
  deepseek: "deepseek";
@@ -51,6 +87,23 @@ export declare const ModelProviderProfileSchema: z.ZodObject<{
51
87
  export type ModelProviderProfile = z.infer<typeof ModelProviderProfileSchema>;
52
88
  /** Pre-normalization shape callers may hand in. */
53
89
  export type ModelProviderProfileInput = z.input<typeof ModelProviderProfileSchema>;
90
+ export interface ExactProviderProfileBinding {
91
+ profileRef: ProviderProfileRef;
92
+ profileRevision: string;
93
+ profileHash: string;
94
+ modelId: string;
95
+ requiredCapabilities: readonly ProviderModelCapability[];
96
+ }
97
+ /**
98
+ * Derive the credential-free wire identity from the normalized local record.
99
+ * `updated_at` is the persisted monotonic revision authority; the hash covers
100
+ * the complete normalized non-secret record with capability order
101
+ * canonicalized. Secret values are not accepted by this type and therefore
102
+ * cannot enter the digest or any mismatch message.
103
+ */
104
+ export declare function exactProviderProfileBinding(profileInput: ModelProviderProfile, requiredCapabilities?: readonly ProviderModelCapability[]): ExactProviderProfileBinding;
105
+ /** Fail closed unless every offered binding field matches local authority. */
106
+ export declare function assertExactProviderProfileBinding(profile: ModelProviderProfile, expected: ExactProviderProfileBinding): void;
54
107
  /**
55
108
  * Parse and normalize a profile, or throw {@link ByokKeysError}. Replaces the
56
109
  * source's `normalizeProviderProfile` (`providers.ts:1489-1556`) for the model
@@ -2,7 +2,7 @@ import { AnthropicMessagesClient } from './anthropic-client';
2
2
  import type { ProviderFetch } from './http';
3
3
  import { OpenAiCompatibleChatClient } from './openai-client';
4
4
  import type { ProviderProfileStore } from './profile-store';
5
- import { type ModelProviderAdapter, type ModelProviderId, type ProviderAuthMode } from './provider-profile';
5
+ import { type ModelProviderAdapter, type ModelProviderKind, type ProviderAuthMode, type ProviderModelCapability, type ProviderProfileRef } from './provider-profile';
6
6
  import { type ModelProviderSecretName, type SecretStore } from './secret-store';
7
7
  /** A transport client for whichever dialect the resolved profile declares. */
8
8
  export type ModelProviderClient = AnthropicMessagesClient | OpenAiCompatibleChatClient;
@@ -16,11 +16,18 @@ export interface ProviderConfiguration {
16
16
  adapter: ModelProviderAdapter;
17
17
  auth_mode: ProviderAuthMode;
18
18
  base_url: string;
19
+ /**
20
+ * Bounded model capabilities this exact profile supports. Declared, never
21
+ * inferred: an omitted capability means the endpoint does not offer it.
22
+ */
23
+ capabilities: readonly ProviderModelCapability[];
19
24
  display_name: string;
20
25
  /** Defaults to `true`: configuring a provider makes it the default. */
21
26
  enabled?: boolean;
22
27
  model: string;
23
- provider_id: ModelProviderId;
28
+ /** This profile's own local identity; several profiles may share one kind. */
29
+ profile_ref: ProviderProfileRef;
30
+ provider_kind: ModelProviderKind;
24
31
  }
25
32
  /**
26
33
  * The registry's outward projection of a profile.
@@ -34,12 +41,18 @@ export interface ProviderStatus {
34
41
  adapter: ModelProviderAdapter;
35
42
  auth_mode: ProviderAuthMode;
36
43
  base_url: string;
44
+ capabilities: readonly ProviderModelCapability[];
37
45
  created_at: string;
38
46
  display_name: string;
39
47
  enabled: boolean;
40
48
  model: string;
41
- provider_id: ModelProviderId;
42
- /** Whether the credential store currently holds this provider's key. */
49
+ profile_ref: ProviderProfileRef;
50
+ /** Canonical credential-free revision used by exact task admission. */
51
+ profile_revision: string;
52
+ /** SHA-256 of the normalized non-secret local record. */
53
+ profile_hash: string;
54
+ provider_kind: ModelProviderKind;
55
+ /** Whether the credential store currently holds this profile's key. */
43
56
  secret_configured: boolean;
44
57
  updated_at: string;
45
58
  }
@@ -86,9 +99,9 @@ export declare class ProviderRegistry {
86
99
  * authentication it cannot perform.
87
100
  */
88
101
  configure(configuration: ProviderConfiguration, secret?: string): Promise<ProviderStatus>;
89
- /** Remove a provider's profile and its secret together. */
90
- delete(providerId: ModelProviderId): Promise<boolean>;
91
- get(providerId: ModelProviderId): Promise<ProviderStatus | undefined>;
102
+ /** Remove a profile and its secret together. */
103
+ delete(profileRef: ProviderProfileRef): Promise<boolean>;
104
+ get(profileRef: ProviderProfileRef): Promise<ProviderStatus | undefined>;
92
105
  list(): Promise<ProviderStatus[]>;
93
106
  /**
94
107
  * Build a client for the one enabled provider (`providers.ts:1331-1354`).
@@ -98,6 +111,6 @@ export declare class ProviderRegistry {
98
111
  * missing secret or an unusable profile is a fault, not an absence.
99
112
  */
100
113
  resolveDefaultModelProvider(): Promise<ModelProviderClient | undefined>;
101
- /** Switch which configured provider is the default. */
102
- setDefaultModelProvider(providerId: ModelProviderId): Promise<ProviderStatus>;
114
+ /** Switch which configured profile is the default. */
115
+ setDefaultModelProvider(profileRef: ProviderProfileRef): Promise<ProviderStatus>;
103
116
  }
@@ -1,4 +1,4 @@
1
- import type { ModelProviderId } from './provider-profile';
1
+ import { type ProviderProfileRef } from './provider-profile';
2
2
  /**
3
3
  * Storage contract for a single secret entry in an operating-system credential
4
4
  * store.
@@ -42,20 +42,23 @@ export interface SecretStore<TName extends string = string> {
42
42
  */
43
43
  export declare const DEFAULT_SECRET_SERVICE_PREFIX = "com.byok.keys";
44
44
  /**
45
- * Credential-store entry name per model provider, ported verbatim from
46
- * `providers.ts:1624-1632`. The names carry no vendor branding — the branding
47
- * lives in the service prefix so they travel unchanged and K4 needs no
48
- * migration.
45
+ * Credential-store entry name for one provider profile.
46
+ *
47
+ * The former fixed `MODEL_PROVIDER_SECRET_NAMES` table could name exactly one
48
+ * entry per provider kind, so two independent `custom` endpoints would have
49
+ * shared — and overwritten — a single credential. The name is now derived from
50
+ * the profile's own {@link ProviderProfileRef}, keeping the shape
51
+ * `providers.ts:1624-1632` established (`model-<id>-api-key`, no vendor
52
+ * branding; the branding lives in the service prefix).
53
+ */
54
+ export type ModelProviderSecretName = `model-${string}-api-key`;
55
+ /**
56
+ * Resolve a provider profile ref to the credential-store entry holding its API
57
+ * key. The ref is validated here as well as by the profile schema: this
58
+ * function composes a storage address, so an unvalidated ref would be an
59
+ * address-injection surface rather than merely an invalid record.
49
60
  */
50
- export declare const MODEL_PROVIDER_SECRET_NAMES: {
51
- readonly anthropic: 'model-anthropic-api-key';
52
- readonly custom: 'model-custom-api-key';
53
- readonly deepseek: 'model-deepseek-api-key';
54
- readonly openai: 'model-openai-api-key';
55
- };
56
- export type ModelProviderSecretName = (typeof MODEL_PROVIDER_SECRET_NAMES)[keyof typeof MODEL_PROVIDER_SECRET_NAMES];
57
- /** Resolve a provider id to the credential-store entry holding its API key. */
58
- export declare function modelProviderSecretName(providerId: ModelProviderId): ModelProviderSecretName;
61
+ export declare function modelProviderSecretName(profileRef: ProviderProfileRef): ModelProviderSecretName;
59
62
  /**
60
63
  * The secret-shape invariant both OS backends share. Their size ceilings differ
61
64
  * in both magnitude and unit (macOS counts 16384 characters, Windows counts
@@ -1,5 +1,5 @@
1
1
  import { type ProviderProfileStore } from './profile-store';
2
- import { type ModelProviderId, type ModelProviderProfile } from './provider-profile';
2
+ import { type ModelProviderProfile, type ProviderProfileRef } from './provider-profile';
3
3
  export interface SqliteProviderProfileStoreOptions {
4
4
  /**
5
5
  * Database file path. `:memory:` exercises the SQLite code path without a
@@ -26,12 +26,12 @@ export declare class SqliteProviderProfileStore implements ProviderProfileStore
26
26
  * teardown.
27
27
  */
28
28
  close(): Promise<void>;
29
- delete(providerId: ModelProviderId): Promise<boolean>;
30
- get(providerId: ModelProviderId): Promise<ModelProviderProfile | undefined>;
29
+ delete(profileRef: ProviderProfileRef): Promise<boolean>;
30
+ get(profileRef: ProviderProfileRef): Promise<ModelProviderProfile | undefined>;
31
31
  getEnabled(): Promise<ModelProviderProfile | undefined>;
32
32
  list(): Promise<ModelProviderProfile[]>;
33
33
  save(profile: ModelProviderProfile): Promise<ModelProviderProfile>;
34
- setEnabled(providerId: ModelProviderId): Promise<ModelProviderProfile>;
34
+ setEnabled(profileRef: ProviderProfileRef): Promise<ModelProviderProfile>;
35
35
  }
36
- /** Exported for the store's own tests to enumerate the CHECK-constrained ids. */
37
- export declare const SQLITE_PROFILE_PROVIDER_IDS: readonly ["openai", "deepseek", "anthropic", "custom"];
36
+ /** Exported for the store's own tests to enumerate the CHECK-constrained kinds. */
37
+ export declare const SQLITE_PROFILE_PROVIDER_KINDS: readonly ["openai", "deepseek", "anthropic", "custom"];
@@ -1,7 +1,17 @@
1
1
  import { type TenantId, type TruthStore } from '@byok-sdk/core';
2
2
  import { type ProviderProfileStore } from './profile-store';
3
- import { type ModelProviderId, type ModelProviderProfile } from './provider-profile';
3
+ import { type ModelProviderProfile, type ProviderProfileRef } from './provider-profile';
4
4
  export declare const PROVIDER_PROFILE_TRUTH_RECORD_KEY = "byok-sdk.keys/model-provider-registry-v1";
5
+ /**
6
+ * Upper bound on how many profiles one tenant's registry snapshot may carry.
7
+ *
8
+ * The former bound was `MODEL_PROVIDER_IDS.length`, which only held because the
9
+ * primary key was a four-value enum. Profile refs are open, so the CAS body
10
+ * needs an explicit ceiling of its own; a registry is device-local operator
11
+ * configuration, and 32 endpoints is far past any real local setup while
12
+ * keeping the single-record snapshot small.
13
+ */
14
+ export declare const MAX_PROVIDER_PROFILES = 32;
5
15
  export interface TruthStoreProviderProfileStoreOptions {
6
16
  tenant: TenantId;
7
17
  truthStore: TruthStore;
@@ -18,10 +28,10 @@ export declare class TruthStoreProviderProfileStore implements ProviderProfileSt
18
28
  #private;
19
29
  constructor(options: TruthStoreProviderProfileStoreOptions);
20
30
  close(): Promise<void>;
21
- delete(providerId: ModelProviderId): Promise<boolean>;
22
- get(providerId: ModelProviderId): Promise<ModelProviderProfile | undefined>;
31
+ delete(profileRef: ProviderProfileRef): Promise<boolean>;
32
+ get(profileRef: ProviderProfileRef): Promise<ModelProviderProfile | undefined>;
23
33
  getEnabled(): Promise<ModelProviderProfile | undefined>;
24
34
  list(): Promise<ModelProviderProfile[]>;
25
35
  save(profile: ModelProviderProfile): Promise<ModelProviderProfile>;
26
- setEnabled(providerId: ModelProviderId): Promise<ModelProviderProfile>;
36
+ setEnabled(profileRef: ProviderProfileRef): Promise<ModelProviderProfile>;
27
37
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byok-sdk/keys",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "BYOK SDK key management: provider profiles, credential-backed auth headers, and direct provider transports",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  "clean": "rm -rf dist"
49
49
  },
50
50
  "dependencies": {
51
- "@byok-sdk/core": "0.11.0",
51
+ "@byok-sdk/core": "0.13.0",
52
52
  "zod": "^4.4.3"
53
53
  }
54
54
  }