Menu
AkurAI-Build
publicLatest change a8a2b41c75d87fb85c6d3edd568b4d56a67bc63c - Harden repository MCP and Bun worker tools by Ólafur Búi Ólafsson
#!/usr/bin/env bash
set -euo pipefail
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
HOST=$(uname -n)
APP=akurai
CONFIG="$HOME/.config/akurai-build"
ENV_FILE="$CONFIG/env"
if [[ -r $ENV_FILE ]]; then set -a; source "$ENV_FILE"; set +a; fi
DOMAIN=${AKURAI_BUILD_DOMAIN:-}
LISTEN=${AKURAI_BUILD_LISTEN:-100.88.0.9:3210}
INSTALL="$HOME/.local/lib/akurai-build"
STATE="$HOME/.local/share/akurai-build"
MCP_BIN="$HOME/.local/bin/akurai-build-mcp"
[[ -x $MCP_BIN ]] || MCP_BIN="$INSTALL/$APP"
source_digest() {
(cd "$1" && find . -type f -not -path './.git/*' -not -path './target/*' -not -path './.refrepos/*' -print0 | xargs -0 sha256sum | sort | sha256sum | cut -d' ' -f1)
}
if [[ $HOST != titan && ${1:-} == sync ]]; then
"$HOME/.local/bin/syncthing-ctl" rescan projects >/dev/null
ssh titan '$HOME/.local/bin/syncthing-ctl rescan projects >/dev/null'
local_digest=$(source_digest "$ROOT")
for _ in {1..60}; do
remote_digest=$(ssh titan "cd /home/olafurbui/Projects/AkurAI-Build && find . -type f -not -path './.git/*' -not -path './target/*' -not -path './.refrepos/*' -print0 | xargs -0 sha256sum | sort | sha256sum | cut -d' ' -f1")
if [[ $local_digest == "$remote_digest" ]]; then
printf 'synced=%s\n' "$local_digest"
exit 0
fi
sleep 1
done
echo 'project sync did not converge within 60 seconds' >&2
exit 1
fi
if [[ $HOST != titan ]]; then
case ${1:-} in
format|lock|clean-build|check|binfmt|docker-smoke|build|publish|release|deploy-local|deploy) bash "$ROOT/deploy.sh" sync ;;
esac
printf -v forwarded '%q ' "$@"
exec ssh titan "cd /home/olafurbui/Projects/AkurAI-Build && bash ./deploy.sh $forwarded"
fi
command=${1:-help}
shift || true
configure_domain() {
local domain=${1:?usage: ./deploy.sh configure-domain <hostname>}
[[ $domain =~ ^[a-z0-9.-]+$ ]] || { echo 'invalid hostname' >&2; return 1; }
install -d -m 700 "$CONFIG"
touch "$ENV_FILE"
chmod 600 "$ENV_FILE"
grep -q '^AKURAI_BUILD_DOMAIN=' "$ENV_FILE" && sed -i "s/^AKURAI_BUILD_DOMAIN=.*/AKURAI_BUILD_DOMAIN=$domain/" "$ENV_FILE" || printf 'AKURAI_BUILD_DOMAIN=%s\n' "$domain" >> "$ENV_FILE"
grep -q '^AKURAI_PUBLIC_ORIGIN=' "$ENV_FILE" && sed -i "s|^AKURAI_PUBLIC_ORIGIN=.*|AKURAI_PUBLIC_ORIGIN=https://$domain|" "$ENV_FILE" || printf 'AKURAI_PUBLIC_ORIGIN=https://%s\n' "$domain" >> "$ENV_FILE"
}
edge_probe() {
[[ -n $DOMAIN ]] || { echo 'AKURAI_BUILD_DOMAIN is not configured' >&2; return 1; }
local address http https certificate failed=0
address=$(getent ahostsv4 "$DOMAIN" | awk 'NR == 1 { print $1 }')
[[ -n $address ]] || { echo 'edge DNS is unresolved' >&2; return 1; }
http=$(curl --silent --output /dev/null --write-out '%{http_code}' --connect-timeout 5 "http://$DOMAIN/" || printf 000)
https=$(curl --insecure --silent --output /dev/null --write-out '%{http_code}' --connect-timeout 5 "https://$DOMAIN/" || printf 000)
certificate=$(timeout 10s openssl s_client -showcerts -connect "$DOMAIN:443" -servername "$DOMAIN" </dev/null 2>/dev/null || true)
printf 'edge_ip=%s http=%s https=%s\n' "$address" "$http" "$https"
if printf '%s' "$certificate" | openssl x509 -noout -enddate; then
if printf '%s' "$certificate" | openssl x509 -checkend 2592000 -noout; then
echo 'certificate_30d=valid'
else
echo 'certificate_30d=renewal-required'
failed=1
fi
else
echo 'certificate=missing-or-invalid'
failed=1
fi
if timeout 10s openssl s_client -verify_return_error -verify_hostname "$DOMAIN" -connect "$DOMAIN:443" -servername "$DOMAIN" </dev/null >/dev/null 2>&1; then
echo 'certificate_chain_hostname=valid'
else
echo 'certificate_chain_hostname=invalid'
failed=1
fi
if ssh -o BatchMode=yes -o ConnectTimeout=10 akurai-ec2 true; then
echo 'akurai_mesh_ssh=reachable'
else
echo 'akurai_mesh_ssh=unreachable; restore akurai-node-tunnel.service on EC2' >&2
failed=1
fi
return "$failed"
}
mesh_recover() {
sudo systemctl restart akurai-node-tunnel.service
ssh -o BatchMode=yes -o ConnectTimeout=10 akurai-ec2 \
'sudo systemctl restart akurai-node.service'
for _ in {1..24}; do
if ssh -o BatchMode=yes -o ConnectTimeout=10 akurai-ec2 \
"curl --fail --silent --show-error --connect-timeout 5 http://$LISTEN/api/health >/dev/null"; then
echo 'akurai_mesh_upstream=reachable'
return
fi
sleep 2
done
echo 'akurai_mesh_upstream=unreachable after tunnel restart' >&2
return 1
}
probe() {
command -v cargo
command -v git
command -v docker
command -v ssh
ip -brief address | grep -F '100.88.0.9/'
"$HOME/.akurai-vpn/bin/akurai-node" status
systemctl is-active akurai-node-tunnel.service
docker info --format 'docker={{.ServerVersion}}'
if [[ -r /proc/sys/fs/binfmt_misc/qemu-aarch64 ]]; then
grep -E '^(enabled|flags)' /proc/sys/fs/binfmt_misc/qemu-aarch64
else
echo 'binfmt_arm64=missing'
fi
git status --short --branch
edge_probe
}
check() {
cd "$ROOT"
bash -n "$ROOT/deploy.sh"
timeout 30s cargo fmt --check
timeout 180s cargo clippy --locked --all-targets -- -D warnings
timeout --kill-after=5s 90s cargo test --locked --all-targets --no-fail-fast
}
build() {
check
timeout --kill-after=5s 600s cargo build --release --locked
}
binfmt() {
if [[ ! -r /proc/sys/fs/binfmt_misc/qemu-aarch64 ]] || ! grep -q '^enabled' /proc/sys/fs/binfmt_misc/qemu-aarch64; then
timeout --kill-after=5s 120s docker run --privileged --rm tonistiigi/binfmt:qemu-v10.2.3-68 --install arm64
fi
grep -E '^(enabled|flags)' /proc/sys/fs/binfmt_misc/qemu-aarch64
timeout --kill-after=5s 120s docker run --rm --platform linux/arm64 alpine:3.22 uname -m | grep -Fx aarch64
}
docker_smoke() {
cd "$ROOT"
timeout --kill-after=5s 180s cargo test --locked --test docker_smoke -- --ignored --nocapture
}
clean_build() {
cd "$ROOT"
cargo clean
}
build_info() {
local binary="$ROOT/target/release/$APP"
test -x "$binary"
"$binary" --version
printf 'bytes=%s\nsha256=' "$(stat -c %s "$binary")"
sha256sum "$binary" | cut -d' ' -f1
}
recover_checks() {
mapfile -t pids < <(pgrep -f "^$HOME/.*/cargo test --locked --all-targets --no-fail-fast$|^$ROOT/target/debug/deps/akurai_build-[0-9a-f]+$" || true)
((${#pids[@]})) || { echo 'no stale AkurAI checks'; return; }
kill -TERM "${pids[@]}"
for _ in {1..5}; do
remaining=()
for pid in "${pids[@]}"; do kill -0 "$pid" 2>/dev/null && remaining+=("$pid"); done
((${#remaining[@]} == 0)) && { echo 'stale checks stopped'; return; }
sleep 1
done
kill -KILL "${remaining[@]}"
echo 'stale checks killed'
}
pipeline_deploy_only() {
echo 'Direct deployment is retired. Use the AkurAI Build MCP release/run/promotion path; deploy/service-deploy.sh owns the self-hosted service rollout.' >&2
return 1
}
install_skill() {
local target="$HOME/.skills/akurai-build"
install -d "$(dirname "$target")"
ln -sfn "$ROOT/skills/akurai-build" "$target"
}
vhost() {
edge_probe || true
local ssh=(ssh -o BatchMode=yes -o ConnectTimeout=10 akurai-ec2)
"${ssh[@]}" 'true'
if ! "${ssh[@]}" 'command -v nginx >/dev/null && command -v certbot >/dev/null'; then
"${ssh[@]}" 'sudo apt-get update && sudo apt-get install -y nginx certbot python3-certbot-nginx'
fi
"${ssh[@]}" "curl --fail --silent --show-error --connect-timeout 10 http://$LISTEN/api/health >/dev/null"
if ! "${ssh[@]}" "sudo test -s /etc/letsencrypt/live/$DOMAIN/fullchain.pem"; then
"${ssh[@]}" "sudo tee /etc/nginx/sites-available/$DOMAIN >/dev/null && sudo ln -sfn /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled/$DOMAIN && sudo nginx -t && sudo systemctl reload nginx" <<NGINX_HTTP
server {
listen 80;
listen [::]:80;
server_name $DOMAIN;
location / {
proxy_pass http://$LISTEN;
proxy_set_header Host \$host;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
NGINX_HTTP
"${ssh[@]}" "sudo certbot certonly --nginx --non-interactive --agree-tos --register-unsafely-without-email -d $DOMAIN"
elif ! "${ssh[@]}" "sudo openssl x509 -checkend 2592000 -noout -in /etc/letsencrypt/live/$DOMAIN/fullchain.pem"; then
"${ssh[@]}" "sudo certbot renew --cert-name $DOMAIN --deploy-hook 'systemctl reload nginx'"
fi
"${ssh[@]}" "sudo tee /etc/nginx/sites-available/$DOMAIN >/dev/null && sudo ln -sfn /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled/$DOMAIN && sudo nginx -t && sudo systemctl reload nginx" <<NGINX_TLS
server {
listen 80;
listen [::]:80;
server_name $DOMAIN;
return 301 https://\$host\$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name $DOMAIN;
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000" always;
client_max_body_size 1m;
location / {
proxy_pass http://$LISTEN;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 3600;
proxy_buffering off;
}
}
NGINX_TLS
"${ssh[@]}" "if systemctl list-unit-files certbot.timer >/dev/null 2>&1; then sudo systemctl enable --now certbot.timer; fi"
"${ssh[@]}" "sudo openssl x509 -noout -enddate -in /etc/letsencrypt/live/$DOMAIN/fullchain.pem"
}
http_smoke() {
local base="http://$LISTEN" headers code
curl --fail --silent --show-error "$base/" | grep -Fq 'AkurAI Build'
headers=$(curl --silent --show-error --head "$base/")
grep -Fiq 'content-security-policy:' <<<"$headers"
grep -Fiq "script-src 'self'" <<<"$headers"
grep -Fiq 'x-frame-options: DENY' <<<"$headers"
grep -Fiq 'x-content-type-options: nosniff' <<<"$headers"
code=$(curl --silent --output /dev/null --write-out '%{http_code}' "$base/api/state")
[[ $code == 401 ]] || { echo "unauthenticated API returned $code" >&2; return 1; }
echo 'http_smoke=ok'
}
wait_local() {
for _ in {1..60}; do
if curl --fail --silent "http://$LISTEN/api/health" >/dev/null 2>&1 \
&& curl --fail --silent "http://127.0.0.1:3211/api/health" >/dev/null 2>&1; then
return 0
fi
sleep 0.25
done
docker logs --tail 50 akurai-build-service >&2 || true
docker logs --tail 50 akurai-build-worker >&2 || true
echo 'AkurAI Build service or worker did not become ready within 15 seconds' >&2
return 1
}
status_local() {
wait_local
AKURAI_BUILD_VERSION=$(current_version) docker compose --project-name akurai-build --file "$ROOT/deploy/docker-compose.yml" ps
curl --fail --silent --show-error "http://$LISTEN/api/health"
printf '\n'
curl --fail --silent --show-error "http://127.0.0.1:3211/api/health"
printf '\n'
http_smoke
}
status() {
status_local
curl --fail --silent --show-error --connect-timeout 15 "https://$DOMAIN/api/health"
printf '\n'
}
current_version() {
awk -F '"' '/^version = "/ { print $2; exit }' "$ROOT/Cargo.toml"
}
validate_release_files() {
local cargo latest
cargo=$(current_version)
latest=$(awk '/^## [0-9]+\.[0-9]+\.[0-9]+ / { print $2; exit }' "$ROOT/CHANGELOG.md")
[[ $cargo == "$latest" ]] || { echo "Cargo version $cargo does not match changelog $latest" >&2; return 1; }
}
bump_version() {
local level=${1:?version level required} summary=${2:?changelog summary required}
[[ $summary != *$'\n'* ]] || { echo 'changelog summary must be one line' >&2; return 1; }
local current major minor patch
current=$(current_version)
IFS=. read -r major minor patch <<<"$current"
if [[ $current == 0.1.0 ]]; then
major=1; minor=1; patch=1
else
case "$level" in
patch)
((patch += 1))
if ((patch > 10)); then patch=1; ((minor += 1)); fi
if ((minor > 10)); then minor=1; ((major += 1)); fi
;;
minor)
((minor += 1)); patch=1
if ((minor > 10)); then minor=1; ((major += 1)); fi
;;
major) ((major += 1)); minor=1; patch=1 ;;
*) echo 'level must be patch, minor, or major' >&2; return 1 ;;
esac
fi
((major >= 1 && major <= 10 && minor >= 1 && minor <= 10 && patch >= 1 && patch <= 10)) || {
echo 'version range exhausted at 10.10.10' >&2; return 1;
}
local next="$major.$minor.$patch" temporary
sed -i "0,/^version = \"$current\"/s//version = \"$next\"/" "$ROOT/Cargo.toml"
temporary=$(mktemp "$ROOT/.changelog.XXXXXX")
{
printf '# Changelog\n\n## %s - %s\n\n- %s\n\n' "$next" "$(date -u +%F)" "$summary"
if [[ -f $ROOT/CHANGELOG.md ]]; then
awk 'NR == 1 && $0 == "# Changelog" { next } { print }' "$ROOT/CHANGELOG.md" | sed '/./,$!d'
fi
} >"$temporary"
mv "$temporary" "$ROOT/CHANGELOG.md"
(cd "$ROOT" && cargo generate-lockfile)
validate_release_files
printf '%s\n' "$next"
}
ensure_git_identity() {
git -C "$ROOT" config user.name 'Ólafur Búi Ólafsson'
git -C "$ROOT" config user.email 'olibuijr@olibuijr.com'
}
publish() {
local message=${1:?usage: ./deploy.sh publish "message"} head_version
cd "$ROOT"
ensure_git_identity
# Titan's working checkout is the single source of truth; nothing to pull from the mirror.
head_version=$(git show HEAD:Cargo.toml | awk -F '"' '/^version = "/ { print $2; exit }')
if [[ $(current_version) == "$head_version" ]]; then
bump_version patch "$message" >/dev/null
else
validate_release_files
fi
check
git add -A
git diff --cached --quiet && { echo 'nothing to publish'; return; }
git commit -m "$message"
local request response
request=$(printf '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"akurai_repo_sync","arguments":{"name":"AkurAI-Build","source":"%s"}}}' "$ROOT")
response=$("$MCP_BIN" --data "$STATE" --key-file "$CONFIG/database.key" mcp <<<"$request")
[[ $response != *'"isError":true'* ]] || { echo "repo sync failed: $response" >&2; return 1; }
}
case "$command" in
sync) "$HOME/.local/bin/syncthing-ctl" rescan projects ;;
edge-probe) edge_probe ;;
mesh-recover) mesh_recover ;;
http-smoke) http_smoke ;;
probe) probe ;;
bootstrap)
chmod 755 "$ROOT/deploy.sh"
command -v gh >/dev/null || sudo pacman -S --needed --noconfirm github-cli
ensure_git_identity
binfmt
probe
;;
lock) cd "$ROOT"; cargo generate-lockfile ;;
configure-domain) configure_domain "$@" ;;
version)
if [[ ${1:-show} == show ]]; then current_version; else bump_version "$@"; fi
;;
format) cd "$ROOT"; cargo fmt --all ;;
clean-build) clean_build ;;
recover-checks) recover_checks ;;
check) check ;;
binfmt) binfmt ;;
docker-smoke) docker_smoke ;;
build) build ;;
build-info) build_info ;;
deploy-local|deploy) pipeline_deploy_only ;;
skill) install_skill ;;
vhost) vhost ;;
status-local) status_local ;;
status) status ;;
mcp) exec "$MCP_BIN" --data "$STATE" --key-file "$CONFIG/database.key" mcp ;;
logs) AKURAI_BUILD_VERSION=$(current_version) docker compose --project-name akurai-build --file "$ROOT/deploy/docker-compose.yml" logs --tail "${1:-100}" ;;
rollback) pipeline_deploy_only ;;
publish) publish "$@" ;;
release) pipeline_deploy_only ;;
*)
cat <<'HELP'
Usage: ./deploy.sh <command>
sync Rescan midget/Titan and wait for matching source-tree digests
edge-probe Check DNS, HTTP/S, certificate expiry, and EC2 mesh SSH
mesh-recover Restart Titan's VPN tunnel and require EC2 upstream recovery
http-smoke Verify private UI, security headers, health, and API rejection
probe Read-only host, VPN, edge, Git, Rust, and Docker checks
bootstrap Install GitHub CLI on Titan, then probe
configure-domain Store the public domain/origin in the protected environment file
lock Regenerate Cargo.lock after reviewed dependency changes
version Show version, or: version patch|minor|major "summary"
format Apply rustfmt to repository Rust sources
clean-build Remove only this repository's Cargo build artifacts
recover-checks Stop only stale AkurAI Cargo test processes
check Verify formatting, lint, and tests
binfmt Ensure pinned ARM64 QEMU registration and run an ARM smoke command
docker-smoke Run the ignored restricted-container/artifact E2E on Titan
build Check and release-build
build-info Show release binary version, size, and SHA-256
deploy-local Retired: use MCP queue/promotion and the protected self-pipeline
deploy Retired: use MCP queue/promotion and the protected self-pipeline
skill Install/update the Pi agent skill symlink
vhost Check EC2/mesh/Nginx/certificate expiry and provision or renew as needed
status-local Show Titan service and private-mesh health
status Show Titan and public HTTPS health
mcp Serve the AkurAI Build MCP tool set over stdio
logs [N] Show the last N service and worker log lines
rollback Retired: service promotion rolls back automatically on failed health
publish M Legacy source publication helper; agents use akurai_repo_sync MCP
release L M Retired: use akurai_release MCP
HELP
;;
esac