Commit Graph

15 Commits

Author SHA1 Message Date
root 35da6be99e internal/divert: driver installer with SHA256 verification
Build / test (push) Failing after 30s
Build / build-windows (push) Has been skipped
Extracts embedded WinDivert binaries to %PROGRAMDATA%\Drover\windivert\
on first run; subsequent runs detect matching SHAs and no-op. SHA
mismatch after write produces an AV-friendly error message pointing
the user at adding the directory to exclusions.

ARM64 detected at runtime via runtime.GOARCH and refused gracefully.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:45:27 +03:00
root feda075dc4 internal/divert: IPv4+TCP packet parse + RewriteDst + checksums
Build / test (push) Failing after 29s
Build / build-windows (push) Has been skipped
Pure-Go RFC 791/793 checksum implementation. Mutates buffer in
place — no allocations on the hot path. Used by the redirect layer
to NAT-rewrite Discord packets to 127.0.0.1:listener_port before
reinjecting via WinDivertSend.

UDP support deferred to P2.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:43:42 +03:00
root 223c7f5886 internal/divert: filter expression builder
Build / test (push) Failing after 32s
Build / build-windows (push) Has been skipped
Pure-Go assembly of the WinDivert filter clause. Empty PID list →
"false" (captures nothing — used during Discord-not-running window).
Non-IPv4 upstream → 0.0.0.0 fallback (caller should validate; the
builder degrades gracefully rather than panicking).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:41:53 +03:00
root 736c3ecfc7 internal/divert: embed WinDivert64.sys + WinDivert.dll v2.2.2 with SHA256 sentinels
Build / test (push) Failing after 30s
Build / build-windows (push) Has been skipped
Adds github.com/imgk/divert-go v0.1.0 dependency. Embedded driver
binaries land at runtime in %PROGRAMDATA%\Drover\windivert\ via the
installer (next task).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:40:11 +03:00
root 11c4eb7f4a internal/checker+gui: remove voice-srv test (Discord doesn't expose regional voice servers via public DNS)
Build / test (push) Failing after 30s
Build / build-windows (push) Has been skipped
Release / release (push) Failing after 3m20s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:02:11 +03:00
root 9ea777d7b7 internal/gui: surface warn status + new voice tests in Classic UI
Build / test (push) Failing after 30s
Build / build-windows (push) Has been skipped
Frontend now renders three-tier status (passed/warn/failed) instead of
two. Warn rows get a yellow exclamation-mark dot, expand by default to
show the hint, and contribute to a new "(with warnings)" suffix on the
"All checks passed" header.

Test catalog gains "voice-quality" and "voice-srv" rows replacing the
single "stun" row, in the same position (after udp, before api). RU
descriptions explain what each test actually probes.

useDrover's lastSummary now reports {total, failed, warnings} so the
Classic header can pick the right tier color.

App.go counts StatusWarn as passed in the final {passed, failed} summary
emitted via "check:done" — warn is a soft pass per spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:42:24 +03:00
root 0a85979142 internal/checker: voice-quality + voice-srv tests for predictive voice diagnosis
Build / test (push) Has been cancelled
Build / build-windows (push) Has been cancelled
Replaces the single-packet `stun` test with two predictive voice tests:

  - voice-quality: 30-packet STUN burst through the SOCKS5 UDP relay.
    Computes loss%, jitter (RFC-3550-ish mean abs of inter-arrival
    delta), p50/p95 RTT. Three-tier gating: pass (loss≤5%, jitter≤30,
    p50≤250), warn (loss≤15%, jitter≤60, p50≤400 — voice glitches but
    works), fail (anything worse, including 100% loss).

  - voice-srv: parallel-DNS the 16-region <region>.discord.media
    hostnames, then SOCKS5 CONNECT to :443 on each through the proxy.
    Catches the very common Russian-DPI failure mode where the proxy
    passes generic Discord.com TCP but blocks the .discord.media voice
    CIDRs — a regression all 5 prior SOCKS5 sanity checks miss.

New StatusWarn = "warn" — soft pass with Hint kept visible. Counted as
passed in summary but flagged in UI.

Config gains VoiceBurstCount (default 30), VoiceBurstInterval (default
20ms), VoiceServerHostnames (default = built-in 16-region list).

Tests cover happy path, warn-tier (10% drop), fail-tier (100% drop),
voice-srv blocked, plus standalone unit tests on
runVoiceQualityBurst and runVoiceServerProbe with a fake UDP relay
and fake SOCKS5 server. Race + cover stays at 82.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:42:12 +03:00
root 1c1ab566d9 internal/gui: wire real checker.Run + CancelCheck binding + RawHex display
Build / test (push) Failing after 29s
Build / build-windows (push) Has been skipped
- RunCheck now drives internal/checker.Run instead of the fake 7-step
  sleep loop. Streams checker.Result events as "check:result" with
  Duration converted to milliseconds; emits "check:done" summary on
  channel close. Re-running while a check is in flight cancels the
  previous run and waits for its goroutine to drain so two emitter
  goroutines never race on event order.
- New CancelCheck method (no-op when nothing is running) is bound
  through wailsjs/go/gui/App.js and surfaced in useDrover as
  cancelCheck() with a "check cancelled by user" log line.
- Classic.jsx: while phase==='checking', the Check button collapses to
  a disabled "Checking…" pill side-by-side with a Cancel button (uses
  Stop's secondary visual weight, t.danger on hover). The expanded
  failure row now renders r.rawHex (truncated to 64 chars) on its own
  mono line and the copy button includes raw=<hex> when present.
- CheckResult struct gains RawHex (json:"rawHex") and Attempt fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:13:02 +03:00
root 4b985bb7f0 internal/checker: 7-step Run orchestrator + integration tests
Build / test (push) Failing after 29s
Build / build-windows (push) Has been skipped
Public Run(ctx, cfg) <-chan Result streams diagnostic events for the seven
tests (tcp, greet, auth?, connect, udp, stun, api) wired through the
SOCKS5 primitives, STUN codec, retry classification and RU hints.

- Per-test attempt loop with running/passed/failed events, transient-only
  retries (per-attempt timeout treated as transient, parent ctx cancel as
  permanent), context-aware backoff sleep.
- Connection lifecycle: tcpConn shared across greet/auth/connect (closed
  and redialed on retry); separate udpConn2 control channel for UDP
  ASSOCIATE kept alive for the duration of the stun test.
- STUN-via-SOCKS5: builds 10-byte SOCKS5 UDP header + STUN binding
  request, decodes reply with ATYP-aware header strip (1/3/4).
- runAPI plugs SOCKS5 dial into http.Transport.DialContext; passes on
  HTTP 200 OR 401.
- Skip semantics: dependency-failed tests emit single skipped result;
  cancellation latches and propagates as cancelled-failed (current) +
  cancelled-skipped (remaining).
- Defaults applied to a copy of cfg; UseAuth=false suppresses any "auth"
  result entirely.

Tests: 10 TestRun_* covering happy/auth-rejected/all-rejected/
connect-refused/udp-unsupported/timeout-then-ok/cancelled-mid-flight/
defaults plus extractRawHex unit. Fake SOCKS5 proxy + UDP relay echoing
synthetic STUN binding success responses; httptest stub for API splice.

Combined coverage 84.3% (>=80% target). go test -race clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:08:36 +03:00
root acd5291604 internal/checker: error classification + RU hints + tests
Build / test (push) Failing after 38s
Build / build-windows (push) Has been skipped
Adds:
- retry.go: classifyError() splits errors into Permanent vs Transient
  (used to gate auto-retry); isContextErr() detects ctx cancellation
  through wrapping (OpError, errors.Join).
- hints.go: hintFor(testID, err) returns short Russian explanation per
  failure step, with dedicated branches for SOCKS5 sentinels, every
  documented REP code (0x01..0x08), STUN sentinels, timeouts, and a
  friendly-name fallback.

Coverage: retry.go 100%, hints.go 100%; package total 94.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:58:56 +03:00
root 36e788402a internal/checker: STUN codec + tests
Build / test (push) Failing after 32s
Build / build-windows (push) Has been skipped
Hand-rolled RFC 5389 binding-request encoder + binding-success-response
parser. Just enough to extract XOR-MAPPED-ADDRESS from a server's reply
after socks5UDPAssociate returns a relay endpoint. Avoids pulling in
pion/stun for ~80 LOC of encoding/binary work.

Provides NewTransactionID, EncodeBindingRequest, ParseBindingResponse and
six sentinel errors (ErrSTUN*) so HintFor (T11) can match specific
failure modes. Full TLV attribute walking with bounds checks; supports
both IPv4 and IPv6 XOR-MAPPED-ADDRESS values.

Tests cover encoder layout, IPv4/IPv6 happy paths, attribute walking
past unknown attributes, all error paths, sentinel uniqueness, and a
real loopback round-trip via net.ListenPacket. 90.0% combined coverage
(socks5+stun); stun.go funcs all >= 87%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:50:28 +03:00
root 52ce1e0aa7 internal/checker: SOCKS5 primitives + tests
Build / test (push) Failing after 31s
Build / build-windows (push) Has been skipped
socks5Greeting/Auth/Connect/UDPAssociate per docs/superpowers/specs/
2026-05-01-checker-design.md. RFC 1928 + RFC 1929 wire bytes, raw
reply bytes returned on every error path for RawHex display, ctx
deadline applied via SetDeadline, ctx.Err() joined into error chain
on cancellation. Sentinel errors and ErrSocks5Reply{Code} for code
matching via errors.Is.

Tests: 22 subtests with fake net.Listen server, table-driven per
primitive (happy paths, REP codes, short reads, bad version,
oversize input rejection without I/O, ctx-cancel mid-read).
go test -race -cover passes at 89.0%, go vet clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:46:33 +03:00
root b6619ef53b internal/gui: Wails app with Classic React variant + theme toggle
Build / test (push) Failing after 30s
Build / build-windows (push) Has been skipped
- app.go: App struct with stub bindings (RunCheck/StartEngine/
  StopEngine/GetStatus/Version) — emits check:result, check:done,
  engine:status, stats:update events. Real backend lands in Phase 1.
- run.go: wails.Run() with frameless 480x640 fixed window, Classic
  dark bg matching theme.
- embed.go: //go:embed all:frontend/dist for the Vite build output.
- frontend/: Vite + React project derived from `wails init -t react`.
  Removed default template assets and wired Classic variant from
  docs/design/v2/.
  - components/Classic.jsx: variant 1 with custom title bar
    (drag region, sun/moon theme toggle, min/close hooked to
    Wails WindowMinimise/Quit).
  - components/shared.jsx: useDrover hook adapted to call Wails
    bindings and listen on backend events instead of mock SCENARIOS.
    Added IconSun + IconMoon for the title-bar toggle.
  - App.jsx: owns mode state, wraps setMode in
    document.startViewTransition so the title-bar toggle gives a
    circle-reveal sweep from the cursor.
  - style.css: clean reset (overflow hidden, no scrollbars, brand
    background) — replaces the wails-react-template defaults.
  - wailsjs/go/gui/App.js: hand-written bindings since our App
    struct lives in package gui rather than the standard top-level
    main; `wails generate module` would have written package main
    bindings here.
- build/: standard wails artifacts (icon, manifest); will be
  consumed by `wails build` once we wire it through CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:17:19 +03:00
root 1ad8de32f2 Implement internal/updater: selfupdate via Forgejo Releases API
Adds a small, well-tested package that:
- Queries /api/v1/repos/root/drover-go/releases/latest (404 = no updates,
  not an error).
- Compares the published tag against the running Version using
  golang.org/x/mod/semver, so v0.1.0-rc.2 < v0.1.0. "dev" or any
  semver-invalid current version is treated as "always update".
- Downloads the windows-amd64 asset + SHA256SUMS.txt, verifies the
  sha256 of the binary against its line in the sums file (tolerates
  the asterisk binary-mode prefix), and atomically swaps the running
  exe via github.com/minio/selfupdate.
- Uses a 15s connect timeout with no overall request deadline, so
  large asset downloads aren't truncated.
- Reports progress via an optional callback.

Public surface: Source interface + ForgejoSource implementation,
CheckForUpdate, ApplyUpdate, SetVersion. No GUI/cobra/Wails imports
in the package, so the same code is reusable from the CLI, the
Windows service, and the future tray UI.

Wires the package into "drover update" / "drover update --check-only"
in cmd/drover/main.go. --check-only exits 0 whether or not an update
is available; only network/sha/apply errors are non-zero.

Tests cover CheckForUpdate (table-driven incl. semver pre-release
ordering, dev fallthrough, source errors), parseSHA256Sums (text and
binary modes, CRLF, malformed lines, missing entries),
ForgejoSource.Latest (httptest with canned JSON, 404, 500, missing
asset, missing SHA256SUMS), and downloadAndVerify (success, sha
mismatch, HTTP 404, context cancellation). All run with -race.

Smoke-tested manually: built drover.exe and "drover update --check-only"
against git.okcu.io prints "No updates available" and exits 0 (no
releases yet).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:20:24 +03:00
root 0acbc83e40 Add directory skeleton and CLI entry point with Cobra
- Create internal/{app,config,engine,divert,socks5,bypass,checker,service,tray,procscan,updater} with placeholder doc.go per package.
- Add .gitkeep stubs for internal/frontend, third_party/{windivert,icons}, installer/, .forgejo/workflows/, docs/.
- Implement cmd/drover/main.go: Cobra root with Version/Commit/BuildDate ldflags, --config global flag, and stub subcommands (check, update --check-only, service install/uninstall/start/stop).
- Add github.com/spf13/cobra v1.10.2 dependency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:10:30 +03:00