ULX does store all bans in bans.txt (perma and temp)
However, every server start, if Source's /cfg/banned_user.cfg exists, ULX loads it into memory. (exec)
Every ULX ban uses ULib.addBan that also loads the file, and then, for even more sanity, updates it (writeid) when a player is banned.
function ULib.addBan( steamid, time, reason, name, admin )
if file.Exists( "../cfg/banned_user.cfg" ) then
ULib.execFile( "../cfg/banned_user.cfg" )
end
game.ConsoleCommand( string.format( "banid %d %s kick\n", time, steamid ) ) -- Execute NOW
ULib.queueFunctionCall( game.ConsoleCommand, "writeid\n" ) -- Write to file when old bans are done loading
... lots of other code regarding the bans.txt, temp time, admin, reason, etc ...
end