#!/bin/sh set -e # Quint Agent install script # Usage: curl -fsSL https://get.quintai.dev | sudo sh -s -- --token # --------------------------------------------------------------------------- # Banner # --------------------------------------------------------------------------- cat <<'BANNER' ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠀⠀⠀⠀⣠⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⢀⣴⠛⡄⠀⣠⠞⠁⠀⠀⠀⠀⠀⠀⣠⣾⠟⠛⠛⠛⠿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠀⠀⢀⣀⠀⠀⠀⠀⣿⡇⠀⠀⠀⠀⠀ ⠀⠀⠀⢀⣀⣤⣶⣋⣁⣀⣱⣞⣁⣀⣀⣀⡀⠀⠀⠀⢠⣿⠃⠀⠀⠀⠀⠀⢹⣷⠀⢸⣿⠀⠀⠀⠀⣿⠀⠀⢸⣿⠀⠀⣿⡧⠚⠛⠻⣿⡆⠀⠛⣿⡟⠛⠃⠀⠀⠀ ⠀⠀⠀⠈⠉⠉⠉⢉⣽⠟⡉⠉⣩⠿⠛⠉⠁⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⠀⢸⣿⠀⢸⣿⠀⠀⠀⠀⣿⠀⠀⢸⣿⠀⠀⣿⡇⠀⠀⠀⣿⡇⠀⠀⣿⡇⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⢀⣴⠟⠁⠀⣧⡞⠁⠀⠀⠀⠀⠀⠀⠀⠀⢻⣧⣀⠀⠀⢀⣠⣾⠏⠀⠸⣿⡀⠀⢀⡰⣿⠀⠀⢸⣿⠀⠀⣿⡇⠀⠀⠀⣿⡇⠀⠀⣿⡇⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠛⠁⠀⠀⠀⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠛⠛⠛⢻⣧⠀⠀⠀⠙⠛⠛⠋⠀⠛⠀⠀⠘⠛⠀⠀⠛⠃⠀⠀⠀⠛⠃⠀⠀⠘⠛⠛⠃⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ Firewall for AI Agents ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ BANNER # --------------------------------------------------------------------------- # Check root # --------------------------------------------------------------------------- if [ "$(id -u)" -ne 0 ]; then echo "Error: this installer must be run as root." echo "" echo " curl -fsSL https://get.quintai.dev | sudo sh -s -- --token " echo "" exit 1 fi # --------------------------------------------------------------------------- # Defaults # --------------------------------------------------------------------------- TOKEN="" API_URL="https://api.quintai.dev" # --------------------------------------------------------------------------- # Parse arguments # --------------------------------------------------------------------------- while [ $# -gt 0 ]; do case "$1" in --token) TOKEN="$2" shift 2 ;; --api-url) API_URL="$2" shift 2 ;; *) echo "Unknown option: $1" shift ;; esac done if [ -z "$TOKEN" ]; then echo "Usage: curl -fsSL https://get.quintai.dev | sudo sh -s -- --token [--api-url ]" echo "" echo "Options:" echo " --token (required) Agent enrollment token" echo " --api-url API endpoint (default: https://api.quintai.dev)" exit 1 fi # --------------------------------------------------------------------------- # Detect OS and architecture # --------------------------------------------------------------------------- OS="$(uname -s | tr '[:upper:]' '[:lower:]')" ARCH="$(uname -m)" case "$ARCH" in x86_64) ARCH="amd64" ;; aarch64) ARCH="arm64" ;; arm64) ARCH="arm64" ;; *) echo "Error: unsupported architecture: $ARCH" exit 1 ;; esac echo "Detected OS=${OS} ARCH=${ARCH}" # --------------------------------------------------------------------------- # Fetch latest version from GitHub releases API # --------------------------------------------------------------------------- FALLBACK_VERSION="1.0.0" VERSION="$(curl -fsSL https://api.github.com/repos/Quint-Security/proxy/releases/latest 2>/dev/null \ | grep tag_name | cut -d'"' -f4 | sed 's/^v//' || true)" if [ -z "$VERSION" ]; then echo "Warning: could not reach GitHub releases API, falling back to v${FALLBACK_VERSION}" VERSION="$FALLBACK_VERSION" fi echo "Installing Quint Agent v${VERSION} ..." # --------------------------------------------------------------------------- # Download binary (tarball format) # --------------------------------------------------------------------------- # Asset naming verified against the real release: `quint-darwin-arm64.tar.gz`, # containing a single file named `quint`. Both the repo name (renamed # quint-proxy -> proxy) and the `quint-proxy-` asset prefix were wrong here, so # every download 404'd. TARBALL_URL="https://github.com/Quint-Security/proxy/releases/download/v${VERSION}/quint-${OS}-${ARCH}.tar.gz" CHECKSUM_URL="${TARBALL_URL}.sha256" echo "Downloading from ${TARBALL_URL} ..." if ! curl -fsSL -o /tmp/quint-download.tar.gz "$TARBALL_URL"; then echo "Error: failed to download quint v${VERSION}" echo " from ${TARBALL_URL}" echo # Be explicit about the most likely cause rather than leaving a bare 404. # Quint-Security/proxy is a PRIVATE repository, and GitHub returns 404 (not # 403) for unauthenticated requests to a private repo's release assets — so # this path cannot work for anyone outside the org, no matter what the URL says. echo " The Quint repository is currently private, so release assets are not" echo " publicly downloadable. Until public artifact hosting is in place, use" echo " the notarized installer package instead:" echo echo " 1. Get quint-.pkg from your Quint contact" echo " 2. spctl -a -vvv -t install quint-.pkg" echo " (expect: accepted / source=Notarized Developer ID)" echo " 3. Double-click it, then: sudo quint setup --token " echo echo " The .pkg is the supported path — it installs the kernel EndpointSecurity" echo " and NetworkExtension components that this tarball does not contain." exit 1 fi # Verify SHA256 checksum echo "Verifying checksum ..." if curl -fsSL -o /tmp/quint-download.sha256 "$CHECKSUM_URL" 2>/dev/null; then EXPECTED="$(awk '{print $1}' /tmp/quint-download.sha256)" if command -v shasum >/dev/null 2>&1; then ACTUAL="$(shasum -a 256 /tmp/quint-download.tar.gz | awk '{print $1}')" elif command -v sha256sum >/dev/null 2>&1; then ACTUAL="$(sha256sum /tmp/quint-download.tar.gz | awk '{print $1}')" else # Refuse rather than pretend. Setting ACTUAL="$EXPECTED" made the comparison # below trivially pass, so the script printed a warning and then installed an # unverified binary as root. shasum ships with macOS and sha256sum with every # mainstream Linux, so reaching this branch is genuinely anomalous. echo "Error: no SHA256 tool (shasum/sha256sum) found — refusing to install" echo " an unverified binary. Install one, or use the notarized .pkg." rm -f /tmp/quint-download.tar.gz /tmp/quint-download.sha256 exit 1 fi if [ "$EXPECTED" != "$ACTUAL" ]; then echo "Error: checksum mismatch!" echo " Expected: $EXPECTED" echo " Got: $ACTUAL" rm -f /tmp/quint-download.tar.gz /tmp/quint-download.sha256 exit 1 fi echo "Checksum verified." rm -f /tmp/quint-download.sha256 else # Same reasoning: "couldn't fetch the checksum" is not a licence to skip # integrity verification on a root install. If the checksum is genuinely # absent from a release, that release is not fit to install. echo "Error: could not download ${CHECKSUM_URL}" echo " Refusing to install an unverified binary." rm -f /tmp/quint-download.tar.gz exit 1 fi tar xzf /tmp/quint-download.tar.gz -C /tmp # The tarball contains a single file named `quint` (verified against the real # v1.0.0 asset). The legacy `quint-proxy` name is kept as a fallback so an older # release still installs. if [ -f /tmp/quint ]; then mv /tmp/quint /usr/local/bin/quint elif [ -f /tmp/quint-proxy ]; then mv /tmp/quint-proxy /usr/local/bin/quint else echo "Error: could not find quint binary in tarball" rm -f /tmp/quint-download.tar.gz exit 1 fi chmod +x /usr/local/bin/quint rm -f /tmp/quint-download.tar.gz echo "Installed binary to /usr/local/bin/quint" # Also install to homebrew path if it exists (avoid PATH shadowing on macOS) if [ -d "/opt/homebrew/bin" ]; then cp /usr/local/bin/quint /opt/homebrew/bin/quint 2>/dev/null || true echo "Copied binary to /opt/homebrew/bin/quint" fi # --------------------------------------------------------------------------- # Check for dylib (macOS local monitoring) # --------------------------------------------------------------------------- DYLIB_PATH="/usr/local/lib/quint_monitor.dylib" if [ "$OS" = "darwin" ] && [ ! -f "$DYLIB_PATH" ]; then echo "Note: ${DYLIB_PATH} not found. Local monitoring will be unavailable" echo " until the dylib is installed. Run 'quint setup' again after" echo " installing it to enable local monitoring." fi # --------------------------------------------------------------------------- # Delegate to quint setup (handles everything else) # --------------------------------------------------------------------------- # quint setup handles: # - CA certificate generation and system trust # - LaunchAgent for GUI app env vars # - PAC file generation and system proxy configuration # - User config directory (~/.quint/) and CA copy # - env.sh / env.fish with agent wrappers # - Shell profile injection (.zshrc, .bashrc, fish) # - /etc/quint/config.yaml with all required fields # - LaunchDaemon / systemd service install and start # - Daemon readiness check echo "" echo "Running quint setup ..." echo "" SETUP_ARGS="--token $TOKEN --api-url $API_URL" /usr/local/bin/quint setup $SETUP_ARGS # --------------------------------------------------------------------------- # Verify # --------------------------------------------------------------------------- echo "" if /usr/local/bin/quint version >/dev/null 2>&1; then INSTALLED_VERSION="$(/usr/local/bin/quint version 2>/dev/null || echo "v${VERSION}")" echo "Quint Agent installed successfully! (${INSTALLED_VERSION})" echo "" echo " Binary: /usr/local/bin/quint" echo " Proxy: eval \$(quint env --proxy) or open a new terminal" echo " Logs: /var/log/quint/agent.log" echo "" echo " Every new terminal session will auto-route AI agent traffic" echo " through Quint. To use immediately: eval \$(quint env --proxy)" else echo "Warning: 'quint version' check failed, but the binary was installed." echo "Check logs at /var/log/quint/agent.err for details." exit 1 fi