You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
No persistent config file exists today. State lives in src/lib/state.ts (global mutable singleton) and CLI flags via citty in src/main.ts. We need a versioned on-disk config to hold model aliases, retention knobs, and feature toggles for the admin plane.
Goal
Typed config store at ~/.local/share/copilot-api/config.json with atomic writes and fs.watch-based hot reload, used as the source of truth by F1.B / F1.C / F2 / F3 / F4.
Tasks
Define zod schema:
version: 1
models: Record<alias, { upstream: string; enabled: boolean; allowed_keys: string[] }> (use ["*"] for global allow)
retention: { events_days: number; traces_days: number; traces_max_bytes: number }
loadConfig() / saveConfig() with atomic write: write *.tmp, fsync file, rename, fsync parent directory (crash-consistency on ext4/xfs)
Set file mode 0600 on every write; verify on load and warn loudly if wider (security hardening)
Hot reload: watch the directory (not the file — editors do atomic rename, fs.watch loses inode on macOS); 250 ms debounce; on parse error keep previous value, log warning, do not crash
Snapshot semantics: expose config.snapshot() returning a frozen copy. In-flight requests MUST capture a snapshot at request start so a mid-request reload doesn't break SSE alias rewriting (per backend review Preserve encrypted_content for multi-turn reasoning #6).
Migration runner stub: read version, transform if needed, rewrite atomically
Default config seeded on first run if absent
Tests: schema validation, atomic write under stress (no partial files), fs.watch debounce, atomic-rename detection (macOS pattern), snapshot isolation from concurrent reload
Acceptance criteria
Editing the file via vi config.json (uses atomic rename) propagates to in-memory aliases within 1 s
Concurrent saves never produce a partial-write file (verified via stress test)
A malformed write keeps the process alive on the previous valid config
Snapshot held by an in-flight handler is unchanged by a concurrent reload
Part of #23.
Background
No persistent config file exists today. State lives in
src/lib/state.ts(global mutable singleton) and CLI flags via citty insrc/main.ts. We need a versioned on-disk config to hold model aliases, retention knobs, and feature toggles for the admin plane.Goal
Typed config store at
~/.local/share/copilot-api/config.jsonwith atomic writes andfs.watch-based hot reload, used as the source of truth by F1.B / F1.C / F2 / F3 / F4.Tasks
version: 1models: Record<alias, { upstream: string; enabled: boolean; allowed_keys: string[] }>(use["*"]for global allow)retention: { events_days: number; traces_days: number; traces_max_bytes: number }features: { auth: boolean; telemetry: boolean; debug: boolean }loadConfig()/saveConfig()with atomic write: write*.tmp,fsyncfile,rename,fsyncparent directory (crash-consistency on ext4/xfs)0600on every write; verify on load and warn loudly if wider (security hardening)config.snapshot()returning a frozen copy. In-flight requests MUST capture a snapshot at request start so a mid-request reload doesn't break SSE alias rewriting (per backend review Preserve encrypted_content for multi-turn reasoning #6).version, transform if needed, rewrite atomicallyAcceptance criteria
vi config.json(uses atomic rename) propagates to in-memory aliases within 1 s0600after every writeFile pointers
src/lib/config-store.ts,tests/config-store.test.tssrc/lib/paths.ts(addconfigPath()),src/lib/state.ts(read from store)Dependencies
Blocks F1.B, F1.C, F2.B, F3.A, F4.A (everyone reads from this).