carlos / platform Public

infra: bootstrap-box + provision-keys operator scripts (both boxes live)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell d8ee5570e7cf168f9c32a9ecfc56bfcafcfd2768 parent 2024e50
2 files changed, +62 −0
  • infra/scripts/bootstrap-box.sh +47 −0
  • infra/scripts/provision-keys.sh +15 −0
diff --git a/infra/scripts/bootstrap-box.sh b/infra/scripts/bootstrap-box.sh
new file mode 100755
index 000000000..e092cd7a0
--- /dev/null
+++ b/infra/scripts/bootstrap-box.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+# 🤖 Bootstrap a carlos host over SSM: install the binary from the
+# deployment bucket, run `carlos bootstrap`, write host.env, start the
+# edge. Idempotent — safe to re-run.
+# Usage: bootstrap-box.sh <aws-profile> <instance-id> <deployment-bucket> <data-bucket> <pubkey-file>
+set -euo pipefail
+profile=$1; instance=$2; depbucket=$3; databucket=$4; pubfile=$5
+pub=$(cat "$pubfile")
+d=$(mktemp -d)
+
+cat > "$d/bootstrap.sh" <<EOF
+set -e
+aws s3 cp s3://$depbucket/bootstrap/carlos /opt/carlos/carlos.new --no-progress
+install -m755 -o carlos -g carlos /opt/carlos/carlos.new /opt/carlos/carlos && rm /opt/carlos/carlos.new
+/opt/carlos/carlos bootstrap
+cat > /etc/carlos/host.env <<ENV
+CARLOS_DATA_DIR=/data
+CARLOS_DEPLOYMENT_BUCKET=$depbucket
+CARLOS_RELEASE_PUBKEY=$pub
+CARLOS_HIBERNATE_BUCKET=$databucket
+CARLOS_HIBERNATE_REGION=eu-west-1
+CARLOS_HIBERNATE_PREFIX=instances
+AWS_REGION=eu-west-1
+ENV
+chmod 600 /etc/carlos/host.env
+chown carlos:carlos /etc/carlos/host.env
+systemctl start carlos-edge
+sleep 3
+systemctl is-active carlos-edge
+/opt/carlos/carlos version
+EOF
+
+jq -n --rawfile s "$d/bootstrap.sh" '{commands: ($s | split("\n"))}' > "$d/params.json"
+cmdid=$(aws --profile "$profile" ssm send-command --instance-ids "$instance" \
+ --document-name AWS-RunShellScript --parameters "file://$d/params.json" \
+ --query Command.CommandId --output text)
+echo "command $cmdid sent; polling..."
+for i in $(seq 1 30); do
+ sleep 5
+ status=$(aws --profile "$profile" ssm get-command-invocation --command-id "$cmdid" \
+ --instance-id "$instance" --query Status --output text 2>/dev/null || echo Pending)
+ [ "$status" = "Success" ] || [ "$status" = "Failed" ] && break
+done
+echo "status: $status"
+aws --profile "$profile" ssm get-command-invocation --command-id "$cmdid" \
+ --instance-id "$instance" --query StandardOutputContent --output text | tail -8
+[ "$status" = "Success" ]
diff --git a/infra/scripts/provision-keys.sh b/infra/scripts/provision-keys.sh
new file mode 100755
index 000000000..7aa29e8e2
--- /dev/null
+++ b/infra/scripts/provision-keys.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# 🤖 Generate each deployment's Ed25519 release keypair: seed to SSM
+# (SecureString, never printed), pubkey to a local file for host.env.
+# Run once per deployment; -overwrite is intentional only on first setup.
+# Usage: provision-keys.sh <aws-profile> <pubkey-out-file>
+set -euo pipefail
+profile=$1; out=$2
+carlos=${CARLOS_BIN:-./carlos}
+kout=$("$carlos" release-keygen 2>/dev/null)
+seed=$(echo "$kout" | grep '^CARLOS_RELEASE_KEY=' | cut -d= -f2-)
+pub=$(echo "$kout" | grep '^CARLOS_RELEASE_PUBKEY=' | cut -d= -f2-)
+aws --profile "$profile" ssm put-parameter --name /carlos/release-key \
+ --type SecureString --value "$seed" --overwrite >/dev/null
+echo "$pub" > "$out"
+echo "seed stored in SSM /carlos/release-key ($profile); pubkey in $out"