Skip to content
Back to skills

Chrome Extension Deploy

CSecurity

Use to deploy a Chrome extension backend (API + Postgres + admin dashboard) to a VPS, or publish/release a new extension version. Triggers: "deploy extension backend", "VPS docker nginx", "let's encrypt cloudflare 521", "publish extension update", "chrome web store api", "release extension". Covers shared-VPS topology (named Compose project, Postgres never published, API/admin on 127.0.0.1, host nginx proxy, Let's Encrypt via certbot webroot + renewal hook, Cloudflare "Full (strict)", real cl...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 24, 2026
ai-agentsrustgoshellbashdockergitapidatabasebackendsecurity

Works with

  • cli
  • api

Security analysis

C67/100
  • mediumUses curl or wget to download content
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Pro scans all 13 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add thomaslimited/chrome-extension-skills --skill chrome-extension-deploy --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Chrome Extension Deploy?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Chrome Extension Deploy
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/thomaslimited-chrome-extension-deploy/badge)](https://www.skillsdirectory.com/skills/thomaslimited-chrome-extension-deploy)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: chrome-extension-deploy
description: Use to deploy a Chrome extension backend (API + Postgres + admin dashboard) to a VPS, or publish/release a new extension version. Triggers: "deploy extension backend", "VPS docker nginx", "let's encrypt cloudflare 521", "publish extension update", "chrome web store api", "release extension". Covers shared-VPS topology (named Compose project, Postgres never published, API/admin on 127.0.0.1, host nginx proxy, Let's Encrypt via certbot webroot + renewal hook, Cloudflare "Full (strict)", real client IP); prod secret checks, warn-only webhook secrets; first-time setup, upload (Windows gotchas), idempotent redeploys, health checks, rollback, backups; post-deploy webhook/OAuth wiring, CORS smoke tests; `wxt zip --mode store`, dashboard upload, staged rollout, Chrome Web Store Publish API v2 for CI. Real incidents: port 80 clashes, Cloudflare 521, nginx http2 syntax, spoofed X-Forwarded-For. Skips listing copy, OAuth/payment setup, and extension code — see sibling skills.
license: MIT
---

# Chrome Extension Deploy

## Scope

This skill handles **shipping the backend and releasing the extension**: VPS topology, Docker
Compose, host nginx, TLS, secrets, the deploy/rollback runbook, and Chrome Web Store publishing.

It does **not** handle:
- Store listing copy, screenshots, privacy-policy wording → `chrome-web-store-listing`
- Setting up the OAuth provider or payment processor accounts themselves → `chrome-extension-auth`,
  `chrome-extension-payments` (this skill only wires their redirect URIs / webhook URLs once HTTPS
  exists)
- Extension code, manifest, content scripts → `chrome-extension-dev`

If the task touches those, do the deploy/release part here and hand off the rest.

## Workflow

1. **Confirm the topology before writing any config.** One VPS, possibly shared with other sites,
   one Docker Compose project with its own `name:`, Postgres never published to the host, API and
   admin containers bound to `127.0.0.1:<port>` only, host nginx as the single thing listening on
   80/443, Cloudflare in front. Read `references/server-topology.md`.
2. **Write the Compose file and nginx vhosts from the templates**, not from scratch —
   `assets/docker-compose.prod.template.yml` and `assets/nginx-site.template.conf`. Read
   `references/docker-compose-and-nginx.md` for what each service/directive does and why.
3. **Get TLS right before touching Cloudflare's proxy toggle.** Issue the cert with certbot
   webroot on port 80 first, confirm nginx serves 443 directly, *then* enable the Cloudflare
   orange cloud with SSL mode "Full (strict)". Read `references/tls-cloudflare.md` — it documents
   the exact 521 failure mode when this order is reversed, plus the nginx 1.24
   `listen 443 ssl http2` syntax trap and X-Forwarded-For spoofing.
4. **Treat `.env.example` as the contract.** Every var the app reads must have a line there.
   Production boot must refuse to start on placeholder secrets; webhook secrets are the one
   deliberate exception (warn-only, because they can't exist before HTTPS does). Read
   `references/env-and-secrets.md` before editing config/secret-loading code or `.env.example`.
5. **Run the first-time server setup once**, then use the same idempotent redeploy procedure for
   every update after that (`git pull` or tarball upload → `docker compose up -d --build` →
   migration-on-boot → health check). Read `references/deploy-runbook.md`, including the Windows
   Git Bash upload gotchas if deploying from Windows.
6. **Wire what only works after HTTPS exists**: PayPal/Alchemy-style webhooks, OAuth redirect
   URIs, the public privacy-policy URL. Smoke-test with `curl /health` and a CORS preflight using
   `Origin: chrome-extension://<id>`. Covered at the end of `references/deploy-runbook.md`.
7. **Schedule backups before declaring the deploy done.** `assets/backup.template.sh` on a daily
   cron, pushed off the VPS. Restore procedure lives in the same file's header comment.
8. **For an extension release**: bump the version, `pnpm zip` (store mode — no manifest `key`),
   verify the zip's contents, upload via the Chrome Web Store dashboard, use staged rollout for a
   material change. First-ever upload issues a new item ID — feed it back into `ALLOWED_ORIGINS`
   and the OAuth client's redirect URIs. Read `references/extension-release.md`.
9. **Automate publishing only after a manual upload has succeeded once.** The Chrome Web Store
   Publish API (v2) can drive CI, but verify current auth/endpoint details against
   `developer.chrome.com/docs/webstore/using-api` before relying on `assets/github-actions-publish.template.yml`
   — this API is mid-migration and some details in the reference file are marked unverified.
10. **Before calling it done, check `references/troubleshooting.md`** — real symptom → cause → fix
    entries from a shipped deployment (port 80 clash, Cloudflare 521, nginx syntax errors, spoofed
    client IP, stuck migrations, rejected webhooks, CORS failures).

## Go-live checklist

| # | Item | Required for |
|---|---|---|
| 1 | Compose project has its own `name:`, Postgres has no host port mapping | prod |
| 2 | API and admin containers publish only `127.0.0.1:<port>` | prod |
| 3 | Host nginx vhosts installed, `nginx -t` clean, reloaded | prod |
| 4 | Let's Encrypt cert issued via webroot, renewal hook reloads nginx | prod |
| 5 | Cloudflare proxied with SSL mode **Full (strict)**, not Full or Flexible | prod (if using Cloudflare) |
| 6 | `.env` has real values for every required var; `assertProductionSecrets`-style boot check passes | prod |
| 7 | `ALLOWED_ORIGINS` lists every extension ID actually in use (dev key + store ID once assigned) | prod |
| 8 | `curl https://<domain>/health` returns healthy; CORS preflight from the extension origin succeeds | prod |
| 9 | Daily backup cron installed and one restore has been test-run | prod |
| 10 | Webhooks (payment/on-chain) registered against the live HTTPS URL, signing secrets set | store launch |
| 11 | OAuth redirect URIs include both the dev and store extension IDs | store launch |
| 12 | Privacy policy URL is live and matches what the store listing links to | store submission |
| 13 | `pnpm zip --mode store` output verified to exclude the dev signing key | every release |
| 14 | `.gitignore`/`.gitattributes` cover secrets and shell-script line endings; pre-push secret scan run | always |

## Reference files

- `references/server-topology.md` — shared VPS layout, Compose project isolation, port binding, why Postgres is never published
- `references/docker-compose-and-nginx.md` — Compose service walkthrough, Dockerfile choices, nginx vhost anatomy, enabling a site
- `references/tls-cloudflare.md` — certbot webroot issuance/renewal, Cloudflare SSL modes, the 521 incident, nginx http2 syntax, real client IP restoration
- `references/env-and-secrets.md` — `.env.example` as contract, production secret assertions, warn-only webhook secrets, `ALLOWED_ORIGINS`, git hygiene for secrets
- `references/deploy-runbook.md` — first-time setup, upload methods (incl. Windows Git Bash gotchas), redeploy, migrations, health checks, logs, rollback, backups, post-deploy wiring
- `references/extension-release.md` — version bump, `wxt zip --mode store`, dashboard upload, staged rollout, item-ID follow-up, Chrome Web Store Publish API for CI
- `references/troubleshooting.md` — symptom → cause → fix, from a shipped deployment

## Assets

- `assets/docker-compose.prod.template.yml` — Postgres + API + admin, no host-published database, loopback-only app ports
- `assets/nginx-site.template.conf` — HTTP→HTTPS redirect + ACME challenge + TLS proxy vhost, one per subdomain
- `assets/backup.template.sh` — encrypted `pg_dump` to off-site remote storage, with restore instructions in the header
- `assets/github-actions-publish.template.yml` — CI publish to the Chrome Web Store, credentials read from repo secrets (verify endpoint/auth details before use — see `references/extension-release.md`)
- `assets/env.example.template` — annotated env-var contract with placeholder values only; copy to `.env.example` and fill in real names

## Security

- Never read, print, or commit a real `.env` file, `server.txt`, `*.pem`, or `*.key` — only
  `.env.example` is safe to view or template. Never SSH anywhere as part of this skill's tasks.
- Every production secret that gates money movement or auth (session secret, admin secret,
  webhook signing keys) must fail closed: missing or default value either refuses boot or rejects
  every request to the endpoint it guards. See `references/env-and-secrets.md`.
- Nginx must overwrite (not append) `X-Forwarded-For` from a trusted source (Cloudflare's
  `CF-Connecting-IP` via `real_ip`, or `$remote_addr` with no CDN) — a downstream service that
  rate-limits or gates signups on this header must never let a client set it itself.
- Run a secret scan before every push (`references/env-and-secrets.md` has the command); do not
  rely on `.gitignore` alone once a secret has already been committed once.

Files in this skill

  • SKILL.md8.9 KB
  • assets/backup.template.sh1.3 KB
  • assets/docker-compose.prod.template.yml2.2 KB
  • assets/env.example.template2.2 KB
  • assets/github-actions-publish.template.yml3.8 KB
  • assets/nginx-site.template.conf1.8 KB
  • references/deploy-runbook.md6.7 KB
  • references/docker-compose-and-nginx.md4.7 KB
  • references/env-and-secrets.md5.6 KB
  • references/extension-release.md6.8 KB
  • references/server-topology.md3.9 KB
  • references/tls-cloudflare.md5.6 KB
  • references/troubleshooting.md6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.