docs/deploy-bes-website
Squashed from docs/deploy-bes-website into main.
1 file changed,
+68
−78
AGENTS.md+68 −78
diff --git a/AGENTS.md b/AGENTS.md| index caabe96..e33f37c 100644 |
| --- a/AGENTS.md |
| +++ b/AGENTS.md |
| @@ -79,32 +79,37 @@ in `_site/` without you editing its source, something is wrong. |
| ## Deploying |
| -Merging does **not** publish. `carloku.com` is a bucket-mode CARLOS app on the |
| -flagship, and the live site is stale until someone ships. |
| +Merging does **not** publish. The live site is stale until someone deploys. |
| -**There is a build step now, so what ships is `_site/` — never the repo tree.** |
| -A tree ship has no `_site/` in it: it would publish `index.html` buried under |
| -`src/`, no built pages, and no `/docs` at all. |
| - |
| -The deployment coordinates, confirmed against the bucket on 2026-08-27: |
| +**Since 2026-10-04 the site is an ordinary console app** (platform #209): |
| | | | |
| | --- | --- | |
| -| app | `carloku` | |
| -| account (sqid) | `bab` | |
| -| channel | `canary/rehearsal` — the app's only channel | |
| +| console | `https://console.carloku.com` | |
| +| account / app | `bes` (named `carloku`) / `website` | |
| +| channel | `edge`, the app's only channel | |
| | kind | `static` | |
| -| bucket | `carlos-flagship-271376211898` | |
| -| AWS profile / region | `keymail` / `eu-west-1` | |
| +| platform URL | `https://website.bes.oncarlos.com` | |
| +| custom domains | `carloku.com`, `www.carloku.com` | |
| + |
| +carloku.com and www.carloku.com are **platform claims** on that app. They are |
| +in the flagship's own zone, which `carlos domains attach` refuses to members, |
| +so the operator attached them with `carlos domains attach --platform`. Only an |
| +ops account owner or a `CARLOS_SYSTEM_OPERATORS` address can change or detach |
| +them. A deploy never touches them: deploy the app and both hostnames follow. |
| + |
| +**There is a build step, so what ships is `_site/`, never the repo tree.** A |
| +tree ship has no `_site/` in it: it would publish `index.html` buried under |
| +`src/`, no built pages, and no `/docs` at all. |
| ``` |
| SHA=$(git rev-parse --short HEAD) # the sha you are shipping |
| -# 1. Clean export. Never ship a working checkout — PackDir packs every |
| +# 1. Clean export. Never deploy a working checkout: the packer takes every |
| # regular file it sees, including .git, node_modules/ and .claude/. |
| -rm -rf /tmp/carloku-ship && mkdir -p /tmp/carloku-ship |
| -git archive "$SHA" --prefix=export/ | tar -x -C /tmp/carloku-ship |
| -cd /tmp/carloku-ship/export |
| +rm -rf "$TMPDIR/carloku-ship" && mkdir -p "$TMPDIR/carloku-ship" |
| +git archive "$SHA" --prefix=export/ | tar -x -C "$TMPDIR/carloku-ship" |
| +cd "$TMPDIR/carloku-ship/export" |
| # 2. Build, and gate the rendered output. `check` runs eleventy, then |
| # hack/check-docs.mjs over what it produced. |
| @@ -121,33 +126,19 @@ grep -rho '\.css?v=[^"]*"' _site --include='*.html' | sort -u # ONE line: your |
| grep -rn '\.css"' _site --include='*.html' \ |
| && echo "UNBUSTED LINK — do not ship" || echo "cache-bust ok" |
| -# 4. Ship the BUILT OUTPUT, then promote. The pinfra scripts are the |
| -# canonical form for this app: the signing key is read from SSM inside |
| -# the script and never appears in a terminal or a transcript. |
| -cd ~/github.com/carlosframework/platform-infrastructure |
| -CARLOS_BIN=$(command -v carlos) scripts/ship-app.sh \ |
| - keymail carlos-flagship-271376211898 \ |
| - carloku "$SHA" canary/rehearsal static /tmp/carloku-ship/export/_site |
| +# 4. Deploy the BUILT OUTPUT through the console. --version is required: |
| +# the export has no .git, so the default (git rev-parse) has nothing to |
| +# read. |
| +carlos deploy --console https://console.carloku.com \ |
| + --account bes --app website --kind static \ |
| + --version "$SHA" --label "<one line about the change>" --no-prompt _site |
| ``` |
| -`ship-app.sh` takes `<aws-profile> <bucket> <app> <version> <channel> <kind> |
| -<file-or-dir>` and does both halves. The equivalent by hand, if you are not in |
| -the pinfra checkout, is: |
| - |
| -``` |
| -export AWS_PROFILE=keymail AWS_REGION=eu-west-1 \ |
| - CARLOS_DEPLOYMENT_BUCKET=carlos-flagship-271376211898 |
| -carlos ship --app carloku --kind static --version "$SHA" _site |
| -CARLOS_RELEASE_KEY=$(aws ssm get-parameter --name /carlos/release-key \ |
| - --with-decryption --query Parameter.Value --output text) \ |
| - carlos promote --app carloku "$SHA" canary/rehearsal |
| -``` |
| - |
| -The env matters: without `CARLOS_DEPLOYMENT_BUCKET` the CLI goes through the |
| -console API, where this app was never registered, and fails with "not found". |
| -This is bucket mode. Use a current `carlos` binary — the ship/promote pair is |
| -the platform's own release path, and an old binary on your `PATH` is a real |
| -hazard here. |
| +`carlos deploy` ships, promotes onto `edge` and watches the platform URL until |
| +`X-Carlos-Version` reports the new sha. It runs as whoever is signed in |
| +(`carlos auth login --console https://console.carloku.com`) and needs that |
| +address to be a member of `bes`. Use a current `carlos` binary. An old one on |
| +your `PATH` may not know this console's API. |
| **Step 3 explained, because both halves are load-bearing.** The links in `src/` |
| are plain (`href="/site.css"`), so the first expression stamps a token onto a |
| @@ -158,13 +149,12 @@ beginning with `0` — this app has already shipped `01b5365` — makes |
| `?v=0` calls every freshly bumped file stale (13 of 13, measured), and an |
| unanchored `sed` run twice yields `?v=01b536501b5365`. Anchoring on `"` fixes |
| both and makes step 3 idempotent: run it again and again, the answer is the |
| -same. Today that is 24 links across 13 built pages — 13 × `site.css`, 11 × |
| -`docs.css`. Don't hard-code those counts anywhere; the corpus grows, which is |
| +same. Don't hard-code link or page counts anywhere; the corpus grows, which is |
| also why the `find` walks `_site` instead of naming files. |
| -**Caching: the edge sends no `Cache-Control` and no `ETag` on this static |
| -route — only `Last-Modified`** (confirmed live on carloku.com 2026-08-27). |
| -Browsers therefore apply HEURISTIC caching, roughly 10% of the age since |
| +**Caching: the edge sends no `Cache-Control` and no `ETag` on static routes, |
| +only `Last-Modified`** (confirmed live on carloku.com 2026-08-27). Browsers |
| +therefore apply HEURISTIC caching, roughly 10% of the age since |
| `Last-Modified`, so a returning visitor can hold a stale page for days. This |
| bit the sibling site the day its redesign shipped: one browser served the whole |
| old page, another served the NEW html against the OLD stylesheet. The real fix |
| @@ -174,46 +164,46 @@ of deploying this site**, and it is required for `docs.css` exactly as much as |
| for `site.css`: the docs pages link both, and a docs page rendered against a |
| stale `docs.css` loses its whole layout. |
| -**Convergence is seconds, not minutes** (platform PR #108, live 2026-08-08): a |
| -promote is picked up by the edge within ~2s. Verify by header and by content, |
| -and verify `/docs` **specifically** — the landing page looks right whether or |
| -not the built docs made it into the artifact: |
| +**Verify** by header and by content, and verify `/docs` **specifically**: the |
| +landing page looks right whether or not the built docs made it into the |
| +artifact. |
| ``` |
| -# by header — --resolve so a stale DNS answer cannot satisfy it. 99.81.104.219 |
| -# is the flagship, where the carloku.com A record points. |
| -curl -sI --resolve carloku.com:443:99.81.104.219 \ |
| - https://carloku.com/docs/ | grep -i x-carlos-version |
| +carlos channels --console https://console.carloku.com --account bes --app website |
| +carlos releases --console https://console.carloku.com --account bes --app website |
| -# by content |
| +curl -sI https://carloku.com/docs/ | grep -i x-carlos-version # your sha |
| curl -s https://carloku.com/ | grep -i "<something from the change>" |
| -curl -s https://carloku.com/docs/ | grep -o 'docs\.css?v=[^"]*' # your sha |
| +curl -s https://carloku.com/docs/ | grep -o 'docs\.css?v=[^"]*' # your sha |
| ``` |
| -Static routes do carry `X-Carlos-Version` (platform#112). `-I` is fine for that |
| -header, but it sends a HEAD, and HEAD is excluded from edge compression — |
| -anything about `Content-Encoding` needs a GET. |
| +Every edge serves the site, so to check one directly add |
| +`--resolve carloku.com:443:<edge-ip>`. The four flagship edges are |
| +99.81.104.219 (Ireland), 16.60.78.128 (London), 44.210.206.26 (Virginia) and |
| +54.252.186.195 (Sydney). `-I` sends a HEAD, and HEAD is excluded from edge |
| +compression, so anything about `Content-Encoding` needs a GET. |
| -**`carlos channels` cannot verify this app.** It is direct-bucket and invisible |
| -to the console API: `carlos channels -app carloku` answers "not found" |
| -(re-confirmed 2026-08-27). Verify by header or content as above, or read the |
| -pointer out of the bucket |
| -(`aws s3 cp s3://carlos-flagship-271376211898/apps/bab/carloku/channels/canary/rehearsal.json -`). |
| - |
| -Rollback is a pointer move, like any CARLOS app — promote the previous good sha |
| -back onto the same channel; the edge picks it up in seconds: |
| +**Rollback** is a pointer move. Every release stays in the bucket, cache-busted |
| +as it was shipped, so a rollback restores a self-consistent page-plus-stylesheet |
| +pair: |
| ``` |
| -cd ~/github.com/carlosframework/platform-infrastructure |
| -CARLOS_BIN=$(command -v carlos) scripts/promote-app.sh \ |
| - keymail carlos-flagship-271376211898 bab carloku <previous-sha> canary/rehearsal |
| +carlos rollback --console https://console.carloku.com --account bes --app website edge |
| +# or, to a named release: |
| +carlos promote --console https://console.carloku.com --account bes --app website <sha> edge |
| ``` |
| -That previous release is still in the bucket under |
| -`apps/bab/carloku/releases/<sha>/`, cache-busted as it was shipped, so a |
| -rollback restores a self-consistent page-plus-stylesheet pair. |
| - |
| -(The channel is `canary/rehearsal`, not `stable`, for the same reason the |
| -sibling site's is: `stable` bakes 72h on a box's *first* sighting of a channel |
| -head, which would mean 72h of downtime for a never-before-served route. A |
| -future `stable` flip is optional cleanup, not required.) |
| +**DNS** is in DNSimple, by hand (the carloku.com zone is not in the |
| +carloku-infrastructure tofu). The apex is an `ALIAS` and `www` a `CNAME`, both |
| +to `website.bes.oncarlos.com`, so the site follows the app wherever the |
| +platform places it. `dashboard` and `console` are CNAMEs to the console and are |
| +not this site's. One trade-off: DNSimple resolves the apex ALIAS from its own |
| +servers, so carloku.com gets whichever edge `website.bes.oncarlos.com` |
| +latency-steers DNSimple to, not the visitor's nearest. `www` keeps per-visitor |
| +steering. |
| + |
| +**History.** Until 2026-10-04 the site was `bab/carloku`, a direct-bucket app |
| +shipped with the pinfra `ship-app.sh` onto `canary/rehearsal`, with box-local |
| +routes on the Ireland box. Those routes were removed when the hostnames moved |
| +to `bes/website`. The old app's releases are still in the bucket, but nothing |
| +routes to it: **never ship to `bab/carloku` again.** |