#!/bin/sh
# fndry hosted consumer and HTTPS bootstrap. Public CLI/Core Apache-2.0.
# HTTPS authenticates delivery of this program and its pinned trust root.
# SSHSIG separately authenticates the channel, catalog and package digests.
set -eu
umask 077
hr_fail() { printf 'fndry hosted: %s\n' "$*" >&2; exit 69; }
hr_get() { plutil -extract "$2" raw -expect "${3:-string}" -o - "$1" 2>/dev/null || hr_fail "invalid release metadata field: $2"; }
hr_hash() { shasum -a 256 "$1" | awk '{print $1}'; }
hr_hex() { [ "${#1}" -eq 64 ] && case "$1" in *[!0-9a-f]*) return 1;; *) return 0;; esac; }
hr_id() { case "$1" in ''|*[!a-zA-Z0-9._-]*|.*) return 1;; esac; [ "${#1}" -le 81 ]; }
hr_version() { printf '%s\n' "$1" | LC_ALL=C awk '/^[0-9]+\.[0-9]+\.[0-9]+$/ {ok=1} END {exit !ok}'; }
hr_platform() {
  [ "$(uname -s)" = Darwin ] && [ "$(uname -m)" = arm64 ] || hr_fail 'hosted pilot requires native Apple Silicon macOS; Linux, Intel, Windows and translated shells are unqualified. Local/offline installation remains available.'
  [ "$(sysctl -in sysctl.proc_translated 2>/dev/null || printf 0)" != 1 ] || hr_fail 'translated shells are unsupported; use a native ARM64 terminal'
  for tool in curl plutil ssh-keygen base64 shasum tar awk sort uniq wc mktemp zsh; do
    command -v "$tool" >/dev/null 2>&1 || hr_fail "required system tool missing: $tool; install it before retrying (no development runtime is required)"
  done
  zsh -fc 'autoload -Uz is-at-least; is-at-least 5.8.1 "$ZSH_VERSION" && ! is-at-least 6 "$ZSH_VERSION"' || hr_fail 'Zsh >=5.8.1,<6 is required'
}
hr_fetch() {
  # No caller-selected origins, insecure TLS, redirects or partial-file activation.
  curl --fail --silent --show-error --proto '=https' --connect-timeout 15 --max-time 180 --retry 2 --max-filesize "$3" "https://releases.fndry.sh/$1" -o "$2" || hr_fail 'HTTPS download failed; installed copy unchanged. Check network and retry the same command.'
  [ "$(wc -c < "$2" | tr -d ' ')" -le "$3" ] || hr_fail 'oversized download'
}
hr_authenticate() {
  [ "$(hr_get "$1" format)" = fndry.signed-release/v1 ] || hr_fail 'unsupported signed envelope'
  hr_get "$1" payload | base64 -D > "$2" || hr_fail 'invalid signed payload encoding'
  hr_get "$1" signature | base64 -D > "$hr_stage/signature" || hr_fail 'invalid signature encoding'
  printf '%s\n' 'fndry ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDTpEnAmDSx90L0oVUj9YYF4RkyfHjFJg7984y5JTqOe' > "$hr_stage/allowed"
  ssh-keygen -Y verify -f "$hr_stage/allowed" -I fndry -n fndry-release-v1 -s "$hr_stage/signature" < "$2" >/dev/null 2>&1 || hr_fail 'Ed25519 release signature invalid; installed copy unchanged. Do not bypass verification.'
}
hr_resolve() {
  hr_fetch channels/pilot-darwin-arm64.json "$hr_stage/channel.json" 12000000
  hr_authenticate "$hr_stage/channel.json" "$hr_stage/channel"
  [ "$(hr_get "$hr_stage/channel" format)" = fndry.release-channel/v1 ] && [ "$(hr_get "$hr_stage/channel" channel)" = pilot-darwin-arm64 ] || hr_fail 'unexpected channel'
  [ "$(hr_get "$hr_stage/channel" state)" = available ] || hr_fail 'pilot channel is withdrawn or unavailable; existing installation preserved'
  hr_release=$(hr_get "$hr_stage/channel" releaseId); hr_id "$hr_release" || hr_fail 'unsafe release ID'
  hr_catalog_key=$(hr_get "$hr_stage/channel" catalogKey)
  [ "$hr_catalog_key" = "releases/sets/$hr_release/catalog.json" ] || hr_fail 'unexpected catalog key'
  hr_catalog_hash=$(hr_get "$hr_stage/channel" catalogSha256); hr_hex "$hr_catalog_hash" || hr_fail 'invalid catalog hash'
  hr_fetch "$hr_catalog_key" "$hr_stage/catalog.json" 12000000
  [ "$(hr_hash "$hr_stage/catalog.json")" = "$hr_catalog_hash" ] || hr_fail 'channel/catalog hash mismatch'
  hr_catalog=$hr_stage/catalog
  hr_authenticate "$hr_stage/catalog.json" "$hr_catalog"
  [ "$(hr_get "$hr_catalog" format)" = fndry.release-set/v1 ] && [ "$(hr_get "$hr_catalog" publicationState)" = qualified-pilot ] && [ "$(hr_get "$hr_catalog" releaseId)" = "$hr_release" ] && [ "$(hr_get "$hr_catalog" origin)" = https://releases.fndry.sh/ ] && [ "$(hr_get "$hr_catalog" destination)" = fndry-releases-prod-assets-727118420089 ] || hr_fail 'unqualified or unexpected catalog'
  [ "$(hr_get "$hr_catalog" packages array)" = 3 ] || hr_fail 'approved release set must contain only CLI/Core, Work and Git'
  hr_seen=,
  hr_entries='packages.0 packages.1 packages.2'
  hr_optional=$(plutil -extract optionalPackages raw -expect array -o - "$hr_catalog" 2>/dev/null || printf absent)
  case "$hr_optional" in absent|0) :;; 1) hr_entries="$hr_entries optionalPackages.0";; *) hr_fail 'unexpected optional package set';; esac
  for hr_entry in $hr_entries; do
    hr_p=$hr_entry
    hr_r=$(hr_get "$hr_catalog" "$hr_p.resource")
    case "$hr_p:$hr_r" in packages.*:cli|packages.*:work|packages.*:git|optionalPackages.0:skill) :;; *) hr_fail 'unapproved resource';; esac
    case "$hr_seen" in *,$hr_r,*) hr_fail 'duplicate resource';; esac; hr_seen=$hr_seen$hr_r,
    [ "$(hr_get "$hr_catalog" "$hr_p.target")" = darwin-arm64 ] || hr_fail 'unqualified package platform'
    hr_v=$(hr_get "$hr_catalog" "$hr_p.version"); hr_version "$hr_v" || hr_fail 'invalid package version'
    hr_f=$(hr_get "$hr_catalog" "$hr_p.filename")
    case "$hr_r" in cli) hr_expected=fndry-local-$hr_v.tar.gz;; work) hr_expected=fndry-work-$hr_v-darwin-arm64.tar.gz;; git) hr_expected=fndry-git-$hr_v.tar.gz;; skill) hr_expected=fndry-skill-$hr_v.tar.gz;; esac
    [ "$hr_f" = "$hr_expected" ] && [ "$(hr_get "$hr_catalog" "$hr_p.key")" = "releases/$hr_r/$hr_v/$hr_f" ] || hr_fail 'unsafe package key/layout'
    hr_hex "$(hr_get "$hr_catalog" "$hr_p.sha256")" || hr_fail 'invalid package hash'
    hr_size=$(hr_get "$hr_catalog" "$hr_p.bytes" integer)
    [ "$hr_size" -gt 0 ] && [ "$hr_size" -lt 600000000 ] || hr_fail 'invalid package size'
    [ -n "$(hr_get "$hr_catalog" "$hr_p.license")" ] && [ -n "$(hr_get "$hr_catalog" "$hr_p.qualification")" ] || hr_fail 'missing rights or qualification'
    if [ "$hr_r" = work ]; then
      case "$(hr_get "$hr_catalog" "$hr_p.apple.status")" in Accepted|ad-hoc-not-notarized-pilot) :;; *) hr_fail 'missing Apple qualification';; esac
    fi
    if [ "$hr_r" = "$hr_resource" ]; then hr_index=$hr_p; fi
  done
  [ -n "${hr_index:-}" ] || hr_fail 'requested resource not approved'
  hr_p=$hr_index
  hr_selected_version=$(hr_get "$hr_catalog" "$hr_p.version")
  printf 'Authenticated pilot: %s %s\n' "$hr_resource" "$hr_selected_version"
  printf '%s\n' 'Apple Silicon Mac measured-host pilot; Work is ad-hoc signed, not Developer ID signed or notarized. No macOS security bypass.'
  printf 'License: %s\n' "$(hr_get "$hr_catalog" "$hr_p.license")"
}
hr_unpack() {
  hr_archive=$hr_stage/package.tar.gz
  hr_fetch "$(hr_get "$hr_catalog" "$hr_p.key")" "$hr_archive" 600000000
  [ "$(wc -c < "$hr_archive" | tr -d ' ')" = "$(hr_get "$hr_catalog" "$hr_p.bytes" integer)" ] && [ "$(hr_hash "$hr_archive")" = "$(hr_get "$hr_catalog" "$hr_p.sha256")" ] || hr_fail 'package byte count or SHA-256 mismatch; installed copy unchanged'
  tar -tzf "$hr_archive" > "$hr_stage/paths" || hr_fail 'invalid archive'
  LC_ALL=C awk -v resource="$hr_resource" '
    {p=$0; sub(/^\.\//,"",p); sub(/\/$/,"",p); if(p=="." || p=="") next;
     if(p ~ /^\// || p ~ /(^|\/)\.\.?(\/|$)/ || p ~ /[[:space:]\\]/ || seen[p]++) bad=1;
     if((resource!="skill" && p ~ /(^|\/)skills?(\/|$)/) || p ~ /(^|\/)(node_modules|\.git|\.incubator|\.fndry-work)(\/|$)/ || p ~ /\.(tsx?|map|pyc)$/) bad=1;
     if(resource=="cli" && p !~ /^cli\/(bin|lib|modules\/(core|cli)|share\/(legal|module-authoring-kit)|share\/fndry-shell.zsh|install.sh|install-local.sh)(\/|$)/) bad=1;
     if(resource=="git" && p !~ /^(module.manifest|module.zsh|commands.tsv|help.zsh|NOTICE.txt)$/) bad=1;
     if(resource=="skill" && p !~ /^(module.manifest|module.zsh|commands.tsv|skill-install.sh|skills)$/ && p !~ /^skills\/(fndry|fndry-module|fndry-work-coordinate)(\/|$)/) bad=1;
     if(resource=="work" && p !~ /^(app|module.manifest|module.zsh|commands.tsv|help.zsh)(\/|$)/) bad=1;
    } END {exit bad}' "$hr_stage/paths" || hr_fail 'unsafe, duplicate or unapproved archive path'
  tar -tvzf "$hr_archive" | awk 'substr($0,1,1)!="-" && substr($0,1,1)!="d" {bad=1} END {exit bad}' || hr_fail 'archive contains links or special files'
  mkdir "$hr_stage/unpacked"
  tar -xzf "$hr_archive" -C "$hr_stage/unpacked" --no-same-owner --no-same-permissions || hr_fail 'could not extract verified package'
  hr_candidate=$hr_stage/unpacked
  [ "$hr_resource" != cli ] || hr_candidate=$hr_candidate/cli
  if [ "$hr_resource" = cli ]; then
    [ -f "$hr_candidate/install-local.sh" ] && [ -x "$hr_candidate/bin/fndry" ] || hr_fail 'missing CLI installer'
    [ "$("$hr_candidate/bin/fndry" version | sed 's/.*version: //')" = "$hr_selected_version" ] || hr_fail 'CLI version differs from catalog'
  else
    [ "$(sed -n 's/^namespace=//p' "$hr_candidate/module.manifest")" = "$hr_resource" ] && [ "$(sed -n 's/^version=//p' "$hr_candidate/module.manifest")" = "$hr_selected_version" ] || hr_fail 'module identity differs from catalog'
  fi
}
hr_provenance() {
  # Receipt metadata is outside content digests. Keep the immutable catalog and
  # package identity after the ephemeral verified extraction has been removed.
  hr_receipt=$1
  [ -f "$hr_receipt" ] && [ ! -L "$hr_receipt" ] || hr_fail 'installed ownership receipt unavailable; inspect installation before retry'
  sed "s|^$2=.*|$2=$3|" "$hr_receipt" > "$hr_receipt.hosted.$$" || hr_fail 'could not stage hosted provenance'
  mv -f "$hr_receipt.hosted.$$" "$hr_receipt" || hr_fail 'could not record hosted provenance; installed bytes retained'
}
hr_main() {
  hr_mode=${1:-bootstrap}; [ "$#" -eq 0 ] || shift
  case "$hr_mode" in
    --help|-h) printf '%s\n' 'Usage: curl -fsS https://install.fndry.sh | /bin/sh' '       curl -fsS https://install.fndry.sh | /bin/sh -s -- --update' 'Native Apple Silicon macOS pilot. System Zsh >=5.8.1,<6, curl, OpenSSH ssh-keygen -Y, plutil, base64, tar/gzip and shasum required.' 'HTTPS delivers this script and pinned public key; SSHSIG authenticates catalogs/packages. Not Apple notarization.' 'Installs CLI/Core only; writes a managed Zsh startup block with backup. Preserves configuration and independent modules.' 'After install: fndry core:module:install work; fndry core:module:install git. Install agent skills: fndry core:module:install skill; fndry skill:install:codex. No agent is launched.'; return;;
    --update) hr_mode=bootstrap; set -- --update "$@";;
  esac
  hr_operation=install; hr_resource=cli; hr_root=; hr_executable=
  case "$hr_mode" in
    bootstrap) [ "$#" -le 1 ] || hr_fail 'bootstrap accepts only --update'; if [ "$#" -eq 1 ]; then [ "$1" = --update ] || hr_fail 'bootstrap accepts only --update'; hr_operation=update; fi;;
    module) [ "$#" -eq 4 ] || hr_fail 'internal module invocation invalid'; hr_operation=$1; hr_resource=$2; hr_root=$3; hr_executable=$4; case "$hr_resource" in work|git|skill) :;; *) hr_fail 'hosted resources are work, git and skill; use an explicit local directory for offline modules';; esac;;
    core) [ "$#" -eq 3 ] || hr_fail 'internal Core invocation invalid'; hr_operation=$1; hr_root=$2; hr_executable=$3;;
    *) hr_fail 'unknown bootstrap operation; use --help';;
  esac
  case "$hr_operation" in install|update|preview|status) :;; *) hr_fail 'invalid operation';; esac
  hr_platform
  hr_stage=$(mktemp -d "${TMPDIR:-/tmp}/fndry-hosted.XXXXXX") || hr_fail 'cannot create isolated staging directory'
  trap 'rm -rf -- "$hr_stage"' 0
  trap 'exit 129' 1; trap 'exit 130' 2; trap 'exit 143' 15
  hr_resolve
  if [ "$hr_operation" = preview ] || [ "$hr_operation" = status ]; then
    printf 'Selected release: %s\nInstalled version: %s\nNext: fndry core:update\n' "$hr_release" "$("$hr_executable" version | sed 's/.*version: //')"; return
  fi
  printf 'Plan: %s %s %s; configuration, Work data and unrelated modules preserved.\n' "$hr_operation" "$hr_resource" "$hr_selected_version"
  hr_unpack
  if [ "$hr_mode" = module ] && [ "$hr_operation" = install ] && [ -f "$hr_root/modules/$hr_resource/module.manifest" ]; then
    [ -f "$hr_root/module-receipts/$hr_resource.receipt" ] || hr_fail 'resource is bundled or foreign; preserve it and review its lifecycle before replacing'
    [ "$(sed -n 's/^version=//p' "$hr_root/modules/$hr_resource/module.manifest")" = "$hr_selected_version" ] || hr_fail 'resource already installed at another version; use core:module:update with its name'
    hr_operation=update
  fi
  if [ "$hr_resource" = cli ]; then
    # Do not move a running Work package underneath its processes.
    hr_data=${FNDRY_LOCAL_DATA_DIR:-${XDG_DATA_HOME:-${HOME:-}/.local/share}/fndry}
    if [ -x "$hr_data/local/modules/work/app/bin/fndrywork" ]; then "$hr_data/local/modules/work/app/bin/fndrywork" package-guard || hr_fail 'stop Work before updating Core; no service was changed'; fi
    /bin/sh "$hr_candidate/install-local.sh" "$hr_operation" --source "$hr_candidate"
    hr_provenance "$hr_data/local/local-receipt" source hosted-release
    hr_provenance "$hr_data/local/local-receipt" selectedSource "https://releases.fndry.sh/$hr_catalog_key#sha256:$hr_catalog_hash"
    printf '%s\n' 'Next: open a new Zsh terminal, or use the command path printed above.' 'Install Work: fndry core:module:install work' 'Install Git: fndry core:module:install git (requires Git; gh is optional for GitHub actions)' 'Install agent skills: fndry core:module:install skill; fndry skill:install:codex'
  elif [ "$hr_resource" = work ]; then
    "$hr_candidate/app/bin/fndrywork" package "$hr_operation" "$hr_executable" "$hr_candidate"
    printf '%s\n' 'Next: fndry work:backend:start; fndry work:dashboard:start; fndry work:mcp:path' 'Installation starts no backend, dashboard or agent and enrolls no Project. Codex/Chrome are separate prerequisites for optional executor/browser actions.'
  else
    "$hr_executable" "core:module:$hr_operation" "$hr_candidate"
  fi
  if [ "$hr_mode" = module ]; then
    hr_provenance "$hr_root/module-receipts/$hr_resource.receipt" sourceIdentity "hosted-release:https://releases.fndry.sh/$hr_catalog_key#sha256:$hr_catalog_hash,resource:$hr_resource,version:$hr_selected_version"
  fi
}
hr_main "$@"
