Skip to content

Repository files navigation

click

A re-implementation of Shopify's internal Quick hosting platform (shopify.engineering/quick) built with Go, nginx, and AWS S3.

Project status: alpha. The CLI and the platform are usable, but the APIs and the UX still change. Expect breaking changes and rough edges.

Deploy any folder of static files. It is live on its own subdomain seconds later:

$ click deploy examples/hello --name hello
  ↑ index.html
  ↑ style.css
  ↑ app.js
  ↑ docs/index.html

✔ hello deployed: 4 uploaded, 0 unchanged, 0 deleted
  http://hello.click.localhost:8080/

No frameworks, no pipelines, no configuration files — "feels like the good old days of FTP."

Each deployed site is served on its own origin, behind employee sign-in. Each site also gets a set of platform APIs without extra work: who is viewing (/api/identity), a per-site document database (/api/db/*), a realtime change stream, and read-only SQL. A folder of static files can therefore be a real app with no server to run. Read Building a site.

To understand or work on the platform itself, read ARCHITECTURE.md. It covers the architecture, how to run the platform locally, the authentication and ownership model, and how to deploy to production.

Install

Get the binary for your operating system and architecture from the repository Releases. Unpack it, then put click on your PATH:

tar -xzf click_*_$(uname -s | tr A-Z a-z)_*.tar.gz click
sudo mv click /usr/local/bin/
click version

From then on, click update keeps the binary current. It verifies each download against the release checksums before it swaps the binary. To build from source instead, clone the repository and run make build. The binary lands in bin/click.

CLI reference

click new <dir> --prompt "..." [--name <site>] [--deploy]   generate a site with AI, optionally deploy it
click new <dir> --docs-only [--name <site>]                 write just AGENTS.md/CLAUDE.md into an existing folder
click deploy [dir] [--name <site>] [--keep-deleted]         sync a folder to a site (default dir ".")
click list                                                  list deployed sites
click delete <site> [--keep-data]                           delete a site (files + database data)
click login                                                 sign in for authenticated platform calls
click logout                                                forget the cached credentials
click access <site>                                         show a site's owner and collaborators
click share <site> <email>                                  grant someone deploy access (owner only)
click unshare <site> <email>                                revoke deploy access (owner only)
click version                                               print the CLI version
click update                                                update to the latest release

click deploy [dir] [--name <site>] — syncs a folder to the storage of the site, the way gcloud rsync does. It uploads only the files whose contents changed, and it deletes the remote copies of files that you deleted locally. --keep-deleted keeps those remote copies. The site name defaults to the directory name, sanitized to a DNS label ([a-z0-9-]). On an authenticated platform the first deploy claims the site name for you.

click new <dir> --prompt "..." [--name <site>] [--deploy] — generates a complete site from a natural-language prompt and writes it to <dir>. Add --deploy with --name to publish the site in the same command. It also writes an AGENTS.md and a CLAUDE.md into the folder, so a coding agent can keep working on the site afterwards. Each generation takes a few minutes. Generation runs on the platform when you are signed in. If you are not signed in, it falls back to your local ANTHROPIC_API_KEY.

click new <dir> --docs-only [--name <site>] — writes only that AGENTS.md and CLAUDE.md pair: the iteration rules plus the full platform API guide. There is no AI call, no API key, and no other file is touched. Use it to make a hand-written or existing site self-describing for a coding agent. --prompt is optional here and only fills in the "what this site is" note. The command refuses to overwrite agent docs that are already there, unless you pass --force.

click delete <site> [--keep-data] — deletes the files of the site and purges its database documents. It also releases the name, so that anyone can claim the name again. --keep-data keeps both the documents and the claim, for example before you redeploy under the same name.

click login / logout — sign in once through your browser. Tokens are cached under your OS configuration directory and refreshed automatically. You need this only on platforms that enforce authentication (read deploying to a hosted platform). On a headless machine, CLICK_NO_BROWSER=1 click login prints the URL for you to open.

click share / unshare / access — the owner of the site grants or revokes deploy access for teammates, and sees who has access now.

click help <command> (or --help) documents the flags of any command. And click completion bash|zsh|fish|powershell prints a shell-completion script — for example source <(click completion zsh) in your shell rc file.

Environment variables

Variable Purpose
CLICK_API_URL Platform URL for API calls (default http://<CLICK_DOMAIN>)
CLICK_DOMAIN Domain that sites are served under (default click.localhost:8080)
CLICK_BUCKET S3 bucket name (default click-sites)
CLICK_S3_ENDPOINT Custom S3 endpoint, for example MinIO. Empty means real AWS S3
CLICK_OIDC_ISSUER OIDC provider for click login. Unset means no CLI authentication
CLICK_OIDC_CLIENT_ID / CLICK_OIDC_CLIENT_SECRET The OIDC client of the CLI (public client, so the secret is not sensitive)
CLICK_AUTH_TOKEN Bearer token to send as it is, instead of a login (CI)
CLICK_NO_BROWSER 1 makes click login print the URL instead of opening a browser
CLICK_CA_FILE PEM bundle of extra CAs to trust for HTTPS, merged with the system roots (default: ca.pem in the configuration directory of the CLI, if present)
ANTHROPIC_API_KEY Claude API key for the local fallback of click new (not needed when you are signed in to a platform that generates)
CLICK_AI_MODEL Model for click new (default: the choice of the platform, or claude-opus-5 locally)
CLICK_UPDATE_REPO / CLICK_UPDATE_API Override the source that click update pulls releases from (forks, testing)
AWS_* Standard AWS credential chain (used only when the platform does not vend scoped credentials)

Deploy to a hosted platform

To point the CLI at a deployment of the platform instead of your local stack, export the domain and the identity provider of that deployment. DEPLOY.md shows how to stand one up.

export CLICK_DOMAIN=click.example.com
export CLICK_API_URL=https://click.example.com
export CLICK_OIDC_ISSUER=https://dex.click.example.com/dex
export CLICK_OIDC_CLIENT_ID=click-cli
export CLICK_OIDC_CLIENT_SECRET=click-cli    # public client; not a secret
export CLICK_BUCKET=my-click-sites           # the platform's S3 bucket
export AWS_REGION=us-west-2                  # that bucket's region

click login
click deploy ./site --name my-app

Leave CLICK_S3_ENDPOINT and any AWS_ACCESS_KEY_* from local testing unset. No AWS credentials are necessary: each deploy writes with the per-site scoped credentials that the platform mints for you.

Trusting a private or staging CA

A deployment can serve a certificate that your system roots do not trust already. Two examples are a private corporate CA, and Let's Encrypt staging on a pre-production environment. In that case, put the issuing root into the configuration directory of the CLI once. Every click command then trusts it from then on:

# macOS (Linux: ~/.config/click)
mkdir -p "$HOME/Library/Application Support/click"
cp issuing-root.pem "$HOME/Library/Application Support/click/ca.pem"

click login

The CLI merges ca.pem (or a file named by CLICK_CA_FILE) into the system roots. SSL_CERT_FILE replaces them instead, and it cannot satisfy a deploy that talks to the staging-certificate platform and to real-certificate AWS S3 in the same run. The extra trust applies only to the click process. Nothing on the machine changes.

A platform with publicly-trusted certificates needs none of this.

Building a site

A deployed site is only static files (HTML, CSS, and JS — no build step). On its own origin, the platform gives every page a shared API, so plain fetch() is the whole integration. There is no server, no CORS, and no API key in your code:

  • GET /api/identity — who is viewing the page (email, name).
  • /api/db/{collection} — a per-site document database (schemaless JSON, CRUD), isolated so that other sites can never read it.
  • GET /api/db/events — a Server-Sent Events stream, so open tabs update live when data changes.
  • GET /api/db-token — a read-only, site-scoped token for direct SQL reads (when the platform runs the libSQL backend).

The complete author-facing guide is docs/site-api.md: every endpoint, the rules, and worked examples. It is written to double as context for an LLM or an agent. The examples/ folder holds working sites: hello (identity), guestbook (documents and realtime), and demo (the whole API surface). You can also let click new write one for you.

How it works

Start at ARCHITECTURE.md for the component map, the request path, and the repository layout. From there: DESIGN.md covers the document database, the authentication and ownership model, and per-site data isolation. DEPLOY.md covers the local stack, the test suites, and the production blueprint for EKS and Terraform, whose Kubernetes manifests are checked in under kubernetes/.

About

Internal static hosting that feels like FTP again: click deploy a folder and it's live on its own subdomain — with SSO, per-site ownership, isolated per-site databases, and realtime. Go + nginx + S3.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages