From 1610cb84e7fc0bf8f0d8a577d7b9d233ed7fd70a Mon Sep 17 00:00:00 2001
From: taurusismysign
Date: Thu, 11 Jun 2026 11:19:37 +0530
Subject: [PATCH] cloudanix-guard: install the Go binary instead of the Python
wheel
The guard is moving from a Python package to a single static Go binary.
Rewrite the installer to fetch the platform release archive
(cloudanix-guard___.tar.gz) plus its .sha256 sidecar
from the public artifacts mirror, verify the checksum, extract, and
place the self-contained binary at the stable launcher path
~/.cloudanix-guard/bin/cloudanix-guard (so Codex's hook trust-hash stays
valid across in-place upgrades).
- No Python / venv / pip; preflight now just needs curl + tar.
- Host arch mapped to the release-archive token (amd64 -> x86_64).
- Dev overrides: CLOUDANIX_LOCAL_WHEEL -> CLOUDANIX_LOCAL_BINARY /
CLOUDANIX_LOCAL_TARBALL; CLOUDANIX_PYTHON dropped. Still gated behind
CLOUDANIX_INSTALL_DEV=1.
- README + landing page updated to match.
Feature branch only: do NOT merge until the Go guard reaches v1.0.0
parity and its tarballs + sidecars are published to Cloudanix/artifacts.
---
README.md | 42 ++++---
cloudanix-guard | 307 ++++++++++++++++++++++++++----------------------
index.html | 7 +-
3 files changed, 190 insertions(+), 166 deletions(-)
diff --git a/README.md b/README.md
index 89a97d1..6310ae1 100644
--- a/README.md
+++ b/README.md
@@ -48,16 +48,19 @@ curl -fsSL https://install.cloudanix.com/cloudanix-guard | bash
What that does (paraphrased from the [script itself](./cloudanix-guard)):
-1. Verifies Python ≥ 3.9 and `curl` are available.
-2. Downloads the latest wheel + its SHA256 sidecar from
+1. Verifies `curl` and `tar` are available and detects your OS/arch.
+2. Downloads the matching release archive
+ (`cloudanix-guard___.tar.gz`) + its SHA256
+ sidecar from
`github.com/Cloudanix/artifacts/raw/main/coding-agent-guard/`.
-3. Verifies the wheel against its SHA256 — aborts on mismatch.
-4. Creates a venv at `~/.cloudanix-guard/venv` (or reuses one).
-5. `pip install`s the verified wheel into that venv.
-6. Drops a stable launcher shim at
- `~/.cloudanix-guard/bin/cloudanix-guard` so subsequent upgrades
- don't invalidate paths registered with other tools.
-7. Prints next-step instructions, including how to wire the guard
+3. Verifies the archive against its SHA256 — aborts on mismatch.
+4. Extracts the self-contained `cloudanix-guard` binary — no Python,
+ no runtime to manage.
+5. Installs it at the stable path
+ `~/.cloudanix-guard/bin/cloudanix-guard`, so upgrades replace the
+ binary in place and never invalidate paths other tools have
+ registered (e.g. Codex's hook trust-hash).
+6. Prints next-step instructions, including how to wire the guard
into Claude Code / Codex / Kiro.
### Pin a version
@@ -68,17 +71,17 @@ curl -fsSL https://install.cloudanix.com/cloudanix-guard \
```
The named version must exist in `Cloudanix/artifacts/coding-agent-guard/`
-as `cloudanix_guard--py3-none-any.whl` (with a matching
-`.sha256` sidecar). Otherwise the installer aborts with a 404 from
-the artifact CDN.
+as `cloudanix-guard___.tar.gz` (with a matching
+`.sha256` sidecar) for your platform. Otherwise the installer aborts
+with a 404 from the artifact CDN.
---
## Security posture
- **Installer is MIT-licensed.** Inspect, fork, port — no restrictions.
-- **Wheel integrity is verified.** Each release ships a `.sha256`
- sidecar published alongside the wheel; the installer downloads
+- **Binary integrity is verified.** Each release archive ships a
+ `.sha256` sidecar published alongside it; the installer downloads
both and aborts on mismatch.
- **No secrets in the script.** No GitHub tokens, no API keys,
nothing read from the environment that touches an auth header.
@@ -89,11 +92,12 @@ the artifact CDN.
- **Strict mode + ShellCheck on CI** — `set -euo pipefail`; no
unguarded `command-not-found`; no implicit word-split bugs.
- **Dev-only env-var overrides are gated** behind
- `CLOUDANIX_INSTALL_DEV=1`. Without that flag, the four overrides
- (`CLOUDANIX_LOCAL_LIB`, `CLOUDANIX_LOCAL_WHEEL`,
- `CLOUDANIX_INSTALL_BASE`, `CLOUDANIX_ARTIFACTS_URL`) refuse to
- apply — closes a phishing path where someone tricks a developer
- into pasting `CLOUDANIX_LOCAL_WHEEL=/tmp/evil.whl curl … | bash`.
+ `CLOUDANIX_INSTALL_DEV=1`. Without that flag, the overrides
+ (`CLOUDANIX_LOCAL_LIB`, `CLOUDANIX_LOCAL_BINARY`,
+ `CLOUDANIX_LOCAL_TARBALL`, `CLOUDANIX_INSTALL_BASE`,
+ `CLOUDANIX_ARTIFACTS_URL`) refuse to apply — closes a phishing path
+ where someone tricks a developer into pasting
+ `CLOUDANIX_LOCAL_BINARY=/tmp/evil curl … | bash`.
Found a bug? Open an issue on this repo. For sensitive disclosures
contact `security@cloudanix.com`.
diff --git a/cloudanix-guard b/cloudanix-guard
index 3192c54..f502600 100755
--- a/cloudanix-guard
+++ b/cloudanix-guard
@@ -3,30 +3,31 @@
#
# curl -fsSL https://install.cloudanix.com/cloudanix-guard | bash
#
-# No GitHub token required. The wheel is published to a public
-# Cloudanix-controlled CDN repo (Cloudanix/artifacts) and verified
-# locally against a SHA256 sidecar before pip-install.
+# Installs the Cloudanix Guard binary — a single, self-contained
+# executable (no Python, no runtime to manage). The binary is published
+# to a public Cloudanix-controlled mirror (Cloudanix/artifacts) and
+# verified locally against a SHA256 sidecar before it is installed.
#
# Optional env:
-# CLOUDANIX_VERSION Pin a specific version, e.g. "0.1.1".
-# Defaults to "latest" (the alias updated
-# on every release).
-# CLOUDANIX_PYTHON Path to a specific Python ≥ 3.9.
-# Auto-detected from PATH otherwise.
-# CLOUDANIX_INSTALL_DIR Where to put the venv. Defaults to
-# ~/.cloudanix-guard/venv .
+# CLOUDANIX_VERSION Pin a specific version, e.g. "1.0.0".
+# Defaults to "latest" (the alias updated on
+# every release).
+# CLOUDANIX_INSTALL_DIR Base install dir. Defaults to
+# ~/.cloudanix-guard . The binary lands at
+# /bin/cloudanix-guard .
# NO_COLOR Disable colour output.
#
# Dev-only overrides (require CLOUDANIX_INSTALL_DEV=1):
# CLOUDANIX_INSTALL_DEV Set to "1" to enable any of:
-# CLOUDANIX_LOCAL_LIB Path to a local lib/common.sh
-# CLOUDANIX_LOCAL_WHEEL Path to a local .whl (skips download)
+# CLOUDANIX_LOCAL_LIB Path to a local lib/common
+# CLOUDANIX_LOCAL_BINARY Path to a prebuilt binary (skips download)
+# CLOUDANIX_LOCAL_TARBALL Path to a local release tarball (skips download)
# CLOUDANIX_INSTALL_BASE Override install.cloudanix.com URL
# CLOUDANIX_ARTIFACTS_URL Override the artifacts CDN URL
#
# Exit codes:
# 0 success
-# 1 user-facing error (missing Python, missing tools, dev override
+# 1 user-facing error (unsupported OS, missing tools, dev override
# used without CLOUDANIX_INSTALL_DEV=1, etc.)
# 2 network / artifact integrity error (download failed or SHA256
# mismatch)
@@ -39,15 +40,12 @@ set -euo pipefail
# ─── dev-only override gating ───────────────────────────────────────
#
# Phishing defence: a customer can be tricked into pasting
-# CLOUDANIX_LOCAL_WHEEL=/tmp/evil.whl curl ... | bash
+# CLOUDANIX_LOCAL_BINARY=/tmp/evil curl ... | bash
# into their terminal. Requiring CLOUDANIX_INSTALL_DEV=1 makes it
# obvious that something dev-only is happening.
if [ -z "${CLOUDANIX_INSTALL_DEV:-}" ]; then
- for _v in CLOUDANIX_LOCAL_LIB CLOUDANIX_LOCAL_WHEEL CLOUDANIX_INSTALL_BASE CLOUDANIX_ARTIFACTS_URL; do
- # eval indirection — `${!_v}` exists only in bash, and we want this
- # script to fail loudly on dash/sh anyway, but using eval keeps the
- # error message symmetric across shells.
+ for _v in CLOUDANIX_LOCAL_LIB CLOUDANIX_LOCAL_BINARY CLOUDANIX_LOCAL_TARBALL CLOUDANIX_INSTALL_BASE CLOUDANIX_ARTIFACTS_URL; do
eval _val="\${${_v}:-}"
if [ -n "${_val:-}" ]; then
echo "error: ${_v} is a development-only override." >&2
@@ -62,16 +60,15 @@ fi
INSTALL_BASE="${CLOUDANIX_INSTALL_BASE:-https://install.cloudanix.com}"
if [ -n "${CLOUDANIX_LOCAL_LIB:-}" ] && [ -f "${CLOUDANIX_LOCAL_LIB}" ]; then
- # shellcheck source=lib/common.sh
+ # shellcheck source=lib/common
source "${CLOUDANIX_LOCAL_LIB}"
else
- # Fetch common.sh to a real temp file so we can:
+ # Fetch common to a real temp file so we can:
# 1. Check the HTTP fetch succeeded.
# 2. Check the body is non-empty (catches a 200-with-empty-body
# misconfiguration on the CDN — `source <(curl ...)` would
- # silently treat an empty body as "sourced 0 functions" and
- # then die a few lines down with `cdx::init_strict: command
- # not found`, which is a baffling user-facing error).
+ # silently treat an empty body as "sourced 0 functions" and then
+ # die a few lines down with a baffling user-facing error).
COMMON_URL="${INSTALL_BASE}/lib/common"
if ! command -v curl >/dev/null 2>&1; then
echo "error: 'curl' is required to fetch installer helpers." >&2
@@ -97,17 +94,24 @@ cdx::init_strict
# ─── product-specific config ────────────────────────────────────────
readonly PRODUCT="cloudanix-guard"
-readonly DEFAULT_INSTALL_DIR="${HOME}/.cloudanix-guard"
-readonly LAUNCHER_DIR="${DEFAULT_INSTALL_DIR}/bin"
-readonly LAUNCHER_PATH="${LAUNCHER_DIR}/${PRODUCT}"
+INSTALL_DIR="${CLOUDANIX_INSTALL_DIR:-${HOME}/.cloudanix-guard}"
+readonly INSTALL_DIR
+readonly BIN_DIR="${INSTALL_DIR}/bin"
+readonly LAUNCHER_PATH="${BIN_DIR}/${PRODUCT}"
-# Public CDN that holds the wheels. Override only via the
+# Public CDN that holds the release binaries. Override only via the
# dev-gated CLOUDANIX_ARTIFACTS_URL above.
readonly ARTIFACTS_URL="${CLOUDANIX_ARTIFACTS_URL:-https://github.com/Cloudanix/artifacts/raw/main/coding-agent-guard}"
-INSTALL_DIR="${CLOUDANIX_INSTALL_DIR:-${DEFAULT_INSTALL_DIR}/venv}"
VERSION="${CLOUDANIX_VERSION:-latest}"
+# Set by the resolve step (download+extract, or a dev-provided local
+# binary/tarball). Always ends up pointing at the executable to install.
+BINARY_PATH=""
+# Set by download_tarball. Kept as a global so the tmpdir EXIT trap
+# stays in main()'s shell (see cdx::ensure_tmpdir).
+TARBALL_PATH=""
+
# ─── preflight + flow ───────────────────────────────────────────────
main() {
@@ -115,78 +119,89 @@ main() {
cdx::step "Preflight"
cdx::require_cmd curl
+ cdx::require_cmd tar
cdx::detect_os
cdx::info "host: ${CDX_OS}/${CDX_ARCH}"
if [ "${CDX_OS}" = "windows" ]; then
cdx::die "Windows is not yet supported. macOS and Linux only."
fi
+ if [ "${CDX_OS}" = "unknown" ]; then
+ cdx::die "Unsupported OS. macOS and Linux only."
+ fi
- local py
- py="$(cdx::python_bin)"
- cdx::info "python: ${py} ($("${py}" -V 2>&1))"
- cdx::ensure_pip "${py}"
-
- # The wheel download tmpdir's EXIT trap MUST live in main()'s shell
- # (not in a $() subshell) or the dir is removed mid-flow. See
- # lib/common.sh comment on cdx::ensure_tmpdir for the full story.
+ # The tmpdir's EXIT trap MUST live in main()'s shell (not a $()
+ # subshell) or the dir is removed mid-flow. See lib/common's
+ # cdx::ensure_tmpdir for the full story.
cdx::ensure_tmpdir
- # ─── resolve + verify the wheel ──────────────────────────────────
-
cdx::step "Resolving release"
+ resolve_binary "${VERSION}" # sets BINARY_PATH
+ cdx::ok "binary ready: $(basename "${BINARY_PATH}")"
- WHEEL_PATH="" # set by download_wheel / local-wheel branch below
- if [ -n "${CLOUDANIX_LOCAL_WHEEL:-}" ]; then
- [ -f "${CLOUDANIX_LOCAL_WHEEL}" ] \
- || cdx::die "CLOUDANIX_LOCAL_WHEEL=${CLOUDANIX_LOCAL_WHEEL} is not a file"
- WHEEL_PATH="${CLOUDANIX_LOCAL_WHEEL}"
- cdx::warn "using local wheel (skipping checksum): ${WHEEL_PATH}"
- else
- # download_wheel writes to WHEEL_PATH directly (no $() subshell)
- # so the EXIT trap in main()'s shell stays the only thing managing
- # the tmpdir's lifecycle.
- download_wheel "${VERSION}"
- cdx::ok "downloaded + verified: $(basename "${WHEEL_PATH}")"
- fi
- local wheel_path="${WHEEL_PATH}"
+ cdx::step "Installing"
+ install_binary "${BINARY_PATH}" "${LAUNCHER_PATH}"
+ cdx::ok "installed: ${LAUNCHER_PATH}"
+ note_legacy_venv
- # ─── install ─────────────────────────────────────────────────────
+ cdx::step "Next steps"
+ print_next_steps "${LAUNCHER_PATH}"
+}
- cdx::step "Installing"
+# ─── resolve the binary (download+verify+extract, or dev override) ──
- ensure_venv "${py}"
- pip_install_local "${INSTALL_DIR}" "${wheel_path}"
- install_launcher "${INSTALL_DIR}" "${LAUNCHER_PATH}"
+resolve_binary() {
+ local version="$1"
- cdx::ok "installed at: ${INSTALL_DIR}"
- cdx::ok "launcher: ${LAUNCHER_PATH}"
+ if [ -n "${CLOUDANIX_LOCAL_BINARY:-}" ]; then
+ [ -f "${CLOUDANIX_LOCAL_BINARY}" ] \
+ || cdx::die "CLOUDANIX_LOCAL_BINARY=${CLOUDANIX_LOCAL_BINARY} is not a file"
+ cdx::warn "using local binary (skipping download + checksum): ${CLOUDANIX_LOCAL_BINARY}"
+ BINARY_PATH="${CLOUDANIX_LOCAL_BINARY}"
+ return 0
+ fi
- # ─── next steps ──────────────────────────────────────────────────
+ local tarball
+ if [ -n "${CLOUDANIX_LOCAL_TARBALL:-}" ]; then
+ [ -f "${CLOUDANIX_LOCAL_TARBALL}" ] \
+ || cdx::die "CLOUDANIX_LOCAL_TARBALL=${CLOUDANIX_LOCAL_TARBALL} is not a file"
+ cdx::warn "using local tarball (skipping download + checksum): ${CLOUDANIX_LOCAL_TARBALL}"
+ tarball="${CLOUDANIX_LOCAL_TARBALL}"
+ else
+ download_tarball "${version}" # sets TARBALL_PATH
+ tarball="${TARBALL_PATH}"
+ cdx::ok "downloaded + verified: $(basename "${tarball}")"
+ fi
- cdx::step "Next steps"
- print_next_steps "${LAUNCHER_PATH}"
+ extract_binary "${tarball}" # sets BINARY_PATH
+}
+
+# Map the host arch (CDX_ARCH = amd64|arm64) to the token the release
+# archives use in their filename (amd64 → x86_64; arm64 unchanged).
+_goarch() {
+ case "${CDX_ARCH}" in
+ amd64) printf 'x86_64' ;;
+ *) printf '%s' "${CDX_ARCH}" ;;
+ esac
}
-# ─── Wheel download + SHA256 verification ───────────────────────────
+# Download the platform tarball + its .sha256 sidecar into CDX_TMPDIR
+# and verify. Sets the global TARBALL_PATH. Doesn't return via stdout —
+# see the cdx::ensure_tmpdir comment on why the tmpdir trap must stay in
+# main()'s shell.
#
-# Wheels live at ARTIFACTS_URL as flat files named
-# cloudanix_guard--py3-none-any.whl. Each has a sibling
-# .sha256 sidecar in coreutils format (" "). We download
-# both and verify locally — if Cloudanix/artifacts is ever compromised
-# and someone publishes a malicious wheel without also publishing the
-# matching .sha256, the install aborts.
-
-# Writes the downloaded wheel path into the global WHEEL_PATH.
-# Doesn't return via stdout — see the cdx::ensure_tmpdir comment.
-download_wheel() {
+# Archives are named cloudanix-guard___.tar.gz (the
+# goreleaser convention) with a sibling .sha256 sidecar in coreutils
+# format (" "). We download both and verify locally — if the
+# public mirror is ever tampered with and a malicious archive is pushed
+# without a matching sidecar, the install aborts.
+download_tarball() {
local version="$1"
local tmp="${CDX_TMPDIR}"
- [ -n "$tmp" ] || cdx::die "internal: CDX_TMPDIR not initialised"
+ [ -n "${tmp}" ] || cdx::die "internal: CDX_TMPDIR not initialised"
- # Resolve "latest" to a concrete version. PEP 440 requires the wheel
- # filename to carry a real version string ("latest" wouldn't pip-install),
- # so we publish a plain LATEST_VERSION text file alongside the wheels
- # and read it here.
+ # Resolve "latest" to a concrete version. The archive filename must
+ # carry a real version string, so we publish a plain LATEST_VERSION
+ # text file alongside the archives and read it here.
if [ "${version}" = "latest" ]; then
cdx::info "resolving latest via ${ARTIFACTS_URL}/LATEST_VERSION"
version="$(curl -fsSL "${ARTIFACTS_URL}/LATEST_VERSION" 2>/dev/null | tr -d '[:space:]')" || {
@@ -194,45 +209,45 @@ download_wheel() {
exit 2
}
[ -n "${version}" ] || cdx::die "LATEST_VERSION at ${ARTIFACTS_URL} returned empty"
- cdx::info "latest = v${version}"
+ cdx::info "latest = v${version#v}"
fi
- local tag="${version#v}"
- local fn="cloudanix_guard-${tag}-py3-none-any.whl"
- local wheel_url="${ARTIFACTS_URL}/${fn}"
- local sha_url="${ARTIFACTS_URL}/${fn}.sha256"
- local wheel_out="${tmp}/${fn}"
- local sha_out="${tmp}/${fn}.sha256"
-
- cdx::info "fetching ${wheel_url}"
- curl -fsSL -o "${wheel_out}" "${wheel_url}" || {
- cdx::err "wheel download failed: ${wheel_url}"
- cdx::err " (Is CLOUDANIX_VERSION=${version} a published release?"
+ local tag arch fn url sha_url
+ tag="${version#v}"
+ arch="$(_goarch)"
+ fn="${PRODUCT}_${tag}_${CDX_OS}_${arch}.tar.gz"
+ url="${ARTIFACTS_URL}/${fn}"
+ sha_url="${url}.sha256"
+
+ cdx::info "fetching ${url}"
+ curl -fsSL -o "${tmp}/${fn}" "${url}" || {
+ cdx::err "binary download failed: ${url}"
+ cdx::err " (Is CLOUDANIX_VERSION=${tag} published for ${CDX_OS}/${arch}?"
cdx::err " Check https://github.com/Cloudanix/artifacts/tree/main/coding-agent-guard )"
exit 2
}
cdx::info "fetching ${sha_url}"
- curl -fsSL -o "${sha_out}" "${sha_url}" || {
+ curl -fsSL -o "${tmp}/${fn}.sha256" "${sha_url}" || {
cdx::err "SHA256 sidecar download failed: ${sha_url}"
- cdx::err " Refusing to install an unverified wheel."
+ cdx::err " Refusing to install an unverified binary."
exit 2
}
cdx::info "verifying SHA256"
if ! sha256_verify "${tmp}" "${fn}.sha256"; then
cdx::err "SHA256 mismatch on ${fn}."
- cdx::err " The published wheel does not match its sidecar checksum."
+ cdx::err " The published archive does not match its sidecar checksum."
cdx::err " This could indicate corruption in transit or tampering."
cdx::err " Aborting install."
exit 2
fi
- WHEEL_PATH="${wheel_out}"
+ TARBALL_PATH="${tmp}/${fn}"
}
-# Verify a checksum sidecar in coreutils format. Tries sha256sum
-# (Linux default), falls back to shasum (macOS default).
+# Verify a checksum sidecar in coreutils format. Tries sha256sum (Linux
+# default), falls back to shasum (macOS default).
sha256_verify() {
local dir="$1"
local fname="$2"
@@ -246,59 +261,65 @@ sha256_verify() {
fi
}
-# ─── venv + pip ─────────────────────────────────────────────────────
-
-ensure_venv() {
- local py="$1"
- if [ -d "${INSTALL_DIR}" ] && [ -x "${INSTALL_DIR}/bin/python" ]; then
- cdx::info "reusing existing venv at ${INSTALL_DIR}"
- return 0
+# Extract the tarball into CDX_TMPDIR and locate the cloudanix-guard
+# binary. Sets the global BINARY_PATH.
+extract_binary() {
+ local tarball="$1"
+ local dest="${CDX_TMPDIR}/extract"
+ mkdir -p "${dest}"
+ tar -xzf "${tarball}" -C "${dest}" || cdx::die "failed to extract ${tarball}"
+
+ local bin="${dest}/${PRODUCT}"
+ if [ ! -f "${bin}" ]; then
+ # The archive layout is flat, but fall back to a search in case a
+ # future release wraps the binary in a directory.
+ bin="$(find "${dest}" -type f -name "${PRODUCT}" 2>/dev/null | head -n1)"
fi
- cdx::info "creating venv at ${INSTALL_DIR}"
- mkdir -p "$(dirname "${INSTALL_DIR}")"
- "${py}" -m venv "${INSTALL_DIR}" \
- || cdx::die "venv creation failed at ${INSTALL_DIR}"
- cdx::pip "${INSTALL_DIR}/bin/python" install --quiet --upgrade pip \
- || cdx::warn "pip self-upgrade inside venv failed (continuing)"
-}
-
-pip_install_local() {
- local venv_dir="$1"
- local wheel="$2"
- cdx::info "pip install ${wheel}"
- cdx::pip "${venv_dir}/bin/python" install --quiet --upgrade --force-reinstall "${wheel}" \
- || cdx::die "pip install failed"
+ [ -n "${bin}" ] && [ -f "${bin}" ] \
+ || cdx::die "could not find '${PRODUCT}' binary inside the archive"
+ BINARY_PATH="${bin}"
}
-# ─── stable launcher shim ───────────────────────────────────────────
+# ─── install the binary at the stable launcher path ─────────────────
#
-# Codex CLI hashes the exact path of every hook command and rejects
-# unknown handlers. Without a stable shim, every `cloudanix-guard
-# install codex` after a re-install picks up a new venv-relative path
-# and Codex demands re-approval. The shim makes that pain go away:
-# the path stays at ~/.cloudanix-guard/bin/cloudanix-guard for the
-# life of the install, regardless of where pip drops the venv.
-
-install_launcher() {
- local venv_dir="$1"
- local launcher_path="$2"
-
- mkdir -p "$(dirname "${launcher_path}")"
- cat > "${launcher_path}" </dev/null 2>&1; then
+ xattr -d com.apple.quarantine "${dest}" >/dev/null 2>&1 || true
+ fi
+
+ cdx::info "wrote binary: ${dest}"
# Friendly note if the launcher isn't on PATH.
- if ! printf ':%s:' "${PATH}" | grep -q ":${LAUNCHER_DIR}:"; then
- cdx::warn "${LAUNCHER_DIR} is not on your \$PATH"
+ if ! printf ':%s:' "${PATH}" | grep -q ":${BIN_DIR}:"; then
+ cdx::warn "${BIN_DIR} is not on your \$PATH"
cdx::warn " Add this to your shell profile (~/.bashrc, ~/.zshrc):"
- cdx::warn " export PATH=\"${LAUNCHER_DIR}:\$PATH\""
+ cdx::warn " export PATH=\"${BIN_DIR}:\$PATH\""
+ fi
+}
+
+# Older Python installs left a venv at ~/.cloudanix-guard/venv. The Go
+# binary doesn't use it; mention it but don't delete it — that's the
+# user's call.
+note_legacy_venv() {
+ local venv="${INSTALL_DIR}/venv"
+ if [ -d "${venv}" ]; then
+ cdx::info "note: a previous Python install's venv at ${venv} is no longer used (safe to delete)."
fi
}
@@ -328,7 +349,7 @@ ${CDX_C_BOLD}Cloudanix Guard is installed.${CDX_C_RESET}
${launcher} status
-For docs: https://github.com/Cloudanix/cloudanix-coding-agent-guard
+For docs: https://install.cloudanix.com
EOF
}
diff --git a/index.html b/index.html
index 05f15c3..c523f21 100644
--- a/index.html
+++ b/index.html
@@ -151,10 +151,9 @@ Cloudanix Guard
pipe — a classic curl-pipe-bash gotcha.)
- The installer downloads the wheel from a public Cloudanix
- artifact mirror and verifies its SHA256 before
- pip install. No GitHub token
- required.
+ The installer downloads a self-contained binary from a public
+ Cloudanix artifact mirror and verifies its SHA256 before
+ installing it. No Python and no GitHub token required.