# Self-hosted HTTP Toolkit A private distribution repo that builds [HTTP Toolkit](https://httptoolkit.com) into a **single self-contained binary** that runs the backend and serves the web UI locally: ```bash ./httptoolkit --port 7070 # → starts the proxy server and opens the web UI at http://localhost:7070 ``` No Electron, no hosted UI, no account/login. Upstream is tracked as **git submodules** and all local modifications live in this repo as **patches** + an **overlay**, so updating to a new upstream release is a controlled, reviewable step. ## Layout ``` . ├── httptoolkit-ui/ # submodule → github.com/httptoolkit/httptoolkit-ui (pinned) ├── httptoolkit-server/ # submodule → github.com/httptoolkit/httptoolkit-server (pinned) ├── patches/ │ ├── ui/ # diffs against upstream UI files │ └── server/ # diffs against upstream server files ├── overlay/server/ # NEW files copied into the server submodule (never conflict) │ ├── htk-entry.ts # single-file binary entry (Bun) │ └── src/ │ ├── htk-app.ts # shared: run backend + serve UI + open browser │ └── commands/app.ts # oclif fallback command ├── scripts/ │ ├── apply.sh # reset submodules → apply patches → copy overlay │ ├── build.sh # build UI → embed → bun compile → dist/ │ └── smoke.sh # launch binary & verify it serves + backend is up └── .github/workflows/build.yml ``` ## What the patches change | Kind | File | Change | |------|------|--------| | UI patch | `src/model/account/account-store.ts` | injects a synthetic active Pro subscription | | UI patch | `src/components/settings/settings-page.tsx` | removes the account/subscription card | | UI patch | `src/components/app.tsx` | removes the "Give feedback" sidebar button | | Server patch | `src/constants.ts` | allow `localhost`/`127.0.0.1` origins in prod builds | | Overlay | `htk-entry.ts`, `src/htk-app.ts`, `src/commands/app.ts` | serve the UI locally + `--port` | **Security note:** upstream restricts a packaged build's API to `https://app.httptoolkit.tech` so no *other* local page can drive your proxy. Because we self-host the UI on localhost we must allow localhost origins (this matches upstream's own dev-mode allowlist). Trade-off: any local HTTP page could talk to the server while it's running. Acceptable for a personal tool. ## Build locally Requires **node ≥22**, **npm**, **bun**, and **tar**. ```bash git submodule update --init --recursive bash scripts/build.sh # → dist/httptoolkit bash scripts/smoke.sh # optional: verify it runs ``` Run it: ```bash ./dist/httptoolkit --port 7070 # UI on :7070 ./dist/httptoolkit --no-open # don't auto-open a browser ./dist/httptoolkit --help ``` ## Updating to a new upstream release ```bash # bump a submodule to a new tag cd httptoolkit-ui && git fetch && git checkout v && cd .. git add httptoolkit-ui # re-apply and see if patches still fit bash scripts/apply.sh ``` If a patch no longer applies, `apply.sh` fails and names the patch. Fix it: ```bash cd httptoolkit-ui git checkout -- . # reset # hand-apply the change, then regenerate the patch: git diff -- > ../patches/ui/000X-....patch ``` Overlay files never conflict (they're new files), so only the small `patches/` diffs ever need attention on upgrade. ## Packaging notes / risks - **Single file via Bun `--compile`.** The server pulls in native `.node` addons (`node-datachannel`, `registry-js`, `adbkit`, …). These **cannot be cross-compiled**, so CI builds each OS on its own runner (Linux/macOS/Windows matrix). The `smoke.sh` step is the guard: if a native addon won't load from the embedded FS, it fails there. - The UI is embedded as `ui.tar.gz` and extracted to `$TMPDIR/httptoolkit-ui-` on first run. - No `HTK_SERVER_TOKEN` is set, so the local UI talks to the backend without a token. ## CI `.github/workflows/build.yml` builds the matrix on push/PR and publishes a GitHub Release on `v*` tags. **If your CI is Gitea/Forgejo Actions**, this same file works under `.gitea/workflows/` or `.github/workflows/`. **If it's GitLab**, it needs porting to `.gitlab-ci.yml` (same steps: checkout w/ submodules → `scripts/build.sh` → `scripts/smoke.sh`).