#!/usr/bin/env bash # Fresh Compose flow: mounted Pi policy/auth must produce a selectable, credential-ready provider. set -euo pipefail root="$(cd "$(dirname "$0")/.." && pwd -P)" tmp="$(mktemp -d "${TMPDIR%/}/thoth-provider-readiness.XXXXXX")" project="thothii-provider-readiness-$$" compose=( docker compose --project-name "$project" --env-file "$tmp/local.env" -f "$root/compose.yaml" -f "$root/deploy/compose.local.yaml" ) cleanup() { "${compose[@]}" down --volumes --remove-orphans >/dev/null 2>&1 || true rm -rf "$tmp" } trap cleanup EXIT HUP INT TERM printf '%s\n' '{"zai":{"type":"api_key","key":"fixture-native-auth-key"}}' >"$tmp/pi-auth.json" printf '%s\n' 'THT_MODEL_API_KEY=fixture-model-api-key' >"$tmp/thothii.secrets" chmod 0600 "$tmp/pi-auth.json" "$tmp/thothii.secrets" auth_config="$tmp/auth" mkdir "$auth_config" chmod 0700 "$auth_config" printf '%s\n' \ 'version: 1' \ 'mode: local' \ 'publicUrl: http://127.0.0.1:8080' \ 'local:' \ ' usersFile: users.yaml' \ >"$auth_config/auth.yaml" node - "$auth_config/users.yaml" <<'NODE' const { argon2 } = require("node:crypto"); const { writeFileSync } = require("node:fs"); const message = Buffer.from("fixture-local-password", "utf8"); const nonce = Buffer.from([...Array(16).keys()]); argon2("argon2id", { message, nonce, memory: 65_536, parallelism: 1, tagLength: 32, passes: 3, }, (error, digest) => { message.fill(0); nonce.fill(0); if (error || !digest) throw error ?? new Error("fixture password hash failed"); const salt = Buffer.from([...Array(16).keys()]).toString("base64").replaceAll("=", ""); const hash = digest.toString("base64").replaceAll("=", ""); writeFileSync(process.argv[2], [ "version: 1", "users:", " - id: 00000000-0000-4000-8000-000000000001", " username: fixture-user", " displayName: Fixture user", ` passwordHash: $argon2id$v=19$m=65536,t=3,p=1$${salt}$${hash}`, " roles:", " - user", " enabled: true", " authRevision: 1", "", ].join("\\n"), { mode: 0o600 }); }); NODE chmod 0600 "$auth_config/auth.yaml" "$auth_config/users.yaml" printf '%s\n' \ 'THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git' \ "PI_AUTH_FILE=$tmp/pi-auth.json" \ "THT_SECRETS_FILE=$tmp/thothii.secrets" \ "THT_AUTH_CONFIG_ROOT=$auth_config" \ 'THOTH_CORE_HTTP_PORT=0' \ 'THOTH_HTTP_PORT=0' \ >"$tmp/local.env" "${compose[@]}" up --detach --wait --wait-timeout 90 --build core core_id="$("${compose[@]}" ps -q core)" core_address="$("${compose[@]}" port core 8787 | head -n 1)" "${compose[@]}" exec -T core sh -ceu ' test -r /home/thoth/.pi/agent/auth.json test -r /home/thoth/.pi/agent/models.json test -r /home/thoth/.pi/agent/settings.json test -r /run/secrets/thothii.secrets ' curl --fail --silent --show-error "http://$core_address/models" >"$tmp/models.json" node - "$tmp/models.json" <<'NODE' const fs = require("fs"); const body = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); if (!body.models?.some((model) => model.provider === "zai" && model.id === "glm-5.2")) { throw new Error("fresh Compose did not expose the mounted Pi-enabled model"); } NODE curl --fail --silent --show-error -X PUT \ -H 'content-type: application/json' \ --data '{"provider":"zai","model":"glm-5.2","reasoning":"low"}' \ "http://$core_address/pi-management/config" >"$tmp/configured.json" curl --fail --silent --show-error \ "http://$core_address/pi-management/status" >"$tmp/status.json" node - "$tmp/status.json" <<'NODE' const fs = require("fs"); const body = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); if (!body.ready || body.credentials !== "present") { throw new Error("mounted Pi provider is not credential-ready"); } if (body.config?.provider !== "zai" || body.config?.model !== "glm-5.2") { throw new Error("Pi provider configuration was not persisted"); } NODE inspect="$(docker inspect "$core_id")" for secret in fixture-native-auth-key fixture-model-api-key; do if grep -Fq "$secret" <<<"$inspect"; then echo "container inspection leaked $secret" >&2 exit 1 fi done echo "Compose provider-readiness contract passed."