{"id":48866733,"url":"https://github.com/marcodellemarche/cubbit-pages","last_synced_at":"2026-05-11T23:08:43.121Z","repository":{"id":351421580,"uuid":"1210863398","full_name":"marcodellemarche/cubbit-pages","owner":"marcodellemarche","description":"CLI to deploy static sites to Cubbit S3 with optional AES-256-GCM encryption. Includes a reusable GitHub Action.","archived":false,"fork":false,"pushed_at":"2026-04-14T23:17:55.000Z","size":65,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T00:32:14.470Z","etag":null,"topics":["aes-256-gcm","cli","cubbit","deploy","encryption","github-action","golang","s3","static-site"],"latest_commit_sha":null,"homepage":"https://s3.cubbit.eu/pages/index.html","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcodellemarche.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-14T20:43:49.000Z","updated_at":"2026-04-14T23:17:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/marcodellemarche/cubbit-pages","commit_stats":null,"previous_names":["marcodellemarche/cubbit-pages"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/marcodellemarche/cubbit-pages","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcodellemarche%2Fcubbit-pages","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcodellemarche%2Fcubbit-pages/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcodellemarche%2Fcubbit-pages/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcodellemarche%2Fcubbit-pages/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcodellemarche","download_url":"https://codeload.github.com/marcodellemarche/cubbit-pages/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcodellemarche%2Fcubbit-pages/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31855432,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aes-256-gcm","cli","cubbit","deploy","encryption","github-action","golang","s3","static-site"],"created_at":"2026-04-15T19:00:37.143Z","updated_at":"2026-05-11T23:08:43.114Z","avatar_url":"https://github.com/marcodellemarche.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cubbit Pages\n\nCLI to deploy static sites to [Cubbit](https://cubbit.io) S3, with optional AES-256-GCM client-side encryption.\n\nZero backend. Zero server. Zero npm dependencies.\n\n## How it works\n\n1. You have a folder with a static site (HTML, CSS, JS, images...)\n2. Cubbit Pages uploads it to a Cubbit bucket, making it publicly accessible as a website\n3. Optionally, all files are encrypted: the only plaintext page is `index.html` (an auto-generated login page)\n4. Once the correct password is entered, the browser decrypts every page directly from the bucket\n\n## Installation\n\n### Automatic script\n\n```bash\ncurl -sSL \\\n  https://github.com/marcodellemarche/cubbit-pages/releases/latest/download/install.sh \\\n  | bash\n```\n\n### Build from source\n\n```bash\ngit clone https://github.com/marcodellemarche/cubbit-pages.git\ncd cubbit-pages\nmake build\n# Binary is in bin/cubbit-pages\n```\n\n## Cubbit setup\n\n1. Create an account at [console.cubbit.io](https://console.cubbit.io)\n2. Create a bucket\n3. Generate an API key from [API Keys](https://console.cubbit.io/api-keys)\n4. Run the interactive setup wizard:\n\n```bash\ncubbit-pages setup\n```\n\nThe wizard prompts for Access Key, Secret Key, Endpoint, Bucket, and login page locale. It creates the bucket if it doesn't exist, verifies the connection, then saves everything to `~/.cubbit/pages/config.yaml` so you don't need to repeat credentials on every deploy.\n\n5. Show the bucket configuration snippets:\n\n```bash\ncubbit-pages snippets --bucket MY-BUCKET\n```\n\n6. For encrypted sites, also configure CORS:\n\n```bash\ncubbit-pages snippets --bucket MY-BUCKET --type cors\n```\n\n## Usage\n\n### Plain deploy\n\n```bash\n# After setup — credentials loaded from ~/.cubbit/pages/config.yaml\ncubbit-pages deploy ./my-site\n\n# Or pass credentials explicitly\ncubbit-pages deploy ./my-site \\\n  --bucket my-bucket \\\n  --access-key AKIAIOSFODNN7EXAMPLE \\\n  --secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n```\n\n### Encrypted deploy\n\n```bash\n# Password prompted interactively (recommended)\ncubbit-pages deploy ./my-site --encrypt\n\n# Password via environment variable (good for CI)\nexport CUBBIT_PASSWORD=\"my-secret-password\"\ncubbit-pages deploy ./my-site --encrypt\n\n# Password from stdin\necho \"my-secret-password\" | cubbit-pages deploy ./my-site --encrypt\n```\n\n### Preview before deploying\n\n```bash\n# Dry run — shows exactly what would be uploaded with real file sizes, no upload\ncubbit-pages deploy ./my-site --encrypt --dry-run\n```\n\n```\nDeploying to s3://my-bucket/\nMode: encrypted (AES-256-GCM)\n⚠ Dry run — no files will be uploaded\n\n  index.html                                         10.2 KB  [dry]\n  sw.js                                               7.9 KB  [dry]\n  _verify.enc                                           64 B  [dry]\n  about.html.enc                                       3.9 KB  [dry]\n  style.css.enc                                        3.8 KB  [dry]\n\nDeploy complete: 0 file(s) uploaded\nURL: https://my-bucket.s3.cubbit.eu/index.html\n```\n\n### Environment variables\n\nAll credentials can be passed via env (overrides config file):\n\n```bash\nexport CUBBIT_ACCESS_KEY=...\nexport CUBBIT_SECRET_KEY=...\nexport CUBBIT_BUCKET=my-bucket\nexport CUBBIT_PASSWORD=my-secret-password  # for encrypted deploys\nexport CUBBIT_LOCALE=it                    # login page language\n\ncubbit-pages deploy ./my-site --encrypt\n```\n\nPriority: CLI flags \u003e environment variables \u003e `~/.cubbit/pages/config.yaml`\n\n### Check current config and last deploy\n\n```bash\ncubbit-pages status\n```\n\n```\n  Config (~/.cubbit/pages/config.yaml)\n  ────────────────────────────────────────────\n  Bucket:      my-bucket\n  Endpoint:    https://s3.cubbit.eu\n  Locale:      en\n\n  Last deploy\n  ────────────────────────────────────────────\n  Bucket:      my-bucket\n  Prefix:      weekly/2026-05-11\n  Files:       7\n  Mode:        encrypted (AES-256-GCM)\n  Date:        2026-05-11 14:32\n  URL:         https://my-bucket.s3.cubbit.eu/weekly/2026-05-11/index.html\n```\n\n### Full bucket inventory (multi-machine, CI/CD)\n\nAdd `--deep` to query S3 directly and list all deploys in the bucket — useful on a new machine without a local config file, or to verify CI/CD deploys:\n\n```bash\ncubbit-pages status --deep\n# credentials loaded from config file, or pass explicitly:\ncubbit-pages status --deep --bucket my-bucket --access-key ... --secret-key ...\n```\n\n```\n  Config (~/.cubbit/pages/config.yaml)\n  ────────────────────────────────────────────\n  Bucket:      my-bucket\n  ...\n\n  Bucket inventory: my-bucket (2 deploy)\n  ────────────────────────────────────────────\n\n  #1  demo-backup          7 files   142.3 KB  encrypted   2026-05-11 15:10\n      https://my-bucket.s3.cubbit.eu/demo-backup/index.html\n\n  #2  demo                 7 files   142.3 KB  encrypted   2026-05-11 14:32\n      https://my-bucket.s3.cubbit.eu/demo/index.html\n```\n\nDeploy metadata (encryption mode, locale, CLI version, timestamp) is stored as S3 object metadata on `index.html` at deploy time and read back on demand. Deploys made before v0.5.0 show `(no metadata)`.\n\nThe `last_deploy` in `~/.cubbit/pages/config.yaml` is now always written after every successful deploy, even if `setup` was never run.\n\n## GitHub Action\n\nCubbit Pages ships as a reusable GitHub Action. Drop it into any workflow:\n\n```yaml\n- uses: marcodellemarche/cubbit-pages@main\n  with:\n    source-dir: ./dist\n    bucket: my-bucket\n    access-key: ${{ secrets.CUBBIT_ACCESS_KEY }}\n    secret-key: ${{ secrets.CUBBIT_SECRET_KEY }}\n```\n\n### Action inputs\n\n| Input | Required | Default | Description |\n|-------|----------|---------|-------------|\n| `source-dir` | yes | — | Directory to deploy |\n| `bucket` | yes | — | Cubbit S3 bucket name |\n| `access-key` | yes | — | Cubbit access key |\n| `secret-key` | yes | — | Cubbit secret key |\n| `endpoint` | no | `https://s3.cubbit.eu` | S3 endpoint |\n| `encrypt` | no | `false` | Enable AES-256-GCM encryption |\n| `password` | no | — | Encryption password |\n| `public-bucket` | no | `false` | Skip per-object ACL |\n| `prefix` | no | — | S3 key prefix |\n| `locale` | no | `en` | Login page language (`en`, `it`) |\n| `concurrency` | no | `5` | Parallel uploads |\n| `version` | no | `latest` | CLI version to download |\n\n### Action output\n\n| Output | Description |\n|--------|-------------|\n| `url` | URL of the deployed site |\n\n## Encrypted site navigation\n\nWhen deploying with `--encrypt`:\n\n1. All files are encrypted with AES-256-GCM and uploaded with `.enc` extension\n2. A login `index.html` is auto-generated\n3. The user's original `index.html` becomes `index.html.enc`\n4. A `_verify.enc` canary file validates the password\n5. A **service worker** (`sw.js`) is uploaded alongside the login page:\n   - After login, the SW is registered and receives the password via `postMessage`\n   - It intercepts every browser fetch (scripts, stylesheets, images, fonts, etc.)\n   - For each request: if the original resource returns 404, it tries `\u003curl\u003e.enc`, decrypts in-memory, and returns the plaintext with the correct `Content-Type`\n   - Decrypted responses are cached for performance\n   - Password is persisted to IndexedDB so it survives service worker restarts without requiring re-login\n6. For each HTML file, a \"loader\" page handles direct navigation (e.g., bookmark to `/about.html`)\n7. A dark loading overlay (Cubbit colors + spinner) is injected into every decrypted page before `document.write` and dissolves on `window.load`, eliminating the white flash while external CSS is being fetched and decrypted\n\nThis means **multi-file sites work out of the box** — SPAs (Vite, React, etc.), sites with separate JS/CSS/images, all work transparently after login.\n\n## CLI reference\n\n### `cubbit-pages deploy \u003cdirectory\u003e`\n\n| Flag | Description |\n|------|-------------|\n| `--bucket`, `-b` | Bucket name (or `CUBBIT_BUCKET`) |\n| `--access-key` | API key (or `CUBBIT_ACCESS_KEY`) |\n| `--secret-key` | Secret key (or `CUBBIT_SECRET_KEY`) |\n| `--endpoint` | S3 endpoint (default: `https://s3.cubbit.eu`) |\n| `--region` | AWS/S3 region (default: `eu-west-1`) |\n| `--encrypt`, `-e` | Enable AES-256-GCM encryption |\n| `--password`, `-p` | Encryption password (or `CUBBIT_PASSWORD`; omit to be prompted) |\n| `--public-bucket` | Assume public bucket policy (skip per-object ACL) |\n| `--dry-run` | Show what would be uploaded without uploading |\n| `--clean` | Delete S3 files not in source directory after upload (default: `true`; use `--clean=false` to disable) |\n| `--concurrency` | Parallel uploads (default: 5) |\n| `--prefix` | S3 key prefix for all files |\n| `--locale` | Login page language: `en`, `it` (default: `en`, or `CUBBIT_LOCALE`) |\n\n### `cubbit-pages setup`\n\nInteractive wizard. Prompts for Access Key, Secret Key, Endpoint, Bucket, and login page locale. Creates the bucket if it doesn't exist. Verifies the connection with `HeadBucket`. Saves to `~/.cubbit/pages/config.yaml` (mode 0600).\n\n### `cubbit-pages status`\n\n| Flag | Description |\n|------|-------------|\n| `--deep` | Query S3 for full deploy inventory (requires credentials) |\n| `--json` | Output as JSON (machine-readable; works with or without `--deep`) |\n| `--bucket`, `-b` | Bucket name (or `CUBBIT_BUCKET`) — needed for `--deep` without config file |\n| `--access-key` | API key (or `CUBBIT_ACCESS_KEY`) |\n| `--secret-key` | Secret key (or `CUBBIT_SECRET_KEY`) |\n| `--endpoint` | S3 endpoint |\n| `--region` | AWS/S3 region (default: `eu-west-1`) |\n\nWithout `--deep`: reads from `~/.cubbit/pages/config.yaml` (fast, offline). With `--deep`: does 1 ListObjects + 1 HeadObject per deploy found; shows full inventory from S3.\n\n### `cubbit-pages update`\n\nDownloads and installs the latest release in-place:\n\n```bash\ncubbit-pages update\n```\n\nIf the binary is in a system directory (e.g. `/usr/local/bin`), run with `sudo`. On Windows, run as Administrator.\n\n### `cubbit-pages list`\n\n| Flag | Description |\n|------|-------------|\n| `--bucket`, `-b` | Bucket name (or `CUBBIT_BUCKET`) |\n| `--access-key` | API key (or `CUBBIT_ACCESS_KEY`) |\n| `--secret-key` | Secret key (or `CUBBIT_SECRET_KEY`) |\n| `--endpoint` | S3 endpoint |\n| `--prefix` | Filter by S3 key prefix |\n\n### `cubbit-pages delete`\n\n| Flag | Description |\n|------|-------------|\n| `--bucket`, `-b` | Bucket name (or `CUBBIT_BUCKET`) |\n| `--access-key` | API key (or `CUBBIT_ACCESS_KEY`) |\n| `--secret-key` | Secret key (or `CUBBIT_SECRET_KEY`) |\n| `--endpoint` | S3 endpoint |\n| `--prefix` | Delete only files with this prefix (omitting deletes the entire bucket — a warning is shown) |\n| `--yes`, `-y` | Skip confirmation prompt |\n\nExits with code 1 if the user aborts the confirmation.\n\n### `cubbit-pages open`\n\n| Flag | Description |\n|------|-------------|\n| `--bucket`, `-b` | Bucket name (or `CUBBIT_BUCKET`) |\n| `--endpoint` | S3 endpoint |\n| `--prefix` | S3 key prefix (defaults to the prefix of the last deploy) |\n\nOpens the deployed site URL in the system browser (`xdg-open` on Linux, `open` on macOS, `start` on Windows). Does not require S3 credentials — only bucket and endpoint are needed to build the URL.\n\n### `cubbit-pages snippets`\n\n| Flag | Description |\n|------|-------------|\n| `--bucket`, `-b` | Bucket name |\n| `--type` | `bucket-policy`, `cors`, `iam`, `lifecycle`, `all` (default: `all`) |\n\n### `cubbit-pages version`\n\nShows version, commit hash and build date.\n\n### Adding a new locale (contributors only)\n\n```bash\nmake add-locale LOCALE=fr\n```\n\nInteractive wizard that prompts for each UI string and appends the new entry to `internal/login/locales.go`. Run `make test` afterwards to verify all fields are populated.\n\n## Security\n\n- AES-256-GCM encryption with PBKDF2 key derivation (100,000 iterations, SHA-256)\n- Random salt and nonce per file (16 bytes and 12 bytes)\n- Password never transmitted over the network — decryption happens in the browser\n- Canary file (`_verify.enc`) validates the password without downloading large files\n- Service worker persists the password in IndexedDB (SW scope only, not accessible to page scripts) so it survives SW restarts; the login and loader pages use `sessionStorage` (cleared on tab close); password is never transmitted or stored on disk\n- `sw.js` is the only unencrypted file besides the login page (it contains no secrets)\n- S3 credentials are never saved to disk by the CLI\n\n## Related: Cubbit Seal\n\nCubbit Pages is the companion project to [Cubbit Seal](https://github.com/marcodellemarche/cubbit-seal):\n\n- **Visual style**: same colors, fonts and patterns in the login page\n- **Crypto format**: intentionally different (`CPGS` vs `CBSH`) — not interoperable\n- **Conventions**: same project structure and code style\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcodellemarche%2Fcubbit-pages","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcodellemarche%2Fcubbit-pages","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcodellemarche%2Fcubbit-pages/lists"}