Skip to content

Commit e0233be

Browse files
tombeckenhamclaude
andcommitted
refactor(byok): drop vendor branding from wire and storage identifiers
The BYOK header prefix, IndexedDB name, passkey relying-party name, and HKDF label all hardcoded "tanstack". None of these need the vendor name: the header is a private protocol between this package's own client and server (both resolve it via byokHeaderName), and the storage identifiers are already overridable per instance. Neutral defaults let the toolkit read as reusable rather than TanStack-specific. - header prefix: x-tanstack-byok- -> x-byok- - IndexedDB default: tanstack-byok -> byok - passkey rpName: "TanStack AI BYOK" -> "BYOK" - HKDF info label: tanstack-byok:keyring:v1 -> byok:keyring:v1 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent cd6251e commit e0233be

7 files changed

Lines changed: 14 additions & 14 deletions

File tree

.changeset/byok-package.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
'@tanstack/ai-byok': minor
33
---
44

5-
Add `@tanstack/ai-byok`: a bring-your-own-key toolkit for TanStack AI. Keys live client-side and travel to the relay in a per-provider header (`x-tanstack-byok-<provider>`), never the request body or message history.
5+
Add `@tanstack/ai-byok`: a bring-your-own-key toolkit for TanStack AI. Keys live client-side and travel to the relay in a per-provider header (`x-byok-<provider>`), never the request body or message history.
66

77
- **Client** (`@tanstack/ai-byok`): `byokHeaders`, `withByok`/`byokFetch` (attach BYOK headers to a connection and detect the relay's `byokMissing` 401 so the UI can prompt for or unlock the missing key), a typed provider registry, pluggable storage (session-only memory by default, opt-in passkey-encrypted persistence — WebAuthn PRF → HKDF → AES-256-GCM in IndexedDB, no plaintext option), and `validateKey`. Storage may expose `peek()` to report `provider → last-4` presence without decrypting.
88
- **React** (`@tanstack/ai-byok/react`): `<ByokProvider storage={…}>`, `useByok()` (with `locked`/`unlock` for encrypted storage), and a drop-in `<ByokKeyManager>` settings UI that only ever shows the last 4 characters of a saved key. After a refresh, saved keys from encrypted storage surface as a `locked` status (with last-4) before the biometric unlock.

packages/ai-byok/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ ever persisting or logging them**.
1414
- **No central endpoint.** The server helper is trivially self-hostable; there
1515
is no hardcoded relay URL.
1616

17-
Keys travel in the `x-tanstack-byok-<provider>` header — never the request body
17+
Keys travel in the `x-byok-<provider>` header — never the request body
1818
or message history — so they stay out of persisted conversations and the
1919
event/observability stream.
2020

packages/ai-byok/src/client/passkey.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import type { ProviderId } from '../shared/providers'
1818

1919
const STORE_NAME = 'keyring'
2020
const RECORD_ID = 'default'
21-
const HKDF_INFO = 'tanstack-byok:keyring:v1'
22-
const DEFAULT_DB = 'tanstack-byok'
21+
const HKDF_INFO = 'byok:keyring:v1'
22+
const DEFAULT_DB = 'byok'
2323

2424
interface StoredRecord {
2525
id: string
@@ -270,7 +270,7 @@ export interface PasskeyStorageOptions {
270270
* domains.
271271
*/
272272
rpId?: string
273-
/** IndexedDB database name. Defaults to `tanstack-byok`. */
273+
/** IndexedDB database name. Defaults to `byok`. */
274274
dbName?: string
275275
}
276276

@@ -283,7 +283,7 @@ export interface PasskeyStorageOptions {
283283
export function passkeyStorage(
284284
options: PasskeyStorageOptions = {},
285285
): KeyringStorage {
286-
const rpName = options.rpName ?? 'TanStack AI BYOK'
286+
const rpName = options.rpName ?? 'BYOK'
287287
const userName = options.userName ?? 'byok-keyring'
288288
const { rpId } = options
289289
const dbName = options.dbName ?? DEFAULT_DB

packages/ai-byok/src/shared/providers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export function providerValidateConfig(
128128
}
129129

130130
/** Prefix for every per-provider BYOK header. */
131-
export const BYOK_HEADER_PREFIX = 'x-tanstack-byok-'
131+
export const BYOK_HEADER_PREFIX = 'x-byok-'
132132

133133
/**
134134
* The HTTP header name that carries the key for a given provider. Keys always

packages/ai-byok/tests/byok.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ describe('byokHeaders', () => {
2323
gemini: 'g-1',
2424
})
2525
expect(headers).toEqual({
26-
'x-tanstack-byok-openai': 'sk-abc',
27-
'x-tanstack-byok-gemini': 'g-1',
26+
'x-byok-openai': 'sk-abc',
27+
'x-byok-gemini': 'g-1',
2828
})
2929
})
3030
})
@@ -33,11 +33,11 @@ describe('withByok / byokFetch', () => {
3333
it('withByok attaches BYOK headers read fresh at request time', () => {
3434
let keys: Keyring = { openai: 'sk-a' }
3535
const build = withByok(() => keys)
36-
expect(build().headers).toEqual({ 'x-tanstack-byok-openai': 'sk-a' })
36+
expect(build().headers).toEqual({ 'x-byok-openai': 'sk-a' })
3737
keys = { openai: 'sk-b', gemini: 'g-1' }
3838
expect(build().headers).toEqual({
39-
'x-tanstack-byok-openai': 'sk-b',
40-
'x-tanstack-byok-gemini': 'g-1',
39+
'x-byok-openai': 'sk-b',
40+
'x-byok-gemini': 'g-1',
4141
})
4242
})
4343

packages/ai-byok/tests/react.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('useByok', () => {
4545
await result.current.setKey('openai', 'sk-live-1234')
4646
})
4747
expect(byokHeaders(result.current.keys)).toEqual({
48-
'x-tanstack-byok-openai': 'sk-live-1234',
48+
'x-byok-openai': 'sk-live-1234',
4949
})
5050
// Only the last 4 are exposed as status.
5151
expect(result.current.status.openai).toEqual({

testing/e2e/tests/byok.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ test.describe('byok', () => {
3535
await sendMessage(page, '[chat] recommend a guitar')
3636

3737
const request = await requestPromise
38-
expect(request.headers()['x-tanstack-byok-openai']).toBe(KEY)
38+
expect(request.headers()['x-byok-openai']).toBe(KEY)
3939
expect(request.postData() ?? '').not.toContain(KEY)
4040

4141
await waitForResponse(page)

0 commit comments

Comments
 (0)