# ADR 0116: A selected Unix transport never becomes TCP

> Source: https://github.com/max-listov/stitchkit/blob/master/docs/decisions/0116-a-selected-unix-transport-never-becomes-tcp.md

---

# ADR 0116 — A selected Unix transport never becomes TCP

## Context

`createHttpClient({ unix })` passed a Bun-specific option into `fetch`. On Bun
that selected a socket file; elsewhere the option was not a portable Fetch
contract and the same call could reach `baseUrl` over TCP. A deployment boundary
must not depend on a runtime silently understanding an extension.

The transport also owns work after response headers arrive. Bounding only the
JSON parser leaves unread response bytes, open sockets and ambiguous delivery
outside the abstraction.

## Decision

`createUnixClientTransport({ socketPath })` is the explicit Fetch-compatible
adapter exported by `stitchkit/server` and `stitchkit/node`. The typed client uses
it through the existing `HttpClientConfig.fetch` seam:

```ts
const transport = createUnixClientTransport({ socketPath: '/run/worker.sock' })
const http = createHttpClient({ baseUrl: 'http://worker', fetch: transport.fetch })
```

The path is absolute, every redirect remains on that path, requests and
responses have byte ceilings, response headers have time and byte ceilings,
connections are finite, and `close()` owns active work. Failures report whether
dispatch did not begin, may have begun, or a response was received; the adapter
never guesses that an ambiguous operation is replay-safe.

The legacy `unix` option remains Bun-only for source compatibility. Selecting it
on another runtime now refuses before dispatch instead of using TCP, and it is
mutually exclusive with an injected `fetch`.

## Consequences

- One typed-client path works on Bun and Node without importing runtime code into
  the browser entrypoint.
- A missing socket, redirect, timeout or close cannot cross the declared
  transport boundary.
- Applications own retry policy. Delivery uncertainty is data, not an implicit
  retry or fallback.
- The adapter is process-local plumbing; it does not discover sockets, start a
  daemon or authenticate callers.
