Shell Scripts
Pipe install scripts through a clean, memorable URL. easy-install.sh proxies the raw file from your GitHub repo so users get a single command to run your installer.
Quick Start
Run your first script
Replace namespace and project with your GitHub username/org and repo name.
curl -sSL easy-install.sh/acme/cli/install.sh | sh
Deep Dive
How it works
When a user runs curl -sSL easy-install.sh/acme/cli/install.sh | sh, here is what happens:
-
Namespace resolution
The proxy parses the URL into namespace (
acme), project (cli), and path (install.sh). The namespace is validated against the allowlist. -
Branch lookup
The proxy resolves the repository's default branch using the GitHub API. The result is cached in the database so subsequent requests skip this step.
-
Upstream fetch
The proxy fetches the file from GitHub's raw content endpoint and streams the response directly to the client. The file is never buffered in memory.
GET https://raw.githubusercontent.com/acme/cli/main/install.sh -
Analytics recorded
An analytics event fires in the background with the download metadata. This is fire-and-forget and never blocks the response. The download continues regardless of whether analytics succeeds.
-
Script delivered
The script content is streamed to the client with the correct
Content-Typeheader. If piped to a shell, it executes immediately.
Routing
Path resolution
The URL path after the project name maps directly to a file path in the repository. Files are always fetched from the repository's default branch.
| easy-install.sh URL | Upstream Path |
|---|---|
| acme/cli/install.sh | raw.githubusercontent.com/acme/cli/main/install.sh |
| jane/dotfiles/setup.sh | raw.githubusercontent.com/jane/dotfiles/main/setup.sh |
| my-org/infra/scripts/bootstrap.bash | raw.githubusercontent.com/my-org/infra/main/scripts/bootstrap.bash |
Note
Nested paths work too. If your script lives at scripts/install.sh in your repo, use easy-install.sh/you/repo/scripts/install.sh.
Detection
Script detection
The proxy identifies script downloads by file extension. Files ending in .sh, .bash, or .zsh are tracked as script hits in your analytics dashboard. All other file extensions are tracked as content hits.
| Category | Extensions |
|---|---|
| Tracked as "script" | .sh, .bash, .zsh |
| Tracked as "content" | All other extensions |
Headers
Response headers
The proxy sets the following headers on every response:
| Header | Value |
|---|---|
| Content-Type | MIME type guessed from file extension (e.g. text/x-shellscript for .sh files) |
| Cache-Control | public, max-age=300 (5-minute cache) |
| X-Proxied-From | raw.githubusercontent.com (informational) |
Analytics
What gets tracked
Each script download records the following anonymous metadata. No personal information is collected.
| Field | Description |
|---|---|
| Namespace | GitHub user or org |
| Project | Repository name |
| File Path | Which file was downloaded |
| Hit Type | "script" for .sh/.bash/.zsh files |
| Client Tool | Detected from User-Agent (curl, wget, browser, etc.) |
| Client Version | Version string extracted from User-Agent |
| Timestamp | When the download occurred (UTC) |
| Country | Derived from IP via geo lookup |
Note
IP addresses are hashed with a one-way SHA-256 function and never stored in plaintext. Analytics are fully anonymous.
Examples
Common usage
Pipe to shell
curl -sSL easy-install.sh/acme/cli/install.sh | sh
Pipe to bash explicitly
curl -sSL easy-install.sh/acme/cli/install.sh | bash
Download first, then run
curl -sSL easy-install.sh/acme/cli/install.sh -o install.sh
chmod +x install.sh
./install.sh
Pass arguments to the script
curl -sSL easy-install.sh/acme/cli/install.sh | sh -s -- --prefix=/usr/local
Use with wget
wget -qO- easy-install.sh/acme/cli/install.sh | sh
Nested path in repo
curl -sSL easy-install.sh/acme/infra/scripts/bootstrap.sh | sh
Include in a README
## Install
\`\`\`sh
curl -sSL easy-install.sh/acme/cli/install.sh | sh
\`\`\`
Limitations
Current limitations
Public repos only
easy-install.sh can only proxy files from public GitHub repositories. Private repos require authentication that the proxy cannot forward.
Default branch only
Scripts are always fetched from the repository's default branch. There is no way to specify a tag, branch, or commit hash in the URL yet.
No checksum verification
The proxy streams the file as-is from GitHub. It does not verify checksums or signatures. If integrity matters, consider adding verification steps in your script.
5-minute cache
Responses are cached for 5 minutes (Cache-Control: public, max-age=300). After pushing changes to your repo, it may take up to 5 minutes for the proxy to serve the updated file.