#!/usr/bin/env bash set -euo pipefail APP=/root/asset-proxy SITE=/root/projects-site SLUG=asset-proxy OUT=$SITE/data/$SLUG echo "==> building linux release binary" cd "$APP" cargo build --release echo "==> building windows release binary" cargo build --release --target x86_64-pc-windows-gnu echo "==> staging binaries" mkdir -p "$OUT/files" "$OUT/src" "$OUT/shots" rm -f "$OUT/files/"* cp target/release/asset-proxy "$OUT/files/asset-proxy-linux-x86_64" cp target/x86_64-pc-windows-gnu/release/asset-proxy.exe "$OUT/files/asset-proxy.exe" echo "==> snapshotting source" rm -rf "$OUT/src"; mkdir -p "$OUT/src" find . \ -path ./target -prune -o \ -path ./cache -prune -o \ -path ./swaps -prune -o \ -name 'Cargo.lock' -prune -o \ -name 'ca-*.pem' -prune -o \ -type f -print | while read -r f; do dest="$OUT/src/${f#./}"; mkdir -p "$(dirname "$dest")"; cp "$f" "$dest" done echo "==> source archive" ( cd "$OUT/src" && zip -q -r -9 "$OUT/files/asset-proxy-source.zip" . ) chmod 644 "$OUT/files/"* echo "==> registering in projects.json" python3 - "$SITE/data/projects.json" <<'PY' import json, sys path = sys.argv[1] d = json.load(open(path)) entry = { "slug": "asset-proxy", "name": "asset-proxy", "tagline": "A local proxy that swaps Roblox assets on the wire — textures, audio, meshes — with a live web dashboard. From-scratch Fleasion, and it never touches the game process.", "language": "Rust", "year": "2026", "tags": ["Rust","MITM proxy","Roblox","Asset swap","Live dashboard","No injection"], "summary": "Sits between Roblox and its CDN as a MITM proxy — its own CA, a leaf cert minted per host — and rewrites asset downloads before they reach the client. Replace any texture, sound or mesh with a local file, all client-side; only you see it. It also rips every original asset a game loads straight to disk, and ships a live web dashboard (browser, not egui): a real-time feed of everything the client fetches, image previews, and click-to-swap. It is not an executor and does no code injection — it's a plain HTTPS proxy, the same class of tool as Burp, so there's no anti-cheat anywhere in the picture.", "highlights": [ {"value": "0", "label": "code injected into Roblox"}, {"value": "80+", "label": "asset types you can swap"}, {"value": "live", "label": "web dashboard, no egui"}, {"value": "rip", "label": "every asset a game loads, to disk"} ], "sections": [ {"title": "How it works", "body": "On startup it points Roblox's asset hostnames (assetdelivery.roblox.com, fts.rbxcdn.com) at 127.0.0.1 via the hosts file and serves them from a local HTTPS server on :443 — so the client thinks it's talking to Roblox but reaches the proxy, which presents a leaf certificate signed by a CA you install once. Per request it reads the asset id (or CDN hash); if a matching file is in ./swaps it serves that, otherwise it fetches the real asset — resolving Roblox's true IP first so it never loops back through the hijacked name — streams it, and caches the original. Needs root (Linux) or admin (Windows) to edit the hosts file and bind :443. A CONNECT proxy on :8080 is kept as a fallback for proxy-aware clients."}, {"title": "The dashboard", "body": "A second server on :8081 serves a hand-built web UI: a live table of every asset the client pulls, with type badges, sizes, image thumbnails straight from the cache, and a one-click swap (pick a local file, it's replaced on the next fetch). No desktop toolkit, no egui — just open it in a browser."}, {"title": "Not an executor", "body": "It runs entirely outside Roblox. No injection into RobloxPlayerBeta, no VM hooking, no Hyperion — the client just talks HTTPS through a proxy it trusts. That's the whole reason it's a clean, buildable tool where a real executor isn't."} ], "downloads": [ {"label": "Windows x86-64", "file": "asset-proxy.exe", "platform": "windows", "note": "the proxy + web dashboard in one .exe; set it as your system/Roblox proxy"}, {"label": "Linux x86-64", "file": "asset-proxy-linux-x86_64", "platform": "linux", "note": "same, for Sober — pair with Sober's proxy setting"} ], "source_zip": "asset-proxy-source.zip", "screenshots": [] } d.setdefault("projects", []) d["projects"] = [p for p in d["projects"] if p.get("slug") != "asset-proxy"] d["projects"].insert(0, entry) json.dump(d, open(path, "w"), indent=1) print(" ok, projects:", [p["slug"] for p in d["projects"]]) PY echo "==> rebuilding site" cd "$SITE"; cargo build --release if systemctl is-active --quiet projects-site; then systemctl restart projects-site; fi echo "done."