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:

  1. Namespace resolution

    The proxy parses the URL into namespace (acme), project (cli), and path (install.sh). The namespace is validated against the allowlist.

  2. 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.

  3. 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
    
  4. 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.

  5. Script delivered

    The script content is streamed to the client with the correct Content-Type header. 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 URLUpstream Path
acme/cli/install.shraw.githubusercontent.com/acme/cli/main/install.sh
jane/dotfiles/setup.shraw.githubusercontent.com/jane/dotfiles/main/setup.sh
my-org/infra/scripts/bootstrap.bashraw.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.

CategoryExtensions
Tracked as "script".sh, .bash, .zsh
Tracked as "content"All other extensions

Headers

Response headers

The proxy sets the following headers on every response:

HeaderValue
Content-TypeMIME type guessed from file extension (e.g. text/x-shellscript for .sh files)
Cache-Controlpublic, max-age=300 (5-minute cache)
X-Proxied-Fromraw.githubusercontent.com (informational)

Analytics

What gets tracked

Each script download records the following anonymous metadata. No personal information is collected.

FieldDescription
NamespaceGitHub user or org
ProjectRepository name
File PathWhich file was downloaded
Hit Type"script" for .sh/.bash/.zsh files
Client ToolDetected from User-Agent (curl, wget, browser, etc.)
Client VersionVersion string extracted from User-Agent
TimestampWhen the download occurred (UTC)
CountryDerived 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

README.md
## 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.