carlos / platform Public

harness: release v0.28.5 with test sums key, digests on, console shipped and promoted (step 7)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 60eef8dee639d53ef8d5b84574a12b4a68621db9 parent 1b635e6
6 files changed, +141 −0
  • harness/RUNLOG.md +44 −0
  • harness/checks/check7.sh +7 −0
  • harness/infra-copies/PROVENANCE.md +1 −0
  • harness/infra-copies/a0b3f5a/scripts/ship-app.sh +13 −0
  • harness/release/release-setup.sh +51 −0
  • harness/release/step7-operator.sh +25 −0
diff --git a/harness/RUNLOG.md b/harness/RUNLOG.md
index e0dcc5782..6bb0b6c85 100644
--- a/harness/RUNLOG.md
+++ b/harness/RUNLOG.md
@@ -370,3 +370,47 @@ runuser -l operator # the operator shell: harness carlos CLI, aws -> stan
Reset: `incus stop infra leo sophie dennis signer; for c in infra leo sophie dennis signer; do
incus snapshot restore $c boot; done; incus start infra; incus start leo sophie dennis signer`.
+
+## Step 7: test keys, the harness release, digests, the console shipped and promoted
+
+Release **v0.28.5** (a harness-only tag; nothing outside harness MinIO carries it), built on
+DOCS-T1 from branch head `1b635e61` (code = main `96db0efe` + the harness file), `-tags harness`,
+`-ldflags "-s -w -X .../internal/version.Build=v0.28.5"`, `GOFLAGS=-buildvcs=false`:
+`carlos-linux-arm64` `111a317c...5c31`, `console-linux-arm64` `b2b431f3...17e0`
+(`go version -m`: `-ldflags=... Build=v0.28.5`, `-tags=harness`). Console ship version CV =
+`1b635e61`. At `s3://carlos-harness-640012953448/artefacts/v0.28.5/`.
+
+`harness/release/release-setup.sh` (on host, root, 01:12Z):
+- **Test sums key**: `openssl genpkey -algorithm ed25519` -> `/run/harness/keys/sums.key`
+ (root 600, tmpfs). Public half `mAnSlX23...` -> `/etc/carlos/harness-sums-pubkey` (root 644)
+ on the host, leo, sophie, dennis and signer. `CARLOS_HARNESS_INFO=1` now reports it.
+- **SHA256SUMS.txt** (`b51b4663...`) lists `carlos-linux-arm64` and
+ `carlos_0.28.5_{amd64,arm64}.deb`; signed with `openssl pkeyutl -sign -rawin` ->
+ `SHA256SUMS.txt.sig` (base64); `openssl pkeyutl -verify`: Signature Verified Successfully.
+ **Deviation:** no debs are built; the two `.deb` entries hash one-line placeholder files,
+ because `publish-identity` requires both names in the list.
+- **Release key** private half -> fake Parameter Store `/carlos/release-key` (SecureString,
+ version 1), via a 0600 tmpfs file and `--value file://`, never on a command line.
+- `/usr/local/bin/carlos` on the host is now the v0.28.5 release CLI (the runbook's `$CARLOS`).
+
+`harness/release/step7-operator.sh` (operator shell): runbook 5.0a first run item 3 and item 4.
+Command changes: profile `kass` -> `harness`, bucket `carlos-dev-640012953448` ->
+`harness-deployment`, CV and the console file are the harness release's; `ship-app.sh` is
+verbatim (`infra-copies/a0b3f5a/scripts/ship-app.sh`, `CARLOS_BIN=/usr/local/bin/carlos`).
+
+```
+carlos v0.28.5
+== 3: CARLOS_RELEASE_KEY=$(aws ssm get-parameter --name /carlos/release-key ...) carlos channels digests on --account-id bab
+pointer digests on for bab. Upgraded writers sign digests from now on
+pointer digests: on for bab since 2026-10-20T01:12:47Z
+== 4: CARLOS_BIN=$CARLOS ~/infra/scripts/ship-app.sh harness harness-deployment console 1b635e61 canary/console binary $CONSOLE
+shipped console 1b635e61 (1 artifacts)
+promoted console 1b635e61 -> canary/console
+canary/console 1b635e61 2026-10-20T01:12:48Z yes <- carlos channels (verifies under the test release pubkey)
+releases/bab/pointer-digests 231 bytes <- the digests marker
+manifest: console 1b635e61 binary github.com/carlosframework/platform/cmd/console carlos v0.28.5 {'linux-arm64': 'b2b431f3...17e0'}
+```
+
+Check (`harness/checks/check7.sh`): under the test release public key the pointer verifies
+(`canary/console 1b635e61 ... yes`); under a freshly minted other key the same read prints
+`ERROR: channel pointer signature verification failed`. **Step 7: GREEN** (2026-10-20T01:14Z).
diff --git a/harness/checks/check7.sh b/harness/checks/check7.sh
new file mode 100644
index 000000000..9cc336fbd
--- /dev/null
+++ b/harness/checks/check7.sh
@@ -0,0 +1,7 @@
+runuser -l operator -c '
+export CARLOS_DEPLOYMENT_BUCKET=harness-deployment
+echo "-- under the test release key:"; CARLOS_RELEASE_PUBKEY="$(cat ~/release.pub)" carlos channels --app console --account-id bab 2>&1 | tail -1
+OTHER=$(carlos release-keygen 2>/dev/null | sed -n "s/^CARLOS_RELEASE_PUBKEY=//p")
+echo "-- under another key:"; CARLOS_RELEASE_PUBKEY="$OTHER" carlos channels --app console --account-id bab 2>&1 | tail -2
+aws s3 cp --quiet s3://harness-deployment/apps/bab/console/channels/canary/console.json - | python3 -c "import json,sys; d=json.load(sys.stdin); print(\"pointer keys:\", sorted(d)); c=d.get(\"pointer\") or d; print(json.dumps(c)[:400])"
+'
diff --git a/harness/infra-copies/PROVENANCE.md b/harness/infra-copies/PROVENANCE.md
index 30db28a9f..f56e9ed36 100644
--- a/harness/infra-copies/PROVENANCE.md
+++ b/harness/infra-copies/PROVENANCE.md
@@ -11,3 +11,4 @@ the infrastructure repo holds.
| `a0b3f5a/scripts/bootstrap-box.sh` | `scripts/bootstrap-box.sh` | `a0b3f5a5f2221429e8fb18e69eba504704be25b7` | origin/main | `46aa1642a24dde15...` |
| `a0b3f5a/scripts/update-platform-binary.sh` | `scripts/update-platform-binary.sh` | `a0b3f5a5f2221429e8fb18e69eba504704be25b7` | origin/main | `61bf3ef8f4e6f878...` |
| `747f699/dev/host-init.sh` | `dev/host-init.sh` | `747f699d8e48e14fd012a4ccd2f7ea830701dcee` | origin/host-root-owned-opt | `9ef0c3dbe9506571...` |
+| `a0b3f5a/scripts/ship-app.sh` | `scripts/ship-app.sh` | `a0b3f5a5f2221429e8fb18e69eba504704be25b7` | origin/main | `1274367d5f64fe2e...` |
diff --git a/harness/infra-copies/a0b3f5a/scripts/ship-app.sh b/harness/infra-copies/a0b3f5a/scripts/ship-app.sh
new file mode 100755
index 000000000..55781e2c2
--- /dev/null
+++ b/harness/infra-copies/a0b3f5a/scripts/ship-app.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+# ๐Ÿค– Ship and promote a release to a deployment. The signing key is read
+# from SSM inside this script and exported only to the promote call โ€”
+# it never appears in a terminal or transcript.
+# Usage: ship-app.sh <aws-profile> <deployment-bucket> <app> <version> <channel> <kind> <file-or-dir>
+set -euo pipefail
+profile=$1; bucket=$2; app=$3; ver=$4; channel=$5; kind=$6; artifact=$7
+carlos=${CARLOS_BIN:-./carlos}
+export AWS_PROFILE=$profile AWS_REGION=eu-west-1 CARLOS_DEPLOYMENT_BUCKET=$bucket
+"$carlos" ship -app "$app" -kind "$kind" -version "$ver" "$artifact"
+CARLOS_RELEASE_KEY=$(aws ssm get-parameter --name /carlos/release-key \
+ --with-decryption --query Parameter.Value --output text) \
+ "$carlos" promote -app "$app" "$ver" "$channel"
diff --git a/harness/release/release-setup.sh b/harness/release/release-setup.sh
new file mode 100755
index 000000000..650b66b26
--- /dev/null
+++ b/harness/release/release-setup.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+# Step 7 (host side): the harness release v0.28.5 with test keys (H5).
+# REL_URL_CARLOS=... REL_URL_CONSOLE=... SUMS="<carlos sha> <console sha>" CV=<ship version> release-setup.sh
+# The sums key is generated here on tmpfs and never leaves /run; only its
+# public half is written to /etc/carlos/harness-sums-pubkey (host, boxes, signer).
+set -euo pipefail
+TAG=v0.28.5; V=${TAG#v}
+R=/root/harness/release/$TAG; K=/run/harness/keys
+mkdir -p $R
+curl -sf -o $R/carlos-linux-arm64 "$REL_URL_CARLOS"; curl -sf -o $R/console-linux-arm64 "$REL_URL_CONSOLE"
+set -- $SUMS
+[ "$(sha256sum < $R/carlos-linux-arm64 | cut -d' ' -f1)" = "$1" ] && [ "$(sha256sum < $R/console-linux-arm64 | cut -d' ' -f1)" = "$2" ] || { echo "artefact sha256 mismatch"; exit 1; }
+chmod 755 $R/carlos-linux-arm64 $R/console-linux-arm64
+
+# The test sums key (ed25519), as the README's release recipe signs with openssl.
+if [ ! -s $K/sums.key ]; then (umask 077; openssl genpkey -algorithm ed25519 -out $K/sums.key); fi
+openssl pkey -in $K/sums.key -pubout -outform DER | tail -c 32 | base64 -w0 > $R/sums.pub
+# No debs are built for a harness release: placeholder files stand in, so the
+# signed list names carlos_<V>_{amd64,arm64}.deb as publish-identity requires.
+for a in amd64 arm64; do printf 'harness placeholder: no deb is built for %s %s\n' "$TAG" "$a" > $R/carlos_${V}_$a.deb; done
+(cd $R && sha256sum carlos-linux-arm64 carlos_${V}_amd64.deb carlos_${V}_arm64.deb > SHA256SUMS.txt)
+openssl pkeyutl -sign -inkey $K/sums.key -rawin -in $R/SHA256SUMS.txt | base64 -w0 > $R/SHA256SUMS.txt.sig
+echo >> $R/SHA256SUMS.txt.sig
+
+# The sums public key where the harness build reads it.
+install -m 0644 $R/sums.pub /etc/carlos/harness-sums-pubkey
+for c in leo sophie dennis signer; do incus file push $R/sums.pub $c/etc/carlos/harness-sums-pubkey --uid 0 --gid 0 --mode 0644 -q; done
+
+# The release key's private half into the fake Parameter Store (spec ยง3.11):
+# read from release.env into a 0600 tmpfs file, passed with file://, never on a command line.
+(umask 077; sed -n 's/^CARLOS_RELEASE_KEY=//p' $K/release.env | tr -d '\n' > $K/release-key.value)
+eval "$(python3 -c 'import json; u=json.load(open("/root/harness/minio-users.json"))["operator"]; print("export AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s" % (u["access_key"], u["secret_key"]))')"
+AWS_ENDPOINT_URL_SSM=http://203.0.113.1:8100 AWS_REGION=eu-west-1 aws ssm put-parameter --name /carlos/release-key --type SecureString \
+ --value file://$K/release-key.value --overwrite --query Version --output text
+rm -f $K/release-key.value
+unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
+
+# The operator's CLI is the release's (the runbook's "$CARLOS").
+install -m 0755 $R/carlos-linux-arm64 /usr/local/bin/carlos
+
+# The operator gets the release files (no key).
+O=/home/operator/release-$TAG; install -d -o operator -g operator -m 0755 $O
+for f in carlos-linux-arm64 console-linux-arm64 SHA256SUMS.txt SHA256SUMS.txt.sig carlos_${V}_amd64.deb carlos_${V}_arm64.deb sums.pub; do install -o operator -g operator -m 0644 $R/$f $O/$f; done
+chmod 755 $O/carlos-linux-arm64 $O/console-linux-arm64
+echo "$CV" > $O/CV; chown operator:operator $O/CV
+install -o operator -g operator -m 0755 /root/harness/infra-copies/ship-app.sh /home/operator/infra/scripts/ship-app.sh
+echo "== release $TAG"; (cd $R && cat SHA256SUMS.txt && sha256sum SHA256SUMS.txt SHA256SUMS.txt.sig)
+echo "sums pubkey $(cut -c1-8 $R/sums.pub)... on: host $(stat -c '%U:%G %a' /etc/carlos/harness-sums-pubkey)"; for c in leo sophie dennis signer; do echo " $c $(incus exec $c -- stat -c '%U:%G %a' /etc/carlos/harness-sums-pubkey)"; done
+echo "sums.key $(stat -c '%U:%G %a' $K/sums.key) on $(findmnt -no FSTYPE --target $K)"
+echo "verify sums with openssl: $(openssl pkey -in $K/sums.key -pubout > /tmp/sums.pub.pem; base64 -d $R/SHA256SUMS.txt.sig > /tmp/sig.bin; openssl pkeyutl -verify -pubin -inkey /tmp/sums.pub.pem -rawin -in $R/SHA256SUMS.txt -sigfile /tmp/sig.bin; rm -f /tmp/sums.pub.pem /tmp/sig.bin)"
+echo "harness info now: $(CARLOS_HARNESS_INFO=1 $R/carlos-linux-arm64 | tr '\n' ' ')"
diff --git a/harness/release/step7-operator.sh b/harness/release/step7-operator.sh
new file mode 100755
index 000000000..35e183062
--- /dev/null
+++ b/harness/release/step7-operator.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# Step 7 (operator shell): runbook 5.0a first run item 3 (digests on for bab)
+# and item 4 (ship and promote the console with ship-app.sh, unchanged).
+# Substitutions for the harness: profile kass -> harness, bucket
+# carlos-dev-640012953448 -> harness-deployment, CV and the console file are
+# the harness release's.
+set -euo pipefail
+cd ~/release-v0.28.5
+CARLOS=/usr/local/bin/carlos
+CONSOLE=~/release-v0.28.5/console-linux-arm64
+CV=$(cat CV)
+"$CARLOS" version
+export AWS_PROFILE=harness AWS_REGION=eu-west-1 CARLOS_DEPLOYMENT_BUCKET=harness-deployment CARLOS_RELEASE_PUBKEY="$(cat ~/release.pub)"
+echo "== 1a"; aws s3 ls --recursive s3://harness-deployment/apps/ | awk '{print $4}' | grep -E '^apps/[^/]+/console/channels/' || echo "(no console pointer yet)"
+echo "== 2"; sha256sum "$CONSOLE"; grep -c "$(sha256sum "$CONSOLE" | cut -d' ' -f1)" SHA256SUMS.txt || true
+echo "== 3: digests on"
+CARLOS_RELEASE_KEY=$(aws ssm get-parameter --name /carlos/release-key --with-decryption --query Parameter.Value --output text) \
+ "$CARLOS" channels digests on --account-id bab
+echo "== 4: ship and promote"
+CARLOS_BIN=$CARLOS ~/infra/scripts/ship-app.sh harness harness-deployment console "$CV" canary/console binary "$CONSOLE"
+echo "== checks"
+"$CARLOS" channels --app console --account-id bab
+"$CARLOS" channels digests --account-id bab
+aws s3 ls s3://harness-deployment/releases/bab/
+aws s3 cp --quiet s3://harness-deployment/apps/bab/console/releases/$CV/manifest.json - | python3 -c 'import json,sys; m=json.load(sys.stdin); print("manifest:", m.get("app"), m.get("version"), m.get("kind"), m.get("program"), m.get("built_by"), {k:v.get("sha256") for k,v in (m.get("artifacts") or {}).items()})'