- Rust 81%
- Python 10.8%
- Shell 3.4%
- JavaScript 2%
- C 1%
- Other 1.8%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| bench | ||
| browser | ||
| config | ||
| deploy | ||
| docs | ||
| LICENSES | ||
| rarng | ||
| release/evidence | ||
| src | ||
| tests | ||
| tools | ||
| vendor/hickory-server | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| deny.toml | ||
| docker-compose.bridge.yml | ||
| docker-compose.ps5.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| README.md | ||
| REUSE.toml | ||
| THIRD_PARTY_NOTICES.md | ||
WebSploot
WebSploot is one Rust/Tokio binary that provides filtering DNS and hosts
PlayStation user-guide content. It answers UDP and TCP DNS, serves a
console-focused landing page, mounts guide trees directly at /ps4/ and
/ps5/, and supports the firmware URL shape
/document/{lang}/{console}/.
The server loads verified content, gzip, and Brotli representations into memory. Serving static files does not touch the filesystem or recompress content. Upstream DNS uses Hickory's reusable UDP/TCP connection pool with failover and TCP fallback, surrounded by websploot's bounded cache, singleflight, failure suppression, and stale-on-error behavior.
WebSploot terminates plain HTTP only. Put Caddy or another reverse proxy in
front when public TLS is needed. manuals.playstation.net must normally remain
plain HTTP: a public CA cannot issue a certificate for Sony's hostname.
Native quick start
Build, generate configuration, install the pinned PS4 and PS5 guides, and validate the complete deployment:
cargo build --release
# Interactive on a terminal; public is the prompted default. `setup` is
# idempotent, so rerun it after an interrupted download.
sudo ./target/release/websploot \
--config /etc/websploot/websploot.toml setup \
--state-dir /var/lib/websploot
For automation, the public profile requires an address. Public recursion is the default and always runs behind the fixed safety ceilings described below:
sudo ./target/release/websploot \
--config /etc/websploot/websploot.toml setup \
--profile public \
--dns-address 192.0.2.10 \
--override-address 192.0.2.10 \
--state-dir /var/lib/websploot
Other profiles are lan (private recursion) and web-only (DNS disabled).
init is the configuration-only primitive and supports init --stdout for
container bootstrap. It refuses to replace an existing configuration unless
--force is supplied. setup adds guide state, installs both immutable guide
archives, and runs the same complete validation as check. Displayed DNS
addresses and the exact
manuals.playstation.net override are requested separately because they are
different configuration responsibilities.
Generated configurations trust X-Forwarded-For only from loopback Caddy.
Replace server.trusted_proxies with the proxy's exact peer CIDR when Caddy is
on another host or container network; never put an end-user or public range in
that list. Administrative HTTP access is default-deny: repeat --admin-client
at setup time, or explicitly populate [admin].allow_clients, with only the
resolved client CIDRs that need the detailed APIs and metrics.
Inspect the installed revision or validate again without binding a port, then run:
websploot --config /etc/websploot/websploot.toml check
websploot --config /etc/websploot/websploot.toml guides status
sudo websploot --config /etc/websploot/websploot.toml run
The PS4 catalog pins WebKitty commit
10846c6cf201d62f71d8374edc08d489331a6368 from its renamed upstream
ArabPixel/WebKitty repository and archive SHA-256
4d20bd7e5c38b0c239220ed7ae9228c20cd4c3a6bcda0e9f4a605654e0a006e0.
The archive is downloaded with a hard size limit, hash-checked, extracted into
a traversal-safe staging directory, manifested, preloaded, and only then
activated. The previous revision is retained.
The PS5 catalog and offline product share one SlopKit lock at commit
85b5759dd9a48f3cc17650065484314419c43577 with archive SHA-256
99ab56622ff06563b2ff6891a9e1cab1301c678ae530cfcf3d047ab3399f874c.
It contains no server or telemetry client, setting, route, or telemetry storage. During
installation, WebSploot relocates the release's root AppCache inventory beneath
/ps5/, rewrites its source link to the public repository, generates a strict
served-tree manifest, validates offline closure, and atomically activates it.
An administrator-provided --ps5-root can replace managed SlopKit. The public
release intentionally omits the optional browser-install handoff declaration;
to enable that action, pass an override tree with the capability declaration
and the installed tile target. setup validates the contract before writing
the generated configuration:
websploot --config /etc/websploot/websploot.toml setup \
--profile public --dns-address 192.0.2.10 \
--ps5-root /srv/websploot/ps5 \
--public-url https://guides.example.com/ \
--browser-category media \
--state-dir /var/lib/websploot
PS5 browser ELF products
Each release attaches two independently installable Media tiles; ELFs are
generated under target/browser-products/ and are not committed:
WSPL00001-websploot-online.elfinstalls WebSploot and openshttps://jb.rar.ng/. DNS, live status, and server-backed content remain available because this is the network variant.WSPL00002-websploot-offline.elfinstalls WebSploot Offline at the dedicatedhttps://web.sploot/origin. The ELF pre-seeds the shared home, pinned SlopKit tree, fallback navigation, license, notices, and provenance directly into the foreground PS5 profile's WebKit AppCache database. Its bundled pages need neither DNS nor an HTTP server.
Both pages share the address bar, favorites, and recents model. The offline variant permanently favorites SlopKit and omits DNS, service-status, and installer controls. Its address bar may still open external HTTP(S) URLs when connectivity exists.
Verify generated artifacts and exact hashes through the locked external builder interface with:
python3 tools/browser_artifacts.py products-check
python3 tools/browser_artifacts.py inspect \
target/browser-products/WSPL00002-websploot-offline.elf --json
The deterministic offline exporter and full two-product rebuild are documented
in browser/QUICKSTART.md. Physical firmware evidence
is exact-artifact scoped; see
docs/console-acceptance.md.
Guide management
websploot --config websploot.toml guides status
websploot --config websploot.toml guides install [ps4|ps5]
websploot --config websploot.toml guides install ps5 --archive ./pinned.tar.gz
websploot --config websploot.toml guides update --check
websploot --config websploot.toml guides update [ps4|ps5]
websploot --config websploot.toml guides rollback ps5
Managed revisions live below the guide state directory. current changes by an
atomic symlink rename. A guide lock generation is polled by the running server;
successful changes are loaded off the serving threads and published through an
ArcSwap. A malformed or mismatched generation leaves the previous in-memory
snapshot live.
There is no runtime git fetch, reset --hard, or Git dependency. Existing
custom [[tree]] root configurations remain supported.
Configuration reference
The annotated, load-tested reference is
config/websploot.example.toml. Important
production surfaces are:
http.listenanddns.listen: one socket string or an array of up to 16; dual-stack deployments normally use explicit IPv4 and IPv6 sockets.http.max_connections: shared HTTP admission cap, default 32768.[dns.transport]: UDP task, global TCP, per-prefix TCP, idle-time, and per-connection query bounds. Defaults are 8192, 4096, 64, 5 seconds, and 256.dns.allow_clients: recursion ACL. The public profile deliberately uses both all-address CIDRs; the LAN profile emits private CIDRs only.dns.trusted_clients: bypasses only per-prefix/per-client controls, never global resource ceilings.[dns.lists]: local paths or HTTP(S) sources. The built-in PSN snapshot is immediately usable; default project lists refresh conditionally every 12h.[admin].allow_clients: the exact resolved client CIDRs allowed to use detailed health, DNS status, and metrics. It is empty by default.[[tree]]: administrator-provided roots or the managed PS4/PS5currentsymlinks. Direct mounts and deep manual assets remain stable.[telemetry]:mode = "off"by default;anonymousenables only this instance's fixed-schema SQLite store.
landing.auto_redirect, landing.redirect_delay, and
tree.console_index are removed. During 0.x they produce actionable migration
errors rather than being treated as unknown keys. The default log filter is
warn,websploot=info; set RUST_LOG only when diagnosing a bounded interval.
DNS behavior
Policy precedence is:
- exact
[[dns.override]]local data; - filter allowlist bypass;
- blocklist;
- upstream recursion.
An allowlist entry no longer creates a local address. The legacy
dns.allow_ips key maps only to an exact manuals.playstation.net override for
one migration cycle.
The built-in psn preset contains the shipped PSN block/allow snapshot. Local
and cached remote sources layer over it, and startup never depends on a network
fetch. config/psn-snapshot.json records the source-base revision, snapshot
date, line counts, and exact hashes; tools/psn_snapshot.py checks or
deliberately refreshes it from a local psn-blocklist checkout. preset = "none" enables a completely custom ruleset.
An exact override may contain independent IPv4 and IPv6 addresses:
[[dns.override]]
name = "manuals.playstation.net"
addresses = ["192.0.2.10", "2001:db8::10"]
ttl = 300
A and AAAA receive only their configured family. Unsupported types receive
cacheable NODATA with SOA. Blocked A/AAAA answers use zero IPs by default;
block_mode = "nxdomain" is available.
Forwarding provides:
- reusable pooled UDP/TCP upstream transports and TCP retry after truncation;
- at most two upstream attempts within a two-second end-to-end deadline;
- one shared flight for identical concurrent cache misses;
- a 4,096-request global forwarding ceiling;
- a bounded cache of at most 100,000 entries and 256 MiB, with TTL aging and DO/CD-bit keys;
- passive per-upstream health with 1, 2, 4, 8, 16, then 30-second backoff, healthy-server preference, and one half-open recovery probe;
- five-second failure caching;
- stale serving for up to one hour after an upstream failure, with a 30-second response TTL;
- complete answer, authority, additional, response-code, EDNS, and DNSSEC state;
- per-upstream state, retry time, latency, success, failure, and timeout data in admin JSON and Prometheus, plus stale, coalescing, and in-flight metrics.
DO, CD, and AD state is preserved, but WebSploot does not validate DNSSEC itself. Validation is delegated to the configured recursive upstream.
Malformed and multi-question requests are rejected. UDP answers never exceed
1232 bytes; oversized answers set TC=1 so a real client retries over TCP.
ANY receives a small RFC 8482-style HINFO response.
Public recursion warning
The requested default allows 0.0.0.0/0 and ::/0. Loopback, RFC1918, and ULA
clients are trusted by default; trusted clients bypass per-client controls but
not global resource ceilings. Other clients receive 50 QPS/100 burst budgets
and response-rate limiting at 15 responses/sec/30 burst, aggregated over IPv4
/24 and IPv6 /56.
A global 50,000 QPS and 100 Mbps response ceiling applies in addition. Limiter tables have fixed memory and attacker-chosen keys cannot grow them.
These controls mitigate abuse; they cannot eliminate reflector or volumetric risk. Restricting recursion is still the IETF recommendation in RFC 5358. Firewall/provider UDP protection remains necessary for an internet-facing listener. WebSploot keeps a plain statement of this residual risk in setup/check output and startup logs. The warning is intentionally non-blocking; there is no acknowledgement setting, and operational warnings are not exposed on the public health page.
Also check port 53 before deployment. systemd-resolved, another DNS daemon,
or a provider firewall commonly owns or blocks it.
For an internet-facing resolver, apply coarse upstream filtering before the process: provider DDoS controls and an nftables rate/policer sized above the measured legitimate peak. Application admission preserves service under bounded overload but cannot recover bandwidth already saturated on the wire.
For example, after measuring the real peak and choosing a deliberately higher
edge ceiling, an optional host rule can discard excess UDP packets before they
allocate application work (replace 100000/second, the table, and the chain to
match the host's existing policy):
table inet websploot_edge {
chain input {
type filter hook input priority filter; policy accept;
udp dport 53 limit rate over 100000/second counter drop
}
}
The provider-side equivalent is an ACL exposing only required TCP/UDP ports, a UDP packets/second alert above the measured envelope, and automatic scrubbing or upstream null-routing for traffic the host link cannot absorb. Test either policy from a separate source before production; an edge rule that is set below legitimate bursts merely moves the outage in front of WebSploot.
HTTP routes
| Route | Access | Purpose |
|---|---|---|
/ |
public | landing page |
/ps4/, /ps5/ |
public | direct guide mounts |
/document/{lang}/{id}/ |
public | console firmware URL; index redirects to /?console={id}&lang={lang} |
/websploot.manifest |
public | versioned offline inventory for the home page and compatible guides |
/health |
public | sanitized human status and configured DNS addresses |
/livez |
public | process liveness |
/readyz |
public | listener/content readiness |
/healthz |
public | SlopKit-compatible build/status identity |
/api/v1/telemetry/batch |
public when enabled | anonymous fixed-schema telemetry |
/api/v1/actions/browser-installer |
public when configured | no-store schema-1 action descriptor |
/api/v1/actions/browser-installer/{sha256}.elf |
public when configured | immutable specialized ELF; GET/HEAD only |
/api/v1/health |
admin CIDRs | detailed health |
/api/v1/dns/status |
admin CIDRs | DNS/cache/limiter detail |
/metrics |
admin CIDRs | Prometheus metrics |
/api/health and /api/dns/status are protected 0.x compatibility aliases and
will be removed after one 0.x release.
The action routes return 404 when [browser] is absent and deliberately do not
enable CORS. Static files support strong representation-specific ETags,
conditional 304s, byte ranges, HEAD, AppCache manifests, and precompressed
negotiation. Revalidated static responses also allow a saved response to be
used for up to one year when the origin fails; online requests remain
immediately revalidated. Direct mounts never redirect back to /, so the
landing flow cannot loop.
Offline caching
The built-in landing page declares /websploot.manifest, a bounded
HTML5 Application Cache
inventory generated from the exact in-memory content
generation. Its version changes when any owned landing variant, path, or guide
byte changes, so a successful reload publishes a matching cache update. It
includes the home variants, local artwork, and served files from compatible
guides that do not own an AppCache manifest. Dynamic health, telemetry, and
action APIs remain in the NETWORK: * section and keep their no-store policy.
Guide ownership is explicit. If any HTML page in a guide declares its own
manifest, the complete tree is delegated to that guide. This preserves the
external PS4 guide's five firmware-specific manifests under
includes/caches/manifest/. Managed SlopKit likewise owns
/ps5/slopkit.appcache and its compatibility alias; root URLs are
deterministically relocated beneath /ps5/ during installation.
WebSploot also enforces SlopKit's narrow relative
notify.html notify.html fallback so unique renderer warm-up query URLs reuse
the cached document when the origin is unreachable; arbitrary network and API
requests remain outside that fallback. The managed revision name versions this
deterministic transformation independently of the verified upstream archive,
so changing it publishes a new tree instead of overwriting an installed
revision. A guide with no manifest is preloaded by the WebSploot home cache; a
custom guide that declares
manifest="/websploot.manifest" joins that cache when opened directly. The
generated home manifest is capped at 8 MiB, counted in
http.max_loaded_content_bytes, and reports any omitted unsafe/oversized tree
through admin health.
Every guide-owned manifest is closure-checked before publication. Same-origin
CACHE entries and FALLBACK targets must resolve to served files under a
direct or manual mount. Lexical parent segments are allowed only while they
remain inside the guide root; root escapes, encoded separators, denied files,
malformed sections, and missing targets fail closed while the last-good
generation stays active. Makefile is intentionally not in the default deny
set because the pinned PS4 guide requires it offline.
On PlayStation browsers, wait for Home available offline before deliberately
disconnecting the server. The first successful load is required: no cache can
make content available before the console has downloaded it. AppCache is a
legacy API retained by the console WebKit builds; newer browsers that removed
it still benefit from validators and
stale-if-error, but their offline
behavior depends on their HTTP cache implementation.
The landing footer deliberately says Home available offline because that
event covers /websploot.manifest, not a guide-owned inventory. Open managed
SlopKit once and let its separate /ps5/slopkit.appcache download finish before
disconnecting the origin or relying on the launcher after a reboot. When
upgrading an already-cached deployment, close and reopen SlopKit once more while
the origin is reachable to activate the downloaded cache generation.
Landing page
The built-in page is a flat, dependency-free graphite design for 10-foot and D-pad use: a 5% overscan-safe area, content width up to 1280px, 18px-or-larger copy, 64px controls, and a 4px gold focus ring. All CSS and inline SVG controls ship in the binary; there are no remote fonts or third-party visual assets. Flexbox declarations precede grid declarations for older console WebKit. Focus never scales a control and the page does not intercept D-pad/arrow keys; it leaves spatial movement to the console browser. Targets exceed WCAG 2.2's minimum target-size guidance, with a persistent treatment aligned to its focus-appearance guidance.
Its order is a compact aligned title/settings row, address/search form,
favorites, administrator quick links, recents, and a compact DNS/status footer.
Favorites begin with server-rendered, non-removable console-guide entries:
PS4 / WebKitty and PS5 / SlopKit. Recognized PS4/PS5
clients see only their matching entry; unknown clients see both. An absent guide
remains visible as Not installed without a broken link, so a failed or
intentionally skipped guide install does not break the home page. Install Browser joins the same pinned shelf only when the PS5 capability contract is
ready. The address form permits only HTTP(S)
and same-origin paths; websploot never proxies an entered URL. Console- and
locale-specific landing documents are private, immediately revalidated, and
vary on User-Agent and Accept-Language. Their strong ETags avoid resending an
unchanged page, while AppCache supplies the console-offline copy.
User favorites (websploot.favourites) remain capped at 24, independently of
the pinned guide entries, and recents (websploot.recents) at eight. Favorites
may contain safe same-origin paths, but pinned destinations are removed from
stored favorites to avoid duplicate entries. Recents retain only cross-origin
HTTP(S) destinations; old same-origin entries are pruned when rendered. Removing
one favorite or clearing all saved favorites and recents requires confirmation;
pinned entries are never written to or cleared from local storage. The obsolete
websploot.autoopen key is ignored. Storage denial/quota failures disable only
these conveniences.
Settings and the destructive-action confirmation are ARIA modals with background focus suppression, focus trapping, Escape/cancel handling, and focus restoration. Settings controls preferred language, clearing local history, and opening health. Its two actions share the available width equally and stack on narrow screens. WebSploot never automatically opens a guide.
All interface copy, ARIA labels, and direction come from an embedded catalog.
Catalogs must have equal key sets and are RTL-ready; English is the only current
catalog. Selection precedence is
URL lang → stored preference → Accept-Language → English. The JavaScript is
ES5 and uses no dependencies, fetch, modern URL API, or generated markup from
stored URLs.
Content manifests
The canonical generated build-manifest.json shape is compatible with slopkit:
{
"schema": 1,
"algorithm": "sha256",
"buildId": "…64 lowercase hex…",
"sourceRevision": "optional",
"files": {
"index.html": {"sha256": "…", "size": 1234}
}
}
buildId hashes sorted lines of
<sha256> <size> <path>\n. Generate one with:
websploot manifest /path/to/tree --revision REV
The reader also accepts slopkit's validated releaseBuildId and
qualifiedRuntimeId identity fields; websploot does not invent either field
when generating a guide manifest.
verify = "strict" serves only verified inventory. manifest-if-present
scans only when the manifest is absent; a present malformed, incomplete, or
mismatched manifest fails closed. The earlier supported manifest schema remains
readable without preserving any retired project-name aliases.
Every tree is preflighted before body allocation. max_files,
max_file_bytes, and max_total_bytes bound its raw inventory;
http.max_loaded_content_bytes bounds identity plus enabled compressed
representations, installer bytes, pre-rendered landing snapshots, and the
generated offline inventory. Reloads
must fit the current and complete staged generations simultaneously. A failed
size, integrity, traversal, symlink, capability, or memory check leaves the old
generation serving.
Browser installer and PS5 handoff
[browser] is optional. If absent, startup is normal, Health reports “not
configured,” and no install action is shown. If present, a console-enabled
ps5 tree must include websploot-capabilities.json with exactly the schema-1
postActionHandoff contract. A typical section is:
[browser]
target_url = "https://guides.example.com/"
category = "media"
title_id = "WSPL00001"
title = "WebSploot"
language = "en-US"
version = "01.00"
category accepts only game and media, which emit PS5
applicationCategoryType values 0 and 65536 respectively. Omitting it
preserves the legacy implicit Games metadata byte-for-byte; newly generated
configuration writes an explicit category. Adding or changing an explicit
category changes installed metadata, so remove an existing shortcut with the
same title ID through the PS5 UI before installing the newly categorized
version.
The PS5 card links only to /ps5/?post-action=browser-installer. A compatible
SlopKit guide accepts that fixed ID, fetches only the fixed same-origin
descriptor path, validates its exact schema/path/size/hash/firmware coverage,
and queues it for the next committed jailbreak attempt. After elfldr is ready,
SlopKit verifies and sends the requested ELF before an optional terminal
Payload Manager action. No descriptor or artifact URL is accepted from a query
parameter.
The embedded ELF is a locked carrier generated by the separately maintained
ps5-browser-builder. WebSploot checks its hash, ELF/PT_LOAD layout, and unique
4096-byte configuration record, then specializes only that record at startup.
Production does not need an SDK. The builder’s released OCI digest, CLI
contract, carrier, artwork, qualification, and provenance are bound in
browser/README.md.
SlopKit integration
WebSploot is the production static host for the public SlopKit tree. Run only
this process in production; no SlopKit development server accompanies it. The
managed release contains no telemetry implementation and generated
configurations keep [telemetry].mode = "off".
WebSploot retains /healthz and its fixed-schema telemetry endpoint for wire
compatibility with administrator-provided development/private clients. Using
that endpoint requires mode = "anonymous" plus a separate client-side user
opt-in; neither exists in managed public SlopKit. There is no relay,
federation, device identifier, or upstream collector.
Anonymous telemetry
Telemetry is off by default:
[telemetry]
mode = "off" # or "anonymous"
database = "/var/lib/websploot/telemetry.sqlite3"
pending_hours = 3
retention_days = 30
max_state_mib = 512
Browser consent remains a separate guide-side opt-in and is also expected to default off. The endpoint accepts at most 32 KiB and 64 events with fixed event codes, metric names, metadata, outcomes, and exact idempotency semantics. Duplicate JSON keys and free-form errors are rejected. Source rate limiting is 60 requests/minute with burst 20 and a bounded in-memory source table; an aggregate 120 requests/minute, burst-40 ceiling applies across sources.
The dedicated SQLite thread persists no IP, user agent, payload, or free-form
error. Pending attempts become interrupted after three hours; terminal records
are retained 30 days; logical used pages are capped at 512 MiB. A database
failure degrades telemetry and /healthz but does not stop guide hosting.
websploot --config websploot.toml telemetry report
websploot --config websploot.toml telemetry report --json
Native Linux deployment
The supported production platform is Linux. Install the binary, configuration, and unit:
sudo install -m755 target/release/websploot /usr/local/bin/websploot
sudo install -m644 deploy/websploot.service /etc/systemd/system/websploot.service
sudo systemctl daemon-reload
sudo systemctl enable --now websploot
The unit runs a dynamic user, read-only system paths, LimitNOFILE=131072, and
only CAP_NET_BIND_SERVICE. Persistent guide/list/telemetry data is under
/var/lib/websploot.
The example Caddyfile has a plain-HTTP Sony vhost, a normal HTTPS public guide hostname including action routes, a LAN-only HTTP host/IP, and a CIDR-restricted admin hostname. Caddy sets and sanitizes forwarded headers itself; configure websploot to trust only Caddy's actual peer. Both public vhosts also return 404 for detailed admin diagnostics as a second guard behind websploot's own client-CIDR checks.
Docker
The runtime image contains no Git client, runs as UID 10001, and supports a
read-only root filesystem. The binary carries only the
cap_net_bind_service file capability; Compose drops the runtime capability
set and permits only NET_BIND_SERVICE in its bounding set. Linux Compose
defaults to host networking so DNS sees real source addresses and avoids NAT
overhead:
./deploy/docker-setup.sh 192.168.1.10
docker compose up -d
The local Compose profile rebuilds the image when the checkout changes, so a
plain up cannot silently keep running an older websploot:local image.
The setup script writes configuration once, initializes the persistent state
volume, downloads and verifies both pinned guide archives, and can be rerun
safely. No host guide checkout is required. To replace managed SlopKit with
administrator-provided PS5 content, set PS5_ROOT, point its [[tree]] at
/srv/websploot/ps5, and use the explicit override:
docker compose -f docker-compose.yml -f docker-compose.ps5.yml up -d
To make that selection persistent, set this alongside PS5_ROOT in .env:
COMPOSE_FILE=docker-compose.yml:docker-compose.ps5.yml
Otherwise leave that variable absent; the base deployment uses managed SlopKit from the persistent state volume and has no PS5 host bind mount. Guide files must be readable by the container's unprivileged UID 10001.
Before bringing up an existing 0.x deployment, apply the migration notes below and validate the exact mounted configuration:
docker compose run --rm websploot \
--config /etc/websploot/websploot.toml check
Docker host networking is primarily a Linux facility. For development or non-Linux environments:
docker compose -f docker-compose.yml -f docker-compose.bridge.yml up -d
Bridge/NAT mode can hide DNS client identity and adds forwarding overhead. The
container keeps only /var/lib/websploot persistent and receives
CAP_NET_BIND_SERVICE; its nofile limit is 131072.
Release automation currently builds a Linux amd64 binary and container image. ARM64 publication is temporarily deferred because emulation on the Forgejo runner repeatedly exceeds its Docker/act deadline.
Operations and troubleshooting
The admin health document and /metrics report active/rejected DNS TCP and
HTTP connections, dropped UDP work, open/maximum file descriptors, list age and
refresh timestamps, limiter occupancy, guide reload failures, telemetry drops,
cache bounds, and per-upstream health. Alert on sustained admission rejections,
any guide reload failure, file descriptors above 80%, a project list older than
24 hours, telemetry drops when anonymous mode is expected, and repeated
upstream backoff. A low nofile limit is reported by check, startup logs, and
admin health; the production examples set 131072.
Typical Prometheus alert expressions are:
increase(websploot_dns_tcp_connections_rejected_total[5m]) > 0
increase(websploot_http_connections_rejected_total[5m]) > 0
increase(websploot_dns_udp_work_dropped_total[5m]) > 0
websploot_process_open_fds / websploot_process_max_fds > 0.80
time() - websploot_dns_list_last_refresh_timestamp_seconds > 86400
increase(websploot_guide_reload_failures_total[5m]) > 0
Route these with the provider's bandwidth/packet-rate alarms; process metrics cannot observe packets dropped upstream or a saturated access link.
Common failures:
- Port 53 already in use: bind the actual LAN address or resolve the conflict
with
systemd-resolved; do not silently move the console-facing port. - A setup download was interrupted: rerun
setup. Staging is disposable and the current/rollback links move only after hash, archive, manifest, AppCache closure, and preload validation. - A manual index loops or bypasses home: remove obsolete
console_indexand verify Caddy routeshttp://manuals.playstation.netto WebSploot unchanged. - A guide works online but not offline: inspect its declared AppCache
CACHEandFALLBACKclosure. Missing, denied, traversing, or cross-mount local targets failsetup,check, and reload before activation. - Docker loses client identity: use the host-networking production compose; bridge mode is only the development/non-Linux fallback.
Migration from 0.x
- Move local DNS addresses from
dns.allow_ipsto an exact[[dns.override]]. The old field temporarily maps only tomanuals.playstation.net. - Allowlist rules now mean “bypass blocking and ask upstream,” not “return a local address.”
- Remove
[tree.source]; useguides install/update/rollbackor manage a plaintree.rootyourself. - Remove
features = ["attempt-state"],latch_file, andtelemetry_log. Configure global[telemetry]instead. These legacy fields fail with a targeted migration error rather than being silently ignored. - Replace old public diagnostics with
/health; detailed APIs are admin-CIDR only. - Add every required administrator CIDR explicitly. Missing
[admin]now meansallow_clients = []; private and loopback ranges are no longer implicit. - Delete
dns.open_resolver_acknowledged. It is obsolete and intentionally produces a targeted migration error instead of being silently ignored. - Delete
landing.auto_redirect,landing.redirect_delay, andtree.console_index. During 0.x they deserialize only to produce a targeted removal error; manual indexes now always enter through the landing page. - To adopt managed public SlopKit in an existing configuration, add the
ps5tree fromconfig/websploot.example.toml, rooted at<state-dir>/guides/ps5/current, then rerunsetup. It adds the pinned PS5 descriptor to an older PS4-only lockfile and installs the verified archive.
Verification
Automated checks:
cargo fmt --package websploot --check
cargo clippy --all-targets --all-features --locked -- -D warnings
cargo test --all-targets --all-features --locked
cargo build --release --locked
python3 tools/browser_artifacts.py lock-check
python3 tools/browser_artifacts.py carrier
python3 tools/browser_artifacts.py check-binary \
"${CARGO_TARGET_DIR:-target}/release/websploot"
python3 -m unittest discover -s bench -p 'test_*.py' -v
python3 -m unittest discover -s tools -p 'test_*.py' -v
python3 tools/psn_snapshot.py --check
The tag workflow additionally runs REUSE, RustSec, cargo-deny license/source policy, OSV Scanner, deployment-file validation, a clean container smoke test, CycloneDX generation, and amd64 verification. It is the only workflow allowed to publish version tags. Release candidates stop at the automated gate; a stable tag is rejected unless release/evidence contains hashed capacity and physical-console records for an unchanged tested candidate and the exact generated artifacts.
Before a release, smoke-test a physical PS4 and PS5: configure DNS, open User Guide, confirm the identity-preserving landing redirect, D-pad every control, open/close settings without a focus trap, invoke the system keyboard, reject unsafe URLs, use favorites/recents, verify absent-guide entries, and enter each configured guide.
The browser-specific console checklist, including absent-ID install, launch target, rerun, partial staging, conflict refusal, reboot persistence, and both AppInst paths, is in docs/console-acceptance.md.
The release performance targets on a dedicated 4-vCPU/2-GiB host are 40k local/cache-hit DNS QPS (p99 <10ms), 20k static HTTP requests/sec (p99 <100ms), 20k simultaneous HTTP keep-alives, one healthy upstream transaction for 10k identical cold misses, and a 30-minute 70% soak below 1 GiB RSS with under 5% post-warmup growth. These are acceptance targets, not benchmark results or a capacity guarantee; validate them on the intended kernel, network, guide corpus, and reverse proxy before publishing a high-traffic deployment. The pinned, repeatable, fail-closed harness and result schema are in bench/README.md.
License and source
WebSploot code and its original UI artwork are AGPL-3.0-or-later. Corresponding source is published at https://git.richey.dev/miles/websploot. Guide content is separately licensed; release binaries additionally embed a GPL-3.0-or-later PS5 SDK-derived browser carrier. See THIRD_PARTY_NOTICES.md.