Content Pipes

Serve any raw file from your GitHub repo through a clean URL. Configuration files, manifests, templates, or anything else in your repository is accessible through your namespace.

Quick Start

Fetch your first file

Replace namespace, project, and path with your GitHub username/org, repo name, and file path.

curl -sSL easy-install.sh/acme/app/config.yaml > config.yaml

Deep Dive

How it works

When a user fetches easy-install.sh/acme/app/config.yaml, here is what happens:

  1. Namespace resolution

    The proxy parses the URL into namespace (acme), project (app), and path (config.yaml). The namespace is validated against the allowlist.

  2. Branch lookup

    The proxy resolves the default branch via the GitHub API and caches the result. The file is always fetched from the default branch.

  3. Upstream fetch

    The proxy fetches the file from GitHub's raw content endpoint and streams it directly to the client. No buffering in memory.

    GET https://raw.githubusercontent.com/acme/app/main/config.yaml
    
  4. MIME type detection

    The proxy guesses the correct Content-Type from the file extension (e.g. application/json for .json, text/yaml for .yaml). If the extension is unrecognized, it falls back to the upstream header or application/octet-stream.

  5. Analytics recorded

    An analytics event fires in the background (fire-and-forget). The download is never blocked by analytics. Content files are tracked separately from script files in your dashboard.


MIME Types

Content-Type handling

The proxy detects the correct MIME type from the file extension so downstream tools can parse the response correctly.

ExtensionContent-Type
.jsonapplication/json
.yaml / .ymltext/yaml
.tomltext/toml
.xmlapplication/xml
.txttext/plain
.mdtext/markdown
.csvtext/csv
.envtext/plain
unknownapplication/octet-stream (fallback)

Detection

Scripts vs content

Content pipes and script forwarding use the same URL pattern and the same proxy. The only difference is how the hit is categorized in analytics:

CategoryExtensions
Script hitsFiles ending in .sh, .bash, or .zsh
Content hitsAll other file extensions

Note

Both types appear separately in your analytics dashboard, so you can see how many users are downloading your install script vs your config files.


Headers

Response headers

The proxy sets the following headers on every response:

HeaderValue
Content-TypeMIME type guessed from file extension
Cache-Controlpublic, max-age=300 (5-minute cache)
X-Proxied-Fromraw.githubusercontent.com (informational)

Analytics

What gets tracked

Each content 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"content" for non-script 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

Download a config file

curl -sSL easy-install.sh/acme/app/config.yaml > config.yaml

Pipe JSON into jq

curl -sSL easy-install.sh/acme/app/defaults.json | jq '.database'

Fetch a Docker Compose file

curl -sSL easy-install.sh/acme/app/docker-compose.yml > docker-compose.yml
docker compose up -d

Fetch a Kubernetes manifest

curl -sSL easy-install.sh/acme/app/k8s/deployment.yaml | kubectl apply -f -

Use with wget

wget -qO config.toml easy-install.sh/acme/app/config.toml

Nested paths

curl -sSL easy-install.sh/acme/templates/envs/.env.production > .env

HEAD request to check availability

curl -I easy-install.sh/acme/app/config.yaml

Returns headers only (no body). Useful for checking file existence or content type before downloading.


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

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

Extension-based detection

Content type detection relies on file extensions, not file contents. If your file has no extension or an unusual one, the proxy falls back to application/octet-stream.

5-minute cache

Responses are cached for 5 minutes. After pushing changes, it may take up to 5 minutes for the proxy to serve the updated file.