<feed xmlns='http://www.w3.org/2005/Atom'>
<title>mitmux/internal/proxy/tee.go, branch main</title>
<subtitle>Terminal-based intercepting HTTP proxy.
</subtitle>
<id>https://srdusr.com/git/mitmux/atom?h=main</id>
<link rel='self' href='https://srdusr.com/git/mitmux/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://srdusr.com/git/mitmux/'/>
<updated>2026-05-15T17:48:00+00:00</updated>
<entry>
<title>Stop mislabeling truncated captures as "exact"</title>
<updated>2026-05-15T17:48:00+00:00</updated>
<author>
<name>srdusr</name>
<email>99972264+srdusr@users.noreply.github.com</email>
</author>
<published>2026-05-15T17:48:00+00:00</published>
<link rel='alternate' type='text/html' href='https://srdusr.com/git/mitmux/commit/?id=dd1621c0077e079e0693f16fe83f17d50338216e'/>
<id>urn:sha1:dd1621c0077e079e0693f16fe83f17d50338216e</id>
<content type='text'>
internal/proxy/tee.go's teeConn silently drops bytes past
maxCaptureBytes (10 MiB) but callers unconditionally marked the result
"exact" anyway. Confirmed live: proxying a 15 MiB response worked
correctly end-to-end (the client got the full, real 15 MiB - proxying
itself is unbounded, only storage is capped), but the stored history
entry was exactly 10485760 bytes with response_exact=1 still set. For a
tool whose core value proposition is "raw bytes are the source of
truth," a silently truncated capture presented as complete could hide
the very evidence a smuggling or parser-differential investigation is
looking for in the tail of a large body - and give false confidence
that it isn't there.

teeConn.Take() now returns (data, truncated) instead of just data;
truncated is true whenever a Read had to drop bytes because the buffer
was already at cap. Every caller (proxy.go's forward() on both the
request and response side, repeat.go's sendRaw for Repeater/Intruder)
now folds truncated into exact - a truncated capture is never marked
exact - and additionally threads a distinct RequestTruncated/
ResponseTruncated bool through store.Entry, ipc.EntryDetail, and the
TUI, since "truncated" and "reconstructed" (HTTP/2, which never had
wire-exact bytes to begin with) are different situations worth telling
apart: a truncated capture is still real wire bytes, just incomplete,
not a synthesized reconstruction. The detail/repeater views now show
"truncated (hit capture size limit)" specifically rather than lumping
it in with "reconstructed", which would have implied more
transformation happened than actually did.

Schema: history gains request_truncated/response_truncated columns via
the same ALTER-TABLE-and-ignore-duplicate-column pattern already used
for source/flagged, so existing databases upgrade in place.

Verified live: a target server returning a 15 MiB body (over the 10 MiB
cap) proxied through cleanly - full body reached the client - while the
stored entry shows length=10485760, response_exact=0,
response_truncated=1 (previously would have shown response_exact=1);
the TUI's Detail view correctly displays "Response (10485760 bytes,
truncated (hit capture size limit))" instead of "exact".

go build/vet/gofmt/test/mod tidy all clean.
</content>
</entry>
<entry>
<title>History view: SQLite storage, daemon/TUI split over Unix socket</title>
<updated>2024-02-13T22:37:00+00:00</updated>
<author>
<name>srdusr</name>
<email>99972264+srdusr@users.noreply.github.com</email>
</author>
<published>2024-02-13T22:37:00+00:00</published>
<link rel='alternate' type='text/html' href='https://srdusr.com/git/mitmux/commit/?id=8d15c2e0b326933f8fc912e3b13f37e78a9bc0b6'/>
<id>urn:sha1:8d15c2e0b326933f8fc912e3b13f37e78a9bc0b6</id>
<content type='text'>
Implements build-order step 3. Adds:

- internal/store: SQLite (WAL, single-writer) history table, raw
  request/response blobs plus metadata for the list view.
- internal/proxy: request/response capture wired into forward(). HTTP/1.1
  legs are captured byte-exact via a teeConn that records wire bytes as
  they're read, taken right after the message is fully drained (so no
  manual re-reading/replaying is needed - RoundTrip's own streaming does
  the draining). HTTP/2 legs (no meaningful "raw bytes" of their own -
  multiplexed, HPACK-compressed framing) are reconstructed instead, and
  marked as such in storage.
- internal/ipc: JSON-over-Unix-socket protocol between mitmuxd (owns the
  proxy and the DB) and any client - list/get for queries, subscribe for
  a live push stream of newly captured entries. Keeps the proxy engine
  independent of the UI, per the architecture sketch.
- cmd/mitmux: Bubble Tea TUI - a live-updating history table and a
  request/response detail view with raw bytes.

Two real bugs surfaced during testing and got fixed before commit:
1. http.Transport's HTTP/2 auto-dispatch does a literal *tls.Conn type
   assertion on the dialed connection; wrapping it in a capturing teeConn
   broke that silently, and HTTP/2 framing got parsed as HTTP/1.1 text.
   Fixed by dropping http.Transport for the upstream leg entirely in
   favor of an explicit per-protocol round trip (see PLAN.md stack note).
2. singleConnListener wrapped the client teeConn *inside* a
   closeSignalConn, so ConnContext's type assertion for it silently
   failed and HTTP/1.1 client-side capture never activated. Fixed the
   wrap order; verified via direct SQLite inspection that request_exact
   flips back to 1 and the stored bytes are genuinely wire-exact
   (preserved chunked-encoding framing, original header casing/order).

Verified live: plain HTTP, HTTPS H1.1, HTTPS H2, and a POST with a body,
checked against the raw stored bytes directly in SQLite; IPC list/get/
subscribe against a throwaway client; and the TUI driven end-to-end in a
tmux session (list, detail view, tab between request/response, live
update on a new request while sitting on the list).
</content>
</entry>
</feed>
