Operate Search docs

The Bridge

Tag your traffic, fork your boundary, keep everyone else live.

Bridge to Kubernetes had one great idea: develop one service on your laptop while it participates in the real deployed environment — your requests detour to your machine, everyone else’s don’t. Its limits were structural: it worked by intercepting pod traffic, so it only worked on Kubernetes.

HoBo’s version keeps the idea and drops the requirement. Every service boundary is a WIT port whose peers are PACT-emulable, and every boundary supports per-request routing modes (live · mock · bridge · replay · shadow · fault). Forking traffic to your laptop is just one more route — applied at the WIT port boundary, not in any orchestrator — so it works identically on any provider, a mix of them, or your own metal.

The three moves

1. Tag your traffic. A dev session is a string — your branch name is the natural one. Session tags ride with requests (as baggage on wire transports; explicitly in-process). Untagged traffic can never hit a fork.

2. Bridge the boundary. Point the boundary at the implementation you’re editing, scoped to your session:

import { createBoundaryRouter } from "hobo/runtime";

const router = createBoundaryRouter();
const payments = router
.boundary<Charge, Receipt>("payments")
.live(deployedPayments); // the version running in the camp

await payments.bridge(myLocalPayments, {
 session: "tyler/feature-refunds",
 contract: paymentsPact, // the boundary's PACT interactions
});

3. Work against reality. Your requests take the bridge; the rest of the team’s — and production’s — stay live:

await payments.call(charge, { session: "tyler/feature-refunds" }); // → your laptop
await payments.call(charge); // → the deployed camp

Tear it down with payments.unbridge("tyler/feature-refunds"). A bridge with no session forks the whole boundary — the classic “route this service to me” mode — and unbridge() restores it.

The part Bridge-to-Kubernetes never had

A bridge is contract-gated. Before your local implementation carries a single request, it’s verified against the boundary’s contract — the PACT interactions every implementation of that boundary must satisfy, using the same subset matching as provider verification. A local build that would break consumers never activates; the error tells you which interactions failed, and your session’s traffic keeps flowing to the live route:

await payments.bridge(brokenLocalBuild, {
 session: "tyler/feature-refunds",
 contract: paymentsPact,
});
// BridgeContractError: bridged implementation for boundary "payments"
// fails its contract (1 interaction): for input {"amount":42,…}
// expected {"status":"charged",…} ⊆ actual, got {"status":"declined",…}

And because recording is always on, every call your bridged session serves is captured as a specification-by-example trace — your dev session’s traffic becomes tomorrow’s contract candidates and regression inputs.

Composing with preview environments

A dev branch’s preview environment is a camp with your branch’s session tag pre-applied. The two compose naturally:

  • Preview env alone — the whole branch deployed, isolated, shareable.
  • Preview env + bridge — the branch deployed except the one service you’re actively editing, which detours to your laptop. Save a file, hit the endpoint, no redeploy.
  • Shared env + bridge — the lightest loop: nothing deployed at all for your branch; just your session forked out of the shared environment.

The rest of the modes

The bridge is one routing mode of six, and they compose per boundary:

ModeWhat it doesUse for
live(handler)the deployed routenormal traffic
mock(examples)serve PACT examples, no dependencyunit tests, offline dev
bridge(handler, { session })session-scoped forklocal/remote dev
replay()serve recorded outputsdeterministic re-runs
shadow(candidate)duplicate traffic, compare, discardtesting the next version on real load
fault({ message, session })scoped failure injectionchaos with no blast radius

router.status() reports every boundary’s routing state — who’s bridged, what’s shadowed, which faults are armed — so “what is my environment actually doing right now” is one call.

Honest status

The boundary router above ships today with in-process session tagging, and its tests are the executable example of everything on this page. Session baggage on wire transports, the hobo bridge CLI verb (open tunnel, register fork, stream contract verification), automatic pact-broker → bridge-contract conversion, and branch-tagged preview environments are staged next — the design is pinned even where the plumbing isn’t.

  • Testing — the same modes, pointed at tests.
  • Core concepts — WIT/WASM ports the routing lives in.
  • Custom providers — why boundaries are provider-agnostic in the first place.