A v* release shows its CHANGELOG.md section before what the assets are;
a node-v* release shows the newest released section, the core the node
runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The exit-node build of this tree (-tags exitnode, reproducible: -trimpath,
-buildvcs=false, -buildid=) pinned by SHA-256 for linux amd64, arm64 and
arm; the changelog section becomes 0.2.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
log.Printf("Transport: %s", *transportType) ran unconditionally right
after flag.Parse(), so for any .conf [Transport ...] session or
--transports run it printed the --transport flag's untouched default
("yandex") no matter what was actually configured - a boards+direct
exit, for instance, always logged "Transport: yandex" while correctly
running boards.
Print the actual configured carriers when there are any (from
confTransports or the raw --transports flag), the flag's value only
in true single-transport mode.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Follow-up to e8f735a (deploy/node-install.sh -> node-v1.0.1): the
wizard's SSH-side installer was still fetching and hash-verifying the
old, pre-fix script by commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
isExit (client vs. exit for Session/encryption directionality) was
computed from *role right after flag.Parse(), before the .conf file's
"Role = exit" line gets applied a few lines later. Every node-wizard
deployment runs the core as `--config node.conf` with no --role on the
command line, so isExit stayed false for every exit node: the Session
came up side=CLIENT, the direct transport refused to start ("DialAddr
is empty", it had a Listen= not a Dial=), vyandex hit the client-only
auth flow instead of the passive exit one, and the whole thing died
with "session: handshake failed: handshake timed out" after the
client-side handshake timeout - then systemd (Restart=always) looped
it forever, roughly every 26s.
Recompute isExit right after the config's Role is applied, before it's
used to build the .conf's [Transport] specs and the Session itself.
Bumps deploy/node-install.sh to node-v1.0.1 with this fix so new and
upgraded node deployments stop hitting it; the binaries were built
with the Node release workflow's exact recipe (see its SHA-256 check).
Confirmed live: deployed the fixed binary to an existing broken node
(root@191.44.112.34, channel of-kqszey) in place of the crash-looping
one; both channels came up clean (0 restarts, listeners bound) and a
real client completed the handshake and pushed encrypted traffic
through it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Points the node-deployment wizard's install-script fetch at this repo's
own commit instead of meepo161/openfluxfork, matching the previous commit
that moved the script's own download source. TestPinnedScriptHash confirms
the hash.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
deploy/node-install.sh downloaded the exit-node core from
meepo161/openfluxfork's releases; point it at this repository's own
node-v* release instead (published by node-release.yml, unchanged
otherwise), and fix that workflow's Go version pin (1.26.8, stale vs.
go.mod's 1.26.4) so its reproducible build actually reproduces.
Add release.yml: a v* tag cross-compiles the CLI for every supported
platform and publishes it here, replacing the ad-hoc manual 0.0.x releases.
Drop docs/plans/*coordinated-node-release*: that design dispatched a
release in meepo161/openfluxfordesktop from a core tag here. The client
apps are now self-sufficient (their own submodule-pinned core, their own
release workflow), so the cross-repo dispatch it planned no longer applies.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OpenFluxAndroid and OpenFluxDesktop now run the Compose Multiplatform app
and shared module from meepo161/OpenFluxClient (moved with his agreement),
instead of OpenFluxAndroid's old standalone single-transport APK.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The androidApp build previously needed a separate meepo161/openfluxfork
checkout (fork-main branch) alongside this repo just to find mobile/; it now
ships as part of the core, so one checkout is enough for both the CLI and
the mobile bindings.
Authorship of mobile/ is preserved as-is (meepo161, from
github.com/meepo161/openfluxfork@fork-main): this merge only relocates it.
Both bench roles compared *role against roleExit, which is never true
for "bench-send" or "bench-sink" - so a negotiated session between two
bench processes had both sides resolve to CLIENT, and neither ever
played the passive/exit side that answers a hello with a challenge.
The handshake retried forever. The same bug swapped nothing in
NewEncryptedTransport's key derivation for a plain (non-negotiated)
encrypted bench run, since both sides picked the same key direction.
Introduce isExit = role==exit || role==bench-sink (sink is the
always-listening side, like an exit; send is the initiator, like a
client) and use it everywhere the session/encryption/oneme/cupsonline/
direct construction needs to know which side this process plays.
Verified with a local negotiated direct-transport bench-send/bench-sink
run, which previously hung retrying hellos forever and now completes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every run logged "=== Universal Bypass Tool ===" instead of "===
OpenFlux ===" - a leftover from the project's pre-rename history that
never got updated in this one log line. README.md's own verification
steps already say to look for "=== OpenFlux ===", so this was also
inconsistent with the documented behavior, not just a naming concern.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
README.md/README.ru.md were still describing Windows as SOCKS5-only,
missing --yandex-cookies-file, --http-proxy, and --node-wizard (all
folded in by the encrypted-logging hotfix merge). Also both docs and
PROTOCOL_NEGOTIATION.md said the replay window was 64 packets; the code
(transport/session.go's replayWindowSize) has been 4096 since the
session/manager reliability rework.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Android bridge stays in the Android fork, so the node wizard and the
provision package no longer live "next to mobile/node.go" / on "the phone
side". Reword those comments to match; the code is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Base is hotfix/oflx-encrypted-logging-context; in every place both trees
touched the same code, the hotfix version wins (3-level logger, heard-first
routing + MarkStalled, replay window, boards JSON API, robust SOCKS5 auth).
Brought over from meepo161/openfluxfork@fork-main (the mobile/ Android bridge
is intentionally left in the Android fork):
- provision/ + node_wizard.go + --node-wizard: SSH VDS exit-node install.
- deploy/node-install.sh + .github/workflows/node-release.yml: pinned,
hash-verified node release.
- netbind/ + tun_windows.go + --inbound=tun on Windows (Wintun): full tunnel
with the core's own sockets bound to the real interface; dial sites in
direct/boards/vyandex/cupsonline/mailru/yandex now dial bound.
- transport/yandex/cookies.go + --yandex-cookies-file: Netscape cookie import
into the shared vyandex jar (sessions and single-transport alike).
- yandex: CheckVolgaDocument (doc suitability for the wizard) and the PoW
captcha fix (a rejected challenge is a captcha, not a bad document).
- --http-proxy: HTTP CONNECT proxy over the same tunnel as SOCKS5.
- ipcStatusLoop: per-second IPC Status frame with the active carrier
(Session.LiveTransports / ActiveTransport).
Builds for darwin, windows and linux; go vet and the full test suite pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Brings the validated best-of line onto the encrypted-logging hotfix:
multi-transport session routing (heard-first carriers, route around
checks), 4096-packet replay window, the -d/-dd/-ddd logging scheme with
the mobile log sink, KDF-context derivation matching the Android client,
socks5/tunnel core changes, vyandex recovery and the cupsonline KDF fix.
best-of is taken as the base for every conflicted file (it already holds
functional supersets of the hotfix's committed changes); the hotfix-only
logging helpers Verbosef, Sensitivef and Redact are ported onto best-of's
level constants so the encrypted-logging WIP keeps compiling.
Build, go vet and transport/network/utils tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The room list is created by the exit when it starts and handed to
clients as --cupsonline-url, so the client derived the context from it
while the exit, which has no URL, used "http://#": different keys and
no handshake over real cups.online in --transports mode. It is skipped
like a missing URL now, which is also what upstream exits derive.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Data sequence numbers are shared by every carrier, and the receiver took
a packet only within 64 of the newest one. Carriers of equal priority
share flows; with one 150ms slower than the other, 1532 of 3000 packets
arrived at all, since everything on the slow one came in more than 64
behind. cupsonline reorders whole batches (up to 64 packets each) across
its rooms, so one late frame was enough.
The window is now a 4096-bit ring. Receiver-only, no wire change.
Also: TestSessionReplayWindow fed the exit packets as soon as Start
returned, but the exit returns before it is ready; it now waits, which
fixes the failure seen under -race.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A carrier can be connected on one side and stuck on the other: a
document attached while the exit's side waits on a captcha. Until the
first keepalive pong, which comes on the second tick (~20s), liveness
was IsConnected alone, so a stuck carrier ranked above a working one
took all data and control. In the new matrix test every such case
failed: data went nowhere for ~20s after connecting, the exit's
AuthRequired took ~20s to reach the client, and a carrier that hit a
check mid-session swallowed its own AuthRequired (it never arrived).
- Routing takes carriers the peer was heard on within linkTimeout
first, in priority order. Connected ones stand in only when none was
heard lately, so a peer that predates keepalive still works.
- Session.MarkStalled forgets that a carrier was heard. The exit's
Manager calls it when a transport reports a check, and the client's
when AuthRequired arrives, so both sides route around the stuck
carrier at once, the AuthRequired and the cookies included. Anything
arriving on it makes it heard again.
- A carrier that (re)starts in an established session is pinged at
once, so it is heard without waiting for a keepalive tick.
No wire change.
matrix_test.go runs a client and an exit Manager, wired as main.go does
for --negotiate, over in-process carriers named after the real types:
- all 127 combinations handshake, carry data both ways and fail over
through each carrier in turn;
- for every ordered pair, a carrier stuck on a check at the exit (down,
or claiming to be connected) is brought up through the other one:
AuthRequired out, cookies back, then it carries data alone;
- a carrier that hits a check mid-session is routed around at once.
Worst cases now: data 18ms after connecting, AuthRequired 1.3s, 0.5s
mid-session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The oflx hotfix made the context follow per-type URLs, but in a fixed
type order (yandex, vyandex, boards, ...) and fell back to --transport
or "openflux". OpenFlux-Android derives it for a Session profile as the
URL of the highest-priority transport that has one, else "http://#",
so an exit on the hotfix could not talk to the app whenever it had no
--url or the priorities did not follow that type order.
pickSessionContext now works on the final transport list:
--session-context explicit override
--url if set and not the "http://#" placeholder
transports URL of the highest-priority transport with one
fallback "http://#"
The fallback is what every earlier build used without --url, so nodes
with no document URL (direct, oneme) keep their key. The one change
from upstream is a node with per-type URLs and no --url: it used
"http://#" there; pass --session-context=http://# to keep talking to
such an old peer. --share links carry the context either way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0 off: status lines only
1 -d packet movement, one line per IPv4 packet:
-> 52 bytes - UDP 10.10.10.2:53000 -> 8.8.8.8:53 len=42 ttl=64
2 -dd plus operational logs (sessions, carriers, crypto, control)
3 -ddd plus hexdumps of packets and ciphertext
--debug=N sets the level directly; a bare --debug means -d. Only a run of
d's counts as the counted flag, so -direct-listen=... and other
single-dash long flags starting with d are no longer turned into
--debug. -d=N is accepted too. --sensetive is accepted as --sensitive,
as the previous commit message promised.
utils:
- Packetf logs at level 1, Debugf at level 2, IsVerbose() means level 3;
- the logger is created once and SetOutput swaps its writer, so the
mobile bridges can flip the level and the output while goroutines of
the previous connection are logging (from OpenFlux-Android 80fc99d);
- SetLogSink mirrors debug and packet lines to an embedding app, as the
Android bridge expects; EnableDebug/SetDebug(true) mean level 2, what
debug meant before levels;
- the unused Redact, Sensitivef and Verbosef are gone.
network.LogPacket is the one place that prints a packet line and, at
level 3, its hexdump. Every packet is logged once per side: the [NIC]
lines duplicated [TUNNEL] and are gone, and the iOS L3 path and
PacketTunnel, which logged nothing, now log too. On both sides "->"
points towards the internet and "<-" back towards the device: the L4
exit used to print them the other way round from L3 and the client, so
one flow now reads the same in both logs.
--sensitive now covers what can reveal a secret:
- key material, as before;
- hexdumps of plaintext frames (crypto plaintext, batch records, control
messages): control messages carry cookie jars, including an account
login, so -ddd alone never dumps them. Packet and ciphertext hexdumps
need only -ddd.
The [KEY] line and the [KEYDUMP] digest no longer print a SHA-256 of the
secret without --sensitive: it let anyone with the log test guesses
without paying for scrypt. The digest keeps the context and derived-key
prefixes, which is enough to compare peers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From damnurmum/OpenFlux-Android (merged there in d3d8a10):
- socks5: optional RFC 1929 username/password auth (SetAuth), running
byte counters (BytesSent/BytesReceived), explicit reply codes and
per-connection logs;
- tunnel: DialTCP resolves host names itself and picks an IPv4 address
instead of failing when the resolver returns IPv6 first.
On top of that:
- authenticate reads the request with io.ReadFull field by field; a
single Read could return part of it and reject valid credentials;
- CONNECT flows log their byte totals, as the oflx hotfix did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fix landed upstream in cc76773; this is the regression test from
the OpenFlux-Android fork (19baffa).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
transport: make --sensitive actually compile and behave.
* encrypted.go: rebuild file cleanly. The previous patch inserted the
Sensitive() guard in the wrong place, so master / clientToExit /
exitToClient / sendAEAD / receiveAEAD / sendDirection / receiveDirection
ended up outside their declaring scope and the package stopped building.
New layout:
- always emit one [KEYDUMP] digest line (sha256 prefixes only),
safe to paste between peers when debugging a mismatch;
- emit the full key dump (secret hex, master, directional keys,
sendKey/recvKey) only when --sensitive is set;
- counters (sendOK/sendErr/recvOK/recvFail/recvReplay/recvBadHdr/
recvBadLen) are unchanged and reachable via CryptoStats().
transport: canonical one-line packet logging everywhere.
* network/format.go (new): FormatPacket(dir, pkt) produces
<- 40 bytes - TCP 10.10.10.2:53725 -> 216.58.205.163:443 [RST] seq=... ack=... win=... len=40 ttl=64
-> 52 bytes - UDP 10.10.10.2:53000 -> 8.8.8.8:53 len=42 ttl=64
DirInbound = "<-", DirOutbound = "->". DescribeIPv4 kept as a
compatibility shim (no arrow) for callers that already know the
direction.
* tunnel/l3/l3.go: handleFromTransport logs with "->" (packet leaving
for the internet), handleFromInternet logs with "<-" (packet coming
back). Dropped packets that aren't addressed to us are logged at
verbose level with the "(not for us)" suffix.
* tunnel/tunnel.go (L4) and tunnel/endpoint.go (gVisor NIC): both
directions logged via FormatPacket. The exit-side proxy and the
client-side stack now produce identical line formats, so a packet
can be followed end to end by matching src/dst/ports.
* tun_darwin.go: readFromTun logs "->", writeToTun logs "<-", matching
the L3 and L4 conventions.
* socks5/socks5.go: accept, CONNECT, and byte counters on both
directions of every relayed flow now logged in the same shape.
main: accept --sensetive as an alias of --sensitive.
* Both spellings map to the same flag; whichever the operator passes
wins. -S is reserved for the short form (uppercase so it does not
collide with -s / --socks5). .conf key "Sensitive" still works; the
alias is CLI-only.
main: pickSessionContext, actually derive a stable KDF context.
* Before: sessionContext was taken from --url, which is the placeholder
"http://#" whenever the operator supplies URLs per transport
(--yandex-url, [Transport "yandex"] URL = ...). Result: two peers
with identical secrets derived different keys and the handshake
failed with "message authentication failed" on both sides.
* After: pickSessionContext reads whichever way the document URL was
actually provided, in priority order:
1. --session-context (explicit override)
2. --url (unless it is the placeholder)
3. any --<type>-url (yandex/vyandex/boards/mailru/cupsonline)
4. [Transport] URL from --config
5. --transport (last resort, e.g. direct or oneme)
* New --session-context flag and .conf key SessionContext let operators
rotate keys between otherwise identical deployments.
utils: counted debug levels and --sensitive gate.
* --debug is now an int: -d / --debug=1 = operational logs, -dd /
--debug=2 = hexdumps. -ddd is clamped to 2. IsVerbose() stays
Level() >= 2 so existing call sites keep working.
* utils.Sensitive() / utils.Sensitivef() gate secrets. Nothing that
reveals a key or user payload is printed unless --sensitive is on,
even at -dd.
* utils.Redact(label, b) gives a one-liner that is "" at level 0, a
sha256 prefix at level 1, and raw hex at level 2 with --sensitive.
Notes:
* --sensetive is accepted, not advertised.
* .conf accepts Debug as either a bool or a 0/1/2 integer.
* No wire-format change in this commit: batched v2, AES-GCM envelope,
and the negotiated session format are untouched. Only logging and
the KDF-context derivation changed.
transport: make --sensitive actually compile and behave.
* encrypted.go: rebuild file cleanly. The previous patch inserted the
Sensitive() guard in the wrong place, so master / clientToExit /
exitToClient / sendAEAD / receiveAEAD / sendDirection / receiveDirection
ended up outside their declaring scope and the package stopped building.
New layout:
- always emit one [KEYDUMP] digest line (sha256 prefixes only),
safe to paste between peers when debugging a mismatch;
- emit the full key dump (secret hex, master, directional keys,
sendKey/recvKey) only when --sensitive is set;
- counters (sendOK/sendErr/recvOK/recvFail/recvReplay/recvBadHdr/
recvBadLen) are unchanged and reachable via CryptoStats().
transport: canonical one-line packet logging everywhere.
* network/format.go (new): FormatPacket(dir, pkt) produces
<- 40 bytes - TCP 10.10.10.2:53725 -> 216.58.205.163:443 [RST] seq=... ack=... win=... len=40 ttl=64
-> 52 bytes - UDP 10.10.10.2:53000 -> 8.8.8.8:53 len=42 ttl=64
DirInbound = "<-", DirOutbound = "->". DescribeIPv4 kept as a
compatibility shim (no arrow) for callers that already know the
direction.
* tunnel/l3/l3.go: handleFromTransport logs with "->" (packet leaving
for the internet), handleFromInternet logs with "<-" (packet coming
back). Dropped packets that aren't addressed to us are logged at
verbose level with the "(not for us)" suffix.
* tunnel/tunnel.go (L4) and tunnel/endpoint.go (gVisor NIC): both
directions logged via FormatPacket. The exit-side proxy and the
client-side stack now produce identical line formats, so a packet
can be followed end to end by matching src/dst/ports.
* tun_darwin.go: readFromTun logs "->", writeToTun logs "<-", matching
the L3 and L4 conventions.
* socks5/socks5.go: accept, CONNECT, and byte counters on both
directions of every relayed flow now logged in the same shape.
main: accept --sensetive as an alias of --sensitive.
* Both spellings map to the same flag; whichever the operator passes
wins. -S is reserved for the short form (uppercase so it does not
collide with -s / --socks5). .conf key "Sensitive" still works; the
alias is CLI-only.
main: pickSessionContext, actually derive a stable KDF context.
* Before: sessionContext was taken from --url, which is the placeholder
"http://#" whenever the operator supplies URLs per transport
(--yandex-url, [Transport "yandex"] URL = ...). Result: two peers
with identical secrets derived different keys and the handshake
failed with "message authentication failed" on both sides.
* After: pickSessionContext reads whichever way the document URL was
actually provided, in priority order:
1. --session-context (explicit override)
2. --url (unless it is the placeholder)
3. any --<type>-url (yandex/vyandex/boards/mailru/cupsonline)
4. [Transport] URL from --config
5. --transport (last resort, e.g. direct or oneme)
* New --session-context flag and .conf key SessionContext let operators
rotate keys between otherwise identical deployments.
utils: counted debug levels and --sensitive gate.
* --debug is now an int: -d / --debug=1 = operational logs, -dd /
--debug=2 = hexdumps. -ddd is clamped to 2. IsVerbose() stays
Level() >= 2 so existing call sites keep working.
* utils.Sensitive() / utils.Sensitivef() gate secrets. Nothing that
reveals a key or user payload is printed unless --sensitive is on,
even at -dd.
* utils.Redact(label, b) gives a one-liner that is "" at level 0, a
sha256 prefix at level 1, and raw hex at level 2 with --sensitive.
Notes:
* --sensetive is accepted, not advertised.
* .conf accepts Debug as either a bool or a 0/1/2 integer.
* No wire-format change in this commit: batched v2, AES-GCM envelope,
and the negotiated session format are untouched. Only logging and
the KDF-context derivation changed.