Releases: gl0bal01/pai-anywhere
v0.2.2 — security hardening pass
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) viauserdel -rwhile printing that it preserved the data. Home deletion now requires--purge-datawith a typed confirmation. Tailscale Serve cleanup no longer runs a globalserve resetthat 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), safereset-accessrotation (aborts before writing if the managed user is unresolved; loud on restart failure),Serverversion header stripped, HSTS added, tightened cookie/pairing validation. - Install — pinned
@anthropic-ai/claude-code@2.1.183and verified the Tailscale apt signing-key fingerprint (both were previously unverified code-execution paths); tty-gated pairing-code display (no leak tocurl|bash/tee/CI logs); stopped writing the Tailscale auth URL to world-readable/tmp; single-flightflock; scoped ERR→rollback trap; and self-bootstrap so the advertisedcurl … | sudo bashpaste 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.omcworking artifacts; scrubbed local-path topology fromCLAUDE.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 bashv0.2.1 — security patch: gateway SSRF + reset-access ownership
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 throughnew 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-accessleft rotated secrets root-owned.reset-accessruns as root and wrotegateway-secrets.jsonwithout re-chowning it to the managed user. The gateway (User=pai) then crash-looped onEACCESat startup, so credential rotation broke access until a manualchown. Now chownsgateway-secrets.jsonto the managed user andgateway.envtoroot:<group>, matchinginstall.sh. - LOW — request body caps were header-only.
MAX_PAIRING_BODY_BYTES/MAX_PULSE_BODY_BYTEStrustedContent-Length; omitting/spoofing it bypassed the cap. Caps are now enforced on the actual buffered bytes. - LOW — unbounded uninstall path allowlist.
is_allowed_pathmatched"${prefix}"*, so/home/paialso matched siblings like/home/pai-evil. Now bounded to an exact match or a/segment boundary. - INFO — header hygiene. Client-forged
x-forwarded-*/forwardedheaders 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
Breaking
- preflight refuses non-systemd hosts; minimal containers no longer install
gateway-secrets.jsonreplacessession-secret.txt+PAI_ANYWHERE_SESSION_SECRETenv (single source of truth on disk)
Highlights
install.sh:url_safe_b64helper,jq-based JSON write, deterministic ERR exituninstall.sh: symlink unlink path +APP_DIRbundle removal- tests:
container-installsystemd skip;partial-install-rollbackcovers 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 fromPAI_ANYWHERE_GATEWAY_PORTinstead of hardcoded8787scripts/collect-diagnostics.sh: readsinstall-manifest.jsonl(was.json)scripts/vps-smoke.sh: dropped calls to non-existentcli.ts install/rollback; apply→install.sh, rollback→uninstall.sh --rollback; removed unusedrun_allow_statushelperinstall.sh: shell var renamedPAI_ANYWHERE_SESSION_TTL_SECONDSto match systemd Environment + server reader; server default unified to 86400stests/manifest-completeness.sh: replaced brokenfind -type fsymmetric-diff with per-entry path-existence check (manifest records files and directories)uninstall.sh:tac→ POSIXawkreverse (busybox compatibility)uninstall.sh: rollback mode (called frominstall.shERR trap) now preservespaiuser +/home/pai— no longer nukes the account on mid-install failureserver.ts:escapeHtmladds'→'(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/readDirSafefrominspect.ts+probes.tsintosrc/lib/sys.ts - docs:
install --dry-runclaim 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
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.shend-screen prints a ready-to-paste SSH alias:alias pai='ssh you@your-vps.tailnet.ts.net -t "sudo -iu pai -- pai"'
README.mdrewritten community-first: ASCII architecture diagram, 5-step quickstart, daily-use, security table, FAQ.docs/QUICKSTART.mdadds 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_PATHSenv-var references — gateway proxies all paths to Pulse on127.0.0.1:31337; the allowlist knob was removed but docs still mentioned it. - Replace fictional Architecture Sketch in
CLAUDE.md(referencedsrc/install/,src/rollback/,src/verify/trees that were never built) with the real source tree. - Drop 4 dead
gap.mdreferences and ghost CLI subcommand mentions fromCLAUDE.md.
Repo hygiene
.gitignoreexcludes.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 test29/29 passbunx tsc --noEmitcleanshellcheck -S warning install.sh uninstall.sh scripts/*.sh tests/*.shclean
Install
curl -fsSL https://raw.githubusercontent.com/gl0bal01/pai-anywhere/v0.1.1/install.sh | sudo bashUpgrading 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