Internals
How a request actually reaches these servers: your client, Cloudflare, nginx, one of three instances, its WireGuard exit, and the destination it asked for.
The path of a request
A call to either server crosses the same six steps before it comes back — the figure draws them, and the numbered list under it walks through each one. The one thing the caller chooses is which server: pointing a client at /libgen or /gitlab happens before the request exists on the wire, and everything after that is the same pipeline.
- https
- local
- WireGuard
Your client opens the request, with a Bearer token or without one
Cloudflare passes it through to the origin
Here the key gets computed: your IP, or the token's hash
The same key always picks the same node
That node leaves through its own fixed exit
The request goes out to the server that answers it
The same six steps, in full:
It starts on your machine: your MCP client sends the request to whichever endpoint you pointed it at, /libgen or /gitlab, carrying an `Authorization: Bearer` header if that server needs one — gitlab does, libgen does not.
Cloudflare receives it at the edge and forwards it, unchanged, to the home server that fronts both MCPs.
nginx turns the request into a routing key: your IP address if there is no credential, or an MD5 of a secret salt plus the token out of your `Authorization: Bearer` header if there is — the directive that does it is quoted in full under “Affinity” below.
That key feeds a consistent-hash balancer, which is why it keeps choosing the same one of the three running instances for you — never a different one from one call to the next. If that instance does not answer, nginx retries the same request on another one and leaves the failed one out for a short while; a restarted instance rejoins on its own, and updates are rolled out one instance at a time, so the other two keep serving — see “When something fails” below.
That instance always leaves through its own exit node — one of three, two in Spain and one in the United Kingdom, fixed per instance unless the watcher described under “When something fails” has to move it — so your calls keep appearing to come from the same address instead of alternating.
That connection reaches the actual destination: a Library Genesis mirror if you called libgen, or gitlab.com if you called gitlab — fixed by this deployment, not a host the caller picks.
What the inspector keeps, and how to check
Nothing. The token you paste — or, when the sign-in button is enabled, the one it obtains — lives in the memory of the page's component and nowhere else: no localStorage, no sessionStorage, no cookies, no query string, no logs. Reloading drops it, navigating anywhere drops it — this site has no client-side router, so every link is a fresh document — and closing the tab drops it.
That is a claim about an absence, which is exactly the kind you should never take on trust. Here is how to see it for yourself, with the browser you already have open.
In Chrome or Edge, press F12 and go to Application → Storage. Paste a token in the inspector, call something, and look again: Local Storage, Session Storage and Cookies for this site stay empty. In Firefox that panel is called Storage; in Safari it is Develop → Show Web Inspector → Storage.
Then look at where it goes. In the Network tab, run a call and open the request to /gitlab: the Authorization header is on that request and on no other. The only other place the token could appear is the sign-in exchange with gitlab.com, and only if you used that button, which is disabled at the moment — so today there is exactly one destination. The browser itself enforces the boundary, because this page's Content-Security-Policy names those two destinations and no others. Everything in this paragraph is visible in that panel, without taking anyone's word for it.
On the wire: what is encrypted, and where it is not
Every hop that crosses a network is encrypted. Your client reaches Cloudflare over HTTPS; Cloudflare reaches this server over HTTPS too — plain HTTP gets a redirect and the domain is on the HSTS preload list; and the call that finally leaves for its destination is HTTPS as well, negotiated by the instance itself and only forwarded, still sealed, through the WireGuard tunnel that gives it its exit. Between nginx and the instances nothing leaves this machine, though it is worth being exact about what that means: nginx dials 127.0.0.1 and the kernel hands the connection straight to the container, with no user-space proxy in between. That segment is not loopback, so the honest claim is not that there is no network but that nobody is on it — the six containers on that private bridge are the six WireGuard sidecars, one per instance; each instance lives inside its sidecar's network namespace with every Linux capability dropped and a read-only filesystem, and only the sidecar holds the one capability a tunnel needs.
So nobody sitting between the hops can read your token. Two points do see it, and by design: Cloudflare's edge, which decrypts and re-encrypts everything it proxies, as any CDN does; and this server, where nginx needs the token to compute the affinity hash and the instance needs it to make the call you asked for. Neither one writes it down — no access log on this machine records the `Authorization` header, and the hash derived from it is not logged either: it lives just long enough to pick an instance.
The last hop is HTTPS to a host this deployment declares — gitlab.com, the one authorization server OAuth allows — never to one a caller supplies.
Three instances, one nginx
Behind that nginx sit three instances of each server, load-balanced rather than run as a single process.
Each instance keeps its own cache, so a hit on one is not a hit on the others: the three-way split is the price of being able to spread load across them at all.
Affinity: why the second request lands on the same node
nginx does not rotate round-robin across those three instances: it picks one with a consistent hash, so the same client keeps landing on the same node instead of a different one each time.
libgen hashes on the client's IP address (`hash $binary_remote_addr consistent`) — there is no token to key on, and the payoff is the read cache: repeat calls from the same visitor keep hitting the instance that already has them warm.
gitlab hashes on the client's `Authorization: Bearer` credential instead, falling back to the IP address when a request carries none. The reason is structural, not a cache: gitlab spins up an isolated server per client, and its connection pool is indexed by `(token, URL)` — each entry checks scopes and edition against GitLab the first time it is used. With OAuth it matters even more: an instance also caches the identity of a verified token for fifteen minutes, so bouncing between nodes would force that token to be re-verified against gitlab.com — a network call to a third party — on every hop. An OAuth access token hashes exactly like a PAT, since it arrives in the same header, with one wrinkle: gitlab.com expires access tokens after two hours, and a refreshed token is a different string. So affinity holds for the life of a token rather than forever, and moving costs one verification.
The directive that turns a credential into a routing decision looks like this. It reads the `Authorization` header, and two things happen to the token inside it: nginx hashes it together with a secret salt into `$mcp_affinity`, which is the value the consistent-hash balancer actually keys on.
That hash is necessary, not decorative — it is the only way nginx can send a client back to the instance that already holds its pool, without ever comparing tokens to each other directly.
What does not happen: the token itself is never written anywhere, and neither is the hash. `$mcp_affinity_salt` and `$mcp_affinity` are ordinary nginx variables, scoped to the single request that computed them — nothing here reaches a log line, and nothing outlives the request.
Even so: default to distrusting any remote server you hand a token to — this one included. That is exactly why the directive is shown in full instead of just asserted.
set $mcp_affinity_salt "…"; # the real value is never published
set_by_lua_block $mcp_affinity {
local auth = ngx.var.http_authorization
if auth then
local scheme, token = auth:match("^(%a+)%s+([^%s]+)")
if scheme and scheme:lower() == "bearer" and token ~= "" then
return ngx.md5(ngx.var.mcp_affinity_salt .. token)
end
end
return ngx.var.remote_addr or ""
}The practical effect: because each instance also has a fixed exit node (next section), landing on the same node means your calls keep appearing to come from the same address — stable, not alternating request to request.
Egress: which exit a request leaves from
Every outbound call, from any instance, leaves through a WireGuard tunnel to one of three exit nodes — a VPS run by IONOS in the United Kingdom, another in Spain, and a home connection elsewhere in Spain whose dynamic address is kept current by DDNS — never through the address of the network this server sits on.
The assignment is fixed per instance and identical for both servers: each has one instance on each of the three exit nodes, so two of its three instances leave through Spain and one through the United Kingdom. Whichever it is, what the destination sees is that exit's address, never this network's.
When something fails
An instance can crash or hang. An update takes one out on purpose, but that is not a failure nginx ever sees: the instance is drained first, as the next paragraph describes. A request whose connection to a crashed instance never opened is retried on another one — up to three attempts in all, and transparently: the client sees one answer, not a failure. A request that had already been delivered is not retried, and that is deliberate: a tool call can have side effects — a write to GitLab, say — and repeating one blindly is worse than reporting that it failed. A hung instance is that case: it accepts the call and never answers, so the call times out rather than being replayed. Three failures in a row, refused or timed out, set the instance aside for ten seconds; after that it is tried again, and a success keeps it in.
Underneath, Docker restarts an instance whose process dies, and a check that runs every two minutes recreates any container that has disappeared altogether. Updates take the same path, one instance at a time: it is marked down in nginx, its open connections get up to forty-five seconds to finish, it is recreated on the new version, it has to answer its own health check, and only then does it return — and none of that starts unless another instance of the same server is verifiably serving at that moment. Whoever was pinned to it is handed to another instance for those couple of minutes, and comes back afterwards.
- 0 s No answer Its connection is refused because the process is gone. A hung instance is the harder case: it accepts the call, lets it time out, and that call is not retried either.
- same call Retried The same request goes to another instance, up to three attempts in all; one already delivered is never replayed.
- 3 failures Set aside Ten seconds out, then tried again.
- 2 min Restarted Docker restarts a dead process at once; a check every two minutes notices a container that vanished and recreates it.
- next call Back One success keeps it in, and whoever was pinned to it returns with it.
An exit can fail too, and that is the failure this deployment is built around. Each instance's tunnel measures its own way out once a minute — a connection through the tunnel to the open internet and back, not a greeting to the far end, because a node can answer the handshake and still route nothing. Three failed readings in a row, and a watcher that reads them every minute takes that instance out of rotation: roughly three to four minutes after the cut, while the other two instances of that server carry on. It returns once it has been out for at least three minutes and has read as healthy on two checks a minute apart. Only when both instances that share an exit node have been out for twenty minutes — or sooner, if a second node fails and a server would be left with one instance — does the watcher move one of them to another node, leaving the other out as the sensor that says when the node is back; twenty minutes of that sensor working and the moved one goes home. If all six tunnels fail at once, nothing moves: that is this machine or the internet, not three nodes at once — and traffic is never sent out through the home connection instead.
- 0 min Cut The node stops routing; it may still answer the tunnel's handshake.
- about 3 min Unhealthy Three failed readings in a row, one a minute, through the tunnel to the open internet.
- about 4 min Out of rotation The watcher, reading every minute, marks the instance down in nginx; the other two carry on.
- +3 min Back If the exit recovers: out for at least three minutes, and healthy again on two checks a minute apart.
- +20 min Moved If it does not: with both instances of that node out for twenty minutes, or sooner if a second node fails, one moves to another node and the other stays out as the sensor.
- +20 min Home The sensor returns first, the same way as above; twenty minutes of it healthy and the moved one goes back.
What that means for you follows from the affinity section. Your key on the ring does not change when an instance is out: only the arc that instance owned is handed on, to the next point on the ring, so you land on one of the other two — the same one for as long as the outage lasts, not a different one per request — and everyone else stays where they were. Being handed on may change the country your calls appear to come from, since the substitute exits through its own; that comes back with your instance. Being moved is the reverse: after the couple of minutes the move itself takes, you are back on your instance, and its exit is that of its new node for as long as it is away from home. An instance that was only taken out of rotation still holds whatever it had for you when it returns; one that was recreated — for a move or an update — starts empty: for gitlab that is the one verification the affinity section already prices in, for libgen a cold read cache, which refills on use.
A personal service
None of this changes what these servers are: a personal project, run by one person, with no SLA and no guarantee that either endpoint stays online — or unchanged — from one day to the next.
This page was last updated on