# Operator Checklist — `cameleer-website` One-time setup that lives outside code. Do these before the first `main` merge that ships live. ## 1. Hetzner Webhosting L - [ ] Provision Webhosting L plan. Note the SSH hostname and SFTP path. - [ ] In the Hetzner control panel, **enable SSH access** for the main user. - [ ] Generate an ed25519 SSH key pair locally (once): ```bash ssh-keygen -t ed25519 -f ~/.ssh/cameleer-website-deploy -C "cameleer-website CI" ``` - [ ] Add the **public** key to `~/.ssh/authorized_keys` on the Hetzner account. - [ ] Test SSH: `ssh -i ~/.ssh/cameleer-website-deploy user@hetzner-host "ls -la"`. - [ ] Create a subdirectory for the site (typical path: `public_html/www.cameleer.io/`). - [ ] Grab the SSH host key for pinning: ```bash ssh-keyscan -t ed25519 hetzner-host > hetzner-known-hosts.txt ``` - [ ] Install Let's Encrypt (or use Hetzner's built-in) for the origin hostname. Cloudflare Full (strict) requires a valid origin cert. ## 2. Cloudflare (zone: cameleer.io) ### DNS - [ ] `A` record `www.cameleer.io` → Hetzner IP. **Proxied (orange).** - [ ] `A` record `@` (apex) → Hetzner IP. **Proxied (orange).** - [ ] `A`/`CNAME` for `auth.cameleer.io` → SaaS ingress. **Proxied.** - [ ] `A`/`CNAME` for `platform.cameleer.io` → SaaS ingress. **Proxied.** - [ ] NO bare MX. If email is needed at `@cameleer.io`, use **Cloudflare Email Routing** or a distinct hostname on a different provider. ### SSL/TLS - [ ] Mode: **Full (strict)**. - [ ] Minimum TLS: **1.2**. - [ ] TLS 1.3: **on**. - [ ] Always Use HTTPS: **on**. - [ ] Automatic HTTPS Rewrites: **on**. - [ ] HSTS: `max-age=31536000; includeSubDomains; preload`. (Add the domain to `https://hstspreload.org/` only after the site is stable and serving HSTS cleanly for a couple of weeks.) ### Security - [ ] WAF → **Cloudflare Managed Ruleset**: enabled (Free plan includes this since 2024). - [ ] Bot Fight Mode: **on**. - [ ] Browser Integrity Check: **on**. - [ ] Security Level: **medium**. - [ ] Email Obfuscation: **on**. - [ ] Rate Limiting rule: 20 req/min per IP on `/*` (marketing pages). ### Transform Rules (edge-level security headers) Create a Transform Rule — "Modify Response Header" — matching `http.host eq "www.cameleer.io"`: | Operation | Header | Value | |-----------|--------|-------| | Set | `Content-Security-Policy` | `default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'none'; object-src 'none'` | | Set | `X-Content-Type-Options` | `nosniff` | | Set | `X-Frame-Options` | `DENY` | | Set | `Referrer-Policy` | `strict-origin-when-cross-origin` | | Set | `Permissions-Policy` | `geolocation=(), microphone=(), camera=(), payment=(), usb=()` | ### Page Rules / Redirect - [ ] `cameleer.io/*` → `https://www.cameleer.io/$1` (301 permanent). ## 3. Gitea Actions secrets (in the repo settings) Add these under Repository settings → Actions → Secrets (or variables): | Name | Type | Value | |------|------|-------| | `SFTP_HOST` | secret | Hetzner SSH hostname | | `SFTP_USER` | secret | Hetzner SSH user | | `SFTP_PATH` | secret | Absolute path to document root (e.g., `/usr/home/cameleer/public_html/www.cameleer.io`) | | `SFTP_KEY` | secret | Contents of `~/.ssh/cameleer-website-deploy` (private key, PEM) | | `SFTP_KNOWN_HOSTS` | secret | Contents of `hetzner-known-hosts.txt` (captured via `ssh-keyscan`) | | `PUBLIC_AUTH_SIGNIN_URL` | secret | `https://auth.cameleer.io/sign-in` | | `PUBLIC_AUTH_SIGNUP_URL` | secret | `https://auth.cameleer.io/sign-in?first_screen=register` | | `PUBLIC_SALES_EMAIL` | secret | `sales@cameleer.io` (or whatever sales alias you set up) | These three are not actually secret (they end up in the built HTML), but Gitea's Actions UI puts them in the **Secrets** tab alongside the SFTP credentials. The workflows read them via the `${{ secrets.* }}` context. ## 4. Content TODO — before go-live - [ ] Fill in `src/pages/imprint.astro` `operator` object with real legal details. - [ ] Fill in `operatorContact` in `src/pages/privacy.astro`. - [ ] Review the "Why us" / nJAMS wording in `src/components/sections/WhyUs.astro` for trademark safety. - [ ] Confirm MID-tier retention: spec says **7 days**; `cameleer-saas/HOWTO.md` says **30 days**. Reconcile one side or the other. ## 5. First deploy 1. Merge a PR to `main`. 2. Watch the Gitea Actions run: `build`, then `deploy`. 3. The workflow includes a post-deploy smoke check — if HSTS / CSP / XFO are missing from the live response, the deploy fails and must be debugged at the Cloudflare Transform Rule layer. 4. Manually verify: - `curl -sI https://www.cameleer.io/` returns all six security headers. - `https://cameleer.io/` → `https://www.cameleer.io/` 301 redirect. - Open the site in an incognito window on desktop + mobile.