Commit Graph
3 Commits
Author SHA1 Message Date
25f2002654 f6a233220a perf(auth): look up api tokens by their hash index (#6915)
Every bearer-authenticated request, including each node-sync call, loaded all
enabled api_tokens rows and compared each stored SHA-256 hash in constant time.
Tokens are stored hashed, so the database can compare hashes directly: a
timing leak there reveals only a stored hash, which cannot authenticate. The
lookup is now one indexed seek on idx_api_tokens_token, which AutoMigrate
creates from the model tag on new and existing databases; scope and expiry
are still checked afterwards.
2026-10-24 05:06:49 +02:00
25f2002654 9f0f2b3802 fix(service): run inbound deletes and depleted-client purges on the serial traffic writer (#6912)
Inbound and traffic writes are serialised on one goroutine because the traffic
poll locks inbounds then client_traffics while an admin mutation does the
reverse, which Postgres aborts as a deadlock (40P01). Deleting an inbound and
purging depleted clients still opened their own transactions, so they could
deadlock the poll, and a delete committing between a concurrent edit's read
and its Save let GORM's Save fall back to CREATE and resurrect the inbound.
Both now go through runSerializedTx like every other inbound write.
2026-10-24 05:06:03 +02:00
25f2002654andAlireza Ahmadi f172bc3f10 fix(node-sync): do not latch node disable when master allows (#6870) (#6876)
* fix(node-sync): do not latch node disable when master allows (#6870)

nodeDisableIsStale trusted any node-reported enable=false whose limits matched
the master's, treating it as a genuine verdict and latching it on the master.
But matching limits no longer prove the verdict: a single stray false
(a depleted snapshot the master later re-enabled, a stale
disableInvalidClients hit, or a push that lagged a quota reset) leaves the
client stuck disabled on the node, and every master tick flips the client's
state again. This is the loop reported in #6870.

Make nodeDisableIsStale a single rule: stale iff the master's own deadline
and quota still permit the client. A genuine combined-quota or expiry
disable (masterLimitsAllowClient == false) still latches, preserving #4917.
Mirror the same rule in ClientTrafficEnableMergeExpr so the SQL UPDATE and
the Go in-memory replay cannot drift.

* fix(node-sync): keep node usage over quota latching a node disable

Dropping the matching-limits latch (#6870) let the master re-enable a
client the node had genuinely counted past its quota whenever the
master's own counters lagged, e.g. after a seeded-at-zero adoption,
reopening #4917. A node's enable=false now stays latched when the
node's own usage already spends the master's quota; otherwise the
master's limits decide, so a stray disable no longer loops.

Restores TestNodeQuotaDisable_SameExpiryStillLatches as it was on main.

---------

Co-authored-by: Alireza Ahmadi <alireza7@gmail.com>
2026-10-23 19:04:46 +02:00