✨ feat(dev): portless local-dev proxy — per-worktree URLs for parallel agents (U1–U6, U10)#2543
✨ feat(dev): portless local-dev proxy — per-worktree URLs for parallel agents (U1–U6, U10)#2543andrew-bierman wants to merge 9 commits into
Conversation
- portless.json maps workspace apps to stable names - scripts/portless-setup.sh: idempotent one-time host bootstrap (CA trust) - scripts/portless-check.ts: non-privileged postinstall readiness reminder - root package.json: portless:setup/portless:check scripts + portless devDep - .gitignore: .portless/
…(U3)
- web/admin: next dev --port ${PORT:-3001/3002} (portless injection wins, distinct fallback)
- api/mcp: wrangler dev --port ${PORT:-8787}
- api/mcp wrangler.jsonc: pin account_id so remote-mode dev works in non-interactive agent shells
Prints per-worktree portless URLs for all configured apps so agents discover service URLs without scraping terminal output. Encodes the U1 routing rules: worktree-aware invocation, read PORTLESS_URL, never a root proxy.
- extract ALLOWED_ORIGIN_PATTERNS/isAllowedOrigin into utils/cors-origins.ts (unit-testable) - add CORS pattern for https://<worktree>.<app>.localhost[:port] - add dev-gated better-auth trustedOrigins: https://*.localhost and https://*.localhost:* (verified against better-auth wildcardMatch semantics) - 21 unit tests covering allowed/portless/rejected origins
- centralize the 4 duplicated 'NEXT_PUBLIC_API_URL ?? localhost:8787' into getApiBaseUrl() - under portless, derive the API's sibling origin (web.localhost -> api.localhost), carrying the per-worktree prefix and port automatically (works for :443 and :1355) - explicit NEXT_PUBLIC_API_URL still wins; localhost:8787 fallback for direct runs
…U10) - docs/portless-dev-workflow.md: host setup, :443-vs-:1355, root-proxy pitfall, multi-agent workflow, service discovery, Expo (planned), recovery - portless:clean script (portless prune) for stale-process recovery
…iew) - swap the app label generically (<app>.localhost -> api.localhost) instead of hardcoding 'web.localhost' — same logic now works for admin/trails when adopted - document deferred portless API-origin parity for admin/trails/guides (U6 review finding)
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
packrat-admin | 89eeaa5 | Commit Preview URL Branch Preview URL |
Jun 01 2026, 08:39 PM |
Coverage Report for API Unit Tests Coverage (./packages/api)
File Coverage
|
||||||||||||||||||||||||||||||||||||||
Coverage Report for Expo Unit Tests Coverage (./apps/expo)
File CoverageNo changed files found. |
Summary
Adopt portless as the local-dev front door so every service gets a stable, per-worktree
.localhostURL instead of a hardcoded port — fixing the port-collision storm from 50+ parallel host-side agents trampling:3001/:8787/:8081. Built on the Turborepo migration, now merged intodevelopment(PR #2539), so this PR targetsdevelopmentdirectly.The entire browser/agent surface works end-to-end (verified live: a real app
307served through the proxy). Native (Expo) is scoped but deferred — it needs a real simulator/device to validate.Origin docs:
docs/brainstorms/2026-05-22-portless-turborepo-dev-workflow-requirements.md, plan:docs/plans/2026-06-01-001-feat-portless-turborepo-dev-workflow-plan.md.What's included (U1–U6, U10)
portless.jsonmaps apps to stable names;scripts/portless-setup.sh(idempotent, CA trust) + a non-privilegedpostinstallreadiness check.$PORTinjection + account pin.next dev --port ${PORT:-…},wrangler dev --port ${PORT:-8787}; pinnedaccount_idinwrangler.jsoncso remote-modewrangler devworks in non-interactive agent shells (the real blocker — fixed independent of portless).bun run portless:urlsprints this worktree's service URLs.https://*.localhost[:*]origins (extracted, unit-testedcors-origins.ts+ better-authtrustedOrigins). Relaxations are gated toENVIRONMENT === 'development'(z.enum().default('production')) and cannot reach prod.apps/webderives the API's sibling origin fromwindow.location(<app>.localhost→api.localhost), carrying the worktree prefix and port automatically (works for both:443and:1355). Centralizes 4 duplicated fallbacks.docs/portless-dev-workflow.md(incl. the:443vs:1355decision and the root-proxy split-brain pitfall) +portless:clean.Key operational decision:
:443vs:1355portless 0.13.1 hardcodes a sudo elevation for
:443(ignoressetcap). Default is a no-sudo user-owned:1355proxy (URLs carry:1355); clean:443needs sudo at start kept user-context (sudo -E/ passwordless rule /service install). Never run the proxy as root — a root proxy split-brains from user route registrations and 404s every backend. Documented.Testing
cors-origins.test.ts); full auth+cors suite green (39 tests).wildcardMatchimplementation.apps/webviaportless run→https://portless-packrat.web.localhost:1355/→ real307.wrangler dev --dry-runpasses account selection with the pinnedaccount_id.Deferred (not in this PR)
apps/admin/apps/trails/apps/guides(surfaced in code review) — not a regression;adminrequires its env var (no default),trailsdefaults to prod, so each needs its own care via a sharedresolveApiBaseUrlextracted to@packrat/api-client.apps/landingneeds nothing.Known Residuals (accepted from review)
Post-Deploy Monitoring & Validation
No additional operational monitoring required — this is local-dev tooling. The only runtime-touching change is the API CORS/
trustedOriginsallowlist, and the new entries are gated to local.localhostorigins (CORS) andENVIRONMENT === 'development'(better-auth), so production auth behavior is unchanged. Sanity check after merge: confirm prod login still rejects an untrusted origin (covered by the prod-origin unit test).🤖 Compound-engineered with Claude Code (Opus 4.8) via ce-brainstorm → ce-plan → ce-work.