From edb90c30c28d9908b70aca992ca57b1ae558daa9 Mon Sep 17 00:00:00 2001 From: "operator-stack-publisher[bot]" Date: Sun, 26 Jul 2026 16:22:53 +0000 Subject: [PATCH] Sync Interlock from Intelligence Flow @ 0c891b5d9054 --- .goreleaser.yml | 20 +--- UPSTREAM.json | 10 +- clients/typescript/package.json | 1 - install.ps1 | 139 ++++++++++----------------- install.sh | 163 ++++++++++++-------------------- 5 files changed, 119 insertions(+), 214 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 4af2367..97433de 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -60,23 +60,11 @@ sboms: - id: archive artifacts: archive +# Binaries are published to GCP Artifact Registry (the generic `dist` repo) by the +# release workflow (gcloud artifacts generic upload), fronted by get.operatorstack.systems. +# GoReleaser only builds + checksums + SBOMs; it does NOT create a GitHub Release. release: - github: - owner: operatorstack - name: interlock - # v0.1.0 is the first release; treat a missing prior tag gracefully. - mode: replace - footer: | - ## Protocol versions - - | Contract | Version | - | --- | --- | - | policy protocol | `interlock.policy.v1` | - | effect protocol | `interlock.effect.v1` | - | receipt schema | `interlock.receipt.v1` | - - Verify a downloaded archive against `checksums.txt`, and verify build - provenance with `gh attestation verify --repo operatorstack/interlock`. + disable: true changelog: use: github diff --git a/UPSTREAM.json b/UPSTREAM.json index 244b9af..61ad09a 100644 --- a/UPSTREAM.json +++ b/UPSTREAM.json @@ -1,7 +1,7 @@ { "files": { ".gitattributes": "3d38cf98dcc69b2af4941b76ed3f04e51273fdbd4fc54032e88cf3223b4b9198", - ".goreleaser.yml": "cdb7f4f950f7e70c0052205966cb11ef7b9a0b270ff03a670deba696cf1c61b8", + ".goreleaser.yml": "ad08b869ad32652522cef430ba556a86d09a43ea53b5a3935425204dcedb8b33", "LICENSE": "bd17d47aefaa7406616179288058001b6532881a2517254fe668d7e9c4965cfb", "README.md": "39b4cc851a57c654132a73b84d594e61c8a568fd3aed26d86e503133dcdb84c8", "assets/interlock-boundary.png": "9ae73098102d0589bf1bb912af6d39df4d1465eb93e8cea5b7e982e6fa79ef51", @@ -27,7 +27,7 @@ "clients/schema/interlock.schema.json": "b637af7ab11b324f9b018656942552858143b412e5083d18285abadc6d056eba", "clients/typescript/examples/decision-request.ts": "0a398c305f1fc89b7d0c70c68e34f56c170ba0ee456e0447103c0419c50ad159", "clients/typescript/examples/parity.ts": "2d73ad5d3c9f2b87bf3755fd029f449afcfcc3dd820877f02ae516228196a1b1", - "clients/typescript/package.json": "d6b747a44a20a2377274e7f5cc12bf292fdec10f60fb7c099a17e3b3bd6f0837", + "clients/typescript/package.json": "91cf220bb3aa4e02c426231d04b32207f5ba10cd92d07812e67f16073690e3bb", "clients/typescript/src/canonical.ts": "be376928b4563f998a5879201a9cc36407b9c13218ed6357722424e044d1247b", "clients/typescript/src/index.ts": "b7ccd06a0bfb6e938ff88746ba48b3179b1b855ab6523e9f749658ad514e9728", "clients/typescript/src/protocol.ts": "a5413e22897330135591f8c44a24eb64cdbfea7762f3f1d2e0866e1424ece145", @@ -92,8 +92,8 @@ "examples/release-manifest/publish_test.go": "7594fc7ff5a2abaf3216c74879c8d5873b636f8091d532c39d93971a276f4f4c", "examples/repository-policy/policy.go": "8c7fe12c487c1369d5ce8ae24e647e050557cd2bf29c56d4010b782603debbd1", "go.mod": "a9a846b064eac2e330c18198dae19044e438bff31dcb26d928cdb2f9b5cae3e1", - "install.ps1": "ada3f94562569929446dd6ca200f993854fa10081785e3ad1a09db285dacca6a", - "install.sh": "7d62cccc9b35280490c3332279743050957ab7e648b4b115c86211ba8995ab4e", + "install.ps1": "675ffcba0a61ae3cfd87c821ccb13e310a4b96d56272db6167e2b75efb4fdaf3", + "install.sh": "c322f7b8d532d63aa6a2fc1953e972eb7952d7c40ee925db2146aab37deeb3e2", "interlock.go": "de4ff7ee8dd15ab12d0bea387b66ad6e7abfbb66553839ecc0befc32ec8fb606", "interlock_test.go": "06b56780f7ae90fe12b055bf426aab8b87ed4fe174698a49d729bdc6484299e2", "ir/ir.go": "8765290a06278981beff40410092c63e0df3b1561e4cbd7e6c63217f1213390d", @@ -117,7 +117,7 @@ "generator": "operatorstack/interlock:project-upstream", "schema_version": 1, "source": { - "commit": "15a45d5d730690e825b3c9cf1de077ec876a0c2b", + "commit": "0c891b5d90548ec81d5f1bafe9dd63e06a5df2b2", "path": "labs/21-interlock", "repository": "operatorstack/intelligence-flow" } diff --git a/clients/typescript/package.json b/clients/typescript/package.json index aeb339c..eb3027c 100644 --- a/clients/typescript/package.json +++ b/clients/typescript/package.json @@ -1,7 +1,6 @@ { "name": "@operatorstack/interlock", "version": "0.1.0", - "private": true, "description": "Generated protocol types and canonical encoder for Interlock (interlock.spec.v1 / interlock.policy.v1). Data types only — enforcement stays the Go executable.", "license": "Apache-2.0", "type": "module", diff --git a/install.ps1 b/install.ps1 index b584747..6c77b8d 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,96 +1,53 @@ -<# -.SYNOPSIS - Interlock installer (Windows) — no Go toolchain required. - -.DESCRIPTION - Downloads a pinned prebuilt release, verifies its SHA-256 checksum, installs - interlock.exe, adds the install directory to the user PATH, then runs - `interlock doctor` and the repository-policy demo. Fails closed on a checksum - mismatch. - - irm https://raw.githubusercontent.com/operatorstack/interlock/main/install.ps1 | iex - -.PARAMETER Version - Release tag to install. Defaults to $env:INTERLOCK_VERSION, else latest. -.PARAMETER InstallDir - Install directory. Defaults to $env:INTERLOCK_INSTALL_DIR, else - %LOCALAPPDATA%\interlock\bin. -#> -[CmdletBinding()] -param( - [string]$Version = $env:INTERLOCK_VERSION, - [string]$InstallDir = $env:INTERLOCK_INSTALL_DIR -) - -$ErrorActionPreference = 'Stop' -$Repo = 'operatorstack/interlock' -$Binary = 'interlock' - -function Info($m) { Write-Host "interlock-install: $m" } - -# --- detect arch (Windows amd64 only) ------------------------------------ -$arch = switch ($env:PROCESSOR_ARCHITECTURE) { - 'AMD64' { 'amd64' } - default { throw "unsupported architecture: $($env:PROCESSOR_ARCHITECTURE) (only windows/amd64 is published)" } -} -$os = 'windows' - -# --- resolve version ------------------------------------------------------ -if ([string]::IsNullOrEmpty($Version)) { - Info 'resolving latest release' - $latest = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" -Headers @{ 'User-Agent' = 'interlock-install' } - $Version = $latest.tag_name - if ([string]::IsNullOrEmpty($Version)) { throw 'could not resolve latest release tag' } +# interlock installer (Windows) — rendered by distribution/render.mjs, do not edit by hand. +# +# irm https://get.operatorstack.systems/interlock/install.ps1 | iex +# +# Toolchain-free: downloads a prebuilt, checksum-verified binary from OperatorStack's +# GCP Artifact Registry, fronted by get.operatorstack.systems. All-GCP. +# +# Env overrides: INTERLOCK_VERSION (pin), INTERLOCK_INSTALL_DIR (location). +$ErrorActionPreference = "Stop" + +$binary = "interlock" +$getHost = "get.operatorstack.systems" +$version = [Environment]::GetEnvironmentVariable("INTERLOCK_VERSION") +$dir = [Environment]::GetEnvironmentVariable("INTERLOCK_INSTALL_DIR") + +$arch = if ([Environment]::Is64BitOperatingSystem) { "amd64" } else { throw "unsupported architecture" } + +if (-not $version) { + $latest = Invoke-RestMethod -Uri "https://$getHost/$binary/latest" + $version = $latest.version + if (-not $version) { throw "could not resolve latest version" } } -Info "installing $Binary $Version ($os/$arch)" +Write-Host " installing $binary $version (windows/$arch)" -$archive = "${Binary}_${Version}_${os}_${arch}.zip" -$base = "https://github.com/$Repo/releases/download/$Version" - -# --- download + verify ---------------------------------------------------- -$tmp = Join-Path $env:TEMP ("interlock-" + [System.Guid]::NewGuid().ToString('N')) -New-Item -ItemType Directory -Path $tmp -Force | Out-Null +$tmp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ([guid]::NewGuid())) try { - $archivePath = Join-Path $tmp $archive - $sumsPath = Join-Path $tmp 'checksums.txt' - Invoke-WebRequest -Uri "$base/$archive" -OutFile $archivePath -UseBasicParsing - Invoke-WebRequest -Uri "$base/checksums.txt" -OutFile $sumsPath -UseBasicParsing - - $want = (Select-String -Path $sumsPath -Pattern ([regex]::Escape($archive)) | - Select-Object -First 1).Line -split '\s+' | Select-Object -First 1 - if ([string]::IsNullOrEmpty($want)) { throw "no checksum for $archive in checksums.txt" } - $got = (Get-FileHash -Path $archivePath -Algorithm SHA256).Hash.ToLower() - if ($want.ToLower() -ne $got) { throw "checksum mismatch for $archive (want $want, got $got)" } - Info 'checksum verified' - - # --- install ------------------------------------------------------------ - if ([string]::IsNullOrEmpty($InstallDir)) { - $InstallDir = Join-Path $env:LOCALAPPDATA 'interlock\bin' + $archive = "${binary}_${version}_windows_${arch}.zip" + $base = "https://$getHost/$binary/dl/$version" + Invoke-WebRequest -Uri "$base/$archive" -OutFile (Join-Path $tmp $archive) + Invoke-WebRequest -Uri "$base/checksums.txt" -OutFile (Join-Path $tmp "checksums.txt") + + $want = (Select-String -Path (Join-Path $tmp "checksums.txt") -Pattern ([regex]::Escape($archive)) | + ForEach-Object { ($_ -split '\s+')[0] } | Select-Object -First 1) + if (-not $want) { throw "no checksum listed for $archive" } + $got = (Get-FileHash -Algorithm SHA256 -Path (Join-Path $tmp $archive)).Hash.ToLower() + if ($want.ToLower() -ne $got) { throw "checksum mismatch — refusing to install" } + Write-Host " checksum verified" + + Expand-Archive -Path (Join-Path $tmp $archive) -DestinationPath $tmp -Force + if (-not $dir) { $dir = Join-Path $env:LOCALAPPDATA "$binary\bin" } + New-Item -ItemType Directory -Force -Path $dir | Out-Null + Move-Item -Force -Path (Join-Path $tmp "$binary.exe") -Destination (Join-Path $dir "$binary.exe") + Write-Host " installed to $dir\$binary.exe" + + $userPath = [Environment]::GetEnvironmentVariable("Path", "User") + if ($userPath -notlike "*$dir*") { + [Environment]::SetEnvironmentVariable("Path", "$userPath;$dir", "User") + Write-Host " added $dir to your PATH (restart your shell)" } - New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null - Expand-Archive -Path $archivePath -DestinationPath $tmp -Force - $exe = Join-Path $tmp "$Binary.exe" - if (-not (Test-Path $exe)) { throw "archive did not contain $Binary.exe" } - Copy-Item -Path $exe -Destination (Join-Path $InstallDir "$Binary.exe") -Force - Info "installed $InstallDir\$Binary.exe" - - # add to user PATH if missing - $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') - if (($userPath -split ';') -notcontains $InstallDir) { - [Environment]::SetEnvironmentVariable('Path', "$userPath;$InstallDir", 'User') - $env:Path = "$env:Path;$InstallDir" - Info "added $InstallDir to your user PATH (restart your shell to pick it up)" - } - - # --- prove it works ----------------------------------------------------- - $bin = Join-Path $InstallDir "$Binary.exe" - Write-Host '' - & $bin doctor - Write-Host '' - & $bin demo repository-policy - Write-Host '' - Info "done — author your own policy with '$Binary init'" -} -finally { - Remove-Item -Path $tmp -Recurse -Force -ErrorAction SilentlyContinue + & (Join-Path $dir "$binary.exe") doctor + Write-Host " done. run '$binary --help' to get started." } +finally { Remove-Item -Recurse -Force $tmp } diff --git a/install.sh b/install.sh index 270a7c6..eea7a7f 100644 --- a/install.sh +++ b/install.sh @@ -1,124 +1,85 @@ #!/bin/sh -# Interlock installer (macOS / Linux) — no Go toolchain required. +# interlock installer — rendered by distribution/render.mjs, do not edit by hand. # -# curl -fsSL https://raw.githubusercontent.com/operatorstack/interlock/main/install.sh | sh +# curl -fsSL https://get.operatorstack.systems/interlock | sh # -# It detects your OS/arch, downloads a pinned prebuilt release, verifies the -# SHA-256 checksum, installs the `interlock` binary, then runs `interlock doctor` -# and the repository-policy demo. It fails closed on a checksum mismatch. +# Toolchain-free: downloads a prebuilt, checksum-verified binary from OperatorStack's +# GCP Artifact Registry, fronted by get.operatorstack.systems. All-GCP, no GitHub Releases, no npm. # -# Environment overrides: -# INTERLOCK_VERSION release tag to install (default: latest) -# INTERLOCK_INSTALL_DIR install directory (default: /usr/local/bin, else ~/.local/bin) +# Env overrides: +# INTERLOCK_VERSION pin an exact version (e.g. v0.3.1); default: latest channel +# INTERLOCK_INSTALL_DIR install location; default: /usr/local/bin or ~/.local/bin set -eu -REPO="operatorstack/interlock" BINARY="interlock" +GET_HOST="get.operatorstack.systems" +SMOKE_CMD="doctor" +VERSION="${INTERLOCK_VERSION:-}" +INSTALL_DIR="${INTERLOCK_INSTALL_DIR:-}" -info() { printf 'interlock-install: %s\n' "$1" >&2; } -die() { printf 'interlock-install: error: %s\n' "$1" >&2; exit 1; } +say() { printf ' %s\n' "$1"; } +die() { printf 'error: %s\n' "$1" >&2; exit 1; } +have() { command -v "$1" >/dev/null 2>&1; } -need() { command -v "$1" >/dev/null 2>&1 || die "required tool not found: $1"; } - -# --- detect platform ------------------------------------------------------ -os=$(uname -s) +# --- resolve OS/arch ----------------------------------------------------------- +os=$(uname -s | tr '[:upper:]' '[:lower:]') +arch=$(uname -m) case "$os" in - Linux) os=linux ;; - Darwin) os=darwin ;; - *) die "unsupported OS: $os (Windows: use install.ps1)" ;; + linux|darwin) ;; + *) die "unsupported OS: $os (use install.ps1 on Windows)";; esac - -arch=$(uname -m) case "$arch" in - x86_64 | amd64) arch=amd64 ;; - arm64 | aarch64) arch=arm64 ;; - *) die "unsupported architecture: $arch" ;; + x86_64|amd64) arch=amd64;; + arm64|aarch64) arch=arm64;; + *) die "unsupported architecture: $arch";; esac -need uname -need tar -if command -v curl >/dev/null 2>&1; then - dl() { curl -fsSL "$1" -o "$2"; } - fetch() { curl -fsSL "$1"; } -elif command -v wget >/dev/null 2>&1; then - dl() { wget -qO "$2" "$1"; } - fetch() { wget -qO - "$1"; } -else - die "need curl or wget" -fi - -# sha256 tool differs across platforms. -if command -v sha256sum >/dev/null 2>&1; then - sha256() { sha256sum "$1" | awk '{print $1}'; } -elif command -v shasum >/dev/null 2>&1; then - sha256() { shasum -a 256 "$1" | awk '{print $1}'; } -else - die "need sha256sum or shasum" -fi +# --- downloader ---------------------------------------------------------------- +if have curl; then dl() { curl -fsSL "$1"; }; dlf() { curl -fsSL "$1" -o "$2"; } +elif have wget; then dl() { wget -qO- "$1"; }; dlf() { wget -qO "$2" "$1"; } +else die "need curl or wget"; fi -# --- resolve version ------------------------------------------------------ -version="${INTERLOCK_VERSION:-}" -if [ -z "$version" ]; then - info "resolving latest release" - version=$(fetch "https://api.github.com/repos/${REPO}/releases/latest" \ - | grep '"tag_name":' | head -n1 | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/') - [ -n "$version" ] || die "could not resolve latest release tag" +# --- resolve version (latest channel unless pinned) ---------------------------- +if [ -z "$VERSION" ]; then + VERSION=$(dl "https://${GET_HOST}/${BINARY}/latest" | sed -n 's/.*"version"[ ]*:[ ]*"\([^"]*\)".*/\1/p') + [ -n "$VERSION" ] || die "could not resolve latest version from https://${GET_HOST}/${BINARY}/latest" fi -info "installing ${BINARY} ${version} (${os}/${arch})" - -archive="${BINARY}_${version}_${os}_${arch}.tar.gz" -base="https://github.com/${REPO}/releases/download/${version}" +say "installing ${BINARY} ${VERSION} (${os}/${arch})" -# --- download + verify ---------------------------------------------------- +# --- download + checksum-verify ------------------------------------------------ tmp=$(mktemp -d) -trap 'rm -rf "$tmp"' EXIT INT TERM - -dl "${base}/${archive}" "${tmp}/${archive}" || die "downloading ${archive}" -dl "${base}/checksums.txt" "${tmp}/checksums.txt" || die "downloading checksums.txt" - -want=$(grep " ${archive}\$" "${tmp}/checksums.txt" | awk '{print $1}' | head -n1) -[ -n "$want" ] || die "no checksum for ${archive} in checksums.txt" -got=$(sha256 "${tmp}/${archive}") -if [ "$want" != "$got" ]; then - die "checksum mismatch for ${archive} (want ${want}, got ${got})" -fi -info "checksum verified" - -tar -xzf "${tmp}/${archive}" -C "$tmp" -[ -f "${tmp}/${BINARY}" ] || die "archive did not contain ${BINARY}" +trap 'rm -rf "$tmp"' EXIT +archive="${BINARY}_${VERSION}_${os}_${arch}.tar.gz" +base="https://${GET_HOST}/${BINARY}/dl/${VERSION}" +dlf "${base}/${archive}" "${tmp}/${archive}" || die "download failed: ${base}/${archive}" +dlf "${base}/checksums.txt" "${tmp}/checksums.txt" || die "checksums download failed" + +want=$(grep " ${archive}\$" "${tmp}/checksums.txt" | awk '{print $1}') +[ -n "$want" ] || die "no checksum listed for ${archive}" +if have sha256sum; then got=$(sha256sum "${tmp}/${archive}" | awk '{print $1}') +elif have shasum; then got=$(shasum -a 256 "${tmp}/${archive}" | awk '{print $1}') +else die "need sha256sum or shasum"; fi +[ "$want" = "$got" ] || die "checksum mismatch (expected $want, got $got) — refusing to install" +say "checksum verified" + +# --- extract + install --------------------------------------------------------- +tar -xzf "${tmp}/${archive}" -C "$tmp" || die "extract failed" +[ -f "${tmp}/${BINARY}" ] || die "binary ${BINARY} not found in archive" chmod +x "${tmp}/${BINARY}" -# --- install -------------------------------------------------------------- -dir="${INTERLOCK_INSTALL_DIR:-}" -if [ -z "$dir" ]; then - if [ -w /usr/local/bin ] 2>/dev/null || { [ "$(id -u)" = 0 ] && [ -d /usr/local/bin ]; }; then - dir=/usr/local/bin - else - dir="${HOME}/.local/bin" - fi +if [ -z "$INSTALL_DIR" ]; then + if [ -w /usr/local/bin ] 2>/dev/null; then INSTALL_DIR=/usr/local/bin + elif have sudo; then INSTALL_DIR=/usr/local/bin; SUDO=sudo + else INSTALL_DIR="${HOME}/.local/bin"; mkdir -p "$INSTALL_DIR"; fi fi -mkdir -p "$dir" +${SUDO:-} mv "${tmp}/${BINARY}" "${INSTALL_DIR}/${BINARY}" || die "install to ${INSTALL_DIR} failed" +say "installed to ${INSTALL_DIR}/${BINARY}" -if [ -w "$dir" ]; then - mv "${tmp}/${BINARY}" "${dir}/${BINARY}" -elif command -v sudo >/dev/null 2>&1; then - info "elevating with sudo to write ${dir}" - sudo mv "${tmp}/${BINARY}" "${dir}/${BINARY}" -else - die "cannot write ${dir}; set INTERLOCK_INSTALL_DIR to a writable path" -fi -info "installed ${dir}/${BINARY}" - -case ":${PATH}:" in - *":${dir}:"*) ;; - *) info "note: ${dir} is not on your PATH; add it to use \`${BINARY}\` directly" ;; -esac +case ":$PATH:" in *":${INSTALL_DIR}:"*) ;; *) say "note: add ${INSTALL_DIR} to your PATH";; esac -# --- prove it works ------------------------------------------------------- -bin="${dir}/${BINARY}" -echo >&2 -"$bin" doctor -echo >&2 -"$bin" demo repository-policy -echo >&2 -info "done — author your own policy with \`${BINARY} init\`" +# --- smoke test ---------------------------------------------------------------- +if [ -n "$SMOKE_CMD" ] && have "${INSTALL_DIR}/${BINARY}"; then + say "running: ${BINARY} ${SMOKE_CMD}" + "${INSTALL_DIR}/${BINARY}" ${SMOKE_CMD} || say "(${BINARY} ${SMOKE_CMD} reported issues — see above)" +fi +say "done. run '${BINARY} --help' to get started."