Hi, everyone.
credits: OpenAI, esoterik, llama, soufiw
# C2-Loader — hardened loader
Console loader that pulls the build **from the auth server**, verifies it, and injects it.
No DLL is shipped next to the exe.
## Build
Flags: `/O2 /GL /Gw /Gy /GS /guard:cf /Qspectre /sdl /DYNAMICBASE /GR- /W4 /WX`,
link `/LTCG /OPT:REF /OPT:ICF /DEBUG:NONE /PDB:NONE /HIGHENTROPYVA /CETCOMPAT /GUARD:CF`.
## Auxiliary commands
## Endpoint config — `api.txt` next to the exe
Defaults are `127.0.0.1:8787`, plaintext.
## What the loader does on `[2]`
1. heartbeat — re-validate the license with the server (instant kill on revoke/expire)
2. decision gate — bytecode VM, see below
3. `GET /api/v1/payload?key=…&hwid=…` — downloads the active build
4. verify SHA-256 against the server header
5. verify the **RSA-2048 / SHA-256 signature** with the public key compiled into the binary
(the private key never leaves the server, so a patched server or a hand-made DLL cannot pass)
6. **manual map** the image into the target — headers, sections, relocations, imports,
TLS callbacks, section protections, `DllMain`
7. nothing is written to disk at any point; the buffer is wiped afterwards
## Manual map
| Step | Detail |
|---|---|
| base | preferred `ImageBase` when free (`VirtualQueryEx` probe), otherwise `VirtualAllocEx` anywhere; `delta` computed |
| headers | `SizeOfHeaders` bytes at the base |
| sections | each `SizeOfRawData` at `base + VirtualAddress` |
| relocations | `IMAGE_DIRECTORY_ENTRY_BASERELOC`, `IMAGE_REL_BASED_DIR64` only (x64) |
| imports | `IMAGE_DIRECTORY_ENTRY_IMPORT` (and delay-load when present); resolved locally when the target has the module at the same base, otherwise through a remote `GetProcAddress` |
| protections | `VirtualProtectEx` per section from its characteristics |
| TLS | `AddressOfCallBacks` rebased and invoked before `DllMain` (the static CRT registers one) |
| entry | `DllMain(base, DLL_PROCESS_ATTACH, 0)`; its return value is read back and treated as success |
A small position-independent stub (`ENDBR64` + 41 bytes) lives in the target and performs
the calls that must execute there. It is validated before use by calling `GetTickCount`
through it — a bad stub would otherwise take the game down.
Because the image is never handed to the loader, it does **not** appear in the target's
module list. Repeat injections inside one game session are caught with a cookie: a small
remote block holding a magic value and the mapped base, whose address is remembered in
`%LOCALAPPDATA%\CS2L\map_<pid>.bin`. Deleting that file defeats the check.
The DLL must be **x64**. Imports are resolved against the target, so a missing dependency
surfaces as `import <dllname>` rather than a silent failure.
## Anti-reversing layer (`harden.h`)
| Layer | What it does |
|---|---|
| String pool | every string XTEA-encrypted in `strings.h`, decrypted per use into a buffer that is `memset`-wiped on scope exit (`hz::Str`). `strings cs2_loader.exe` finds no `cs2.exe`, no API names, no endpoint paths. |
| Anti-debug | `PEB.BeingDebugged`, `NtQueryInformationProcess(ProcessDebugPort / ProcessDebugObjectHandle)`, `CheckRemoteDebuggerPresent`, the `OutputDebugString` + last-error trick, self-calibrated `rdtsc` vs `QueryPerformanceCounter` timing, `ThreadHideFromDebugger`. |
| Integrity | `kernel32`/`ntdll` must resolve from `%SystemRoot%\System32`, and the prologues of the six APIs the injector depends on must be unhooked (no `int3`, no `jmp`, no `ret`-stub). |
| Environment | FNV-1a hashed blocklist of debuggers / sandboxes, scanned across running processes. Recorded, not fatal by default (`guard.strict_env`). |
| Decision VM | the "licensed && !tampered && not expired" decision is bytecode executed by a tiny stack machine whose opcodes are re-keyed every launch (`seed = QPC ^ PID ^ rdtsc`), so it has a different byte signature on every run and cannot be located by pattern scanning. |
| Payload trust | RSA-2048 signature over the SHA-256 of the build — patching the loader is pointless because the server will not serve anything else. |
A detection never announces itself. It raises a flag, and the flag makes the VM gate return
"deny" — the process keeps running normally, which is what makes it hard to find the check.
### Adding a string
Add an entry to `tools/gen_strings.js`, run the generator, use `hz::Str(STR_ID)`.
Never put a literal in `main.cpp` — it will survive into the binary.
## Measured
* `strings` scan: no plaintext for target exe, endpoints, or API names
(only the imports the OS requires are visible, as always)
* `--selftest` green: SHA-256 vectors, all five VM gate outcomes, forged signature rejected
* `--stubs` green: stub calls `GetTickCount` and `lstrlenA` correctly, direct and via thread
* `--mapcheck` on the real `cs2.dll`: 10 sections, 59 relocation blocks / 8852 entries,
126 imports, TLS callbacks present
* end to end at the preferred base and at a relocated base: image mapped, `DllMain`
ran, staging directory empty, target alive
* second injection in the same session detected and refused
* `--guard` on a clean machine: `flags=0x0000`
## Limits — be honest about these
* A manual map is invisible to the module list but **very visible to memory scanners**:
anything walking the target's address space with `VirtualQueryEx` will find a private
region with a `MZ` header and a suspicious entry point. The loader's anti-RE work does
nothing for that — it belongs in the DLL.
* Cookie-based double-injection detection depends on a sidecar file. Delete it and the
loader will map a second copy.
* Anti-debug and integrity checks raise the cost of casual patching. A determined
reverser with time still gets there; VMProtect/Themida on top of this is the next step
if you want a higher floor.
* There is no packing: the `.text` section is normal code. `/O2 /GL` plus string
encryption is obfuscation, not packing.
* Anti-VM / sandbox detection is **off** by default on purpose — false positives cost you
paying customers.
Console loader that pulls the build **from the auth server**, verifies it, and injects it.
No DLL is shipped next to the exe.
Code:
```
cs2_loader/
main.cpp entry, menu, HTTP client, payload pipeline, injection
harden.h anti-reversing layer (strings, anti-debug, integrity, decision VM)
strings.h GENERATED - encrypted string pool
tools/gen_strings.js generator for strings.h
build_hardened.bat release build (LTCG, no PDB, full mitigations)
cs2_loader.exe output
```
## Build
Code:
```bat
node tools\gen_strings.js :: only when you add or change a string
build_hardened.bat
```
Flags: `/O2 /GL /Gw /Gy /GS /guard:cf /Qspectre /sdl /DYNAMICBASE /GR- /W4 /WX`,
link `/LTCG /OPT:REF /OPT:ICF /DEBUG:NONE /PDB:NONE /HIGHENTROPYVA /CETCOMPAT /GUARD:CF`.
## Auxiliary commands
Code:
```
cs2_loader.exe --selftest SHA-256 vectors + decision VM + forged-signature rejection
cs2_loader.exe --stubs run the remote-call stub in this process and verify it
cs2_loader.exe --mapcheck <dll> parse a PE and print sections/relocs/imports/TLS
cs2_loader.exe --hwid machine fingerprint the server stores
cs2_loader.exe --api endpoint in use
cs2_loader.exe --check <key> one-shot auth, non-interactive
cs2_loader.exe --guard integrity check bitmask (dev only, drop before release)
```
## Endpoint config — `api.txt` next to the exe
Code:
```
host=auth.example.com
port=443
secure=1
```
Defaults are `127.0.0.1:8787`, plaintext.
## What the loader does on `[2]`
1. heartbeat — re-validate the license with the server (instant kill on revoke/expire)
2. decision gate — bytecode VM, see below
3. `GET /api/v1/payload?key=…&hwid=…` — downloads the active build
4. verify SHA-256 against the server header
5. verify the **RSA-2048 / SHA-256 signature** with the public key compiled into the binary
(the private key never leaves the server, so a patched server or a hand-made DLL cannot pass)
6. **manual map** the image into the target — headers, sections, relocations, imports,
TLS callbacks, section protections, `DllMain`
7. nothing is written to disk at any point; the buffer is wiped afterwards
## Manual map
| Step | Detail |
|---|---|
| base | preferred `ImageBase` when free (`VirtualQueryEx` probe), otherwise `VirtualAllocEx` anywhere; `delta` computed |
| headers | `SizeOfHeaders` bytes at the base |
| sections | each `SizeOfRawData` at `base + VirtualAddress` |
| relocations | `IMAGE_DIRECTORY_ENTRY_BASERELOC`, `IMAGE_REL_BASED_DIR64` only (x64) |
| imports | `IMAGE_DIRECTORY_ENTRY_IMPORT` (and delay-load when present); resolved locally when the target has the module at the same base, otherwise through a remote `GetProcAddress` |
| protections | `VirtualProtectEx` per section from its characteristics |
| TLS | `AddressOfCallBacks` rebased and invoked before `DllMain` (the static CRT registers one) |
| entry | `DllMain(base, DLL_PROCESS_ATTACH, 0)`; its return value is read back and treated as success |
A small position-independent stub (`ENDBR64` + 41 bytes) lives in the target and performs
the calls that must execute there. It is validated before use by calling `GetTickCount`
through it — a bad stub would otherwise take the game down.
Because the image is never handed to the loader, it does **not** appear in the target's
module list. Repeat injections inside one game session are caught with a cookie: a small
remote block holding a magic value and the mapped base, whose address is remembered in
`%LOCALAPPDATA%\CS2L\map_<pid>.bin`. Deleting that file defeats the check.
The DLL must be **x64**. Imports are resolved against the target, so a missing dependency
surfaces as `import <dllname>` rather than a silent failure.
## Anti-reversing layer (`harden.h`)
| Layer | What it does |
|---|---|
| String pool | every string XTEA-encrypted in `strings.h`, decrypted per use into a buffer that is `memset`-wiped on scope exit (`hz::Str`). `strings cs2_loader.exe` finds no `cs2.exe`, no API names, no endpoint paths. |
| Anti-debug | `PEB.BeingDebugged`, `NtQueryInformationProcess(ProcessDebugPort / ProcessDebugObjectHandle)`, `CheckRemoteDebuggerPresent`, the `OutputDebugString` + last-error trick, self-calibrated `rdtsc` vs `QueryPerformanceCounter` timing, `ThreadHideFromDebugger`. |
| Integrity | `kernel32`/`ntdll` must resolve from `%SystemRoot%\System32`, and the prologues of the six APIs the injector depends on must be unhooked (no `int3`, no `jmp`, no `ret`-stub). |
| Environment | FNV-1a hashed blocklist of debuggers / sandboxes, scanned across running processes. Recorded, not fatal by default (`guard.strict_env`). |
| Decision VM | the "licensed && !tampered && not expired" decision is bytecode executed by a tiny stack machine whose opcodes are re-keyed every launch (`seed = QPC ^ PID ^ rdtsc`), so it has a different byte signature on every run and cannot be located by pattern scanning. |
| Payload trust | RSA-2048 signature over the SHA-256 of the build — patching the loader is pointless because the server will not serve anything else. |
A detection never announces itself. It raises a flag, and the flag makes the VM gate return
"deny" — the process keeps running normally, which is what makes it hard to find the check.
### Adding a string
Add an entry to `tools/gen_strings.js`, run the generator, use `hz::Str(STR_ID)`.
Never put a literal in `main.cpp` — it will survive into the binary.
## Measured
* `strings` scan: no plaintext for target exe, endpoints, or API names
(only the imports the OS requires are visible, as always)
* `--selftest` green: SHA-256 vectors, all five VM gate outcomes, forged signature rejected
* `--stubs` green: stub calls `GetTickCount` and `lstrlenA` correctly, direct and via thread
* `--mapcheck` on the real `cs2.dll`: 10 sections, 59 relocation blocks / 8852 entries,
126 imports, TLS callbacks present
* end to end at the preferred base and at a relocated base: image mapped, `DllMain`
ran, staging directory empty, target alive
* second injection in the same session detected and refused
* `--guard` on a clean machine: `flags=0x0000`
## Limits — be honest about these
* A manual map is invisible to the module list but **very visible to memory scanners**:
anything walking the target's address space with `VirtualQueryEx` will find a private
region with a `MZ` header and a suspicious entry point. The loader's anti-RE work does
nothing for that — it belongs in the DLL.
* Cookie-based double-injection detection depends on a sidecar file. Delete it and the
loader will map a second copy.
* Anti-debug and integrity checks raise the cost of casual patching. A determined
reverser with time still gets there; VMProtect/Themida on top of this is the next step
if you want a higher floor.
* There is no packing: the `.text` section is normal code. `/O2 /GL` plus string
encryption is obfuscation, not packing.
* Anti-VM / sandbox detection is **off** by default on purpose — false positives cost you
paying customers.
# CS2 AUTH — license server + control panel
Zero dependencies. Node >= 18 (tested on Node 20). All data lives in `db.json`.
```
cs2_auth/
server.js HTTP server: loader API + admin API + static panel
config.json admin password hash + HMAC secret (created on first run)
db.json keys, bindings, login counters, event log, active build
keys/ RSA-2048 signing keypair (private key must stay on the server)
payloads/ uploaded builds
start.bat launcher
public/
index.html panel (login + dashboard + licenses + log)
style.css dark / crimson theme
app.js vanilla JS, no build step
```
## 1. Start
```bat
start.bat
REM or:
node server.js
REM or with your own password / port:
set ADMIN_PASS=my-password
set PORT=9000
node server.js
```
First run prints the admin password once (unless `ADMIN_PASS` is set).
Panel: **
Default `HOST=0.0.0.0`, so the loader can reach it from other machines on the LAN
once you allow the port in the firewall:
```bat
netsh advfirewall firewall add rule name="CS2 Auth" dir=in action=allow protocol=TCP localport=8787
```
## 2. Panel
* **Dashboard** — tiles (total / active / lifetime / expired / unbound / online 24h /
logins 24h / denied 24h) and the issue form.
* **Issue license** — duration in days (`0` = lifetime), prefix, note, optional preset
HWID. If *bind to first machine* is on and no HWID is preset, the license binds to
whatever machine logs in first.
* **Licenses** — search + status filter, per-row: `COPY`, `+D` (extend),
`UNBIND` (free the machine), `REVOKE` / `RESTORE`, `DEL`.
* **Event log** — every login, bind, mismatch, revoke, issue, extend, delete.
A revoked or expired license is cut off on the next loader heartbeat (the loader
re-checks every 60 s and always right before injecting).
## 3. Build delivery (payload)
The DLL is **not** shipped with the loader. The loader downloads the active build from
the server, verifies its SHA-256 and its **RSA-2048 / SHA-256 signature**, stages it,
injects it and wipes the file. The private key lives only in `keys/private.pem`.
On first run the server generates `keys/` (RSA-2048) if it is missing.
### Panel → **Payload** tab
* Drag & drop (or browse) the compiled `.dll`
* Version, build note, "make this the active build"
* Active card: version, size, SHA-256, signature, download link, **force update** toggle
* History: activate / download / delete any previous build
`force update` sets `X-Force-Update: 1` so clients never run a stale cached build.
### How the loader consumes it
```
GET /api/v1/payload?key=…&hwid=…
200 + application/octet-stream
X-Payload-Version: 2.1.0
X-Payload-Sha256: 1b73e879…
X-Payload-Signature: <base64 RSA-2048/SHA-256 over the file>
X-Auth-Token: <hmac>
```
Failure modes: `401 invalid_key`, `403 revoked|expired|hwid_mismatch`, `404 no_payload`.
```
GET /api/v1/pubkey public key (JWK) for clients that want it
POST /api/admin/payload X-Payload-Version / -Note / -Filename / -Activate
GET /api/admin/payloads active build + history + force flag
POST /api/admin/payload/activate {version}
POST /api/admin/payload/force {force:true|false}
GET /api/admin/payload/:version/download
DELETE /api/admin/payload/:version
```
The upload endpoint rejects anything that is not a PE image (checks for the `MZ` header),
caps the size at 32 MB, signs the bytes and stores the file in `payloads/`.
### Regenerating the signing key
Delete `keys/private.pem` and `keys/public.pem` and restart. **Existing payloads keep
their old signatures** and the loader will reject them — re-upload every build after a
rotation. The loader binary has the public modulus compiled in (`tools/gen_strings.js`,
entries `P_PUB_N` / `P_PUB_E`), so a new key means a new loader build.
## 4. Loader API (what the exe calls)
| Endpoint | Purpose |
|---|---|
| `POST /api/v1/auth` | login: `{key, hwid, machine, version, ts}` → `{ok, reason, token, sub:{...}}` |
| `POST /api/v1/heartbeat` | keep-alive, re-issues the token, kills access if cut |
| `POST /api/v1/validate-token` | let the injected DLL verify `token` on its own |
`reason` on failure: `invalid_key`, `revoked`, `expired`, `hwid_mismatch`.
`token` = `HMAC-SHA256(secret, key|hwid|expires)`. The DLL can recompute it with the
same secret and refuse to run if it does not match.
## 5. Admin API
All under `/api/admin/*`, cookie session (`cs2_sid`, HttpOnly, 8 h), created by
`POST /api/admin/login {password}`.
```
GET /api/admin/stats
GET /api/admin/keys
POST /api/admin/keys {days, prefix?, note?, hwid?, lock_hwid?}
POST /api/admin/keys/:id/revoke | /unbind | /extend {days} | /note {note}
DELETE /api/admin/keys/:id
GET /api/admin/log
POST /api/admin/logout
```
## 6. Loader side
Put `api.txt` next to `cs2_loader.exe` when the server is not on localhost:
```
host=your.domain.com
port=443
secure=1
```
Defaults are `127.0.0.1:8787`. The loader ships the machine fingerprint
`sha256(MachineGuid + volume serial + salt)` — the raw serial never leaves the client,
but the raw MachineGuid is sent once as `machine` so you can recognise the box in the panel.
## 7. Before going public
1. **HTTPS.** Terminate TLS in front of the server — Caddy is one line:
```
auth.example.com {
reverse_proxy 127.0.0.1:8787
}
```
Then `secure=1` + `host=auth.example.com` in the loader's `api.txt`. Plain HTTP means
the key travels in clear text and anyone can replay it.
2. **Back up `db.json`** (and keep `config.json` — losing it invalidates every issued
token). The file is written atomically and re-read if you edit it by hand while the
server runs; a broken file is moved aside instead of being silently wiped.
3. **Change the admin password** — delete `config.json` and restart (a new one is
generated), or set `ADMIN_PASS` before starting.
## 8. Notes
* Single-process, single-file JSON store. Fine for hundreds/thousands of keys.
If you need multiple processes or >~100k keys, swap `loadDb`/`saveDb` for SQLite.
* Rate limiting is intentionally absent — put it in your reverse proxy if the panel
or the loader API is internet-facing.
* Password and key comparisons use `crypto.timingSafeEqual`.
Zero dependencies. Node >= 18 (tested on Node 20). All data lives in `db.json`.
```
cs2_auth/
server.js HTTP server: loader API + admin API + static panel
config.json admin password hash + HMAC secret (created on first run)
db.json keys, bindings, login counters, event log, active build
keys/ RSA-2048 signing keypair (private key must stay on the server)
payloads/ uploaded builds
start.bat launcher
public/
index.html panel (login + dashboard + licenses + log)
style.css dark / crimson theme
app.js vanilla JS, no build step
```
## 1. Start
```bat
start.bat
REM or:
node server.js
REM or with your own password / port:
set ADMIN_PASS=my-password
set PORT=9000
node server.js
```
First run prints the admin password once (unless `ADMIN_PASS` is set).
Panel: **
You must be registered for see links
— login `admin` + that password.Default `HOST=0.0.0.0`, so the loader can reach it from other machines on the LAN
once you allow the port in the firewall:
```bat
netsh advfirewall firewall add rule name="CS2 Auth" dir=in action=allow protocol=TCP localport=8787
```
## 2. Panel
* **Dashboard** — tiles (total / active / lifetime / expired / unbound / online 24h /
logins 24h / denied 24h) and the issue form.
* **Issue license** — duration in days (`0` = lifetime), prefix, note, optional preset
HWID. If *bind to first machine* is on and no HWID is preset, the license binds to
whatever machine logs in first.
* **Licenses** — search + status filter, per-row: `COPY`, `+D` (extend),
`UNBIND` (free the machine), `REVOKE` / `RESTORE`, `DEL`.
* **Event log** — every login, bind, mismatch, revoke, issue, extend, delete.
A revoked or expired license is cut off on the next loader heartbeat (the loader
re-checks every 60 s and always right before injecting).
## 3. Build delivery (payload)
The DLL is **not** shipped with the loader. The loader downloads the active build from
the server, verifies its SHA-256 and its **RSA-2048 / SHA-256 signature**, stages it,
injects it and wipes the file. The private key lives only in `keys/private.pem`.
On first run the server generates `keys/` (RSA-2048) if it is missing.
### Panel → **Payload** tab
* Drag & drop (or browse) the compiled `.dll`
* Version, build note, "make this the active build"
* Active card: version, size, SHA-256, signature, download link, **force update** toggle
* History: activate / download / delete any previous build
`force update` sets `X-Force-Update: 1` so clients never run a stale cached build.
### How the loader consumes it
```
GET /api/v1/payload?key=…&hwid=…
200 + application/octet-stream
X-Payload-Version: 2.1.0
X-Payload-Sha256: 1b73e879…
X-Payload-Signature: <base64 RSA-2048/SHA-256 over the file>
X-Auth-Token: <hmac>
```
Failure modes: `401 invalid_key`, `403 revoked|expired|hwid_mismatch`, `404 no_payload`.
```
GET /api/v1/pubkey public key (JWK) for clients that want it
POST /api/admin/payload X-Payload-Version / -Note / -Filename / -Activate
GET /api/admin/payloads active build + history + force flag
POST /api/admin/payload/activate {version}
POST /api/admin/payload/force {force:true|false}
GET /api/admin/payload/:version/download
DELETE /api/admin/payload/:version
```
The upload endpoint rejects anything that is not a PE image (checks for the `MZ` header),
caps the size at 32 MB, signs the bytes and stores the file in `payloads/`.
### Regenerating the signing key
Delete `keys/private.pem` and `keys/public.pem` and restart. **Existing payloads keep
their old signatures** and the loader will reject them — re-upload every build after a
rotation. The loader binary has the public modulus compiled in (`tools/gen_strings.js`,
entries `P_PUB_N` / `P_PUB_E`), so a new key means a new loader build.
## 4. Loader API (what the exe calls)
| Endpoint | Purpose |
|---|---|
| `POST /api/v1/auth` | login: `{key, hwid, machine, version, ts}` → `{ok, reason, token, sub:{...}}` |
| `POST /api/v1/heartbeat` | keep-alive, re-issues the token, kills access if cut |
| `POST /api/v1/validate-token` | let the injected DLL verify `token` on its own |
`reason` on failure: `invalid_key`, `revoked`, `expired`, `hwid_mismatch`.
`token` = `HMAC-SHA256(secret, key|hwid|expires)`. The DLL can recompute it with the
same secret and refuse to run if it does not match.
## 5. Admin API
All under `/api/admin/*`, cookie session (`cs2_sid`, HttpOnly, 8 h), created by
`POST /api/admin/login {password}`.
```
GET /api/admin/stats
GET /api/admin/keys
POST /api/admin/keys {days, prefix?, note?, hwid?, lock_hwid?}
POST /api/admin/keys/:id/revoke | /unbind | /extend {days} | /note {note}
DELETE /api/admin/keys/:id
GET /api/admin/log
POST /api/admin/logout
```
## 6. Loader side
Put `api.txt` next to `cs2_loader.exe` when the server is not on localhost:
```
host=your.domain.com
port=443
secure=1
```
Defaults are `127.0.0.1:8787`. The loader ships the machine fingerprint
`sha256(MachineGuid + volume serial + salt)` — the raw serial never leaves the client,
but the raw MachineGuid is sent once as `machine` so you can recognise the box in the panel.
## 7. Before going public
1. **HTTPS.** Terminate TLS in front of the server — Caddy is one line:
```
auth.example.com {
reverse_proxy 127.0.0.1:8787
}
```
Then `secure=1` + `host=auth.example.com` in the loader's `api.txt`. Plain HTTP means
the key travels in clear text and anyone can replay it.
2. **Back up `db.json`** (and keep `config.json` — losing it invalidates every issued
token). The file is written atomically and re-read if you edit it by hand while the
server runs; a broken file is moved aside instead of being silently wiped.
3. **Change the admin password** — delete `config.json` and restart (a new one is
generated), or set `ADMIN_PASS` before starting.
## 8. Notes
* Single-process, single-file JSON store. Fine for hundreds/thousands of keys.
If you need multiple processes or >~100k keys, swap `loadDb`/`saveDb` for SQLite.
* Rate limiting is intentionally absent — put it in your reverse proxy if the panel
or the loader API is internet-facing.
* Password and key comparisons use `crypto.timingSafeEqual`.
credits: OpenAI, esoterik, llama, soufiw
Password for .zip file is hvh.net
Last edited by a moderator:


