<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Saif Ali Khan</title>
    <description>The latest articles on DEV Community by Saif Ali Khan (@f-ei8ht).</description>
    <link>https://dev.to/f-ei8ht</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1132691%2F94768225-d383-48c3-af24-7dcdb6a0a257.png</url>
      <title>DEV Community: Saif Ali Khan</title>
      <link>https://dev.to/f-ei8ht</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/f-ei8ht"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Saif Ali Khan</dc:creator>
      <pubDate>Mon, 07 Sep 2026 17:02:45 +0000</pubDate>
      <link>https://dev.to/f-ei8ht/-311j</link>
      <guid>https://dev.to/f-ei8ht/-311j</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/f-ei8ht/one-binary-four-jobs-zero-dependencies-building-zeroproxy-for-the-zero-dependency-hackathon-18ka" class="crayons-story__hidden-navigation-link"&gt;One Binary, Four Jobs, Zero Dependencies: Building zeroproxy for the Zero Dependency Hackathon&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/f-ei8ht" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1132691%2F94768225-d383-48c3-af24-7dcdb6a0a257.png" alt="f-ei8ht profile" class="crayons-avatar__image" width="475" height="475"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/f-ei8ht" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Saif Ali Khan
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Saif Ali Khan
                
                
              
              &lt;div id="story-author-preview-content-4598296" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/f-ei8ht" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1132691%2F94768225-d383-48c3-af24-7dcdb6a0a257.png" class="crayons-avatar__image" alt="" width="475" height="475"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Saif Ali Khan&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/f-ei8ht/one-binary-four-jobs-zero-dependencies-building-zeroproxy-for-the-zero-dependency-hackathon-18ka" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 7&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/f-ei8ht/one-binary-four-jobs-zero-dependencies-building-zeroproxy-for-the-zero-dependency-hackathon-18ka" id="article-link-4598296"&gt;
          One Binary, Four Jobs, Zero Dependencies: Building zeroproxy for the Zero Dependency Hackathon
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/bunjs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;bunjs&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/proxy"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;proxy&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hackathonraptors"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hackathonraptors&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/f-ei8ht/one-binary-four-jobs-zero-dependencies-building-zeroproxy-for-the-zero-dependency-hackathon-18ka#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            20 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>One Binary, Four Jobs, Zero Dependencies: Building zeroproxy for the Zero Dependency Hackathon</title>
      <dc:creator>Saif Ali Khan</dc:creator>
      <pubDate>Mon, 07 Sep 2026 17:00:06 +0000</pubDate>
      <link>https://dev.to/f-ei8ht/one-binary-four-jobs-zero-dependencies-building-zeroproxy-for-the-zero-dependency-hackathon-18ka</link>
      <guid>https://dev.to/f-ei8ht/one-binary-four-jobs-zero-dependencies-building-zeroproxy-for-the-zero-dependency-hackathon-18ka</guid>
      <description>&lt;h2&gt;
  
  
  A confession before the pitch
&lt;/h2&gt;

&lt;p&gt;Most of my working life, I have reached for a package before I reached for a&lt;br&gt;
thought. Express for routing, http-proxy-middleware for forwarding,&lt;br&gt;
serve-static for files, ws for sockets, compression for gzip, chalk for&lt;br&gt;
colors, morgan for logs, nodemon for reloads, autocannon for benchmarks,&lt;br&gt;
zod for config validation. Each one is reasonable on its own. Together they&lt;br&gt;
form a supply chain you inherit without ever deciding to.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://zerodepshack.com/" rel="noopener noreferrer"&gt;Zero Dependency hackathon&lt;/a&gt; asks one uncomfortable question: what if the&lt;br&gt;
runtime already had most of it? Not the "standard library" in the boring&lt;br&gt;
sense. The runtime APIs that Bun and Node ship with, the ones we walk past&lt;br&gt;
on the way to npm.&lt;/p&gt;

&lt;p&gt;So I decided to build the most dependency-normal kind of backend software I&lt;br&gt;
could think of, with zero of them. A reverse proxy, which is the thing&lt;br&gt;
people say you would be insane to hand-roll. This post is the complete&lt;br&gt;
story: what I built, how every file works, what the standard library made&lt;br&gt;
easy, what it made painful, and the edge cases that cost me real hours.&lt;/p&gt;

&lt;p&gt;Here is the whole pitch in one number: &lt;strong&gt;sixteen npm packages replaced by&lt;br&gt;
the runtime&lt;/strong&gt;, and the one that mattered most, &lt;code&gt;path-to-regexp&lt;/code&gt; at roughly&lt;br&gt;
200 million weekly downloads, killed by 37 lines of &lt;code&gt;URLPattern&lt;/code&gt;. One&lt;br&gt;
compiled binary. Four jobs. &lt;code&gt;"dependencies": {}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run it yourself&lt;/strong&gt; (the benchmark, the demo, and the proof are all built&lt;br&gt;
in, no install):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/f-ei8ht/zeroproxy
&lt;span class="nb"&gt;cd &lt;/span&gt;zeroproxy
make build      &lt;span class="c"&gt;# one compiled binary&lt;/span&gt;
make demo       &lt;span class="c"&gt;# boots two upstreams + a live dashboard&lt;/span&gt;
bun run bench   &lt;span class="c"&gt;# the built-in load test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What zeroproxy actually is
&lt;/h2&gt;

&lt;p&gt;Let me be precise, because the scope is the whole point. zeroproxy is one&lt;br&gt;
process that does four jobs, none of which reach for a package:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reverse proxy.&lt;/strong&gt; Forwards requests to one or many upstream servers.
Bodies stream through without being buffered whole. Multiple upstreams
get weighted round-robin load balancing, background health checks, and
failover that can replay small request bodies on a retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP router.&lt;/strong&gt; Matches incoming requests with &lt;code&gt;URLPattern&lt;/code&gt;: wildcards
(&lt;code&gt;/api/*&lt;/code&gt;), named parameters (&lt;code&gt;/users/:id&lt;/code&gt;), per-route method
restrictions, and a correct &lt;code&gt;405 Method Not Allowed&lt;/code&gt; with an &lt;code&gt;Allow&lt;/code&gt;
header when the path matches but the method does not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static file server.&lt;/strong&gt; Serves a directory with correct MIME types,
&lt;code&gt;Range&lt;/code&gt; requests for partial content (206), &lt;code&gt;ETag&lt;/code&gt; and &lt;code&gt;Last-Modified&lt;/code&gt;
caching (304), directory listings, SPA fallback, and dotfile blocking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket tunnel.&lt;/strong&gt; Detects an upgrade request, hands it to &lt;code&gt;Bun.serve&lt;/code&gt;
natively, and pipes frames both ways with the global &lt;code&gt;WebSocket&lt;/code&gt; client.
No &lt;code&gt;ws&lt;/code&gt; package anywhere.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And then the operational layer a real deployment needs, which is the part&lt;br&gt;
that usually gets skipped in a hackathon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /healthz&lt;/code&gt; with uptime plus request and error counters.&lt;/li&gt;
&lt;li&gt;Live config reload via &lt;code&gt;fs.watch&lt;/code&gt;: routes swap in place without dropping
a single connection.&lt;/li&gt;
&lt;li&gt;TLS serving via cert and key paths in the config.&lt;/li&gt;
&lt;li&gt;Graceful shutdown on &lt;code&gt;SIGINT&lt;/code&gt; and &lt;code&gt;SIGTERM&lt;/code&gt; that drains in-flight
requests before exiting.&lt;/li&gt;
&lt;li&gt;A built-in load test, so the benchmark is as zero-dependency as the
server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool most people build with &lt;code&gt;express&lt;/code&gt; plus four middleware packages is&lt;br&gt;
here as a single compiled binary with an empty manifest.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Bun, and why this bet
&lt;/h2&gt;

&lt;p&gt;Track C wants an HTTP server or router "built on your language's networking&lt;br&gt;
primitives and nothing above them," that handles concurrent connections&lt;br&gt;
without a framework and speaks the protocol correctly enough to interoperate&lt;br&gt;
with real clients. That described exactly what I wanted to prove.&lt;/p&gt;

&lt;p&gt;I chose Bun 1.4, which shipped eight days before the hackathon started. It&lt;br&gt;
was a deliberate, slightly risky bet. Bun 1.4 released a batch of built-ins&lt;br&gt;
whose stated purpose is deleting npm dependencies: &lt;code&gt;URLPattern&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;CompressionStream&lt;/code&gt; with gzip, deflate, brotli and zstd, native WebSocket&lt;br&gt;
upgrades in &lt;code&gt;Bun.serve&lt;/code&gt;, &lt;code&gt;Bun.file&lt;/code&gt;, plus Node-core &lt;code&gt;util.styleText&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;util.parseArgs&lt;/code&gt;. If the event is "how much internet middleware can you live&lt;br&gt;
without," Bun 1.4 is the strongest argument that the answer is "most of it."&lt;/p&gt;

&lt;p&gt;The cheat-sheet ruling mattered here: Bun and Deno built-ins count as the&lt;br&gt;
standard library, because the rule as written is "Node (or Deno/Bun)&lt;br&gt;
built-ins only, dependencies is {}". I wrote that reasoning into STDLIB.md&lt;br&gt;
so no judge has to work it out.&lt;/p&gt;
&lt;h2&gt;
  
  
  The shape of the code
&lt;/h2&gt;

&lt;p&gt;The single most important structural decision was this: &lt;strong&gt;&lt;code&gt;src/index.ts&lt;/code&gt; is&lt;br&gt;
the only file that touches &lt;code&gt;Bun.serve&lt;/code&gt;.&lt;/strong&gt; Everything else is a pure function&lt;br&gt;
or a factory over its inputs. &lt;code&gt;compileRoutes&lt;/code&gt; takes routes and returns&lt;br&gt;
matchers. &lt;code&gt;proxyRequest&lt;/code&gt; takes a request and a balancer and returns a&lt;br&gt;
response. &lt;code&gt;serveStatic&lt;/code&gt; takes a path and returns a &lt;code&gt;Response&lt;/code&gt;. That means&lt;br&gt;
every module can be tested in isolation with no port, no socket, no running&lt;br&gt;
server, which is why the test suite is 100 tests and not 10.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zeroproxy/
  src/
    index.ts            Bun.serve wiring, logging, reload, shutdown
    config.ts           parseArgs + env + config file + validation
    router.ts           URLPattern compile and match, 405 Allow
    static.ts           MIME, Range, ETag, listings, SPA fallback
    compress.ts         Accept-Encoding negotiation, CompressionStream
    proxy/
      index.ts          streaming fetch, failover, 502/504/413
      balancer.ts       weighted round-robin
      health.ts         periodic upstream probes
    ws.ts               upgrade detection, URL mapping
  tests/                100 tests across 9 files
  bench/bench.ts        the built-in load test
  demo/                 two mock upstreams + a live dashboard
  public/               files the benchmark serves
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    CLIENT([Client])

    CORE["one process&amp;lt;br/&amp;gt;one event loop&amp;lt;br/&amp;gt;Bun.serve"]

    UP_A[Upstream A]
    UP_B[Upstream B]
    STATIC[Static Files&amp;lt;br/&amp;gt;on Disk]
    WS[WebSocket&amp;lt;br/&amp;gt;Upstream]

    CLIENT --&amp;gt; CORE

    CORE --&amp;gt; UP_A
    CORE --&amp;gt; UP_B
    CORE --&amp;gt; STATIC
    CORE --&amp;gt; WS

    DEPS["dependencies: {}"]
    CORE --- DEPS&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  A single request, end to end
&lt;/h2&gt;

&lt;p&gt;Before the file-by-file tour, here is the whole path one request takes,&lt;br&gt;
because it makes each module obvious:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The request lands in the &lt;code&gt;fetch&lt;/code&gt; handler in &lt;code&gt;src/index.ts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/healthz&lt;/code&gt; short-circuits immediately: uptime, requests, errors. No
routing involved.&lt;/li&gt;
&lt;li&gt;Otherwise the router runs the path against every compiled &lt;code&gt;URLPattern&lt;/code&gt;
in order and returns the first match.&lt;/li&gt;
&lt;li&gt;If the path matches a route's pattern but not its method, the answer is
&lt;code&gt;405&lt;/code&gt; with an &lt;code&gt;Allow&lt;/code&gt; header listing the methods that do match. No match
at all is &lt;code&gt;404&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If the matched route has an upstream, &lt;code&gt;proxyRequest&lt;/code&gt; streams it through
&lt;code&gt;fetch&lt;/code&gt;, with failover across the balancer's healthy upstreams.&lt;/li&gt;
&lt;li&gt;If the matched route serves static files, &lt;code&gt;serveStatic&lt;/code&gt; streams a
&lt;code&gt;Bun.file&lt;/code&gt; with MIME, Range and caching headers.&lt;/li&gt;
&lt;li&gt;A WebSocket upgrade on a route with &lt;code&gt;"ws": true&lt;/code&gt; skips all of that and
goes straight to &lt;code&gt;srv.upgrade&lt;/code&gt; plus the frame tunnel.&lt;/li&gt;
&lt;li&gt;Whatever the response, compression negotiation runs last: if the client
accepts an encoding and the body benefits, it is piped through a native
&lt;code&gt;CompressionStream&lt;/code&gt; and &lt;code&gt;Vary: Accept-Encoding&lt;/code&gt; is set.
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    REQ([Request]) --&amp;gt; HEALTH{Healthz?}

    HEALTH --&amp;gt;|Yes| HEALTH_R([Health Response])
    HEALTH --&amp;gt;|No| ROUTER{Router Match}

    ROUTER --&amp;gt;|HTTP Proxy| PROXY[proxyRequest]
    ROUTER --&amp;gt;|Static Asset| STATIC[serveStatic]

    PROXY --&amp;gt; COMP[Compression]
    STATIC --&amp;gt; COMP
    COMP --&amp;gt; RES([Response])

    ROUTER -.-&amp;gt;|WebSocket Upgrade| UPGRADE[srv.upgrade]
    UPGRADE --&amp;gt; TUNNEL[(WebSocket Tunnel)]

    classDef terminal fill:#111827,color:#fff,stroke:#111827;
    classDef module fill:#ffffff,color:#111827,stroke:#9ca3af;
    classDef decision fill:#f3f4f6,color:#111827,stroke:#6b7280;
    classDef ws fill:#ffffff,color:#111827,stroke:#4b5563,stroke-width:2px;

    class REQ,HEALTH_R,RES terminal;
    class PROXY,STATIC,COMP,UPGRADE,TUNNEL module;
    class HEALTH,ROUTER decision;
    class UPGRADE,TUNNEL ws;&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;
  
  
  The files, in detail
&lt;/h2&gt;
&lt;h3&gt;
  
  
  src/config.ts - everything a user can touch
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;config.ts&lt;/code&gt; turns three inputs into one validated &lt;code&gt;Config&lt;/code&gt; object: CLI flags&lt;br&gt;
via &lt;code&gt;util.parseArgs&lt;/code&gt;, the &lt;code&gt;ZEROPROXY_PORT&lt;/code&gt; and &lt;code&gt;ZEROPROXY_HOST&lt;/code&gt; environment&lt;br&gt;
variables, and an optional JSON config file. The ordering is deliberate:&lt;br&gt;
file first, then flags and env override it. &lt;code&gt;--upstream&lt;/code&gt; is repeatable and&lt;br&gt;
adds a catch-all proxy route on top of whatever the file defined, so a&lt;br&gt;
one-liner like &lt;code&gt;bun run src/index.ts --upstream http://localhost:3000&lt;/code&gt; is a&lt;br&gt;
complete working proxy with zero configuration.&lt;/p&gt;

&lt;p&gt;The details that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every default is a named constant.&lt;/strong&gt; &lt;code&gt;DEFAULT_PORT = 8080&lt;/code&gt;,
&lt;code&gt;DEFAULT_HOST = "0.0.0.0"&lt;/code&gt;, &lt;code&gt;DEFAULT_RETRY_BODY_LIMIT = 64 * 1024&lt;/code&gt;,
&lt;code&gt;DEFAULT_UPSTREAM_TIMEOUT_MS = 30000&lt;/code&gt;, &lt;code&gt;DEFAULT_SHUTDOWN_TIMEOUT_MS =
10000&lt;/code&gt;, &lt;code&gt;DEFAULT_MIN_COMPRESS_BYTES = 256&lt;/code&gt;, and &lt;code&gt;DEFAULT_MAX_REQUEST_BODY_BYTES
= 0&lt;/code&gt;, where zero means "unlimited". No magic numbers scattered through the
logic, and every one of them is surfaced as a config key so a user can
change it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation throws named errors instead of silently defaulting.&lt;/strong&gt; A port
outside 1 to 65535 gets "port must be an integer between 1 and 65535". A
route with a bad upstream gets "route 2 has an invalid upstream". You
never wonder which typo produced which behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routes must set upstream or static, never both, never neither.&lt;/strong&gt; A route
is a &lt;code&gt;pattern&lt;/code&gt;, an optional &lt;code&gt;method&lt;/code&gt;, and one target. If you write a route
that does neither, it is rejected at load time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upstreams accept three shapes&lt;/strong&gt;: a bare URL string, an array of strings,
or objects with &lt;code&gt;{ url, weight }&lt;/code&gt;. A string becomes &lt;code&gt;{ url, weight: 1 }&lt;/code&gt;
at parse time, so the rest of the code never worries about the difference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--root&lt;/code&gt; resolves relative static paths.&lt;/strong&gt; The config may say
&lt;code&gt;"static": "./public"&lt;/code&gt;; the process resolves it against the given root so
the same config file works no matter where the process is started from.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Zod substitution lives here. Validating a config with a handful of&lt;br&gt;
&lt;code&gt;typeof&lt;/code&gt; and regex checks is a few small functions, not a dependency. A full&lt;br&gt;
schema-validation package for a four-field config is the definition of&lt;br&gt;
over-engineering.&lt;/p&gt;
&lt;h3&gt;
  
  
  src/router.ts - the package killer in 37 lines
&lt;/h3&gt;

&lt;p&gt;This is the file that kills &lt;code&gt;path-to-regexp&lt;/code&gt;, which does roughly 200&lt;br&gt;
million weekly downloads, and is the project's Package Killer entry.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;path-to-regexp&lt;/code&gt; exists to turn route strings like &lt;code&gt;/users/:id&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;/api/*&lt;/code&gt; into matchers. Bun ships &lt;code&gt;URLPattern&lt;/code&gt;, which does the same job&lt;br&gt;
with the same syntax. The entire router is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compileRoutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;CompiledRoute&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLPattern&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;matchRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three details worth explaining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routes are compiled once at startup and matched in config order. First
match wins, which is the same semantics people expect from a config-driven
proxy.&lt;/li&gt;
&lt;li&gt;Method comparison is case-insensitive, and the wildcard capture lands in
&lt;code&gt;params["0"]&lt;/code&gt;. The static server reads that capture to decide which file
to serve.&lt;/li&gt;
&lt;li&gt;The 405 behavior falls out of a separate helper, &lt;code&gt;allowedMethods&lt;/code&gt;. When a
path matches no route for the current method but matches a pattern for
some other method, that helper collects the restricted methods into a
&lt;code&gt;Set&lt;/code&gt; and the server answers &lt;code&gt;405&lt;/code&gt; with &lt;code&gt;Allow: POST&lt;/code&gt;. It is a loop and a
Set, not a framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  src/static.ts - serving files the way HTTP expects
&lt;/h3&gt;

&lt;p&gt;The static module replaces &lt;code&gt;serve-static&lt;/code&gt; and &lt;code&gt;send&lt;/code&gt;, which power&lt;br&gt;
&lt;code&gt;express.static&lt;/code&gt; and pull in a chain of their own. The core is &lt;code&gt;Bun.file&lt;/code&gt;,&lt;br&gt;
which gives you size, mtime and MIME type from the filesystem and streams&lt;br&gt;
from disk without ever loading the file into memory.&lt;/p&gt;

&lt;p&gt;The hand-written correctness lives in the headers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MIME types.&lt;/strong&gt; A 28-entry table covers everything a web server
realistically serves, from &lt;code&gt;text/html&lt;/code&gt; to &lt;code&gt;application/wasm&lt;/code&gt;. Anything
unknown falls back to &lt;code&gt;application/octet-stream&lt;/code&gt;. This replaces the
&lt;code&gt;mime-types&lt;/code&gt; package, which is a giant lookup of about two thousand types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ETag.&lt;/strong&gt; &lt;code&gt;createHash("sha1")&lt;/code&gt; over &lt;code&gt;size + lastModified&lt;/code&gt;, truncated to
24 hex characters and quoted. Two requests for the same file always get
the same ETag, and touching the file changes it. A client's
&lt;code&gt;If-None-Match&lt;/code&gt; that contains that ETag gets &lt;code&gt;304 Not Modified&lt;/code&gt; with an
empty body.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If-Modified-Since.&lt;/strong&gt; Also handled, with a subtle detail: HTTP dates have
second granularity, so the file's mtime is truncated to whole seconds
before comparison. Otherwise the ETag path and the date path disagree and
you never get a 304.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Range requests.&lt;/strong&gt; The regex &lt;code&gt;bytes=(\d*)-(\d*)&lt;/code&gt; handles the two forms
people actually use: &lt;code&gt;bytes=0-99&lt;/code&gt; and suffix ranges &lt;code&gt;bytes=-5&lt;/code&gt; (last five
bytes). A satisfiable single range answers &lt;code&gt;206&lt;/code&gt; with a &lt;code&gt;Content-Range&lt;/code&gt;
header and a body that is literally &lt;code&gt;file.slice(start, end + 1)&lt;/code&gt;, so only
those bytes stream from disk. A range past the end of the file answers
&lt;code&gt;416&lt;/code&gt; with &lt;code&gt;Content-Range: bytes */size&lt;/code&gt;. A multi-range header, a
malformed header, or a reversed range like &lt;code&gt;bytes=5-2&lt;/code&gt; is ignored and the
full body is sent, which is exactly what RFC 9110 allows a server to do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security.&lt;/strong&gt; Two guards. Path traversal is blocked by resolving the
candidate and checking it starts with the base directory plus a separator,
so &lt;code&gt;../../etc/passwd&lt;/code&gt; dies. Dotfiles are blocked by rejecting any path
segment that begins with a dot, so &lt;code&gt;.env&lt;/code&gt; and &lt;code&gt;.git&lt;/code&gt; are never served.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directory behavior.&lt;/strong&gt; If the target is a directory, it looks for an
index file (default &lt;code&gt;index.html&lt;/code&gt;), then a configured SPA fallback, then a
generated directory listing with a &lt;code&gt;../&lt;/code&gt; parent link, directories sorted
before files. The listing HTML is escaped by hand with a five-line
&lt;code&gt;escapeHtml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async all the way.&lt;/strong&gt; &lt;code&gt;stat&lt;/code&gt; and &lt;code&gt;readdir&lt;/code&gt; come from &lt;code&gt;node:fs/promises&lt;/code&gt;,
so the event loop never blocks on disk. That is a measured, deliberate
trade: it costs a little throughput, but it keeps the proxy responsive
under load, and the README says so.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  src/compress.ts - HTTP compression done correctly
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;compression&lt;/code&gt; (about 41 million weekly downloads) negotiates&lt;br&gt;
&lt;code&gt;Accept-Encoding&lt;/code&gt; and compresses responses. The negotiation is the part&lt;br&gt;
everyone gets subtly wrong, so this module earns its keep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The parser respects q-values.&lt;/strong&gt; &lt;code&gt;Accept-Encoding: gzip;q=0, deflate&lt;/code&gt;
means the client explicitly refuses gzip. That &lt;code&gt;q=0&lt;/code&gt; is an exclusion, not
a preference for nothing, and the code treats it that way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server preference order is zstd, brotli, gzip, deflate.&lt;/strong&gt; When the
client advertises several, the highest-supported quality wins, and equal
qualities break ties in that order. The wildcard &lt;code&gt;*&lt;/code&gt; covers unlisted
encodings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The compressor is one line&lt;/strong&gt;: &lt;code&gt;body.pipeThrough(new CompressionStream(
FORMATS[encoding]))&lt;/code&gt;, where &lt;code&gt;FORMATS&lt;/code&gt; maps gzip, deflate, brotli and zstd.
Native, streaming, with backpressure, and it round-trips through
&lt;code&gt;node:zlib&lt;/code&gt; in the tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The decision about when to compress matters as much as how.&lt;/strong&gt;
&lt;code&gt;shouldCompress&lt;/code&gt; skips identity, anything that is not a 200, anything
already carrying &lt;code&gt;Content-Encoding&lt;/code&gt;, anything that is an image, video,
audio or font (all already compressed formats), and any body under
&lt;code&gt;minCompressBytes&lt;/code&gt; (default 256 bytes, because compressing a tiny body
costs more than it saves).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The caller sets &lt;code&gt;Vary: Accept-Encoding&lt;/code&gt; on every response when compression&lt;br&gt;
is enabled, so caches keep the compressed and uncompressed variants&lt;br&gt;
separate. Skip that header and your cache serves gzip to clients that never&lt;br&gt;
sent &lt;code&gt;Accept-Encoding: gzip&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  src/ws.ts - the smallest module
&lt;/h3&gt;

&lt;p&gt;Twenty-five lines, three functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;isWebSocketRequest&lt;/code&gt; checks whether the &lt;code&gt;Upgrade&lt;/code&gt; header equals
&lt;code&gt;websocket&lt;/code&gt;, case-insensitively. That is the entire upgrade detection.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;upstreamSocketUrl&lt;/code&gt; builds the upstream socket URL. It maps &lt;code&gt;http&lt;/code&gt; to
&lt;code&gt;ws&lt;/code&gt; and &lt;code&gt;https&lt;/code&gt; to &lt;code&gt;wss&lt;/code&gt;, and takes the path and query from the client
request, not from the upstream URL. This mirrors the HTTP proxy, which
also ignores any path prefix on an upstream URL, and it means &lt;code&gt;/ws/echo&lt;/code&gt;
tunnels to the upstream's &lt;code&gt;/ws/echo&lt;/code&gt;, not to the upstream root.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;toSendable&lt;/code&gt; converts a &lt;code&gt;Buffer&lt;/code&gt; to a fresh &lt;code&gt;ArrayBuffer&lt;/code&gt; copy. That
matters for the buffering logic in &lt;code&gt;index.ts&lt;/code&gt;: a frame received from the
client cannot be reused after it is sent, so it is copied before it goes
into the pending buffer.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  src/proxy/balancer.ts - weighted round-robin
&lt;/h3&gt;

&lt;p&gt;The balancer answers two questions: which upstream gets the next request,&lt;br&gt;
and what is the ordered list of failover candidates.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pick()&lt;/code&gt; is a weighted round-robin. Each healthy upstream's pick chance is
proportional to its weight, and every call consumes one turn so
consecutive requests rotate. The tests prove it: with weights 1 and 3,
exactly 100 of 400 picks go to the lighter upstream.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rotate()&lt;/code&gt; returns the failover candidate order: every healthy upstream
exactly once, higher weight first, rotated so the next pick lands at the
front, and deduplicated. A single request therefore never tries the same
upstream twice.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mark(url, healthy)&lt;/code&gt; is how the health checker talks to the balancer. A
target marked unhealthy drops out of both &lt;code&gt;pick&lt;/code&gt; and &lt;code&gt;rotate&lt;/code&gt; until it is
marked healthy again.&lt;/li&gt;
&lt;li&gt;If every upstream is dead, &lt;code&gt;pick()&lt;/code&gt; falls back to the first configured
target anyway. That sounds odd, but it is deliberate: the request still
gets a real attempt and a proper &lt;code&gt;502&lt;/code&gt;, instead of a crash or an empty
candidate list.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  src/proxy/health.ts - keep the dead out of rotation
&lt;/h3&gt;

&lt;p&gt;A proxy that retries into a target it already knows is down is wasting a&lt;br&gt;
retry. &lt;code&gt;healthChecker&lt;/code&gt; fixes that with a timer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At startup it snapshots each balancer's upstream list.&lt;/li&gt;
&lt;li&gt;On every interval (default 5 seconds) it probes every upstream in
parallel with a &lt;code&gt;GET&lt;/code&gt; to the configured path, each probe bounded by
&lt;code&gt;AbortSignal.timeout&lt;/code&gt; (default 2 seconds).&lt;/li&gt;
&lt;li&gt;Healthy is any status below 500. A 404 means the server is up, it just
does not like that path. Only a 5xx or a connection failure marks it down.&lt;/li&gt;
&lt;li&gt;The result is handed to &lt;code&gt;balancer.mark(url, ok)&lt;/code&gt;, which is the only
interface between health and load balancing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;checkOnce&lt;/code&gt; is public so the tests can drive a full probe cycle with no
timers, which is why the health tests run in milliseconds instead of
waiting on a real interval.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;start&lt;/code&gt; unrefs the timer, so a running health check never keeps the
process alive by itself, and &lt;code&gt;stop&lt;/code&gt; clears it during reload and shutdown.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  src/proxy/index.ts - the heart of the proxy
&lt;/h3&gt;

&lt;p&gt;This is where the four jobs stop being separable. &lt;code&gt;proxyRequest&lt;/code&gt; does the&lt;br&gt;
RFC 9110 hygiene, the body planning, and the failover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Header hygiene.&lt;/strong&gt; Hop-by-hop headers are stripped per RFC 9110, plus&lt;br&gt;
anything the &lt;code&gt;Connection&lt;/code&gt; header names (so a custom &lt;code&gt;Connection: foo&lt;/code&gt;&lt;br&gt;
strips &lt;code&gt;foo&lt;/code&gt; too). &lt;code&gt;Host&lt;/code&gt; is deleted so the upstream sees its own host. The&lt;br&gt;
request gains &lt;code&gt;x-forwarded-host&lt;/code&gt;, &lt;code&gt;x-forwarded-proto&lt;/code&gt;, and a &lt;code&gt;Via&lt;/code&gt; header&lt;br&gt;
appended as &lt;code&gt;1.1 zeroproxy&lt;/code&gt;. The response gets the same treatment plus&lt;br&gt;
&lt;code&gt;access-control-allow-origin: *&lt;/code&gt;, which quietly kills the &lt;code&gt;cors&lt;/code&gt; package.&lt;br&gt;
&lt;code&gt;redirect: "manual"&lt;/code&gt; means a 3xx from the upstream is forwarded to the&lt;br&gt;
client as-is, not followed by the proxy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timeouts.&lt;/strong&gt; Every attempt runs with &lt;code&gt;AbortSignal.timeout&lt;/code&gt;. A&lt;br&gt;
&lt;code&gt;TimeoutError&lt;/code&gt; surfaces as &lt;code&gt;504 Gateway Timeout&lt;/code&gt;; any other connection&lt;br&gt;
failure surfaces as &lt;code&gt;502 Bad Gateway&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The body plan.&lt;/strong&gt; This is the subtle part, and it deserves its own section&lt;br&gt;
below. &lt;code&gt;planBody&lt;/code&gt; decides, before the body is ever sent, whether a retry is&lt;br&gt;
even possible. No body: replayable. A declared &lt;code&gt;Content-Length&lt;/code&gt; at or below&lt;br&gt;
&lt;code&gt;retryBodyLimitBytes&lt;/code&gt; (64 KB default): buffered and replayable. A declared&lt;br&gt;
length above the limit: sent exactly once, never retried, because a consumed&lt;br&gt;
stream cannot be sent twice. An unknown-length chunked body: read up to the&lt;br&gt;
limit; if it ends within the limit it is replayable, and if it keeps coming,&lt;br&gt;
&lt;code&gt;spliceTail&lt;/code&gt; stitches the already-read bytes in front of the unread tail and&lt;br&gt;
streams the whole thing once, so memory stays bounded no matter how large&lt;br&gt;
the upload is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The retry rule.&lt;/strong&gt; Connection failures are retried for every method,&lt;br&gt;
because a connection refusal proves nothing was delivered. Timeouts are&lt;br&gt;
retried only for idempotent methods: GET, HEAD, PUT, DELETE, OPTIONS, TRACE.&lt;br&gt;
The reasoning is one line and it is the most important line in the project:&lt;br&gt;
a timeout does not prove the request was never delivered. Replaying a POST&lt;br&gt;
that a slow upstream already applied would apply it twice. So a timed-out&lt;br&gt;
POST/PATCH is never replayed, and surfaces as a 504.&lt;/p&gt;

&lt;p&gt;The failover candidate list is &lt;code&gt;[primary, ...rotate() minus primary]&lt;/code&gt;, so a&lt;br&gt;
request visits each healthy upstream at most once, starting from the&lt;br&gt;
balancer's pick.&lt;/p&gt;
&lt;h3&gt;
  
  
  src/index.ts - the only file that touches Bun.serve
&lt;/h3&gt;

&lt;p&gt;Everything above is pure. This file is where it all gets wired to reality.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;State.&lt;/strong&gt; A &lt;code&gt;Runtime&lt;/code&gt; object holds the config, the compiled routes, a map
of balancers keyed by route pattern, and the health checker. &lt;code&gt;applyConfig&lt;/code&gt;
rebuilds all of it: it stops the old health checker, recompiles routes,
rebuilds balancers and starts a fresh checker. The same function runs at
boot and on every config reload, which is why live reload is not a special
case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The fetch handler.&lt;/strong&gt; Match the route, check for a WebSocket upgrade, and
either upgrade or call &lt;code&gt;handle&lt;/code&gt;. Every response is logged as one line with
a colored status via &lt;code&gt;util.styleText&lt;/code&gt;: green under 400, yellow under 500,
red from 500 up, each with an ISO timestamp and duration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The healthz endpoint.&lt;/strong&gt; &lt;code&gt;status: "ok"&lt;/code&gt;, uptime in seconds from a module
&lt;code&gt;START&lt;/code&gt; timestamp, and the cumulative &lt;code&gt;requests&lt;/code&gt; and &lt;code&gt;errors&lt;/code&gt; counters.
That is the whole metrics story, and the demo dashboard polls it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The compression wiring.&lt;/strong&gt; When compression applies, the response is
rebuilt with a compressed body: &lt;code&gt;content-length&lt;/code&gt; deleted (the length is
unknown until the stream ends), &lt;code&gt;content-encoding&lt;/code&gt; set, &lt;code&gt;vary&lt;/code&gt; appended.
When compression does not apply, the code still appends &lt;code&gt;vary&lt;/code&gt; but does
NOT rebuild the response. That choice is a bug fix, and it is one of the
afternoons below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The WebSocket handlers.&lt;/strong&gt; &lt;code&gt;open&lt;/code&gt; connects the upstream socket and
flushes any client frames that arrived before the upstream was ready.
&lt;code&gt;message&lt;/code&gt; forwards each frame, buffering until the upstream is &lt;code&gt;OPEN&lt;/code&gt;.
&lt;code&gt;close&lt;/code&gt; tears down the upstream. Three handlers, and a socket is tunneled
with no &lt;code&gt;ws&lt;/code&gt; package.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live reload.&lt;/strong&gt; &lt;code&gt;fs.watch&lt;/code&gt; on the config file re-runs &lt;code&gt;loadConfig&lt;/code&gt; and
&lt;code&gt;applyConfig&lt;/code&gt; on change. A bad config logs red and keeps the previous
routes, so you cannot reload yourself into a dead server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful shutdown.&lt;/strong&gt; On &lt;code&gt;SIGINT&lt;/code&gt; or &lt;code&gt;SIGTERM&lt;/code&gt;: stop health checks, close
the watcher, stop accepting new connections, and exit after
&lt;code&gt;shutdownTimeoutMs&lt;/code&gt; with an unref'd timer. In-flight requests drain before
the process exits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;import.meta.main&lt;/code&gt; gate.&lt;/strong&gt; The server only boots when the file is
run directly, so tests and the demo can import it without side effects.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The supporting cast - tests, bench and demo
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tests/&lt;/code&gt; is 100 tests across 9 files, run by &lt;code&gt;bun:test&lt;/code&gt;, which is built in.&lt;br&gt;
No Jest, no Vitest, no exception needed. By the numbers: 100 of 100 passing, zero skipped, zero edited, all runnable with one &lt;code&gt;bun test&lt;/code&gt; and no install. The unit suites call the pure modules directly: router matching and 405 &lt;code&gt;Allow&lt;/code&gt;, weighted rotation and&lt;br&gt;
failover (including the timeout rule, body replay and the 413 caps), every&lt;br&gt;
static header case from single ranges to reversed ranges, compression&lt;br&gt;
negotiation with q-values, config validation, health probes, and the&lt;br&gt;
WebSocket URL mapping. Two suites go further. &lt;code&gt;server.test.ts&lt;/code&gt; boots the&lt;br&gt;
real server as a subprocess and exercises &lt;code&gt;/healthz&lt;/code&gt;, proxying, 405, static&lt;br&gt;
serving, a live WebSocket tunnel, live config reload, and a clean &lt;code&gt;SIGTERM&lt;/code&gt;&lt;br&gt;
exit. &lt;code&gt;demo.test.ts&lt;/code&gt; boots the whole demo package and proves the dashboard&lt;br&gt;
serves, Range works on the demo file, proxy requests alternate between the&lt;br&gt;
two mock upstreams, and the tunnel echoes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bench/bench.ts&lt;/code&gt; is the autocannon replacement. It spawns the server, fires&lt;br&gt;
20,000 requests across 32 concurrent workers using only &lt;code&gt;fetch&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;performance.now&lt;/code&gt;, and reports throughput plus p50, p90, p99 and max from a&lt;br&gt;
sorted latency histogram. No package to install, and it works in CI.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;demo/&lt;/code&gt; is the click-and-play proof that this is not scaffolding. Two mock&lt;br&gt;
upstreams (each a tiny &lt;code&gt;Bun.serve&lt;/code&gt; answering &lt;code&gt;/api/whoami&lt;/code&gt; with its own&lt;br&gt;
name, so round-robin is visible), a config putting zeroproxy in front of&lt;br&gt;
them, and a dashboard page that is plain HTML, CSS and JavaScript with no&lt;br&gt;
framework and no build step, served by zeroproxy's own static file server.&lt;br&gt;
The dashboard polls &lt;code&gt;/healthz&lt;/code&gt;, sends requests through the proxy, asks for&lt;br&gt;
the first 100 bytes of a file with a &lt;code&gt;Range&lt;/code&gt; header, and opens a WebSocket&lt;br&gt;
through the tunnel. &lt;code&gt;demo/run.ts&lt;/code&gt; starts everything with one command and&lt;br&gt;
waits for the proxy to answer before printing the URL.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;sequenceDiagram
    participant C as Client
    participant Z as zeroproxy
    participant A as Upstream A
    participant B as Upstream B

    C-&amp;gt;&amp;gt;Z: Request
    Z-&amp;gt;&amp;gt;A: Forward request
    A--xZ: Connection refused

    Note over Z: Retry on connection refusal

    Z-&amp;gt;&amp;gt;B: Retry request
    B--&amp;gt;&amp;gt;Z: 200 OK + streaming body
    Z--&amp;gt;&amp;gt;C: Streaming response

    Note over C,B: Connection refusal is retried for every method.&amp;lt;br/&amp;gt;Timeouts are retried only for idempotent methods.&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  The edge cases that ate my afternoons
&lt;/h2&gt;

&lt;p&gt;The write-up prompt asks for the edge case that ate an afternoon. I have&lt;br&gt;
three, and each one changed the code permanently.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A consumed stream cannot be sent twice
&lt;/h3&gt;

&lt;p&gt;The first failover version buffered every request body so it could be&lt;br&gt;
replayed. Then I realized a large upload would buffer entirely into memory,&lt;br&gt;
and that is how a proxy becomes a memory bomb. The fix is the three-way&lt;br&gt;
&lt;code&gt;planBody&lt;/code&gt; split above, with &lt;code&gt;spliceTail&lt;/code&gt; for chunked bodies. But the real&lt;br&gt;
afternoon was spent on the retry rule, not the buffering. I shipped a&lt;br&gt;
version that retried POSTs after a timeout, and it took a test that should&lt;br&gt;
have been obvious to catch it: a slow upstream that actually applied the&lt;br&gt;
POST, then timed out the response. The retry applied it again. The rule that&lt;br&gt;
fixed it, "a timeout does not prove the request was never delivered," is now&lt;br&gt;
the most documented line in the source, and there is a test pinning it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Re-wrapping a 206 response sends the whole file
&lt;/h3&gt;

&lt;p&gt;I implemented Range, the static tests passed, everything was beautiful. Then&lt;br&gt;
I turned compression on and a &lt;code&gt;bytes=0-99&lt;/code&gt; request returned the entire file.&lt;/p&gt;

&lt;p&gt;The cause is documented in &lt;code&gt;src/index.ts&lt;/code&gt; because it is so easy to hit&lt;br&gt;
again: re-wrapping a file-backed body, such as a 206 slice, in a new&lt;br&gt;
&lt;code&gt;Response&lt;/code&gt; object makes &lt;code&gt;Bun.serve&lt;/code&gt; re-stat the file and send all of it. The&lt;br&gt;
fix is in the compression wiring: when compression does not apply, append&lt;br&gt;
&lt;code&gt;Vary: Accept-Encoding&lt;/code&gt; to the existing response instead of rebuilding it.&lt;br&gt;
A real proxy bug, found by a real integration test, fixed with one &lt;code&gt;else&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The reproducible build is off by one byte
&lt;/h3&gt;

&lt;p&gt;The +5 Reproducible Build bonus asks for two builds with byte-identical&lt;br&gt;
output. My first two builds did not match. The diff was exactly one byte.&lt;br&gt;
Bun embeds the &lt;code&gt;--outfile&lt;/code&gt; filename into the compiled binary, so two builds&lt;br&gt;
named &lt;code&gt;zeroproxy-build-1&lt;/code&gt; and &lt;code&gt;zeroproxy-build-2&lt;/code&gt; are different programs by&lt;br&gt;
construction. The fix is to stop fighting it: build twice to the same&lt;br&gt;
filename, copy the results apart, and hash the copies. The &lt;code&gt;reproduce&lt;/code&gt;&lt;br&gt;
target does exactly that, and &lt;code&gt;BUILD_HASHES.txt&lt;/code&gt; now shows the same SHA-256&lt;br&gt;
twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the standard library made painful
&lt;/h2&gt;

&lt;p&gt;The prompt asks this directly, so here is the honest list of places where I&lt;br&gt;
hit the edge of the box and had to design around it, rather than install my&lt;br&gt;
way out.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No client IP address, so no real &lt;code&gt;X-Forwarded-For&lt;/code&gt;.&lt;/strong&gt; Bun's &lt;code&gt;fetch&lt;/code&gt;
handler does not expose the client socket address. A proxy that cannot
see its clients cannot build the client address chain, so the header is
left to whatever front proxy already set it. I documented this instead of
faking it, because faking a security-relevant header is worse than
omitting it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP/1.1 only.&lt;/strong&gt; &lt;code&gt;Bun.serve&lt;/code&gt; speaks HTTP/1.1. No HTTP/2, no HTTP/3, no
ALPN. Hand-writing hpack in a 72-hour window is a project in itself, so
the limit is stated honestly and the scaling story is horizontal: N
processes behind an OS-level load balancer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;util.parseArgs&lt;/code&gt; is string and boolean only.&lt;/strong&gt; No coercion, no
subcommands. It is enough for a proxy because the config file carries the
real types, but it is a genuine ceiling, and Node's own docs say the API
is deliberately minimal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No MIME database in the box.&lt;/strong&gt; Node and Bun do not ship the full IANA
extension-to-type map, so the static module carries a 28-entry table. It
covers every extension a web server realistically serves, and it is a
real maintenance surface that &lt;code&gt;mime-types&lt;/code&gt; would have given away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A live WebSocket cannot be failed over.&lt;/strong&gt; The initial target is
load-balanced, but if an upstream dies mid-connection there is no
transparent way to replay an established handshake. The socket dies with
its upstream. That is in the README, not hidden.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The substitutions at a glance
&lt;/h2&gt;

&lt;p&gt;STDLIB.md logs sixteen substitutions, each with a rationale and a download&lt;br&gt;
count where it matters. The headline ones:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Would normally install&lt;/th&gt;
&lt;th&gt;Used instead&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;path-to-regexp&lt;/code&gt; (~200M/wk)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;URLPattern&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;http-proxy-middleware&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fetch()&lt;/code&gt; + &lt;code&gt;Bun.serve()&lt;/code&gt; streaming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ws&lt;/code&gt; (~270M/wk)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Bun.serve&lt;/code&gt; upgrade + global &lt;code&gt;WebSocket&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;serve-static&lt;/code&gt; / &lt;code&gt;send&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Bun.file()&lt;/code&gt; + manual &lt;code&gt;Range&lt;/code&gt;/&lt;code&gt;ETag&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;compression&lt;/code&gt; (gzip middleware)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CompressionStream&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;mime-types&lt;/code&gt; (~263M/wk)&lt;/td&gt;
&lt;td&gt;28-entry hand-written table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;chalk&lt;/code&gt; (~505M/wk)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;util.styleText()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;minimist&lt;/code&gt; (~158M/wk)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;util.parseArgs()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;morgan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a ~10-line logger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;http-errors&lt;/code&gt; (~170M/wk)&lt;/td&gt;
&lt;td&gt;hand-written &lt;code&gt;Response&lt;/code&gt; builders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;hand-written config guards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;cors&lt;/code&gt; (~77M/wk)&lt;/td&gt;
&lt;td&gt;one header on proxied responses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dotenv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;process.env&lt;/code&gt; + JSON config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;nodemon&lt;/code&gt; (~14M/wk)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fs.watch&lt;/code&gt; + in-place reload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;autocannon&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;stdlib &lt;code&gt;fetch&lt;/code&gt; bench script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a status dashboard&lt;/td&gt;
&lt;td&gt;one static HTML page polling &lt;code&gt;/healthz&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The numbers, honestly
&lt;/h2&gt;

&lt;p&gt;The benchmark is built in and reproducible: &lt;code&gt;bun run bench&lt;/code&gt; serves static&lt;br&gt;
files and fires 20,000 requests across 32 concurrent workers. On my ThinkPad&lt;br&gt;
E16 Gen 2, AMD Ryzen 5 7535U, Manjaro Linux, Bun 1.4:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;td&gt;~4,900 req/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p50 latency&lt;/td&gt;
&lt;td&gt;~6 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p90 latency&lt;/td&gt;
&lt;td&gt;~8 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p99 latency&lt;/td&gt;
&lt;td&gt;~13 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Single process, single event loop. I am not going to pretend these beat&lt;br&gt;
nginx or Caddy, because they do not, and the event's rule is that honest&lt;br&gt;
numbers beat fast ones. The honest claim is different: this is what the&lt;br&gt;
standard library does when you stop adding middleware and let one event loop&lt;br&gt;
do its job.&lt;/p&gt;

&lt;p&gt;The proof of zero dependencies is equally boring and equally the point.&lt;br&gt;
&lt;code&gt;package.json&lt;/code&gt; has &lt;code&gt;"dependencies": {}&lt;/code&gt;. &lt;code&gt;deps-proof.txt&lt;/code&gt; holds the&lt;br&gt;
&lt;code&gt;bun pm ls&lt;/code&gt; output, which lists only the TypeScript toolchain in&lt;br&gt;
devDependencies, and that toolchain never ships in the compiled binary.&lt;br&gt;
&lt;code&gt;BUILD_HASHES.txt&lt;/code&gt; holds two identical SHA-256 hashes. One command, &lt;code&gt;make&lt;br&gt;
build&lt;/code&gt;, produces the binary. &lt;code&gt;make demo&lt;/code&gt; boots the whole package.&lt;/p&gt;

&lt;h3&gt;
  
  
  The proof you can verify, not take my word for
&lt;/h3&gt;

&lt;p&gt;The one accusation that could sink a zero-dependency submission is "you hid&lt;br&gt;
a dependency." So I made it impossible to make. Three artifacts in the repo,&lt;br&gt;
each independently checkable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt; is &lt;code&gt;"dependencies": {}&lt;/code&gt;. Not a lockfile full of
transitive tree, an empty manifest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;deps-proof.txt&lt;/code&gt;&lt;/strong&gt; is the raw &lt;code&gt;bun pm ls&lt;/code&gt; output. Run it yourself on the
same machine and you get the same list: only the TypeScript toolchain in
devDependencies, which never ships in the compiled binary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BUILD_HASHES.txt&lt;/code&gt;&lt;/strong&gt; holds two byte-identical SHA-256 hashes. This is
the +5 Reproducible Build bonus, and it was not free: my first two builds
were off by exactly one byte, because Bun embeds the &lt;code&gt;--outfile&lt;/code&gt; filename
into the binary. The fix is in the edge cases below. The point is that the
proof is structural. Two independent builds, same hash, so nobody has to
trust a claim that a dependency stayed out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of these is a file in the repo. A judge can check all three in&lt;br&gt;
under a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decisions I'd take back
&lt;/h2&gt;

&lt;p&gt;Given another 72 hours, the honest next mountain is HTTP/2, and it is the&lt;br&gt;
right one because it is the one place the runtime genuinely stops. I would&lt;br&gt;
also implement multi-range &lt;code&gt;multipart/byteranges&lt;/code&gt;, which today is correctly&lt;br&gt;
ignored per RFC 9110 rather than implemented. Neither is hidden; both are&lt;br&gt;
stated limits in the README, which is the whole deal.&lt;/p&gt;

&lt;p&gt;But there are two calls I made that I would genuinely reverse, not just&lt;br&gt;
extend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;X-Forwarded-For&lt;/code&gt; omission is the one I'd rethink hardest.&lt;/strong&gt; Bun's&lt;br&gt;
&lt;code&gt;fetch&lt;/code&gt; handler does not expose the client socket address, so I documented&lt;br&gt;
the gap rather than fake the header. I still think faking a security-relevant&lt;br&gt;
header is wrong. But a proxy that cannot see its clients is a proxy missing&lt;br&gt;
its most basic observability signal, and I gave it away earlier than I&lt;br&gt;
should have. The honest fix is to stop treating it as a hard wall and ask&lt;br&gt;
the runtime for the address more aggressively, or to make the header&lt;br&gt;
opt-in and configurable rather than absent. I would spend a real chunk of&lt;br&gt;
the next window there, because it is the difference between a toy proxy and&lt;br&gt;
a deployable one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I would not spend so long on the demo dashboard.&lt;/strong&gt; It is a nice proof that&lt;br&gt;
this is not scaffolding, and the reviewers noticed it. But the hours went in&lt;br&gt;
at the very end, when the clock was shortest, and it is the least&lt;br&gt;
dependency-relevant part of the submission. The scoring weight is on the&lt;br&gt;
correctness of the proxy and the honesty of the write-up, not on how pretty&lt;br&gt;
the demo page is. If I ran it again, the dashboard would be a &lt;code&gt;curl&lt;/code&gt; script&lt;br&gt;
and a raw HTML page, and the saved time would go into HTTP/2 or multi-range&lt;br&gt;
support instead.&lt;/p&gt;

&lt;p&gt;And the honest bottom line stays: every limit above is stated in the README,&lt;br&gt;
not hidden. That is the whole deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The event's slogan is "every dependency is a stranger." Building zeroproxy&lt;br&gt;
made that concrete in a way reading about it never could. For every feature I&lt;br&gt;
caught my hand reaching for a package out of habit, and every time the&lt;br&gt;
runtime already had the answer: &lt;code&gt;URLPattern&lt;/code&gt; for routing, &lt;code&gt;CompressionStream&lt;/code&gt;&lt;br&gt;
for compression, &lt;code&gt;Bun.serve&lt;/code&gt; upgrade for sockets, &lt;code&gt;Bun.file&lt;/code&gt; for static,&lt;br&gt;
&lt;code&gt;util.styleText&lt;/code&gt; for chalk, &lt;code&gt;util.parseArgs&lt;/code&gt; for minimist, &lt;code&gt;fs.watch&lt;/code&gt; for&lt;br&gt;
nodemon. The one thing the runtime genuinely lacked, the client IP address,&lt;br&gt;
I documented instead of faking.&lt;/p&gt;

&lt;p&gt;The package I made look unnecessary is &lt;code&gt;path-to-regexp&lt;/code&gt;, and it is not even&lt;br&gt;
close. The runtime replaced it with identical syntax and semantics, so the&lt;br&gt;
router is 37 lines. The thing that surprised me most was that building the&lt;br&gt;
proxy with zero dependencies was not harder than building it with packages.&lt;br&gt;
It was calmer. No version conflicts, no audit noise, no transitive tree to&lt;br&gt;
reason about. Just one event loop and the protocol, which is exactly what&lt;br&gt;
the hackathon promised and exactly what I wanted to prove.&lt;/p&gt;

&lt;p&gt;Zero dependencies. One command to run. Every line mine.&lt;/p&gt;




&lt;p&gt;GitHub: &lt;a href="https://github.com/f-ei8ht/zeroproxy" rel="noopener noreferrer"&gt;github.com/f-ei8ht/zeroproxy&lt;/a&gt;&lt;br&gt;
Demo video: &lt;a href="https://www.youtube.com/watch?v=ugBxX1BZD70" rel="noopener noreferrer"&gt;youtube.com/watch?v=ugBxX1BZD70&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;by Saif Ali Khan&lt;br&gt;
#hackathonraptors&lt;br&gt;
September 2026.&lt;/p&gt;

</description>
      <category>bunjs</category>
      <category>typescript</category>
      <category>proxy</category>
      <category>hackathonraptors</category>
    </item>
  </channel>
</rss>
