Skip to content

Unix Domain Sockets Documentation & Example App#222

Merged
ifandelse merged 3 commits intomasterfrom
unix-domain-sockets-deaux
Apr 3, 2026
Merged

Unix Domain Sockets Documentation & Example App#222
ifandelse merged 3 commits intomasterfrom
unix-domain-sockets-deaux

Conversation

@ifandelse
Copy link
Copy Markdown
Contributor

@ifandelse ifandelse commented Apr 3, 2026

Unix Domain Sockets Documentation & Example App

Branch: unix-domain-sockets-deaux → unix-domain-socket
Changes: 3355 additions, 60 deletions across 55 files


Summary

This PR has 55 changed files but the bulk is generated docs and a lock file. The real work spans three commits.

e0b3285 — Harden UDS transport and replace integration tests with multi-process approach

The Mental Model Shift:

The UDS transport's handshake protocol now validates version numbers, not just message type strings. Previously, isUdsSyn/isUdsAck/isUdsEnvelopeMessage accepted any version field value as long as the shape was correct — meaning a v2 client could silently handshake with a v1 server and get garbled envelopes. Now there's a strict version check, with looksLikeSyn/looksLikeAck helpers to distinguish "wrong version" from "not a postal message at all" — giving users an actionable PostalUdsVersionMismatchError instead of a mysterious timeout.

The integration test strategy also flipped entirely. Instead of hand-rolling protocol messages against mock sockets (which can't catch echo-prevention or transport-registration bugs), tests now fork real child processes via a CJS test helper (uds-test-child.cjs). Each child gets its own postal instance, so the tests exercise the actual production flow: handshake, bidirectional pub/sub, fan-out, disconnect cleanup, filters, and version mismatch rejection.

What Changed Structurally:

  1. protocol.ts: isUdsSyn, isUdsAck, isUdsEnvelopeMessage now require version === PROTOCOL_VERSION. New looksLikeSyn/looksLikeAck helpers for loose matching.
  2. errors.ts: New PostalUdsVersionMismatchError class with received and expected fields.
  3. client.ts: Version mismatch detection added before the ACK check — rejects fast with a clear error instead of timing out.
  4. server.ts: Version mismatch detection added before the SYN check — destroys the socket immediately.
  5. index.ts: Exports PostalUdsVersionMismatchError.
  6. uds-test-child.cjs: New CJS child process helper that wraps the real postal + UDS APIs behind an IPC command interface.
  7. uds.integration.test.ts: ~500-line integration test suite using forked child processes. Covers: handshake, bidirectional messaging, fan-out, disconnect cleanup, server filter, onDisconnect callback, handshake timeout, concurrent connections (10 clients), and version mismatch rejection.
  8. Unit test expansions in protocol.test.ts, server.test.ts, serialization.test.ts, socketTransport.test.ts, client.test.ts: Version mismatch cases, duplicate SYN, filter pass-through, startup error, createLineParser coverage, settled-before-connect guard.
  9. ~30 generated API reference docs updated with new commit hashes (noise).

ee9f599 — Add postal-monitor example app

The Mental Model Shift:

This adds a real-world example app demonstrating postal-transport-uds in action. It's an Ink-based TUI dashboard (monitor) that watches task events published by a launcher process over a Unix domain socket. The architecture is intentionally pedagogical — reporter.ts is three postal API calls and nothing else.

What Changed Structurally:

  1. New examples/postal-monitor/ package with package.json (@postal-examples/postal-monitor, private).
  2. reporter.ts: The pedagogical core — connectToSocketgetChannelpublish. Returns a Reporter with reportStarted/reportFinished/disconnect.
  3. task-runner.ts: Spawns pnpm commands via child_process.spawn, reports start/finish via the reporter. Handles double-emit from Node's error+close pattern with a settled guard.
  4. launcher.ts: Entry point — resolves monorepo root, connects to the monitor socket, runs tasks with staggered random delays.
  5. monitor.tsx: UDS server entry point — listenOnSocket, subscribes to task.#, bridges postal events into React state via a mutable setState ref, renders with Ink.
  6. monitor-state.ts: Pure state reducer (immutable updates) with createInitialState, applyTaskStarted, applyTaskFinished. Event log capped at MAX_EVENT_LOG_SIZE (50).
  7. React components: App.tsx (two-column layout), ProcessList.tsx (tasks by PID), EventStream.tsx (scrolling log), format.ts (duration formatting).
  8. types.ts: Payload types + ChannelRegistry module augmentation for compile-time type safety on the "monitor" channel.
  9. Tests: monitor-state.test.ts (~408 lines, pure reducer tests), reporter.test.ts (~268 lines, mocked postal/UDS), launcher.test.ts (~373 lines, mocked spawn).
  10. Lock file updated with new dependencies (ink, chalk, react, ink-testing-library, etc.).

72f9033 — Update docs for postal-monitor example and regenerate API references

What Changed Structurally:

  1. astro.config.mjs: Added postal-monitor to the examples sidebar.
  2. Regenerated API reference docs (commit hash updates only — noise).

Risk Callouts

  • New dependencies: The postal-monitor example adds ink, chalk, react, @types/react, ink-testing-library as dependencies. These are scoped to a private example app, so no supply chain impact on consumers — but it's worth noting the React + Ink dependency tree for an example.

  • as type assertions in monitor.tsx: Lines cast envelope.payload to TaskStartedPayload/TaskFinishedPayload. The comment explains why (ChannelRegistry augmentation not visible at runtime), and the reporter enforces the contract. Not a bug, but worth the reviewer's awareness — if the reporter shape drifts, the monitor would silently accept bad data.

  • Mutable setState ref pattern in monitor.tsx: The monitor bridges postal subscriptions into React state via a mutable setState reference. This is a known React pattern for bridging external event sources. The only risk is the if (!setState) return guard — if an event arrives before the first render completes, it's silently dropped. Low risk for a demo.

  • reporter.disconnect() is not idempotent: The test explicitly verifies that double-disconnect calls the underlying cleanup twice. Fine for a demo, but worth noting if this pattern gets copied into production code.

  • Integration tests fork up to 12 child processes: The concurrent connections test spawns 10 clients + 1 server. This could be flaky on CI if the machine is resource-constrained.

  • Breaking behavioral change: isUdsSyn/isUdsAck/isUdsEnvelopeMessage now reject messages with wrong versions. If anyone was relying on cross-version handshakes, this will break. Pre-1.0 package on a feature branch, so low risk, but worth documenting in release notes.


Tribal Knowledge Checks

General Checks

  • New environment variables: None.
  • Type safety: as casts in monitor.tsx documented and intentional. no-explicit-any pragmas in tests standard for mocking.
  • Dead code: All new exports consumed.
  • ⚠️ Dependency changes: New deps in examples/postal-monitor (ink, chalk, react, etc.). Package is private — no downstream impact.
  • Leftover debugging: None.
  • ⚠️ Breaking interface changes: isUdsSyn/isUdsAck/isUdsEnvelopeMessage now reject wrong versions. New PostalUdsVersionMismatchError export.
  • Large binary/asset additions: None.

Unit Test Checks

  • Style guide adherence: beforeEach-execution / it-assertion pattern followed. Mock config in beforeEach. No mock.calls callback invocation.
  • No testing pure style hooks: N/A.
  • No testing barrel exports: N/A.
  • Test descriptions match assertions: Verified.
  • Missing export default {}: All new test files include it.

Testing Recommendations

Manual Verification

  • Run the postal-monitor demo end-to-end: start:monitor in one terminal, start:launcher in another.
  • Kill the launcher mid-run (SIGKILL) and verify the monitor doesn't crash.
  • Run the monitor without the launcher and verify the waiting state.

Automated Test Gaps

  • format.ts (formatDuration) has no dedicated unit tests — cheap to add.
  • launcher.ts monorepo root resolution and node_modules check are untested (environment-dependent).

Regression Risks

  • Run pnpm --filter postal-transport-uds test to confirm version-check hardening doesn't break existing tests.
  • The postal-monitor example depends on workspace:* links — if either package's API changes before merge, the example could break silently.

ifandelse and others added 3 commits April 2, 2026 01:41
… approach

Rewrites integration tests to fork real child processes instead of using
hand-rolled protocol clients, so echo prevention and transport registration
are exercised exactly as they work in production. Also includes transport
hardening fixes and regenerated API reference docs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ink-based CLI tool for launching and monitoring multi-process postal
scenarios with real-time event streaming via UDS transport.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Base automatically changed from unix-domain-socket to master April 3, 2026 04:06
@ifandelse ifandelse changed the title Unix domain sockets deaux Unix Domain Sockets Documentation & Example App Apr 3, 2026
@ifandelse ifandelse merged commit f60bbb1 into master Apr 3, 2026
1 check passed
@ifandelse ifandelse deleted the unix-domain-sockets-deaux branch April 3, 2026 04:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant