Commit Graph
260 Commits
Author SHA1 Message Date
lukaszraczylo ac11023a45 feat: limit engine with fixed windows, token and cost budgets 2026-08-20 01:28:13 +01:00
lukaszraczylo 15bbf51deb fix(auth): non-blocking reload check, stat-before-load, mtime inequality trigger
- users_file.go: maybeReload uses reloadMu.TryLock() instead of Lock().
  A full reload (stat+read+parse+replaceFileUsers, incl. per-user
  resolveSecret file reads) could otherwise block every concurrent
  ServeHTTP request for its whole duration, negating the buildMu/mu
  lock split. A request that finds a reload in flight now skips this
  cycle; reload stays throttled and best-effort.
- users_file.go: mtime check changed from ModTime().After(lastModTime)
  to ModTime().Equal(lastModTime), so a backward mtime move (restore
  from backup, cp -p) still triggers a reload instead of being
  skipped. Added a regression test with os.Chtimes into the past.
- llmgateway.go: attachUsersFile now stats the file before loading it,
  matching maybeReload's ordering, so a write landing between stat and
  load is caught on the next check instead of being lost. Documented
  why the ignored statErr is safe (falls back to zero lastModTime,
  causing one harmless extra reload).
2026-08-20 01:16:13 +01:00
lukaszraczylo eef33b8232 feat: users file with throttled hot reload
Add usersFile (newUsersFile/load) for the JSON users-file shape
{"users":[{name,group,apiKey,limits?}]}, and authStore.maybeReload
for throttled hot reload: checks mtime at most every reloadEvery
(5s), reloads on change, keeps the last good set and logs via
errorf on any error (stat, parse, or a rejected replace), and logs
via logf when the reload changes the file-sourced user count.
nowFn is injectable for tests.

Wire-up in llmgateway.go: newGateway does a synchronous initial
load when Users.File is set (a bad initial file fails the
constructor); ServeHTTP calls g.auth.maybeReload() at entry
(no-op when no file is configured).

Also fixes replaceFileUsers's merge semantics: a file user now
overrides an inline user of the same name, even when their keys
differ, dropping the inline entry from the rebuilt set. The prior
digest-only keying let a same-named inline/file pair with
different keys coexist instead of the file version winning.
2026-08-20 01:07:31 +01:00
lukaszraczylo 25813f2b14 refactor(auth): split writer serialization from reader lock in replaceFileUsers
Add buildMu to serialize replaceFileUsers's writer body (including
buildEntry's potentially slow file-backed resolveSecret reads for
"file:"-prefixed API keys). Scope mu.Lock() to the final byDigest
swap only, so a rebuild in progress no longer stalls every identify
call for its whole duration. Behavior-preserving: writers stay fully
serialized, same as before.

Adds a regression test that holds buildMu externally and asserts
identify still succeeds — proving identify's read path does not
depend on the writer-serialization lock.
2026-08-20 01:03:57 +01:00
lukaszraczylo 6af11a89ce fix(auth): guard nil user entries and serialize file-user swaps 2026-08-20 00:52:29 +01:00
lukaszraczylo 58ab3ed0b3 feat: auth store with constant-time key identification and group allow rules 2026-08-20 00:42:50 +01:00
lukaszraczylo 16cf665910 fix: error on empty file-resolved secrets; assert no secret leakage in errors 2026-08-20 00:38:23 +01:00
lukaszraczylo 27a8306cb3 feat: secret resolution (literal, env, file) 2026-08-20 00:33:38 +01:00
lukaszraczylo d5bff28c5c fix: harden scaffold after review — manifest drift check, vet-safe logging, panic-path header guard 2026-08-20 00:26:28 +01:00
lukaszraczylo 2469918d7d feat: scaffold traefik-llmgateway plugin skeleton with manifest 2026-08-20 00:17:16 +01:00