feat: graceful drain and multi-package refactoring#14
Merged
Conversation
## Summary - Add graceful drain of active connections on shutdown (TS_DRAIN_TIMEOUT) - Refactor main.go into multi-package architecture (internal/config, internal/proxy, internal/health, internal/telemetry) - Add mock-based unit tests for proxy logic - Update README to problem-first structure - Add Astro output folders to .gitignore
## Changes - Fix missing net/http import in main.go - Refactor IsExpectedCloseError to use a loop/switch for lower complexity - Refactor LoadConfig using helper functions to reduce complexity - Ensure all tests pass after structural changes
mlorentedev
added a commit
that referenced
this pull request
Mar 8, 2026
🤖 I have created a release *beep* *boop* --- ## [1.5.0](v1.4.0...v1.5.0) (2026-03-08) ### Features * graceful drain and multi-package refactoring ([#14](#14)) ([14355b7](14355b7)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Manu Lorente <mlorentedev@gmail.com>
3 tasks
mlorentedev
added a commit
that referenced
this pull request
May 18, 2026
Two related DX papercuts surfaced by the 2026-05-18 multi-dim audit: 1. CLAUDE.md "Tech Stack" claimed `main.go ~785 lines` and "single-file" architecture. After ARCH-002 (PR #14), main.go is ~270 lines of orchestration plus four `internal/` packages (config, proxy, health, telemetry). The "Key Paths" table listed only main_test.go and main_integration_test.go, missing every internal/ package and the specs/ + scripts/tests/ trees added since. New contributors landing on the wrong file wastes onboarding time; tooling (claude-mem, vault refs, ADRs in the doc itself) was the only source of truth. Stale memory is worse than no memory. 2. .github/workflows/ci.yml pinned golangci-lint to `version: latest`, so contributors running `make lint` locally with whatever they installed could see green while CI sees red (or vice versa). Pinned to v1.62.2 in CI and documented the same install command in CLAUDE.md's Commands block so the two stay in lockstep. CLAUDE.md also gains explicit references to ADR-006 (Dialer) and ADR-007 (multi-package split) in the Architecture Decisions section, making the layout discoverable from the project memory file. Audit reference: 40-runbooks/audit-2026-05-18-multi-dim.md (Major DX-1 and DX-2 entries).
mlorentedev
added a commit
that referenced
this pull request
May 18, 2026
## Summary Two DX papercuts from the [2026-05-18 multi-dim audit](https://github.com/mlorentedev/ts-bridge/blob/master/specs/) (Major DX-1 and DX-2). ### Fix 1 — Stale CLAUDE.md \`CLAUDE.md\` claimed: - \"Architecture: Single-binary, single-file (\`main.go\` ~785 lines)\" → wrong since PR #14 (ARCH-002) split into \`main.go\` (~270 lines orchestration) + four \`internal/\` packages. - Key Paths table listed only \`main_test.go\` and \`main_integration_test.go\` — missing every \`internal/*\` package, \`specs/\`, \`scripts/tests/\`. Refreshed: - Updated Tech Stack to "Single binary, multi-package". - Key Paths now lists each \`internal/\` package + their roles, \`specs/\` SDD trees, \`scripts/tests/\` BATS suite, updated CI section. - Architecture Decisions block adds ADR-006 (Dialer) and ADR-007 (multi-package split) — making the current layout discoverable from the project memory file alone. ### Fix 2 — Pin golangci-lint \`.github/workflows/ci.yml\` had \`version: latest\`. Contributors running \`make lint\` locally with an older version see green while CI sees red (or the reverse), wasting investigation time. Pinned to \`v1.62.2\` in CI and documented the same \`go install ...@v1.62.2\` in CLAUDE.md's Commands block. Bump deliberately on upgrade. ## Diff 2 files changed, +22 / -11. ## Test plan - [x] CLAUDE.md text reflects actual file structure as of v1.7.0 (verified \`wc -l\` on every cited file) - [x] CI's existing lint job continues to pass with v1.62.2 (the version was already passing prior commits at \`latest\` resolving to this range) - [ ] CI green on this PR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR modernizes the architecture of
ts-bridgeby splitting the codebase into focused internal packages and adding a graceful shutdown mechanism for active connections.Changes
1. Reliability (REL-001)
TS_DRAIN_TIMEOUT(default:15s) to allow active RDP/SSH connections to finish before the bridge closes.sync.WaitGroupinacceptLoopto track active connections during shutdown.2. Architecture Refactor (ARCH-002)
main.gointo:internal/config: Environment parsing and auto-instance logic.internal/proxy: Core TCP proxying,Dialerinterface, and bidirectional copy loop.internal/telemetry: Atomic counters and metrics snapshotting (decoupled from health).internal/health: HTTP server for liveness, readiness, and metrics.main.goto act as a thin orchestrator.3. Testability (ARCH-003)
internal/proxytests with a mockDialerto verify proxy logic without a Tailscale network.main_integration_test.goto work with the new package structure.4. Documentation (BP-001)
README.mdto use a problem-first approach with quantified value comparison.TS_DRAIN_TIMEOUTto configuration references in README and Starlight docs.Testing
go test ./...— All tests passing (including new unit tests for packages).go build ./...— Clean build.Fixes Stream B (REL-001) and Stream E (ARCH-002, ARCH-003) from the roadmap.