Product Decisions This Supports
- Build vs. buy for developer ingress/tunneling. Expose is an MIT-licensed PHP client (ReactPHP-based) plus a self-hostable relay — the open-source counterpart to ngrok. That converts "do we keep paying per-seat for tunnels?" into a real decision: licence savings versus the engineering, patching, and on-call cost of owning a relay. The defensible framing is own the relay only if data-handling, customer contracts, or egress rules require it; otherwise buy and redeploy the delta.
- Scoping the "let me show you what I'm building" roadmap items. Preview links, stakeholder demos, design review, QA handoff, partner previews, and field-debugging sessions all need the same primitive: a temporary public URL pointed at a non-production build. Each can be estimated against a tunnel instead of a staging deploy — which changes cost, lead time, and the scope of security review for those items. The built-in request inspector (live logs, request/response inspection, replay) is the differentiator: it collapses a bug-report or demo loop from a deploy cycle to a URL.
- Costing integration work on the roadmap. The canonical use case is validating inbound callbacks from Stripe, GitHub, Slack, Shopify, or an OAuth provider against a local Laravel app. A permanent shared staging ingress per provider is slower and more expensive than a tunnel plus test-mode credentials. Book this as a developer-throughput line item, not an architecture change.
- Setting support and field-debugging policy. Short-lived, revocable public access to a developer's local instance is an alternative to screen-sharing or shipping debug builds during triage. Whether that is an approved support pattern is a security-policy decision first and a tooling decision second — but it belongs on the PM's list because it moves support SLAs and data-handling posture.
- A quality signal for host-dependent features. Release 3.2.4 stopped appending the default port to the
Host header — protocol-correct per RFC 7230/9110, but it changes what the app observes on 80/443. That touches virtual-host routing behind proxies, TrustHosts/host allowlists, tenant resolution from host, absolute-URL generation, and URL::signedRoute() signatures. It's a patch, not a feature, but it's evidence the maintainers fix conformance rather than merely "does it connect" — the confidence bar before a tunnel enters a test-harness path.
- Reversibility as a decision input. The client is a sidecar CLI: nothing lands in
app/, config/, service providers, or the container. Adoption and removal both cost zero application code. That makes it a cheap experiment to run alongside an open strategic decision, and a low-regret default while that decision stays open.
- A packaging/opex data point for tooling reviews. It removes a per-seat subscription and replaces it with engineering hours. Capture that explicitly — the two line items aren't comparable on a spreadsheet unless someone names the trade.
- Prioritizing the security work it exposes. Tunnel-level basic auth is not application auth. The adoption conversation is a good forcing function for "which of our routes assume they're only reachable from a trusted network?" — never tunnel a DB admin UI or queue dashboard to a public hostname, and revoke tokens when a developer leaves.
When to Consider This Package
Consider it when:
- The stack is PHP/Laravel and you want a tunnel installable via Composer, PHAR, Homebrew, or Docker — no Node or Go toolchain added to laptops or CI images.
- Compliance, customer contracts, or egress restrictions rule out third-party relays;
expose serve keeps webhook payloads and demo traffic inside infrastructure you control.
- The dominant uses are webhook receiver testing, preview links, and short-lived debugging access — bursty, human-scale traffic, not production ingress.
- You want tunnel auth and access to follow the same token model as the rest of your Laravel services, so security review reasons about one model instead of two.
- You can pin versions and verify checksums, and you will write a regression test for host-based routing and signed URLs before taking the 3.2.4
Host change (keep 3.2.3 available for one cycle).
- Stable subdomains matter to the workflow. Free-tier subdomains rotate each run, and the real cost is not latency — it's re-pointing Stripe/GitHub endpoints and re-registering OAuth callbacks every morning. If a workflow is more than a one-off, budget for reserved subdomains or self-hosting.
Look elsewhere when:
- You need SLA-backed ingress, DDoS protection, or global anycast. A tunnel is not an edge; use a managed provider or your cloud load balancer for anything customer-facing.
- The org is polyglot and wants one tunnel CLI across Node/Go/Python/JVM services. Standardize on a language-agnostic tool rather than standardizing per language.
- Security review requires SSO/SCIM, audit logging, or per-request policy controls out of the box. Assume those don't exist and verify before committing.
- Nobody will own the relay. Self-hosting moves wildcard DNS, certificate renewal, patch cadence, and tunnel availability onto your team — plus subdomain allocation held in memory, so horizontal scaling needs sticky routing or a shared registry, and a relay deploy is a fleet-wide disconnect. An unstaffed "free" option is the expensive one.
- Your traffic profile is WebSocket-heavy, long-lived streaming, or large-file transfer at scale. Reverb, SSE, and long-polling generally pass through, but benchmark before you depend on it — streaming buffering is exactly where a relay becomes the bottleneck. Also note request bodies are capped for display in the inspector, so large/truncated payloads are a bad debugging medium.
- Adoption would mean putting the package in
require. Keep it a global install or PHAR; a downloaded binary on a developer laptop deserves the same provenance policy as any other executable.
How to Pitch It (Stakeholders)
To executives: This is a zero-application-code developer tool that replaces a per-seat subscription with an MIT-licensed binary. It shortens the loop on every integration and demo item on the roadmap — webhook validation, client previews, QA handoff, field debugging — from "deploy to staging" to "run one command." Because it is a sidecar process with no footprint in the codebase, it is fully reversible: the cost of trying it is a few hours, and the cost of stopping is deleting an install. The one strategic question it surfaces is whether we ever need to run the relay ourselves; default to not owning that until compliance or a customer contract forces it, because self-hosting converts a free tool into a wildcard-DNS, certificate-renewal, and on-call obligation.
To engineering: Install it globally (PHAR or Composer global), pin the version, verify checksums, and never add it to require. It gives you a public HTTPS URL for artisan serve or a Valet/Herd hostname, with stable subdomains, password protection on shared sites, and a request inspector for live traffic and replay. Use the hostname rather than the port for Valet/Herd and multi-tenant apps so Route::domain(), tenant resolution from host, and TrustHosts behave as they do locally. Before rolling it into any test harness, handle the three known sharp edges: the 3.2.4 Host-header change (regression-test host routing and signed URLs first), APP_URL still pointing at localhost for emails/redirects/signed links, and local SESSION_DOMAIN causing login loops. Run the client on the host against the published port for Sail/devcontainers, and treat tunnel basic auth as a visitor gate, never as a substitute for app authorization.