| 1 | #!/usr/bin/env bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | APP=/root/asset-proxy |
| 5 | SITE=/root/projects-site |
| 6 | SLUG=asset-proxy |
| 7 | OUT=$SITE/data/$SLUG |
| 8 | |
| 9 | echo "==> building linux release binary" |
| 10 | cd "$APP" |
| 11 | cargo build --release |
| 12 | |
| 13 | echo "==> building windows release binary" |
| 14 | cargo build --release --target x86_64-pc-windows-gnu |
| 15 | |
| 16 | echo "==> staging binaries" |
| 17 | mkdir -p "$OUT/files" "$OUT/src" "$OUT/shots" |
| 18 | rm -f "$OUT/files/"* |
| 19 | cp target/release/asset-proxy "$OUT/files/asset-proxy-linux-x86_64" |
| 20 | cp target/x86_64-pc-windows-gnu/release/asset-proxy.exe "$OUT/files/asset-proxy.exe" |
| 21 | |
| 22 | echo "==> snapshotting source" |
| 23 | rm -rf "$OUT/src"; mkdir -p "$OUT/src" |
| 24 | find . \ |
| 25 | -path ./target -prune -o \ |
| 26 | -path ./cache -prune -o \ |
| 27 | -path ./swaps -prune -o \ |
| 28 | -name 'Cargo.lock' -prune -o \ |
| 29 | -name 'ca-*.pem' -prune -o \ |
| 30 | -type f -print | while read -r f; do |
| 31 | dest="$OUT/src/${f#./}"; mkdir -p "$(dirname "$dest")"; cp "$f" "$dest" |
| 32 | done |
| 33 | |
| 34 | echo "==> source archive" |
| 35 | ( cd "$OUT/src" && zip -q -r -9 "$OUT/files/asset-proxy-source.zip" . ) |
| 36 | chmod 644 "$OUT/files/"* |
| 37 | |
| 38 | echo "==> registering in projects.json" |
| 39 | python3 - "$SITE/data/projects.json" <<'PY' |
| 40 | import json, sys |
| 41 | path = sys.argv[1] |
| 42 | d = json.load(open(path)) |
| 43 | entry = { |
| 44 | "slug": "asset-proxy", |
| 45 | "name": "asset-proxy", |
| 46 | "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.", |
| 47 | "language": "Rust", |
| 48 | "year": "2026", |
| 49 | "tags": ["Rust","MITM proxy","Roblox","Asset swap","Live dashboard","No injection"], |
| 50 | "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.", |
| 51 | "highlights": [ |
| 52 | {"value": "0", "label": "code injected into Roblox"}, |
| 53 | {"value": "80+", "label": "asset types you can swap"}, |
| 54 | {"value": "live", "label": "web dashboard, no egui"}, |
| 55 | {"value": "rip", "label": "every asset a game loads, to disk"} |
| 56 | ], |
| 57 | "sections": [ |
| 58 | {"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."}, |
| 59 | {"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."}, |
| 60 | {"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."} |
| 61 | ], |
| 62 | "downloads": [ |
| 63 | {"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"}, |
| 64 | {"label": "Linux x86-64", "file": "asset-proxy-linux-x86_64", "platform": "linux", "note": "same, for Sober — pair with Sober's proxy setting"} |
| 65 | ], |
| 66 | "source_zip": "asset-proxy-source.zip", |
| 67 | "screenshots": [] |
| 68 | } |
| 69 | d.setdefault("projects", []) |
| 70 | d["projects"] = [p for p in d["projects"] if p.get("slug") != "asset-proxy"] |
| 71 | d["projects"].insert(0, entry) |
| 72 | json.dump(d, open(path, "w"), indent=1) |
| 73 | print(" ok, projects:", [p["slug"] for p in d["projects"]]) |
| 74 | PY |
| 75 | |
| 76 | echo "==> rebuilding site" |
| 77 | cd "$SITE"; cargo build --release |
| 78 | if systemctl is-active --quiet projects-site; then systemctl restart projects-site; fi |
| 79 | echo "done." |