import * as ApiKeys from './ApiKeys.js' import * as ApiKeyAdmissions from './db/tables/apiKeyAdmissions.js' import * as ApiKeyOwnerTombstones from './db/tables/apiKeyOwnerTombstones.js' import * as ApiKeyRevocations from './db/tables/apiKeyRevocations.js' import * as Organizations from './db/tables/organizations.js' import * as Projects from './db/tables/projects.js' import * as Store from './internal/Store.js' import * as TestApp from '../test/App.js' describe('resolve', () => { test('resolves an unknown token to null', async () => { expect(await ApiKeys.resolve(Store.memory(), 'tempo:sk:missing')).toBeNull() }) test('resolves before expiry, rejects after', async () => { vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') }) try { const state = Store.memory() const { token } = await ApiKeys.mint(state, { expiresAt: '2026-01-01T00:01:00.000Z', orgId: 'org_a', scopes: ['data:read'], }) expect((await ApiKeys.resolve(state, token))?.orgId).toBe('org_a') // Advance the clock past the expiry. vi.setSystemTime(new Date('2026-01-01T00:02:00.000Z')) expect(await ApiKeys.resolve(state, token)).toBeNull() } finally { vi.useRealTimers() } }) test('treats a corrupt record as absent', async () => { const state = Store.memory() const { token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) // Overwrite the stored record (keyed by token hash) with invalid JSON. const { keys } = await state.list({ prefix: 'apikey:' }) await state.put(keys[0]!.name, 'not json') expect(await ApiKeys.resolve(state, token)).toBeNull() }) test('rejects a record with an unknown scope', async () => { const state = Store.memory() const { token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) const { keys } = await state.list({ prefix: 'apikey:' }) await state.put( keys[0]!.name, JSON.stringify({ createdAt: '2026-01-01T00:00:00.000Z', id: 'key_x', orgId: 'org_a', scopes: ['data:read', 'totally:made-up'], tokenLast4: 'xxxx', }), ) expect(await ApiKeys.resolve(state, token)).toBeNull() }) test('continues resolving stored records with redundant scopes', async () => { const state = Store.memory() const { token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) const { keys } = await state.list({ prefix: 'apikey:' }) const value = JSON.parse((await state.get(keys[0]!.name))!) as Record await state.put(keys[0]!.name, JSON.stringify({ ...value, scopes: ['data:read', 'data:read'] })) expect((await ApiKeys.resolve(state, token))?.scopes).toEqual(['data:read', 'data:read']) }) test('normalizes persisted per-minute rate limits', async () => { const state = Store.memory() const { token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) const { keys } = await state.list({ prefix: 'apikey:' }) const value = JSON.parse((await state.get(keys[0]!.name))!) as Record await state.put( keys[0]!.name, JSON.stringify({ ...value, rateLimits: { '*': { perMinute: 120 } } }), ) expect((await ApiKeys.resolve(state, token))?.rateLimits).toMatchInlineSnapshot(` { "*": { "limit": 120, "period": "minute", }, } `) }) }) // Mock the system clock so timestamps are deterministic without injecting a // clock into the module. describe('mint', () => { beforeEach(() => vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') })) afterEach(() => vi.useRealTimers()) test('mints a key and resolves it', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { createdBy: 'admin@tempo.xyz', name: 'CI key', orgId: 'org_acme', scopes: ['data:read', 'webhooks:write'], }) expect(token).toMatch(/^tempo:sk:[a-f0-9]{48}$/) expect(record.tokenLast4).toBe(token.slice(-4)) expect({ ...record, id: '', tokenLast4: '' }).toMatchInlineSnapshot(` { "allowedIps": [], "createdAt": "2026-01-01T00:00:00.000Z", "createdBy": "admin@tempo.xyz", "environment": "production", "id": "", "name": "CI key", "orgId": "org_acme", "scopes": [ "data:read", "webhooks:write", ], "tokenLast4": "", } `) const resolved = await ApiKeys.resolve(state, token) expect(resolved?.orgId).toBe('org_acme') expect(resolved?.scopes).toEqual(['data:read', 'webhooks:write']) }) test('rejects an unknown scope at the write boundary', async () => { await expect( ApiKeys.mint(Store.memory(), { orgId: 'org_a', scopes: ['totally:made-up' as never] }), ).rejects.toThrowError(/invalid API key input/) }) test('rejects duplicate scope vectors', async () => { await expect( ApiKeys.mint(Store.memory(), { orgId: 'org_a', scopes: ['data:read', 'data:read'], }), ).rejects.toThrowError(/invalid API key input/) }) test('mints per-zone read and write scopes', async () => { const state = Store.memory() const { token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['zone:421700001:read', 'zone:421700001:write'], }) const resolved = await ApiKeys.resolve(state, token) expect(resolved?.scopes).toEqual(['zone:421700001:read', 'zone:421700001:write']) }) test('rejects malformed zone scopes', async () => { for (const scope of [ 'zone::read', 'zone:abc:read', 'zone:1.5:read', 'zone:-1:read', 'zone:0:read', 'zone:0:write', 'zone:000421700001:read', 'zone:1:admin', ]) await expect( ApiKeys.mint(Store.memory(), { orgId: 'org_a', scopes: [scope as never] }), ).rejects.toThrowError(/invalid API key input/) }) }) describe('markOwnerDeleting', () => { test('idempotently retains the fence across concurrent deletion attempts', async () => { const state = Store.memory() const organization = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'], }) await ApiKeys.markOwnerDeleting(state, { orgId: 'org_a' }) await ApiKeys.markOwnerDeleting(state, { orgId: 'org_a' }) expect(await ApiKeys.resolve(state, organization.token)).toBeNull() await expect(ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] })).rejects.toThrow( ApiKeys.OwnerDeletedError, ) }) test('project fences leave sibling keys active', async () => { const state = Store.memory() const project = await ApiKeys.mint(state, { orgId: 'org_a', projectId: 'prj_deleted', scopes: ['data:read'], }) const sibling = await ApiKeys.mint(state, { orgId: 'org_a', projectId: 'prj_active', scopes: ['data:read'], }) await ApiKeys.markOwnerDeleting(state, { orgId: 'org_a', projectId: 'prj_deleted' }) expect(await ApiKeys.resolve(state, project.token)).toBeNull() expect(await ApiKeys.resolve(state, sibling.token)).not.toBeNull() await expect( ApiKeys.mint(state, { orgId: 'org_a', projectId: 'prj_deleted', scopes: ['data:read'], }), ).rejects.toThrow(ApiKeys.OwnerDeletedError) }) }) describe('mintBounded', () => { test('rejects an authoritative owner tombstone absent from KV', async () => { const db = TestApp.database() const state = Store.memory() await db.transaction((tx) => ApiKeyOwnerTombstones.markDeleted(tx, { orgId: 'org_a' })) await expect( ApiKeys.mintBounded(db, state, { orgId: 'org_a', scopes: ['data:read'] }), ).rejects.toThrow(ApiKeys.OwnerDeletedError) }) test('atomically caps organization keys and releases revoked slots', async () => { const db = TestApp.database() const state = Store.memory() const attempts = await Promise.allSettled( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization + 1 }, () => ApiKeys.mintBounded(db, state, { orgId: 'org_a', scopes: ['data:read'] }), ), ) const minted = attempts.flatMap((attempt) => attempt.status === 'fulfilled' ? [attempt.value] : [], ) expect(minted).toHaveLength(ApiKeys.maxLiveKeysPerOrganization) expect(attempts.filter((attempt) => attempt.status === 'rejected')).toHaveLength(1) const revocations = await Promise.all([ ApiKeys.revokeBounded(db, state, minted[0]!.record.id), ApiKeys.revokeBounded(db, state, minted[0]!.record.id), ]) expect(revocations).toContain(true) const replacements = await Promise.allSettled([ ApiKeys.mintBounded(db, state, { orgId: 'org_a', scopes: ['data:read'] }), ApiKeys.mintBounded(db, state, { orgId: 'org_a', scopes: ['data:read'] }), ]) expect(replacements.filter(({ status }) => status === 'fulfilled')).toHaveLength(1) }) test('rejects a legacy key revoked before bootstrap despite a stale listing', async () => { vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z'), toFake: ['Date'] }) try { const db = TestApp.database() const state = Store.memory() const minted = await Promise.all( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization }, () => ApiKeys.mint(state, { orgId: 'org_revoked', scopes: ['data:read'] }), ), ) const revoked = minted[0]! const recordKey = (await state.get(`apikey_id:${revoked.record.id}`))! const raw = (await state.get(recordKey))! await ApiKeys.revokeBounded(db, state, revoked.record.id) await state.put(recordKey, raw) await state.put(`apikey_org:${revoked.record.orgId}:${revoked.record.id}`, recordKey) const stale = await ApiKeys.resolve(state, revoked.token) expect(stale).not.toBeNull() await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false) await ApiKeys.mintBounded(db, state, { orgId: revoked.record.orgId, scopes: ['data:read'], }) vi.advanceTimersByTime(ApiKeyRevocations.retentionMs + 1) await ApiKeyRevocations.prune(db) expect( await db.kysely .selectFrom('api_key_admissions') .select('id') .where('id', '=', revoked.record.id) .executeTakeFirst(), ).toBeUndefined() await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false) await ApiKeys.revokeBounded(db, state, minted[1]!.record.id) const replacements = await Promise.allSettled([ ApiKeys.mintBounded(db, state, { orgId: revoked.record.orgId, scopes: ['data:read'], }), ApiKeys.mintBounded(db, state, { orgId: revoked.record.orgId, scopes: ['data:read'], }), ]) expect(replacements.filter(({ status }) => status === 'fulfilled')).toHaveLength(1) } finally { vi.useRealTimers() } }) test('revokes without scanning a hostile legacy organization index', async () => { const db = TestApp.database() const state = Store.memory() await Promise.all( Array.from({ length: ApiKeys.maxLegacyAdmissionIndexes + 1 }, (_, index) => state.put(`apikey_org:org_hostile:!${String(index).padStart(4, '0')}`, `missing:${index}`), ), ) const target = await ApiKeys.mint(state, { orgId: 'org_hostile', scopes: ['data:read'], }) const recordKey = (await state.get(`apikey_id:${target.record.id}`))! const raw = (await state.get(recordKey))! await expect(ApiKeys.revokeBounded(db, state, target.record.id)).resolves.toBe(true) await state.put(recordKey, raw) await state.put(`apikey_org:${target.record.orgId}:${target.record.id}`, recordKey) const stale = await ApiKeys.resolve(state, target.token) expect(stale).not.toBeNull() await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false) }) test('keeps a missing-index revocation fenced and admitted until KV deletion succeeds', async () => { vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z'), toFake: ['Date'] }) try { const db = TestApp.database() const state = Store.memory() const minted = await Promise.all( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization + 1 }, () => ApiKeys.mint(state, { orgId: 'org_incomplete', scopes: ['data:read'] }), ), ) await expect( ApiKeys.mintBounded(db, state, { orgId: 'org_incomplete', scopes: ['data:read'], }), ).rejects.toThrow(ApiKeys.LiveKeyLimitError) const admitted = await db.kysely .selectFrom('api_key_admissions') .select('id') .where('orgId', '=', 'org_incomplete') .executeTakeFirstOrThrow() const target = minted.find(({ record }) => record.id === admitted.id)! await state.delete(`apikey_id:${target.record.id}`) await expect(ApiKeys.revokeBounded(db, state, target.record.id)).resolves.toBe(true) const stale = await ApiKeys.resolve(state, target.token) expect(stale).not.toBeNull() await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false) vi.advanceTimersByTime(ApiKeyRevocations.retentionMs + 1) await expect(ApiKeyRevocations.prune(db)).resolves.toBe(0) await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false) await expect(ApiKeyAdmissions.exists(db, target.record.id)).resolves.toBe(true) await expect( ApiKeys.mintBounded(db, state, { orgId: 'org_incomplete', scopes: ['data:read'], }), ).rejects.toThrow(ApiKeys.LiveKeyLimitError) } finally { vi.useRealTimers() } }) test('expires and prunes revocation fences after KV becomes fresh', async () => { vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z'), toFake: ['Date'] }) try { const db = TestApp.database() const state = Store.memory() const revoked = await ApiKeys.mint(state, { orgId: 'org_churn', scopes: ['data:read'], }) await ApiKeys.revokeBounded(db, state, revoked.record.id) vi.advanceTimersByTime(ApiKeyRevocations.retentionMs + 1) expect(await ApiKeyRevocations.prune(db)).toBe(1) expect(await ApiKeyAdmissions.matches(db, revoked.record)).toBe(true) expect(await ApiKeys.resolve(state, revoked.token)).toBeNull() } finally { vi.useRealTimers() } }) test('cascades organization-attributed revocation fences on deletion', async () => { const db = TestApp.database() const state = Store.memory() const organization = await Organizations.create(db, { name: 'Fence owner' }) const revoked = await ApiKeys.mintBounded(db, state, { orgId: organization.id, scopes: ['data:read'], }) await state.delete(`apikey_id:${revoked.record.id}`) await ApiKeys.revokeBounded(db, state, revoked.record.id) expect( await db.kysely .selectFrom('api_key_revocations') .select('orgId') .where('id', '=', revoked.record.id) .executeTakeFirst(), ).toEqual({ orgId: organization.id }) await Organizations.deleteOrganization(db, organization.id) expect( await db.kysely .selectFrom('api_key_revocations') .select('id') .where('id', '=', revoked.record.id) .executeTakeFirst(), ).toBeUndefined() await expect(ApiKeyAdmissions.exists(db, revoked.record.id)).resolves.toBe(false) }) test('attributes a confirmed fence from admission when the first KV read misses', async () => { const db = TestApp.database() const inner = Store.memory() const organization = await Organizations.create(db, { name: 'Admission owner' }) const revoked = await ApiKeys.mintBounded(db, inner, { orgId: organization.id, scopes: ['data:read'], }) const idKey = `apikey_id:${revoked.record.id}` let idReads = 0 const state = Store.from({ ...inner, get(key) { if (key === idKey && idReads++ === 0) return Promise.resolve(null) return inner.get(key) }, }) await expect(ApiKeys.revokeBounded(db, state, revoked.record.id)).resolves.toBe(true) expect( await db.kysely .selectFrom('api_key_revocations') .select('orgId') .where('id', '=', revoked.record.id) .executeTakeFirst(), ).toEqual({ orgId: organization.id }) }) test('removes pending project revocations before releasing owner admissions', async () => { const db = TestApp.database() const state = Store.memory() const organization = await Organizations.create(db, { name: 'Project fence owner' }) const project = await Projects.create(db, { name: 'Project fence', orgId: organization.id }) const revoked = await ApiKeys.mintBounded(db, state, { orgId: organization.id, projectId: project.id, scopes: ['data:read'], }) await state.delete(`apikey_id:${revoked.record.id}`) await ApiKeys.revokeBounded(db, state, revoked.record.id) await Projects.deleteProject(db, project.id) expect( await db.kysely .selectFrom('api_key_revocations') .select('id') .where('id', '=', revoked.record.id) .executeTakeFirst(), ).toBeUndefined() await expect(ApiKeyAdmissions.exists(db, revoked.record.id)).resolves.toBe(false) }) test('does not regress a confirmed fence when another location reads stale KV', async () => { vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z'), toFake: ['Date'] }) try { const database = TestApp.databaseFactory() const inner = Store.memory() const revoked = await ApiKeys.mintBounded(database(), inner, { orgId: 'org_stale_revoke', scopes: ['data:read'], }) const idKey = `apikey_id:${revoked.record.id}` const recordKey = (await inner.get(idKey))! const raw = (await inner.get(recordKey))! await ApiKeys.revokeBounded(database(), inner, revoked.record.id) let idReads = 0 const stale = Store.from({ ...inner, async get(key) { if (key === idKey) return idReads++ === 0 ? recordKey : null if (key === recordKey) return raw return inner.get(key) }, }) await expect(ApiKeys.revokeBounded(database(), stale, revoked.record.id)).resolves.toBe(true) expect( await database() .kysely.selectFrom('api_key_revocations') .select('expiresAt') .where('id', '=', revoked.record.id) .executeTakeFirst(), ).not.toEqual({ expiresAt: ApiKeyRevocations.pendingExpiresAt }) vi.advanceTimersByTime(ApiKeyRevocations.retentionMs + 1) await expect(ApiKeyRevocations.prune(database())).resolves.toBe(1) await expect(ApiKeyAdmissions.exists(database(), revoked.record.id)).resolves.toBe(false) await expect(ApiKeyRevocations.exists(database(), revoked.record.id)).resolves.toBe(false) } finally { vi.useRealTimers() } }) test('does not leave pending fences when owner deletion wins a concurrent revoke', async () => { const database = TestApp.databaseFactory() for (const ownerType of ['organization', 'project'] as const) { const state = Store.memory() const organization = await Organizations.create(database(), { name: `${ownerType} race` }) const project = ownerType === 'project' ? await Projects.create(database(), { name: 'Revoke race', orgId: organization.id }) : undefined const revoked = await ApiKeys.mintBounded(database(), state, { orgId: organization.id, ...(project === undefined ? {} : { projectId: project.id }), scopes: ['data:read'], }) const idKey = `apikey_id:${revoked.record.id}` await state.delete(idKey) const reading = Promise.withResolvers() const deleted = Promise.withResolvers() const delayed = Store.from({ ...state, async get(key) { if (key !== idKey) return state.get(key) reading.resolve() await deleted.promise return null }, }) const revocation = ApiKeys.revokeBounded(database(), delayed, revoked.record.id) await reading.promise if (project === undefined) await Organizations.deleteOrganization(database(), organization.id) else await Projects.deleteProject(database(), project.id) deleted.resolve() await expect(revocation).resolves.toBe(true) await expect(ApiKeyAdmissions.exists(database(), revoked.record.id)).resolves.toBe(false) await expect(ApiKeyRevocations.exists(database(), revoked.record.id)).resolves.toBe(false) } }) test('bootstraps the cap from bounded legacy KV records', async () => { const db = TestApp.database() const state = Store.memory() await Promise.all( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization }, () => ApiKeys.mint(state, { orgId: 'org_legacy', scopes: ['data:read'] }), ), ) await expect( ApiKeys.mintBounded(db, state, { orgId: 'org_legacy', scopes: ['data:read'] }), ).rejects.toThrow(ApiKeys.LiveKeyLimitError) const count = await db.kysely .selectFrom('api_key_admissions') .select((eb) => eb.fn.countAll().as('count')) .where('orgId', '=', 'org_legacy') .executeTakeFirstOrThrow() expect(Number(count.count)).toBe(ApiKeys.maxLiveKeysPerOrganization) }) test('keeps omitted over-cap legacy keys valid without completing bootstrap', async () => { const db = TestApp.database() const state = Store.memory() const legacy = await Promise.all( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization + 1 }, () => ApiKeys.mint(state, { orgId: 'org_over_cap', scopes: ['data:read'] }), ), ) await expect( ApiKeys.mintBounded(db, state, { orgId: 'org_over_cap', scopes: ['data:read'] }), ).rejects.toThrow(ApiKeys.LiveKeyLimitError) const admissions = await db.kysely .selectFrom('api_key_admissions') .select('id') .where('orgId', '=', 'org_over_cap') .execute() const admitted = new Set(admissions.map(({ id }) => id)) const omitted = legacy.find(({ record }) => !admitted.has(record.id)) expect(omitted).toBeDefined() const resolved = await ApiKeys.resolve(state, omitted!.token) expect(resolved).not.toBeNull() await expect(ApiKeyAdmissions.matches(db, resolved!)).resolves.toBe(true) expect( await db.kysely .selectFrom('api_key_admission_bootstraps') .select('orgId') .where('orgId', '=', 'org_over_cap') .executeTakeFirst(), ).toBeUndefined() }) test('scans beyond stale legacy index pages', async () => { const db = TestApp.database() const state = Store.memory() await Promise.all( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization }, (_, index) => state.put(`apikey_org:org_stale:!${String(index).padStart(3, '0')}`, `missing:${index}`), ), ) const { record: legacy } = await ApiKeys.mint(state, { orgId: 'org_stale', scopes: ['data:read'], }) await ApiKeys.mintBounded(db, state, { orgId: 'org_stale', scopes: ['data:read'] }) expect( await db.kysely .selectFrom('api_key_admissions') .select('id') .where('id', '=', legacy.id) .executeTakeFirst(), ).toEqual({ id: legacy.id }) }) test('fails closed when stale indexes exhaust the reconciliation budget', async () => { const db = TestApp.database() const state = Store.memory() await Promise.all( Array.from({ length: ApiKeys.maxLegacyAdmissionIndexes + 1 }, (_, index) => state.put( `apikey_org:org_exhausted:!${String(index).padStart(4, '0')}`, `missing:${index}`, ), ), ) for (let attempt = 0; attempt < 2; attempt++) await expect( ApiKeys.mintBounded(db, state, { orgId: 'org_exhausted', scopes: ['data:read'], }), ).rejects.toThrow(ApiKeys.LiveKeyLimitError) expect(await state.get('apikey_org:org_exhausted:!0000')).toBe('missing:0') }) test('expired keys no longer consume admission slots', async () => { vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z'), toFake: ['Date'] }) try { const db = TestApp.database() const state = Store.memory() const expiresAt = '2026-01-01T00:01:00.000Z' await Promise.all( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization }, () => ApiKeys.mintBounded(db, state, { expiresAt, orgId: 'org_expiring', scopes: ['data:read'], }), ), ) vi.setSystemTime(new Date('2026-01-01T00:02:00.000Z')) await expect( ApiKeys.mintBounded(db, state, { orgId: 'org_expiring', scopes: ['data:read'], }), ).resolves.toBeDefined() } finally { vi.useRealTimers() } }) test('normalizes offset expiries before admission', async () => { const db = TestApp.database() const state = Store.memory() const { record } = await ApiKeys.mintBounded(db, state, { expiresAt: '2030-09-04T00:00:00-08:00', orgId: 'org_offset', scopes: ['data:read'], }) expect(record.expiresAt).toBe('2030-09-04T08:00:00.000Z') expect( await db.kysely .selectFrom('api_key_admissions') .select('expiresAt') .where('id', '=', record.id) .executeTakeFirst(), ).toEqual({ expiresAt: '2030-09-04T08:00:00.000Z' }) }) test('counts a malformed legacy expiry as non-expiring', async () => { const db = TestApp.database() const state = Store.memory() const { record } = await ApiKeys.mint(state, { orgId: 'org_malformed_expiry', scopes: ['data:read'], }) const recordKey = (await state.get(`apikey_id:${record.id}`))! const raw = JSON.parse((await state.get(recordKey))!) as ApiKeys.Record await state.put(recordKey, JSON.stringify({ ...raw, expiresAt: 'not-a-date' })) await expect( ApiKeys.mintBounded(db, state, { orgId: record.orgId, scopes: ['data:read'], }), ).resolves.toBeDefined() expect( await db.kysely .selectFrom('api_key_admissions') .select('expiresAt') .where('id', '=', record.id) .executeTakeFirst(), ).toEqual({ expiresAt: null }) }) }) describe('updateBounded', () => { test('rejects an update when the locked key no longer has the expected owner', async () => { const db = TestApp.database() const state = Store.memory() const { record } = await ApiKeys.mintBounded(db, state, { orgId: 'org_source', projectId: 'prj_source', scopes: ['data:read'], }) await ApiKeys.updateBounded(db, state, record.id, { orgId: 'org_destination', projectId: 'prj_destination', }) await expect( ApiKeys.updateBounded( db, state, record.id, { name: 'stale admin update' }, { expectedOwner: { orgId: 'org_source', projectId: 'prj_source' } }, ), ).resolves.toBeNull() const updated = await ApiKeys.get(state, record.id) expect(updated).toMatchObject({ orgId: 'org_destination', projectId: 'prj_destination', }) expect(updated).not.toHaveProperty('name') }) test('rejects an authoritative destination tombstone absent from KV', async () => { const db = TestApp.database() const state = Store.memory() const { record } = await ApiKeys.mint(state, { scopes: ['data:read'] }) await db.transaction((tx) => ApiKeyOwnerTombstones.markDeleted(tx, { orgId: 'org_a' })) await expect(ApiKeys.updateBounded(db, state, record.id, { orgId: 'org_a' })).rejects.toThrow( ApiKeys.OwnerDeletedError, ) expect((await ApiKeys.get(state, record.id))?.orgId).toBe(record.id) }) test('rolls back admission when the KV attribution update is rejected', async () => { const db = TestApp.database() const state = Store.memory() const { record } = await ApiKeys.mintBounded(db, state, { orgId: 'org_source', scopes: ['data:read'], }) await ApiKeys.markOwnerDeleting(state, { orgId: 'org_target' }) await expect( ApiKeys.updateBounded(db, state, record.id, { orgId: 'org_target' }), ).rejects.toThrow(ApiKeys.OwnerDeletedError) expect((await ApiKeys.get(state, record.id))?.orgId).toBe('org_source') expect( await db.kysely .selectFrom('api_key_admissions') .select(['id', 'orgId']) .where('id', '=', record.id) .executeTakeFirst(), ).toEqual({ id: record.id, orgId: 'org_source' }) }) test('enforces the destination cap after a partial KV attribution write', async () => { const db = TestApp.database() const inner = Store.memory() await Promise.all( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization - 1 }, () => ApiKeys.mintBounded(db, inner, { orgId: 'org_full', scopes: ['data:read'], }), ), ) const { record } = await ApiKeys.mintBounded(db, inner, { orgId: 'org_source', scopes: ['data:read'], }) let primaryWritten = false let rejected = false const state = Store.from({ ...inner, async delete(key) { if (primaryWritten && !rejected) { rejected = true throw new Error('index write rejected') } await inner.delete(key) }, async put(key, value, options = {}) { await inner.put(key, value, options) if (!key.startsWith('apikey:')) return const updated = JSON.parse(value) as { id?: string; orgId?: string } if (updated.id === record.id && updated.orgId === 'org_full') primaryWritten = true }, }) await expect( ApiKeys.updateBounded(db, state, record.id, { orgId: 'org_full' }), ).rejects.toThrow('index write rejected') expect((await ApiKeys.get(inner, record.id))?.orgId).toBe('org_full') expect( await db.kysely .selectFrom('api_key_admissions') .select(['id', 'orgId']) .where('id', '=', record.id) .executeTakeFirst(), ).toEqual({ id: record.id, orgId: 'org_source' }) await ApiKeys.mintBounded(db, inner, { orgId: 'org_full', scopes: ['data:read'] }) await expect( ApiKeys.updateBounded(db, inner, record.id, { orgId: 'org_full' }), ).rejects.toThrow(ApiKeys.LiveKeyLimitError) const count = await db.kysely .selectFrom('api_key_admissions') .select((eb) => eb.fn.countAll().as('count')) .where('orgId', '=', 'org_full') .executeTakeFirstOrThrow() expect(Number(count.count)).toBe(ApiKeys.maxLiveKeysPerOrganization) const stale = await ApiKeys.get(inner, record.id) expect(stale).not.toBeNull() await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false) }) test('serializes admission moves through their KV attribution writes', async () => { const database = TestApp.databaseFactory() const inner = Store.memory() const firstWrite = Promise.withResolvers() const secondWrite = Promise.withResolvers() const state = Store.from({ ...inner, async put(key, value, options = {}) { const record = (() => { try { return JSON.parse(value) as { orgId?: string; projectId?: string } } catch { return undefined } })() if (key.startsWith('apikey:') && record?.orgId === 'org_first') { firstWrite.resolve() // The old lock boundary let the second KV write complete before this delayed write. await Promise.race([ secondWrite.promise, new Promise((resolve) => setTimeout(resolve, 1_000)), ]) } if (key.startsWith('apikey:') && record?.projectId === 'prj_second') secondWrite.resolve() await inner.put(key, value, options) }, }) const { record } = await ApiKeys.mintBounded(database(), state, { orgId: 'org_source', scopes: ['data:read'], }) const first = ApiKeys.updateBounded(database(), state, record.id, { orgId: 'org_first' }) await firstWrite.promise const second = ApiKeys.updateBounded(database(), state, record.id, { projectId: 'prj_second', }) await Promise.all([first, second]) expect(await ApiKeys.get(state, record.id)).toMatchObject({ orgId: 'org_first', projectId: 'prj_second', }) expect( await database() .kysely.selectFrom('api_key_admissions') .select(['id', 'orgId', 'projectId']) .where('id', '=', record.id) .executeTakeFirst(), ).toEqual({ id: record.id, orgId: 'org_first', projectId: 'prj_second' }) }) test('serializes metadata writes with attribution moves', async () => { const database = TestApp.databaseFactory() const inner = Store.memory() const attributionWrite = Promise.withResolvers() const metadataWrite = Promise.withResolvers() const state = Store.from({ ...inner, async put(key, value, options = {}) { const record = (() => { try { return JSON.parse(value) as { name?: string; orgId?: string } } catch { return undefined } })() if (key.startsWith('apikey:') && record?.name === 'renamed') { metadataWrite.resolve() // The old metadata path could overwrite an attribution written while this write waited. await Promise.race([ attributionWrite.promise, new Promise((resolve) => setTimeout(resolve, 1_000)), ]) } if (key.startsWith('apikey:') && record?.orgId === 'org_first') attributionWrite.resolve() await inner.put(key, value, options) }, }) const { record } = await ApiKeys.mintBounded(database(), state, { orgId: 'org_source', scopes: ['data:read'], }) const metadata = ApiKeys.updateBounded(database(), state, record.id, { name: 'renamed' }) await metadataWrite.promise const attribution = ApiKeys.updateBounded(database(), state, record.id, { orgId: 'org_first', }) await Promise.all([metadata, attribution]) expect(await ApiKeys.get(state, record.id)).toMatchObject({ name: 'renamed', orgId: 'org_first', }) expect( await database() .kysely.selectFrom('api_key_admissions') .select(['id', 'orgId']) .where('id', '=', record.id) .executeTakeFirst(), ).toEqual({ id: record.id, orgId: 'org_first' }) }) }) describe('list', () => { beforeEach(() => vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') })) afterEach(() => vi.useRealTimers()) test('lists keys newest first, filtered by org', async () => { const state = Store.memory() // Advance the clock between mints so their createdAt timestamps differ and // "newest first" is well-defined. await ApiKeys.mint(state, { name: 'a', orgId: 'org_a', scopes: ['data:read'] }) vi.setSystemTime(new Date('2026-01-01T00:00:01.000Z')) await ApiKeys.mint(state, { name: 'b', orgId: 'org_b', scopes: ['data:read'] }) const all = await ApiKeys.list(state) expect(all.map((record) => record.name)).toMatchInlineSnapshot(` [ "b", "a", ] `) const scoped = await ApiKeys.list(state, { orgId: 'org_b' }) expect(scoped.map((record) => record.orgId)).toMatchInlineSnapshot(` [ "org_b", ] `) }) test('excludes a key past its expiry', async () => { const state = Store.memory() await ApiKeys.mint(state, { expiresAt: '2026-01-01T00:00:30.000Z', name: 'short-lived', orgId: 'org_a', scopes: ['data:read'], }) expect((await ApiKeys.list(state)).map((record) => record.name)).toMatchInlineSnapshot(` [ "short-lived", ] `) // Advance the clock past the expiry. vi.setSystemTime(new Date('2026-01-01T00:01:00.000Z')) expect(await ApiKeys.list(state)).toMatchInlineSnapshot(`[]`) }) }) describe('revoke', () => { test('revokes a key by id', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) expect(await ApiKeys.revoke(state, record.id)).toBe(true) expect(await ApiKeys.resolve(state, token)).toBeNull() expect(await ApiKeys.revoke(state, record.id)).toBe(false) }) test('revoke is O(1): deletes the record without scanning the prefix', async () => { const state = Store.memory() const { record } = await ApiKeys.mint(state, { environment: 'sandbox', orgId: 'org_a', scopes: ['data:read'], }) await ApiKeys.setBillingActive(state, { active: true, environment: 'sandbox', orgId: 'org_a', }) // The id index entry maps id -> record key; revoke uses it directly. expect(await ApiKeys.revoke(state, record.id)).toBe(true) // The record and both index entries are gone. const { keys } = await state.list({ prefix: 'apikey' }) expect(keys).toMatchInlineSnapshot(`[]`) }) }) describe('get', () => { test('reads a record by id, null when absent', async () => { const state = Store.memory() const { record } = await ApiKeys.mint(state, { orgId: 'org_a', projectId: 'prj_a', scopes: ['data:read'], }) expect((await ApiKeys.get(state, record.id))?.projectId).toBe('prj_a') expect(await ApiKeys.get(state, 'key_missing')).toBeNull() }) }) describe('listByOrg', () => { beforeEach(() => vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') })) afterEach(() => vi.useRealTimers()) test('lists through the org index, newest first, filtered by project', async () => { const state = Store.memory() await ApiKeys.mint(state, { name: 'a', orgId: 'org_a', projectId: 'prj_a', scopes: ['data:read'] }) // prettier-ignore vi.setSystemTime(new Date('2026-01-01T00:00:01.000Z')) await ApiKeys.mint(state, { name: 'b', orgId: 'org_a', projectId: 'prj_b', scopes: ['data:read'] }) // prettier-ignore await ApiKeys.mint(state, { name: 'c', orgId: 'org_b', scopes: ['data:read'] }) expect((await ApiKeys.listByOrg(state, 'org_a')).map((record) => record.name)) .toMatchInlineSnapshot(` [ "b", "a", ] `) expect( (await ApiKeys.listByOrg(state, 'org_a', { projectId: 'prj_b' })).map( (record) => record.name, ), ).toMatchInlineSnapshot(` [ "b", ] `) }) test('bounds legacy owner indexes', async () => { const state = Store.memory() for (let index = 0; index <= ApiKeys.maxLiveKeysPerOrganization; index++) await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) expect(await ApiKeys.listByOrg(state, 'org_a')).toHaveLength(ApiKeys.maxLiveKeysPerOrganization) }) test('skips and lazily cleans dangling index entries', async () => { const state = Store.memory() await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) // Simulate a record evicted out-of-band (e.g. native TTL), leaving the // index entry behind. const recordKey = (await state.list({ prefix: 'apikey:' })).keys[0]!.name await state.delete(recordKey) expect(await ApiKeys.listByOrg(state, 'org_a')).toEqual([]) expect((await state.list({ prefix: 'apikey_org:' })).keys).toMatchInlineSnapshot(`[]`) }) test('lazily cleans index entries whose record has another owner', async () => { const state = Store.memory() const { record } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'], }) const recordKey = (await state.list({ prefix: 'apikey:' })).keys[0]!.name await state.put(`apikey_org:org_b:${record.id}`, recordKey) expect(await ApiKeys.listByOrg(state, 'org_b')).toEqual([]) expect((await state.list({ prefix: 'apikey_org:org_b:' })).keys).toEqual([]) }) }) describe('update', () => { test('rejects duplicate scopes and fenced owner attribution', async () => { const state = Store.memory() const { record } = await ApiKeys.mint(state, { scopes: ['data:read'] }) await expect( ApiKeys.update(state, record.id, { scopes: ['data:read', 'data:read'] }), ).rejects.toThrowError(/invalid scopes/) await ApiKeys.markOwnerDeleting(state, { orgId: 'org_deleted' }) await expect(ApiKeys.update(state, record.id, { orgId: 'org_deleted' })).rejects.toThrow( ApiKeys.OwnerDeletedError, ) }) test('updates the name, org, and project while moving the org index', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) await ApiKeys.setBillingActive(state, { active: true, environment: 'production', orgId: 'org_a', }) expect((await ApiKeys.resolve(state, token))?.billingActive).toBe(true) const updated = await ApiKeys.update(state, record.id, { name: 'Explorer', orgId: 'org_b', projectId: 'prj_b', }) expect(updated?.name).toBe('Explorer') expect(updated?.orgId).toBe('org_b') expect(updated?.projectId).toBe('prj_b') // The original token keeps resolving, now carrying the attribution. const resolved = await ApiKeys.resolve(state, token) expect(resolved?.orgId).toBe('org_b') expect(resolved?.projectId).toBe('prj_b') expect(resolved?.billingActive).toBeUndefined() expect(await ApiKeys.listByOrg(state, 'org_a')).toEqual([]) expect((await ApiKeys.listByOrg(state, 'org_b')).map(({ id }) => id)).toEqual([record.id]) expect(await ApiKeys.update(state, 'key_missing', { projectId: 'prj_x' })).toBeNull() expect(await ApiKeys.attribute(state, 'key_missing', { projectId: 'prj_x' })).toBeNull() }) test('does not list a concurrent reassignment under its stale owner', async () => { const state = Store.memory() const { record } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) await Promise.all([ ApiKeys.update(state, record.id, { orgId: 'org_b' }), ApiKeys.update(state, record.id, { orgId: 'org_c' }), ]) const updated = await ApiKeys.get(state, record.id) if (!updated) throw new Error('API key missing after reassignment.') const staleOrgId = updated.orgId === 'org_b' ? 'org_c' : 'org_b' expect((await state.list({ prefix: `apikey_org:${staleOrgId}:` })).keys).toHaveLength(1) expect(await ApiKeys.listByOrg(state, staleOrgId)).toEqual([]) expect((await ApiKeys.listByOrg(state, updated.orgId)).map(({ id }) => id)).toEqual([record.id]) }) test('replaces and clears the IP allowlist without changing the token', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { allowedIps: ['203.0.113.0/24'], orgId: 'org_a', scopes: ['data:read'], }) const updated = await ApiKeys.update(state, record.id, { allowedIps: ['2001:db8::/32'], }) expect(updated?.allowedIps).toEqual(['2001:db8::/32']) expect((await ApiKeys.resolve(state, token))?.allowedIps).toEqual(['2001:db8::/32']) const cleared = await ApiKeys.update(state, record.id, { allowedIps: [] }) expect(cleared?.allowedIps).toEqual([]) expect((await ApiKeys.resolve(state, token))?.allowedIps).toEqual([]) }) test('replaces scopes without changing the token', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'], }) const updated = await ApiKeys.update(state, record.id, { scopes: ['management:read', 'zone:421700001:write'], }) expect(updated?.scopes).toEqual(['management:read', 'zone:421700001:write']) expect((await ApiKeys.resolve(state, token))?.scopes).toEqual([ 'management:read', 'zone:421700001:write', ]) }) test('replaces rate limits without changing the token', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { orgId: 'org_a', rateLimits: { '*': { limit: 10_000, period: 'minute' } }, scopes: ['data:read'], }) const updated = await ApiKeys.update(state, record.id, { rateLimits: { '*': { limit: 25_000, period: 'minute' } }, }) expect(updated?.rateLimits).toEqual({ '*': { limit: 25_000, period: 'minute' } }) expect((await ApiKeys.resolve(state, token))?.rateLimits).toEqual({ '*': { limit: 25_000, period: 'minute' }, }) }) test('keeps replaced scopes while the billing snapshot changes', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { environment: 'sandbox', orgId: 'org_a', scopes: ['data:read'], }) const recordKey = (await state.list({ prefix: 'apikey:' })).keys[0]!.name await ApiKeys.update(state, record.id, { scopes: ['management:read'] }) const stored = await state.get(recordKey) await ApiKeys.setBillingActive(state, { active: true, environment: 'sandbox', orgId: 'org_a', }) expect(await state.get(recordKey)).toBe(stored) expect(await ApiKeys.resolve(state, token)).toMatchObject({ billingActive: true, scopes: ['management:read'], }) }) test('rejects invalid scopes at the write boundary', async () => { const state = Store.memory() const { record } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) await expect( ApiKeys.update(state, record.id, { scopes: ['not-a-scope'] }), ).rejects.toThrowError(/invalid API key input/) }) test('fails closed when a persisted billing snapshot is corrupt', async () => { const state = Store.memory() const { token } = await ApiKeys.mint(state, { environment: 'sandbox', orgId: 'org_a', scopes: ['data:read'], }) await ApiKeys.setBillingActive(state, { active: true, environment: 'sandbox', orgId: 'org_a', }) const snapshotKey = (await state.list({ prefix: 'apikey_billing:' })).keys[0]!.name await state.put(snapshotKey, 'not json') expect(await ApiKeys.resolve(state, token)).toBeNull() }) test('rejects invalid IP allowlist rules at the write boundary', async () => { const state = Store.memory() const { record } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] }) await expect( ApiKeys.update(state, record.id, { allowedIps: ['203.0.113.0/33'] }), ).rejects.toThrowError(/invalid API key input/) }) }) describe('setBillingActive', () => { test('stamps production billing snapshots', async () => { const state = Store.memory() const { token } = await ApiKeys.mint(state, { environment: 'production', orgId: 'org_a', scopes: ['data:read'], }) expect( await ApiKeys.setBillingActive(state, { active: true, environment: 'production', orgId: 'org_a', }), ).toBe(1) expect((await ApiKeys.resolve(state, token))?.billingActive).toBe(true) }) test('ignores a billing snapshot produced for a previous owner', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { environment: 'sandbox', orgId: 'org_a', scopes: ['data:read'], }) const recordKey = (await state.list({ prefix: 'apikey:' })).keys[0]!.name await ApiKeys.update(state, record.id, { orgId: 'org_b' }) await state.put(`apikey_billing:${recordKey}`, JSON.stringify({ active: true, orgId: 'org_a' })) expect((await ApiKeys.resolve(state, token))?.billingActive).toBeUndefined() }) test('stamps billingActive onto the org keys in the target environment only', async () => { const state = Store.memory() const { token: sandboxToken } = await ApiKeys.mint(state, { environment: 'sandbox', orgId: 'org_a', scopes: ['data:read'], }) const { token: prodToken } = await ApiKeys.mint(state, { environment: 'production', orgId: 'org_a', scopes: ['data:read'], }) const { token: otherToken } = await ApiKeys.mint(state, { environment: 'sandbox', orgId: 'org_b', scopes: ['data:read'], }) const updated = await ApiKeys.setBillingActive(state, { active: true, environment: 'sandbox', orgId: 'org_a', }) expect(updated).toBe(1) expect((await ApiKeys.resolve(state, sandboxToken))?.billingActive).toBe(true) // The production key and another org's sandbox key are untouched. expect((await ApiKeys.resolve(state, prodToken))?.billingActive).toBeUndefined() expect((await ApiKeys.resolve(state, otherToken))?.billingActive).toBeUndefined() }) test('paginates beyond the user-facing organization list bound', async () => { const state = Store.memory() const keys = await Promise.all( Array.from({ length: ApiKeys.maxLiveKeysPerOrganization + 1 }, () => ApiKeys.mint(state, { environment: 'sandbox', orgId: 'org_legacy', scopes: ['data:read'], }), ), ) expect( await ApiKeys.setBillingActive(state, { active: true, environment: 'sandbox', orgId: 'org_legacy', }), ).toBe(ApiKeys.maxLiveKeysPerOrganization + 1) expect((await ApiKeys.resolve(state, keys.at(-1)!.token))?.billingActive).toBe(true) }) test('skips records already carrying the target value', async () => { const state = Store.memory() await ApiKeys.mint(state, { billingActive: true, environment: 'sandbox', orgId: 'org_a', scopes: ['data:read'], }) expect( await ApiKeys.setBillingActive(state, { active: true, environment: 'sandbox', orgId: 'org_a', }), ).toBe(0) }) }) describe('backfill', () => { test('rebuilds index entries for legacy records', async () => { const state = Store.memory() const { record, token } = await ApiKeys.mint(state, { environment: 'sandbox', orgId: 'org_a', scopes: ['data:read'], }) await ApiKeys.update(state, record.id, { scopes: ['management:read'] }) await ApiKeys.setBillingActive(state, { active: true, environment: 'sandbox', orgId: 'org_a', }) // Legacy state: records exist, index entries do not. for (const { name } of (await state.list({ prefix: 'apikey_id:' })).keys) await state.delete(name) for (const { name } of (await state.list({ prefix: 'apikey_org:' })).keys) await state.delete(name) expect(await ApiKeys.listByOrg(state, 'org_a')).toEqual([]) expect(await ApiKeys.backfill(state)).toEqual({ indexed: 1, scanned: 1 }) expect((await ApiKeys.listByOrg(state, 'org_a')).map(({ id }) => id)).toEqual([record.id]) expect(await ApiKeys.resolve(state, token)).toMatchObject({ billingActive: true, scopes: ['management:read'], }) // The id index is restored, so revoke-by-id works again. expect(await ApiKeys.revoke(state, record.id)).toBe(true) }) })