Skip to content

Releases: gl0bal01/pai-anywhere

v0.2.2 — security hardening pass

19 Jun 10:16

Choose a tag to compare

Security hardening pass — reviewed against four independent AI code reviews (codex / deepseek / glm / kimi). No CRITICAL findings.

Highlights

  • Uninstall data-safety — full uninstall no longer deletes /home/pai/.claude (PAI memory, Claude OAuth/session) via userdel -r while printing that it preserved the data. Home deletion now requires --purge-data with a typed confirmation. Tailscale Serve cleanup no longer runs a global serve reset that wiped unrelated loopback routes.
  • Gateway — loopback-validated pulseOrigin (closes an authenticated SSRF / open-proxy), pairing rate-limit now persisted across restarts (no fresh attempt-window on crash-loop), safe reset-access rotation (aborts before writing if the managed user is unresolved; loud on restart failure), Server version header stripped, HSTS added, tightened cookie/pairing validation.
  • Install — pinned @anthropic-ai/claude-code@2.1.183 and verified the Tailscale apt signing-key fingerprint (both were previously unverified code-execution paths); tty-gated pairing-code display (no leak to curl|bash/tee/CI logs); stopped writing the Tailscale auth URL to world-readable /tmp; single-flight flock; scoped ERR→rollback trap; and self-bootstrap so the advertised curl … | sudo bash paste install actually fetches and builds the gateway.
  • CI / backup / repo — pin-bot PR body fixed (was a literal $(cat …)) and third-party actions SHA-pinned; root-only enforcement before sourcing the backup off-site env; untracked .omc working artifacts; scrubbed local-path topology from CLAUDE.md.

Verification

tsc --noEmit + 52 bun test + shellcheck -S warning + the full shell-test suite — all green. Adds regression tests for the SSRF, rate-limit persistence, pairing/cookie validation, pairing-code leak, and uninstall data/route preservation.

Install

curl -fsSL https://raw.githubusercontent.com/gl0bal01/pai-anywhere/v0.2.2/install.sh | sudo bash

v0.2.1 — security patch: gateway SSRF + reset-access ownership

31 May 05:09

Choose a tag to compare

Security patch release. Upgrade recommended for anyone running the v0.2.0 gateway.

Security fixes

  • HIGH — authenticated SSRF in the Pulse proxy. A request path beginning with // (or backslashes that WHATWG normalizes to //) was passed through new URL(path, origin), which let the path override the upstream host. An authenticated client (tailnet access + pairing code) could make the gateway fetch arbitrary hosts — e.g. //169.254.169.254/... for cloud metadata / IMDS credential theft, or internal services — and receive the response. Fixed by rejecting //-prefixed paths and building the upstream URL from a fixed origin (copying only path + query).
  • MEDIUM — reset-access left rotated secrets root-owned. reset-access runs as root and wrote gateway-secrets.json without re-chowning it to the managed user. The gateway (User=pai) then crash-looped on EACCES at startup, so credential rotation broke access until a manual chown. Now chowns gateway-secrets.json to the managed user and gateway.env to root:<group>, matching install.sh.
  • LOW — request body caps were header-only. MAX_PAIRING_BODY_BYTES / MAX_PULSE_BODY_BYTES trusted Content-Length; omitting/spoofing it bypassed the cap. Caps are now enforced on the actual buffered bytes.
  • LOW — unbounded uninstall path allowlist. is_allowed_path matched "${prefix}"*, so /home/pai also matched siblings like /home/pai-evil. Now bounded to an exact match or a / segment boundary.
  • INFO — header hygiene. Client-forged x-forwarded-* / forwarded headers are stripped before requests reach the loopback Pulse upstream.

Tests

Added regression tests for the SSRF host-override (both // and backslash encodings) and the spoofed-Content-Length body cap. Full suite: 33 pass / 0 fail, tsc --noEmit clean, shellcheck -S warning clean.

Full changelog: v0.2.0...v0.2.1

v0.2.0

14 May 16:26

Choose a tag to compare

Breaking

  • preflight refuses non-systemd hosts; minimal containers no longer install
  • gateway-secrets.json replaces session-secret.txt + PAI_ANYWHERE_SESSION_SECRET env (single source of truth on disk)

Highlights

  • install.sh: url_safe_b64 helper, jq-based JSON write, deterministic ERR exit
  • uninstall.sh: symlink unlink path + APP_DIR bundle removal
  • tests: container-install systemd skip; partial-install-rollback covers symlink + bundle
  • Makefile + README Development section
  • MIT LICENSE

Fixes (post-review pass)

  • probes.ts: gateway health probe hits /__gateway/healthz (was /healthz, always false-negative)
  • probes.ts: gateway port now resolved from PAI_ANYWHERE_GATEWAY_PORT instead of hardcoded 8787
  • scripts/collect-diagnostics.sh: reads install-manifest.jsonl (was .json)
  • scripts/vps-smoke.sh: dropped calls to non-existent cli.ts install/rollback; apply→install.sh, rollback→uninstall.sh --rollback; removed unused run_allow_status helper
  • install.sh: shell var renamed PAI_ANYWHERE_SESSION_TTL_SECONDS to match systemd Environment + server reader; server default unified to 86400s
  • tests/manifest-completeness.sh: replaced broken find -type f symmetric-diff with per-entry path-existence check (manifest records files and directories)
  • uninstall.sh: tac → POSIX awk reverse (busybox compatibility)
  • uninstall.sh: rollback mode (called from install.sh ERR trap) now preserves pai user + /home/pai — no longer nukes the account on mid-install failure
  • server.ts: escapeHtml adds '&#39; (defense in depth)
  • diagnostics redaction: removed blanket [0-9]{6,} (was eating timestamps, ports, sizes; pairing codes are base64url and never matched)
  • helper dedup: extracted run/cmdExists/firstLine/readFileSafe/safeJson/getListeners/isLoopback/safeStat/safeLstat/safeRealpath/readDirSafe from inspect.ts + probes.ts into src/lib/sys.ts
  • docs: install --dry-run claim deferred to v0.3 (installer is idempotent bash with explicit phases)

Full Changelog: v0.1.1...v0.2.0

v0.1.1: SSH-alias workflow + repo deslop

03 May 13:17

Choose a tag to compare

Highlights

Type pai from any device → same VPS, same memory, same auth.

The installer now prints a copy-pasteable SSH alias for Desktop/Laptop. One PAI install on the VPS; clients are thin SSH aliases. No sync, no per-device PAI install.

User-facing

  • install.sh end-screen prints a ready-to-paste SSH alias:
    alias pai='ssh you@your-vps.tailnet.ts.net -t "sudo -iu pai -- pai"'
  • README.md rewritten community-first: ASCII architecture diagram, 5-step quickstart, daily-use, security table, FAQ.
  • docs/QUICKSTART.md adds Connect from Desktop / Laptop / Mobile section.

Cleanup (deslop pass)

  • Delete unused systemd/*.tmpl (3 files; install.sh writes units via heredoc, templates were never read).
  • Drop dead PAI_ANYWHERE_PULSE_ALLOW_PATHS env-var references — gateway proxies all paths to Pulse on 127.0.0.1:31337; the allowlist knob was removed but docs still mentioned it.
  • Replace fictional Architecture Sketch in CLAUDE.md (referenced src/install/, src/rollback/, src/verify/ trees that were never built) with the real source tree.
  • Drop 4 dead gap.md references and ghost CLI subcommand mentions from CLAUDE.md.

Repo hygiene

  • .gitignore excludes .omc/{sessions,state,skills} + .omc/project-memory.json (runtime/session noise).
  • Untracks previously-committed OMC session/state files; durable artifacts (.omc/{plans,handoffs,artifacts,research}) stay tracked.

Verification

  • bun test 29/29 pass
  • bunx tsc --noEmit clean
  • shellcheck -S warning install.sh uninstall.sh scripts/*.sh tests/*.sh clean

Install

curl -fsSL https://raw.githubusercontent.com/gl0bal01/pai-anywhere/v0.1.1/install.sh | sudo bash

Upgrading from v0.1.0

No breaking changes. Re-run the installer; manifest tracks all changes for clean rollback.

Full changelog: v0.1.0...v0.1.1