Install any CLI tool with one command. No package managers, no setup β just curl and go.
curl -fsSL install.cat/user/repo | shThat's it. Your users don't need to remember raw GitHub URLs, add https://, or type .sh. It just works.
curl -fsSL install.cat/verseles/xpm | shOr with wget:
wget -O- install.cat/verseles/xpm | shirm install.cat/verseles/xpm | iexJust open install.cat/user/repo in your Android browser β the APK from the latest GitHub release is downloaded automatically, matched to your device architecture.
Open install.cat/user/repo on an iPhone or iPad β if an IPA is found in the latest release, you'll see download instructions with sideload info (AltStore/Sideloadly). You can also use ios.txt to redirect to the App Store or TestFlight.
- Add an
install.shto the root of your repo (for Linux/macOS) - Add an
install.ps1to the root of your repo (for Windows) - For Android: publish APKs in your GitHub releases (naming convention:
{repo}-android-{arch}.apk, or any.apk). Priority chain:android.htmlβandroid.txtβ APK in releases β GitHub repo page. - For iOS: publish IPAs in your GitHub releases, or add an
ios.txtwith an App Store/TestFlight URL. Priority chain:ios.htmlβios.txtβ IPA in releases β GitHub repo page. - Tell your users:
curl -fsSL install.cat/your-username/your-repo | shDone. No registration, no config, no tokens.
Add a plain text file to your repo root containing a single URL on the first line. The user will be redirected there automatically:
# android.txt β redirect to Google Play
https://play.google.com/store/apps/details?id=com.example.app
# ios.txt β redirect to App Store
https://apps.apple.com/app/example/id123456789
Works with any URL: Google Play, F-Droid, App Store, TestFlight, direct downloads, etc.
install.cat runs entirely on Cloudflare Redirect Rules β there is no server, no backend, no application code processing requests. The domain uses Cloudflare's edge network with a set of dynamic redirect rules that route requests based on the User-Agent header:
| User-Agent | Redirect target |
|---|---|
curl |
raw.githubusercontent.com/{user}/{repo}/HEAD/install.sh |
wget |
raw.githubusercontent.com/{user}/{repo}/HEAD/install.sh |
PowerShell |
raw.githubusercontent.com/{user}/{repo}/HEAD/install.ps1 |
Android |
install.cat/android.html?gh={user}/{repo} (detects arch, finds APK) |
iPhone / iPad |
install.cat/ios.html?gh={user}/{repo} (App Store / IPA sideload) |
| Browser (default) | github.com/{user}/{repo}/ |
The full ruleset:
- HTTP β HTTPS β 301 redirect, ensures all traffic is encrypted
- www β root β 301 redirect, canonical domain normalization
- curl β install.sh β 302 redirect to raw script on GitHub
- wget β install.sh β 302 redirect to raw script on GitHub
- PowerShell β install.ps1 β 302 redirect to raw script on GitHub
- iOS β ios.html β 302 redirect to iOS app finder page
- Android β android.html β 302 redirect to APK finder page
- Browser β GitHub β 302 redirect to the repo page
Want to run your own install domain? You just need a Cloudflare zone. Set your env vars:
export CLOUDFLARE_EMAIL="you@example.com"
export CLOUDFLARE_API_KEY="your-global-api-key"
export ZONE_ID="your-zone-id"First, create the ruleset:
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets" \
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "default",
"kind": "zone",
"phase": "http_request_dynamic_redirect",
"rules": [
{
"action": "redirect",
"expression": "(http.request.full_uri wildcard r\"http://*\")",
"description": "HTTP to HTTPS",
"enabled": true,
"action_parameters": {
"from_value": {
"preserve_query_string": true,
"status_code": 301,
"target_url": {
"expression": "wildcard_replace(http.request.full_uri, r\"http://*\", r\"https://${1}\")"
}
}
}
},
{
"action": "redirect",
"expression": "(http.request.full_uri wildcard r\"https://www.*\")",
"description": "WWW to root",
"enabled": true,
"action_parameters": {
"from_value": {
"preserve_query_string": true,
"status_code": 301,
"target_url": {
"expression": "wildcard_replace(http.request.full_uri, r\"https://www.*\", r\"https://${1}\")"
}
}
}
},
{
"action": "redirect",
"expression": "(http.user_agent contains \"curl\" and http.request.uri.path wildcard r\"/*/*\")",
"description": "curl β install.sh",
"enabled": true,
"action_parameters": {
"from_value": {
"preserve_query_string": true,
"status_code": 302,
"target_url": {
"expression": "wildcard_replace(http.request.full_uri, r\"https://YOURDOMAIN.COM/*/*\", r\"https://raw.githubusercontent.com/${1}/${2}/HEAD/install.sh\")"
}
}
}
},
{
"action": "redirect",
"expression": "(http.user_agent contains \"Wget\" and http.request.uri.path wildcard r\"/*/*\")",
"description": "wget β install.sh",
"enabled": true,
"action_parameters": {
"from_value": {
"preserve_query_string": true,
"status_code": 302,
"target_url": {
"expression": "wildcard_replace(http.request.full_uri, r\"https://YOURDOMAIN.COM/*/*\", r\"https://raw.githubusercontent.com/${1}/${2}/HEAD/install.sh\")"
}
}
}
},
{
"action": "redirect",
"expression": "(http.user_agent contains \"PowerShell\" and http.request.uri.path wildcard r\"/*/*\")",
"description": "PowerShell β install.ps1",
"enabled": true,
"action_parameters": {
"from_value": {
"preserve_query_string": true,
"status_code": 302,
"target_url": {
"expression": "wildcard_replace(http.request.full_uri, r\"https://YOURDOMAIN.COM/*/*\", r\"https://raw.githubusercontent.com/${1}/${2}/HEAD/install.ps1\")"
}
}
}
},
{
"action": "redirect",
"expression": "(http.user_agent contains \"iPhone\" or http.user_agent contains \"iPad\") and http.request.uri.path wildcard r\"/*/*\"",
"description": "iOS β ios.html",
"enabled": true,
"action_parameters": {
"from_value": {
"preserve_query_string": false,
"status_code": 302,
"target_url": {
"expression": "wildcard_replace(http.request.full_uri, r\"https://YOURDOMAIN.COM/*/*\", r\"https://YOURDOMAIN.COM/ios.html?gh=${1}/${2}\")"
}
}
}
},
{
"action": "redirect",
"expression": "(http.request.uri.path wildcard r\"/*/*\")",
"description": "Browser β GitHub repo",
"enabled": true,
"action_parameters": {
"from_value": {
"preserve_query_string": true,
"status_code": 302,
"target_url": {
"expression": "wildcard_replace(http.request.full_uri, r\"https://YOURDOMAIN.COM/*/*\", r\"https://github.com/${1}/${2}/\")"
}
}
}
}
]
}'Replace
YOURDOMAIN.COMwith your actual domain in all three places.
To list existing rules:
# First, find your ruleset ID
curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets" \
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" | jq '.result[] | select(.phase == "http_request_dynamic_redirect") | .id'
# Then inspect the rules
RULESET_ID="the-id-from-above"
curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/$RULESET_ID" \
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" | jq '.result.rules[] | {description, expression}'- Zero downtime β There is no server to crash. Cloudflare's edge network handles everything. As long as Cloudflare is up, install.cat is up.
- Zero maintenance β No dependencies, no runtime, no database, no containers. Nothing to update or patch.
- Global performance β Requests are processed at Cloudflare's nearest edge node, not routed to a single origin server.
- Zero cost β Redirect Rules are included in Cloudflare's free plan.
The landing page (index.html) is a static file served via Cloudflare Pages / GitHub Pages. It's purely informational β the actual install routing is handled entirely at the DNS/edge layer.
The landing page at install.cat includes:
- Live example with syntax-highlighted commands
- curl/wget toggle to switch between tools
- Command generator β paste any GitHub URL or
user/repoand get ready-to-copy install commands - Auto-detected language (17 languages supported, including RTL for Arabic and Urdu)
index.src.html # Main page source β edit this
index.html # Minified output β auto-generated, do not edit
android.src.html # Android APK redirect page source β edit this
android.html # Minified output β auto-generated, do not edit
ios.src.html # iOS IPA/App Store redirect page source β edit this
ios.html # Minified output β auto-generated, do not edit
Makefile # Dev commands
Install minhtml (Rust):
cargo install minhtmlFor file watching (Linux):
# Arch
sudo pacman -S inotify-tools
# Debian/Ubuntu
sudo apt install inotify-toolsmake dev # Start local server + watch + open browser
make build # Minify index.src.html β index.html
make watch # Watch for changes and rebuild (no server)
make open # Open in browser- Edit
index.src.html - Run
make devto preview locally - Commit β a git pre-commit hook automatically runs
minhtmland stagesindex.html
Never edit
index.htmldirectly. It is regenerated on every commit.
npx html-minifier-terser --collapse-whitespace --remove-comments --minify-css --minify-js -o index.html index.src.htmlMIT
Made with β€οΈ by @insign