#!/usr/bin/env bash
# MapleMCP Linux Standard public signed release 246.
# This complete launcher is SHA-256 pinned by the dashboard download catalog.
set -Eeuo pipefail
export LC_ALL=C
umask 077
if [[ "$#" -gt 0 && "$1" == "--help" ]]; then
  printf 'Usage: maplemcp-linux-standard-246.sh [--connect one-time-ticket] [--user username]\n'
  printf 'Verify without installation: maplemcp-linux-standard-246.sh --check\n'
  exit 0
fi
for tool in curl sha256sum python3 openssl; do
  command -v "$tool" >/dev/null 2>&1 || { echo "Missing $tool" >&2; exit 2; }
done
if [[ "$EUID" -ne 0 ]]; then
  command -v sudo >/dev/null 2>&1 || { echo "sudo is required; otherwise run in a root shell" >&2; exit 2; }
fi
run_python(){ if [[ "$EUID" -eq 0 ]]; then python3 - "$@"; else sudo python3 - "$@"; fi; }
temp="$(mktemp -d)"
cleanup(){ rm -rf -- "$temp"; }
trap cleanup EXIT
archive="$temp/MapleMCP-Linux-Standard-246-complete-signed-set.zip"
curl -fsSL --proto '=https' --proto-redir '=https' --retry 2 --connect-timeout 15 --max-time 240 \
  'https://maplemcp.ca/releases/linux-metadata-renewal-20261101/standard/MapleMCP-Linux-Standard-246-m434-complete-signed-set.zip' -o "$archive"
printf '%s  %s\n' 'c10eb7eff0622e5955a5947c8c008486ca6b661ac32d3f2e259533e4aebab6f0' "$archive" | sha256sum --check --status
printf 'Linux Standard release 246 download verified; checking the signed release...\n'
run_python "$archive" "$@" <<'MAPLEMCP_PUBLIC_SIGNED_BOOTSTRAP_PY'
#!/usr/bin/env python3
"""Hash-pinned successor to the accepted public Linux trust bootstrap.

Privileged code is supplied on stdin from the already pinned launcher. Only
the authenticated installer receives the user's installation arguments.
"""
import datetime as dt
import fcntl
import hashlib
import json
import os
from pathlib import Path
import stat
import subprocess
import sys
import tempfile
import zipfile

CONFIG = {'edition': 'standard', 'sequence': 246, 'version': '0.2.63-beta', 'sourceCommit': 'f02cc7c7438a7b5f9d8aed1a31ddfab54d67e771', 'issuedAt': '2026-11-01T20:50:37.880Z', 'expiresAt': '2026-12-01T20:50:37.880Z', 'protectedHelperSha256': None, 'artifactName': 'MapleMCP-linux-0.2.63-beta.tar.gz', 'installerName': 'MapleMCP-linux-install.sh', 'zipRoot': 'Linux-Standard-246/', 'zipSha256': 'c10eb7eff0622e5955a5947c8c008486ca6b661ac32d3f2e259533e4aebab6f0', 'zipBytes': 17908276, 'members': {'MapleMCP-linux-0.2.63-beta.tar.gz': {'sha256': 'fb3e876c59a4684bccaa363143b03fe425f561d09b98293b3b8fc249e540f08a', 'bytes': 17900813}, 'MapleMCP-linux-install.sh': {'sha256': '42532acd426765f710ea8261ea1383cb086f91fbbc5bdcae7108c711d3b772d6', 'bytes': 53312}, 'MapleMCP-uninstall-linux.sh': {'sha256': '6c4cb5f748ae750bd57db4170a8522fe55bde35088ed195457356a0c6772b9d4', 'bytes': 20700}, 'release-envelope.json': {'sha256': 'd83d4eb29b5835965816fc9d004c9969196daa222f55d9a6affc7f4eb9d9ac96', 'bytes': 839}, 'trust/maplemcp-linux-install-bootstrap.py': {'sha256': '10de3d66e5a58eb9c9eeae5c67d78015b0319efeabe7359b62e7c28d70d936c8', 'bytes': 5994}, 'trust/maplemcp-linux-release-verifier.py': {'sha256': '5fe635e0abad9849887a7e04fcd626254bf40e2fbc12fc26b565c6982060e91f', 'bytes': 9466}, 'trust/release-2026-09-ed25519.pub.pem': {'sha256': '1b447849fc92f2c840d3f4fd690eecc24f2411a8ea93e214ed351e1c75a004ec', 'bytes': 113}}}
TRUST = Path('/etc/maplemcp-install-trust')
EXPECTED = CONFIG['members']
PINNED_SPKI = 'a7d344bd96a3918343b48c3e708846ee9f5212afbc2fbe8b70458e48a528b2d9'


def abort(message):
    raise RuntimeError(message)


def sha256(data):
    return hashlib.sha256(data).hexdigest()


def trusted_directory(parent):
    p = Path(parent)
    while True:
        s = os.lstat(p)
        if not stat.S_ISDIR(s.st_mode) or stat.S_ISLNK(s.st_mode) or s.st_uid != 0 or (s.st_mode & 0o022):
            abort('unsafe owner, permissions or symlink for directory ' + str(p))
        if p.parent == p:
            break
        p = p.parent


def trusted_existing(path, expected):
    if not os.path.lexists(path):
        return False
    s = os.lstat(path)
    if not stat.S_ISREG(s.st_mode) or stat.S_ISLNK(s.st_mode) or s.st_uid != 0 or (s.st_mode & 0o022):
        abort('unsafe existing trusted file; no replacement: ' + str(path))
    if s.st_size != expected['bytes'] or sha256(path.read_bytes()) != expected['sha256']:
        abort('existing trust file mismatch; manual review required: ' + str(path))
    return True


def stage_archive(source, dest):
    fd = os.open(source, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
    digest = hashlib.sha256()
    total = 0
    try:
        s = os.fstat(fd)
        if not stat.S_ISREG(s.st_mode) or s.st_size != CONFIG['zipBytes']:
            abort('download is not a regular file with the expected size')
        with os.fdopen(fd, 'rb', closefd=False) as inp, open(dest, 'xb') as out:
            os.chmod(dest, 0o400)
            while True:
                chunk = inp.read(1024 * 1024)
                if not chunk:
                    break
                total += len(chunk)
                if total > CONFIG['zipBytes']:
                    abort('download grew during the protected copy')
                digest.update(chunk)
                out.write(chunk)
            out.flush()
            os.fsync(out.fileno())
    finally:
        os.close(fd)
    if total != CONFIG['zipBytes'] or digest.hexdigest() != CONFIG['zipSha256']:
        abort('download hash mismatch; no candidate code was executed')


def stage_files(archive, stage):
    with zipfile.ZipFile(archive) as z:
        members = z.infolist()
        wanted = {CONFIG['zipRoot'] + relative for relative in EXPECTED}
        # Exactly seven pinned regular files, including the uninstaller. Reject
        # duplicate names as well as unknown members and directory entries.
        if len(members) != len(wanted) or {m.filename for m in members} != wanted:
            abort('signed set has missing, duplicate or unexpected members')
        for relative, expected in EXPECTED.items():
            member = z.getinfo(CONFIG['zipRoot'] + relative)
            mode = member.external_attr >> 16
            if not stat.S_ISREG(mode) or member.is_dir() or member.flag_bits & 1:
                abort('nonregular or encrypted member refused: ' + relative)
            if member.file_size != expected['bytes'] or not 0 < member.file_size <= 512 * 1024 * 1024:
                abort('unexpected member size: ' + relative)
            if relative == 'release-envelope.json' and member.file_size > 131072:
                abort('oversized release envelope')
            target = stage / relative
            target.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
            digest = hashlib.sha256()
            total = 0
            with z.open(member, 'r') as inp, target.open('xb') as out:
                while True:
                    chunk = inp.read(1024 * 1024)
                    if not chunk:
                        break
                    total += len(chunk)
                    if total > expected['bytes']:
                        abort('oversized member content: ' + relative)
                    digest.update(chunk)
                    out.write(chunk)
                out.flush()
                os.fsync(out.fileno())
            if total != expected['bytes'] or digest.hexdigest() != expected['sha256']:
                abort('signed set member digest mismatch: ' + relative)
            target.chmod(0o700 if relative.endswith('.py') else 0o400)


def verify_key(stage):
    p = stage / 'trust/release-2026-09-ed25519.pub.pem'
    r = subprocess.run(['openssl', 'pkey', '-pubin', '-in', str(p), '-outform', 'DER'],
                       capture_output=True, check=True, timeout=30)
    if sha256(r.stdout) != PINNED_SPKI:
        abort('release public key fingerprint mismatch')


def verify_signature(stage):
    artifact = CONFIG['artifactName']
    installer = CONFIG['installerName']
    command = [
        sys.executable, str(stage / 'trust/maplemcp-linux-release-verifier.py'),
        '--envelope', str(stage / 'release-envelope.json'),
        '--public-key', str(stage / 'trust/release-2026-09-ed25519.pub.pem'),
        '--pinned-spki', PINNED_SPKI,
        '--edition', CONFIG['edition'], '--version', CONFIG['version'],
        '--sequence', str(CONFIG['sequence']), '--source-commit', CONFIG['sourceCommit'],
        '--artifact-sha256', EXPECTED[artifact]['sha256'],
        '--artifact-size', str(EXPECTED[artifact]['bytes']),
        '--installer-sha256', EXPECTED[installer]['sha256'],
        '--installer-size', str(EXPECTED[installer]['bytes']),
    ]
    if CONFIG['edition'] == 'pro':
        command.extend(['--protected-helper-sha256', CONFIG['protectedHelperSha256']])
    r = subprocess.run(command, capture_output=True, text=True, check=False, timeout=60)
    if r.returncode:
        abort('release signature or expiry rejected: ' + (r.stderr or r.stdout).strip()[:360])
    checked = json.loads(r.stdout)
    release = checked['release']
    if checked['ok'] is not True or release['issuedAt'] != CONFIG['issuedAt'] or release['expiresAt'] != CONFIG['expiresAt']:
        abort('signed release timestamps differ from the pinned public launcher')
    issued = dt.datetime.fromisoformat(release['issuedAt'].replace('Z', '+00:00'))
    expires = dt.datetime.fromisoformat(release['expiresAt'].replace('Z', '+00:00'))
    if expires - issued != dt.timedelta(days=30):
        abort('signed release does not have the required 30-day validity')


def provision(stage):
    trusted_directory(TRUST.parent)
    if os.path.lexists(TRUST):
        trusted_directory(TRUST)
    else:
        TRUST.mkdir(mode=0o700)
        os.chown(TRUST, 0, 0)
        trusted_directory(TRUST)
    # Both editions provision identical accepted trust files. The shared lock
    # leaves existing Standard/Pro release checkpoints untouched.
    lock = TRUST / '.public-bootstrap.lock'
    fd = os.open(lock, os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600)
    try:
        s = os.fstat(fd)
        if not stat.S_ISREG(s.st_mode) or s.st_uid != 0 or (s.st_mode & 0o022):
            abort('unsafe trust provisioning lock')
        fcntl.flock(fd, fcntl.LOCK_EX)
        for relative, expected in EXPECTED.items():
            if not relative.startswith('trust/'):
                continue
            name = relative.removeprefix('trust/')
            target = TRUST / name
            if trusted_existing(target, expected):
                continue
            mode = 0o644 if name.endswith('.pem') else 0o700
            new_fd, temporary = tempfile.mkstemp(prefix='.public-trust-', dir=TRUST)
            try:
                with os.fdopen(new_fd, 'wb') as stream:
                    os.fchmod(stream.fileno(), mode)
                    os.fchown(stream.fileno(), 0, 0)
                    stream.write((stage / relative).read_bytes())
                    stream.flush()
                    os.fsync(stream.fileno())
                if os.path.lexists(target):
                    abort('trusted file appeared during provisioning: ' + str(target))
                os.replace(temporary, target)
                temporary = None
                parent_fd = os.open(TRUST, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
                try:
                    os.fsync(parent_fd)
                finally:
                    os.close(parent_fd)
            finally:
                if temporary is not None and os.path.lexists(temporary):
                    os.unlink(temporary)
            trusted_existing(target, expected)
    finally:
        os.close(fd)


def main():
    if os.geteuid() != 0:
        abort('must run under ordinary sudo/root after the pinned launcher')
    if len(sys.argv) < 2:
        abort('missing signed set download')
    original = sys.argv[1]
    args = sys.argv[2:]
    check_only = args == ['--check']
    bootstrap_only = args == ['--bootstrap-only']
    if args and args[0] in ('--check', '--bootstrap-only') and not (check_only or bootstrap_only):
        abort('unexpected verification-mode arguments')
    trusted_directory('/root')
    with tempfile.TemporaryDirectory(prefix='maplemcp-public-bootstrap-', dir='/root') as tmp:
        stage = Path(tmp)
        stage_archive(original, stage / 'bundle.zip')
        stage_files(stage / 'bundle.zip', stage)
        verify_key(stage)
        verify_signature(stage)
        identity = CONFIG['edition'] + str(CONFIG['sequence'])
        print('Authenticated Linux ' + identity + ' / source ' + CONFIG['sourceCommit'][:10], flush=True)
        if check_only:
            print('MAPLEMCP_PUBLIC_SIGNED_VERIFY_ONLY_PASS (no product or trust state changed)', flush=True)
            return
        provision(stage)
        if bootstrap_only:
            print('MAPLEMCP_PUBLIC_TRUST_PROVISION_PASS (agent not installed)', flush=True)
            return
        command = [
            sys.executable, str(TRUST / 'maplemcp-linux-install-bootstrap.py'),
            '--trust-dir', str(TRUST),
            '--envelope', str(stage / 'release-envelope.json'),
            '--installer', str(stage / CONFIG['installerName']),
            '--artifact', str(stage / CONFIG['artifactName']),
            '--edition', CONFIG['edition'], '--version', CONFIG['version'],
            '--sequence', str(CONFIG['sequence']), '--source-commit', CONFIG['sourceCommit'], '--', *args,
        ]
        env = dict(os.environ, MAPLEMCP_BASE_URL='https://maplemcp.ca')
        code = subprocess.run(command, cwd='/', env=env, check=False).returncode
        if code != 0:
            abort('signed installer rejected or failed (exit ' + str(code) + ')')
        print('MAPLEMCP_PUBLIC_SIGNED_INSTALL_PASS', flush=True)


if __name__ == '__main__':
    try:
        main()
    except (OSError, ValueError, KeyError, RuntimeError, zipfile.BadZipFile,
            subprocess.CalledProcessError, subprocess.TimeoutExpired) as exc:
        print('MapleMCP public signed bootstrap refused: ' + str(exc), file=sys.stderr)
        sys.exit(1)

MAPLEMCP_PUBLIC_SIGNED_BOOTSTRAP_PY
