Files
freeway/packages/core/test/keyvault.test.ts
T
Aryan SinghandClaude Opus 5 cb985dcf9c Freeway: self-hosted OpenAI-compatible gateway for free-tier providers
One endpoint in front of every free LLM tier, with automatic failover and no
provider key ever leaving the box.

Providers are data, not code: adding one is a JSON file plus an env var, never
a TypeScript change. Ships 12 providers pre-written; `verify-provider --apply`
derives the live model list from your own key.

- core: registry, bucketed quota meter (rolling + UTC-calendar windows),
  key-pool store, 6-step model router with diagnostic rejection reasons
- gateway: node:http, failover that commits to a stream only after 200 headers,
  SSE passthrough with usage sniffing, virtual keys with admin/inference roles
- context engine: sqlite sessions and a 5-rung refit ladder, so a 128k thread
  keeps working when it fails over to an 8k model
- cache: exact (sha256, safe-by-default) + opt-in semantic (flat Float32Array)
- guard: inbound limits, quota forecasting, limit calibration from upstream
  headers, key health, secret/PII/injection scanning
- dashboard: one self-contained HTML file, live SSE request log, token usage
- sdk: zero-dep typed client, drop-in for the OpenAI SDK

Zero runtime dependencies. 326 tests, 27-check smoke suite against a scripted
mock upstream so routing is testable without spending quota.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 22:41:49 -05:00

118 lines
4.4 KiB
TypeScript

import { strict as assert } from 'node:assert';
import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { after, describe, it } from 'node:test';
import { KeyVault } from '../src/keyvault.ts';
import { silentLogger } from '../src/util.ts';
const dirs: string[] = [];
after(() => {
for (const d of dirs) rmSync(d, { recursive: true, force: true });
});
function vaultFile(): string {
const dir = mkdtempSync(join(tmpdir(), 'freeway-vault-'));
dirs.push(dir);
return join(dir, 'keys.json');
}
describe('KeyVault', () => {
it('stores a key and hands it to the registry in env-var grammar', () => {
const vault = new KeyVault({ file: vaultFile(), logger: silentLogger });
const masked = vault.add('groq', 'gsk-abcdefghijklmnop');
assert.equal(masked, 'gsk-…mnop');
assert.deepEqual(vault.all(), { groq: 'gsk-abcdefghijklmnop' });
});
it('builds a pool from repeated adds, without duplicates', () => {
const vault = new KeyVault({ file: vaultFile(), logger: silentLogger });
vault.add('groq', 'gsk-aaaaaaaaaaaa');
vault.add('groq', 'gsk-bbbbbbbbbbbb');
vault.add('groq', 'gsk-aaaaaaaaaaaa');
assert.equal(vault.all()['groq'], 'gsk-aaaaaaaaaaaa,gsk-bbbbbbbbbbbb');
});
it('writes the file 0600', () => {
// Anything readable by other users on the box defeats the purpose of the
// gateway holding the keys in the first place.
const file = vaultFile();
const vault = new KeyVault({ file, logger: silentLogger });
vault.add('groq', 'gsk-abcdefghijklmnop');
const mode = statSync(file).mode & 0o777;
assert.equal(mode, 0o600, `expected 0600, got 0${mode.toString(8)}`);
});
it('survives a restart', () => {
const file = vaultFile();
new KeyVault({ file, logger: silentLogger }).add('groq', 'gsk-abcdefghijklmnop');
const reopened = new KeyVault({ file, logger: silentLogger });
assert.equal(reopened.all()['groq'], 'gsk-abcdefghijklmnop');
assert.equal(reopened.has('groq'), true);
});
it('only ever describes keys masked', () => {
const vault = new KeyVault({ file: vaultFile(), logger: silentLogger });
vault.add('groq', 'gsk-supersecret-1234');
vault.add('groq', 'gsk-anothersecret-99');
const described = vault.describe();
assert.deepEqual(described[0]?.masked, ['gsk-…1234', 'gsk-…t-99']);
assert.ok(!JSON.stringify(described).includes('supersecret'));
});
it('removes one key from a pool by its mask', () => {
const vault = new KeyVault({ file: vaultFile(), logger: silentLogger });
vault.add('groq', 'gsk-aaaaaaaaaaaa');
vault.add('groq', 'gsk-bbbbbbbbbbbb');
assert.equal(vault.remove('groq', 'gsk-…aaaa'), true);
assert.equal(vault.all()['groq'], 'gsk-bbbbbbbbbbbb');
assert.equal(vault.remove('groq', 'gsk-…zzzz'), false, 'an unknown mask removes nothing');
});
it('drops the provider entirely when its last key goes', () => {
const vault = new KeyVault({ file: vaultFile(), logger: silentLogger });
vault.add('groq', 'gsk-aaaaaaaaaaaa');
vault.remove('groq', 'gsk-…aaaa');
assert.equal(vault.has('groq'), false);
});
it('rejects a comma-separated blob, so one paste is one key', () => {
const vault = new KeyVault({ file: vaultFile(), logger: silentLogger });
assert.throws(() => vault.add('groq', 'a,b'), /one key at a time/);
assert.throws(() => vault.add('groq', ' '), /empty/);
});
it('refuses every mutation when not writable', () => {
// This is the state the gateway uses when bound to a non-loopback address
// without an admin key.
const vault = new KeyVault({ file: vaultFile(), logger: silentLogger, writable: false });
assert.throws(() => vault.add('groq', 'gsk-abcdefghijkl'), /disabled/);
assert.throws(() => vault.remove('groq'), /disabled/);
assert.equal(vault.writable, false);
});
it('ignores a corrupt file instead of refusing to start', () => {
const file = vaultFile();
const vault = new KeyVault({ file, logger: silentLogger });
vault.add('groq', 'gsk-abcdefghijkl');
// Simulate a truncated write from an older version.
const raw = readFileSync(file, 'utf8').slice(0, 12);
rmSync(file);
new KeyVault({ file, logger: silentLogger });
let reopened: KeyVault | undefined;
assert.doesNotThrow(() => {
reopened = new KeyVault({ file, logger: silentLogger });
});
assert.deepEqual(reopened?.all(), {});
void raw;
});
});