diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-26 22:41:53 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-26 22:41:53 +0000 |
| commit | bbe04dc8c96b4c4766d2771d3438233bf82687c0 (patch) | |
| tree | 62e5a1febfb8e9bc38d133ac7981e9ef93918484 /docs | |
| parent | 991853fa55a749d28d6f9e507ce72b289df92785 (diff) | |
| parent | 9e39a50d1f110020b0841d31e4b931aa877cc448 (diff) | |
Merge upstream v1.2.19 into the fork
Merges upstream tag v1.2.19 (9e39a50) into merge/upstream-1.2.18 (991853f).
Unlike the 1.2.15->1.2.18 replay, bc3de30 is a true ancestor of our branch via
a9b4f89, so this is an ordinary three-way merge with merge-base bc3de30.
Delta: 36 files, +3265/-163, the Tor onion transport (managed Tor, signed
direct onion endpoints, onion-only dial mode) and the nHDNext restore fix.
src/main.cpp, main.h, script.cpp/h and db.cpp/h are byte-identical between the
two tags, and the poll() socket loop carries no diff hunks, so the merge is
consensus-neutral and the soak-tested socket paths are unchanged. relay/ is
untouched; the payout ledger (rpc.cpp) is untouched.
7 conflict blocks in 6 files, resolved as follows.
Makefile -- VERSION. Ours: stays 1.2.19+sec with the naming comment updated,
so artifacts from this tree cannot be mistaken for the upstream release.
src/Makefile, src/makefile.mingw -- HEADERS list. Ours (no market.h) plus
their tor.h. NODE_OBJS auto-merged correctly: no market.o, tor.o present.
src/selftest.cpp -- known-tests list. Union: payout-ledger (ours) and
managed-tor (theirs).
src/main_gui.cpp -- one block. Our /dumpaddresses kept; their
BtfStopManagedTor() on the StartNode failure path taken. The /rvrelay pin,
/noannounce and the marketplace removal auto-merged and were verified intact.
src/nostr.cpp -- two blocks in ResolveDescriptor. Signature takes both
out-params (our fExchangeOk, their rawOut). The body keeps our
drain-to-EOSE read -- returning early poisons the pooled socket for the next
caller -- and adds their rawOut capture. Our TLS 1.2 floor
(SSL_CTX_set_min_proto_version) is untouched by this merge; upstream 1.2.19
still has no floor of its own.
Upstream's release commit also added four build logs and a compiled Python
cache (build-1219.log, build-17.log, build-appimage.log, build-node.log,
scripts/__pycache__/bitflash_chain.cpython-313.pyc). Removed here; they are
build output, not source.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/mining-calculator.md | 22 | ||||
| -rw-r--r-- | docs/pool-directory.md | 18 | ||||
| -rw-r--r-- | docs/release-verification.md | 18 | ||||
| -rw-r--r-- | docs/rendezvous.md | 72 | ||||
| -rw-r--r-- | docs/site.md | 115 | ||||
| -rw-r--r-- | docs/tor.md | 159 |
6 files changed, 386 insertions, 18 deletions
diff --git a/docs/mining-calculator.md b/docs/mining-calculator.md index 352eea1..e6cb61b 100644 --- a/docs/mining-calculator.md +++ b/docs/mining-calculator.md | |||
| @@ -84,3 +84,25 @@ The script is intentionally standalone so the same math can later back a | |||
| 84 | website widget or status dashboard. Until a public price exists, a website | 84 | website widget or status dashboard. Until a public price exists, a website |
| 85 | calculator should keep price as a manual field and label fiat profit as | 85 | calculator should keep price as a manual field and label fiat profit as |
| 86 | speculative. | 86 | speculative. |
| 87 | |||
| 88 | ## Website Fields | ||
| 89 | |||
| 90 | A public calculator should ask for: | ||
| 91 | |||
| 92 | - miner hashrate and unit; | ||
| 93 | - power draw in watts; | ||
| 94 | - electricity price per kWh; | ||
| 95 | - pool fee percent; | ||
| 96 | - optional BTF price; | ||
| 97 | - either current Bitflash difficulty or current network hashrate. | ||
| 98 | |||
| 99 | The output should separate: | ||
| 100 | |||
| 101 | - estimated BTF per day; | ||
| 102 | - estimated gross fiat revenue, if a manual price was provided; | ||
| 103 | - electricity cost; | ||
| 104 | - net profit/loss; | ||
| 105 | - the difficulty/hashrate assumptions used. | ||
| 106 | |||
| 107 | Never present the fiat number as a market quote unless it comes from a real | ||
| 108 | public market. Before that exists, label it as a manual scenario. | ||
diff --git a/docs/pool-directory.md b/docs/pool-directory.md index 76269b9..ff0640a 100644 --- a/docs/pool-directory.md +++ b/docs/pool-directory.md | |||
| @@ -26,6 +26,10 @@ The generated output contains: | |||
| 26 | - `index.html`: human-readable ranking page. | 26 | - `index.html`: human-readable ranking page. |
| 27 | - `pools.json`: machine-readable API for `api.bitflash.network/pools.json`. | 27 | - `pools.json`: machine-readable API for `api.bitflash.network/pools.json`. |
| 28 | 28 | ||
| 29 | The website is a convenience layer, not a consensus or discovery dependency. | ||
| 30 | Nodes discover pools through Nostr descriptors and `.btf`; a missing website | ||
| 31 | must not stop mining or pool operation. | ||
| 32 | |||
| 29 | When a source is an HTTP(S) `pool_status.json`, the directory links to a | 33 | When a source is an HTTP(S) `pool_status.json`, the directory links to a |
| 30 | `pool_rounds.json` proof file next to it. That file records the block height, | 34 | `pool_rounds.json` proof file next to it. That file records the block height, |
| 31 | block hash, coinbase txid, coinbase value, pool fee, total shares, per-address | 35 | block hash, coinbase txid, coinbase value, pool fee, total shares, per-address |
| @@ -41,3 +45,17 @@ Ranking order is intentionally simple: | |||
| 41 | 45 | ||
| 42 | An entry is considered online when the source says the pool is running and its | 46 | An entry is considered online when the source says the pool is running and its |
| 43 | status file is no more than 120 seconds old. | 47 | status file is no more than 120 seconds old. |
| 48 | |||
| 49 | ## Recommended Hostnames | ||
| 50 | |||
| 51 | Use: | ||
| 52 | |||
| 53 | ```text | ||
| 54 | pool.bitflash.network human-readable pool directory | ||
| 55 | api.bitflash.network/pools.json | ||
| 56 | status.bitflash.network relay/node health, not pool ranking | ||
| 57 | docs.bitflash.network operator setup guide | ||
| 58 | ``` | ||
| 59 | |||
| 60 | The GUI may use the API as a shortcut for humans, but it should keep the | ||
| 61 | existing decentralized pool discovery path as the source of truth. | ||
diff --git a/docs/release-verification.md b/docs/release-verification.md index 32de74f..b8eaf5e 100644 --- a/docs/release-verification.md +++ b/docs/release-verification.md | |||
| @@ -34,6 +34,16 @@ From a shell with `curl` or `wget`, `sha256sum` and `gpg`: | |||
| 34 | scripts/verify-release.sh latest | 34 | scripts/verify-release.sh latest |
| 35 | ``` | 35 | ``` |
| 36 | 36 | ||
| 37 | The public release host should expose latest-file aliases for convenience: | ||
| 38 | |||
| 39 | ```text | ||
| 40 | https://releases.bitflash.network/SHA256SUMS | ||
| 41 | https://releases.bitflash.network/SHA256SUMS.asc | ||
| 42 | ``` | ||
| 43 | |||
| 44 | Those aliases must point at the current versioned directory. They do not replace | ||
| 45 | signature verification; they only make the common manual path shorter. | ||
| 46 | |||
| 37 | For a specific release: | 47 | For a specific release: |
| 38 | 48 | ||
| 39 | ```bash | 49 | ```bash |
| @@ -71,6 +81,14 @@ Publish, alongside the assets, `SHA256SUMS` and `SHA256SUMS.asc`, and update | |||
| 71 | is ever rotated, announce the new fingerprint clearly and update the embedded | 81 | is ever rotated, announce the new fingerprint clearly and update the embedded |
| 72 | key in `verify-release.sh`. | 82 | key in `verify-release.sh`. |
| 73 | 83 | ||
| 84 | For Windows releases with managed Tor built in, run `make windows-tor`. It | ||
| 85 | creates `Bitflash-*-windows-with-tor.zip` by downloading the Tor Expert Bundle, | ||
| 86 | checking the pinned SHA256 in `scripts/package-windows-tor.sh`, optionally | ||
| 87 | verifying the Tor Project `.asc` signature, and placing `tor/tor.exe` next to | ||
| 88 | `Bitflash.exe`. For release builds, run `TOR_VERIFY_GPG=required make | ||
| 89 | windows-tor` so the signature check is mandatory. Include that zip in | ||
| 90 | `SHA256SUMS` like any other release asset. | ||
| 91 | |||
| 74 | ## Why this matters | 92 | ## Why this matters |
| 75 | 93 | ||
| 76 | `SHA256SUMS` alone protects against a broken download, but not against someone | 94 | `SHA256SUMS` alone protects against a broken download, but not against someone |
diff --git a/docs/rendezvous.md b/docs/rendezvous.md index ac8e598..68b5df3 100644 --- a/docs/rendezvous.md +++ b/docs/rendezvous.md | |||
| @@ -18,11 +18,16 @@ A connection to a `.btf` service has three layers: | |||
| 18 | 1. **Discovery (Nostr).** The service publishes a signed descriptor as a Nostr | 18 | 1. **Discovery (Nostr).** The service publishes a signed descriptor as a Nostr |
| 19 | event. A caller fetches it by the service public key and verifies the | 19 | event. A caller fetches it by the service public key and verifies the |
| 20 | signature. The descriptor says which rendezvous relay the service is waiting | 20 | signature. The descriptor says which rendezvous relay the service is waiting |
| 21 | at and which encryption key to use. | 21 | at, which encryption key to use, and optionally which Tor hidden service can |
| 22 | be dialed directly. | ||
| 22 | 2. **Rendezvous (TCP).** Caller and service both connect out to the same relay | 23 | 2. **Rendezvous (TCP).** Caller and service both connect out to the same relay |
| 23 | and are paired by public key. Neither needs an inbound port or a public IP. | 24 | and are paired by public key. Neither needs an inbound port or a public IP. |
| 24 | After pairing the relay is a blind byte pipe. | 25 | After pairing the relay is a blind byte pipe. |
| 25 | 3. **Encrypted channel (X25519 + XChaCha20-Poly1305).** Over the paired pipe the | 26 | 3. **Direct onion fallback bypass.** If the descriptor carries a valid signed |
| 27 | `.onion:port` endpoint and the caller is running with Tor mode, the caller | ||
| 28 | tries that endpoint before using rendezvous. This removes the rendezvous | ||
| 29 | relay from the data path for peers that choose to run hidden services. | ||
| 30 | 4. **Encrypted channel (X25519 + XChaCha20-Poly1305).** Over the paired pipe the | ||
| 26 | caller and service run an ephemeral to static handshake and then exchange | 31 | caller and service run an ephemeral to static handshake and then exchange |
| 27 | authenticated frames. The relay sees only ciphertext. | 32 | authenticated frames. The relay sees only ciphertext. |
| 28 | 33 | ||
| @@ -107,6 +112,8 @@ The default rendezvous port is `8434`. | |||
| 107 | The descriptor is a JSON object, signed with the identity key over a domain | 112 | The descriptor is a JSON object, signed with the identity key over a domain |
| 108 | separated digest of its own fields. | 113 | separated digest of its own fields. |
| 109 | 114 | ||
| 115 | Legacy descriptor: | ||
| 116 | |||
| 110 | ```json | 117 | ```json |
| 111 | { | 118 | { |
| 112 | "v": 2, | 119 | "v": 2, |
| @@ -118,7 +125,22 @@ separated digest of its own fields. | |||
| 118 | } | 125 | } |
| 119 | ``` | 126 | ``` |
| 120 | 127 | ||
| 121 | The signed digest is: | 128 | Onion-capable descriptor: |
| 129 | |||
| 130 | ```json | ||
| 131 | { | ||
| 132 | "v": 3, | ||
| 133 | "pubkey": "<hex 32-byte x-only identity key>", | ||
| 134 | "enc": "<hex 32-byte X25519 public key>", | ||
| 135 | "meeting_node": "host:port", | ||
| 136 | "onion": "<56-char-v3-name>.onion:8433", | ||
| 137 | "created": <unix seconds>, | ||
| 138 | "sig": "<legacy v2 signature>", | ||
| 139 | "sig3": "<signature over the onion-capable digest>" | ||
| 140 | } | ||
| 141 | ``` | ||
| 142 | |||
| 143 | The legacy signed digest is: | ||
| 122 | 144 | ||
| 123 | ``` | 145 | ``` |
| 124 | digest = SHA256( ".btf-descriptor-v2" | 146 | digest = SHA256( ".btf-descriptor-v2" |
| @@ -128,17 +150,37 @@ digest = SHA256( ".btf-descriptor-v2" | |||
| 128 | || created (8 bytes, big endian) ) | 150 | || created (8 bytes, big endian) ) |
| 129 | ``` | 151 | ``` |
| 130 | 152 | ||
| 131 | The signature is a deterministic BIP340 Schnorr signature (no auxiliary | 153 | `sig` is kept on v3 descriptors so old nodes can still resolve and dial through |
| 132 | randomness) over `digest` by the identity key. | 154 | the rendezvous relay. The onion-capable digest is: |
| 155 | |||
| 156 | ``` | ||
| 157 | digest3 = SHA256( ".btf-descriptor-v3-onion" | ||
| 158 | || pubkey[32] | ||
| 159 | || enc (raw bytes of the string) | ||
| 160 | || meeting_node (raw bytes of the string) | ||
| 161 | || onion (raw bytes of the normalized string) | ||
| 162 | || created (8 bytes, big endian) ) | ||
| 163 | ``` | ||
| 164 | |||
| 165 | `sig` and `sig3` are deterministic BIP340 Schnorr signatures (no auxiliary | ||
| 166 | randomness) over their respective digests by the identity key. | ||
| 133 | 167 | ||
| 134 | **Verification** (untrusted input, must never throw): parse the JSON, require all | 168 | **Verification** (untrusted input, must never throw): parse the JSON, require all |
| 135 | fields with the right types, confirm `pubkey` equals the address being resolved, | 169 | fields with the right types, confirm `pubkey` equals the address being resolved, |
| 136 | recompute the digest, and verify the Schnorr signature. Any failure rejects the | 170 | recompute the digest, and verify the Schnorr signature. Any failure rejects the |
| 137 | descriptor. | 171 | descriptor. |
| 138 | 172 | ||
| 173 | If `onion` is present, readers accept it only when it is a normalized v3 onion | ||
| 174 | endpoint and `sig3` verifies. A bad or tampered onion field is ignored; the v2 | ||
| 175 | descriptor can still be used for rendezvous fallback if `sig` verifies. | ||
| 176 | |||
| 139 | `created` lets a reader prefer the freshest descriptor when more than one is | 177 | `created` lets a reader prefer the freshest descriptor when more than one is |
| 140 | seen. A node only publishes a descriptor once it has actually registered at a | 178 | seen. A node without a direct onion endpoint only publishes a descriptor once it |
| 141 | `meeting_node`, so it never advertises a relay it cannot be reached at. | 179 | has actually registered at a `meeting_node`, so it never advertises a relay it |
| 180 | cannot be reached at. A node with a direct onion endpoint may publish before | ||
| 181 | rendezvous registration using `meeting_node = "rendezvous-pending"`; Tor peers | ||
| 182 | can still dial the signed onion endpoint, and rendezvous becomes fallback once | ||
| 183 | registration succeeds. | ||
| 142 | 184 | ||
| 143 | ## 6. Rendezvous transport | 185 | ## 6. Rendezvous transport |
| 144 | 186 | ||
| @@ -243,8 +285,9 @@ tunnel. | |||
| 243 | not published anywhere. | 285 | not published anywhere. |
| 244 | - **Forward secrecy.** The caller's per connection ephemeral key means a later | 286 | - **Forward secrecy.** The caller's per connection ephemeral key means a later |
| 245 | compromise of the service encryption key does not decrypt past sessions. | 287 | compromise of the service encryption key does not decrypt past sessions. |
| 246 | - **IP privacy.** Neither side needs a public IP or an inbound port. The observable | 288 | - **IP privacy.** Neither side needs a public IP or an inbound port for |
| 247 | network fact is that each side has a TCP connection to a relay. | 289 | rendezvous. With a signed `.onion` endpoint, the reachable side exposes only a |
| 290 | Tor hidden service and the relay is not in the peer connection path. | ||
| 248 | 291 | ||
| 249 | ## 10. Threat model and limitations | 292 | ## 10. Threat model and limitations |
| 250 | 293 | ||
| @@ -265,6 +308,11 @@ tunnel. | |||
| 265 | unreachable, discovery stalls until one is reachable again. Relay TLS is | 308 | unreachable, discovery stalls until one is reachable again. Relay TLS is |
| 266 | verified, which closes a man in the middle path on discovery, but availability | 309 | verified, which closes a man in the middle path on discovery, but availability |
| 267 | still rests on the relay set. | 310 | still rests on the relay set. |
| 311 | - **Direct onion needs Tor.** With `-managedtor`, Bitflash starts a Tor process, | ||
| 312 | writes a local hidden-service config, and advertises the generated onion | ||
| 313 | endpoint. With `-onionservice`, an operator can still run Tor manually and | ||
| 314 | provide the endpoint. In both cases Tor owns the hidden-service private key and | ||
| 315 | uptime. | ||
| 268 | 316 | ||
| 269 | ## 11. Wire reference | 317 | ## 11. Wire reference |
| 270 | 318 | ||
| @@ -273,9 +321,9 @@ tunnel. | |||
| 273 | | `.btf` Base32 alphabet | `abcdefghijklmnopqrstuvwxyz234567`, no padding | | 321 | | `.btf` Base32 alphabet | `abcdefghijklmnopqrstuvwxyz234567`, no padding | |
| 274 | | Address payload | `pubkey[32] || checksum[2]`, 55 Base32 chars | | 322 | | Address payload | `pubkey[32] || checksum[2]`, 55 Base32 chars | |
| 275 | | Address checksum | `SHA256(".btf-checksum-v1" || pubkey)[0..2]` | | 323 | | Address checksum | `SHA256(".btf-checksum-v1" || pubkey)[0..2]` | |
| 276 | | Descriptor domain sep | `.btf-descriptor-v2` | | 324 | | Descriptor domain sep | `.btf-descriptor-v2`; optional onion sep `.btf-descriptor-v3-onion` | |
| 277 | | Descriptor version | `2` | | 325 | | Descriptor version | `2`; `3` when a signed onion endpoint is present | |
| 278 | | Descriptor signature | BIP340 Schnorr, deterministic (no aux rand) | | 326 | | Descriptor signature | BIP340 Schnorr, deterministic (no aux rand), v3 keeps `sig` plus `sig3` | |
| 279 | | Nostr descriptor event | kind `38501`, `d = btf-descriptor-2` | | 327 | | Nostr descriptor event | kind `38501`, `d = btf-descriptor-2` | |
| 280 | | Nostr relay event | kind `38502`, `d = btf-relay-2` | | 328 | | Nostr relay event | kind `38502`, `d = btf-relay-2` | |
| 281 | | Nostr pool event | kind `38503`, `d = btf-pool-2` | | 329 | | Nostr pool event | kind `38503`, `d = btf-pool-2` | |
diff --git a/docs/site.md b/docs/site.md new file mode 100644 index 0000000..9526a61 --- /dev/null +++ b/docs/site.md | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | # Public Site Contract | ||
| 2 | |||
| 3 | The public Bitflash sites are generated from this repository. If a deployed | ||
| 4 | page and the source disagree, the deployed page is stale. | ||
| 5 | |||
| 6 | ## Canonical Navigation | ||
| 7 | |||
| 8 | Every public page should use the same top navigation: | ||
| 9 | |||
| 10 | | label | target | | ||
| 11 | |---|---| | ||
| 12 | | home | `https://bitflash.network/` | | ||
| 13 | | code | `https://git.bitflash.network/bitflash/bitflash` | | ||
| 14 | | docs | `https://docs.bitflash.network/` | | ||
| 15 | | downloads | `https://releases.bitflash.network/` | | ||
| 16 | | explorer | `https://explorer.bitflash.network/` | | ||
| 17 | | status | `https://status.bitflash.network/` | | ||
| 18 | |||
| 19 | The `code` link intentionally points at the repository, not the Forgejo | ||
| 20 | instance root. A visitor choosing "code" wants the source tree. | ||
| 21 | |||
| 22 | ## Documentation Pages To Publish | ||
| 23 | |||
| 24 | The docs index should expose these source files: | ||
| 25 | |||
| 26 | | page | source | why it matters | | ||
| 27 | |---|---|---| | ||
| 28 | | Manual | `README.md` | run, mine, recover, operate | | ||
| 29 | | Key derivation and address format | `docs/derivation.md` | independent wallet recovery | | ||
| 30 | | Release verification | `docs/release-verification.md` | pinned signing-key flow | | ||
| 31 | | Dependencies | `docs/dependencies.md` | pinned secp256k1/RandomX supply chain | | ||
| 32 | | Rendezvous and .btf addressing | `docs/rendezvous.md` | network design and caveats | | ||
| 33 | | Tor mode | `docs/tor.md` | `-tor`, `-socks`, onion relay operation | | ||
| 34 | | Fair launch verifier | `docs/fair-launch.md` | no-premine launch audit | | ||
| 35 | | UTXO set commitment | `docs/utxo-commitment.md` | deterministic supply/root audit | | ||
| 36 | | UTXO inclusion proofs | `docs/utxo-proofs.md` | proof for one unspent output | | ||
| 37 | | Mining calculator | `docs/mining-calculator.md` | local WhatToMine-style estimates | | ||
| 38 | | Public pool directory | `docs/pool-directory.md` | `pool_status.json`, `pools.json`, ranking | | ||
| 39 | |||
| 40 | ## Release Mirror Aliases | ||
| 41 | |||
| 42 | Each release remains under its versioned directory: | ||
| 43 | |||
| 44 | ```text | ||
| 45 | https://releases.bitflash.network/v1.2.19/SHA256SUMS | ||
| 46 | https://releases.bitflash.network/v1.2.19/SHA256SUMS.asc | ||
| 47 | ``` | ||
| 48 | |||
| 49 | The release root should also alias the latest files: | ||
| 50 | |||
| 51 | ```text | ||
| 52 | https://releases.bitflash.network/SHA256SUMS | ||
| 53 | https://releases.bitflash.network/SHA256SUMS.asc | ||
| 54 | https://releases.bitflash.network/Bitflash-latest-windows.zip | ||
| 55 | https://releases.bitflash.network/Bitflash-latest-x86_64.AppImage | ||
| 56 | https://releases.bitflash.network/bitflash-node-latest-x86_64 | ||
| 57 | ``` | ||
| 58 | |||
| 59 | The aliases are convenience only. Verification must still require the pinned | ||
| 60 | release key documented in `docs/release-verification.md`. | ||
| 61 | |||
| 62 | ## Static Explorer | ||
| 63 | |||
| 64 | `scripts/build-explorer.py` writes a static explorer with no backend: | ||
| 65 | |||
| 66 | ```text | ||
| 67 | index.html | ||
| 68 | style.css | ||
| 69 | explorer.js | ||
| 70 | logo.png | ||
| 71 | blocks.json | ||
| 72 | block/<height>.json | ||
| 73 | ``` | ||
| 74 | |||
| 75 | Serve it with a strict content policy: | ||
| 76 | |||
| 77 | ```text | ||
| 78 | default-src 'none'; | ||
| 79 | img-src 'self'; | ||
| 80 | style-src 'self'; | ||
| 81 | script-src 'self'; | ||
| 82 | connect-src 'self'; | ||
| 83 | base-uri 'none'; | ||
| 84 | form-action 'none'; | ||
| 85 | frame-ancestors 'none'; | ||
| 86 | ``` | ||
| 87 | |||
| 88 | The viewer must never use `innerHTML` for chain data. A malicious transaction | ||
| 89 | must be rendered as text, not interpreted as markup. | ||
| 90 | |||
| 91 | ## Cache Policy | ||
| 92 | |||
| 93 | Recommended defaults: | ||
| 94 | |||
| 95 | | path | cache | | ||
| 96 | |---|---| | ||
| 97 | | HTML pages | `public, max-age=0, must-revalidate` | | ||
| 98 | | `status.json` | `no-store` | | ||
| 99 | | `blocks.json`, `block/*.json` | `public, max-age=0, must-revalidate` | | ||
| 100 | | release assets | `public, max-age=14400, must-revalidate` | | ||
| 101 | | `SHA256SUMS`, signatures, keys | `public, max-age=0, must-revalidate` | | ||
| 102 | |||
| 103 | ## Security Headers | ||
| 104 | |||
| 105 | Recommended for all static pages: | ||
| 106 | |||
| 107 | ```text | ||
| 108 | Strict-Transport-Security: max-age=31536000; includeSubDomains | ||
| 109 | X-Content-Type-Options: nosniff | ||
| 110 | Referrer-Policy: no-referrer | ||
| 111 | Permissions-Policy: interest-cohort=(), geolocation=(), camera=(), microphone=() | ||
| 112 | ``` | ||
| 113 | |||
| 114 | Forgejo has its own application headers and should not blindly inherit the | ||
| 115 | static-site CSP. | ||
diff --git a/docs/tor.md b/docs/tor.md index 087c822..0016f8b 100644 --- a/docs/tor.md +++ b/docs/tor.md | |||
| @@ -25,15 +25,106 @@ bitflash -tor=127.0.0.1:9150 | |||
| 25 | bitflash -tor=[::1]:9050 | 25 | bitflash -tor=[::1]:9050 |
| 26 | ``` | 26 | ``` |
| 27 | 27 | ||
| 28 | For a SOCKS5 proxy that is not Tor, use `-socks` directly: | ||
| 29 | |||
| 30 | ```bash | ||
| 31 | bitflash -socks=127.0.0.1:9050 | ||
| 32 | bitflash -socks=[::1]:9050 | ||
| 33 | ``` | ||
| 34 | |||
| 35 | If both `-tor` and `-socks` are present, Tor mode wins and the node prints a | ||
| 36 | warning. That keeps a command line with `-tor` from silently becoming a custom | ||
| 37 | proxy route. | ||
| 38 | |||
| 39 | ## Managed Tor mode | ||
| 40 | |||
| 41 | `-managedtor` starts a Tor process for this node, writes a local `torrc`, creates | ||
| 42 | a v3 hidden service for the normal Bitflash P2P listener, routes outbound | ||
| 43 | discovery through that Tor instance, and signs the generated `.onion:8433` | ||
| 44 | endpoint into the node's `.btf` descriptor. | ||
| 45 | |||
| 46 | ```bash | ||
| 47 | bitflash -managedtor | ||
| 48 | ``` | ||
| 49 | |||
| 50 | Without a value, Bitflash looks for Tor in this order: | ||
| 51 | |||
| 52 | 1. `tor/tor.exe` or `tor/tor` beside the Bitflash binary; | ||
| 53 | 2. `tor.exe` or `tor` beside the Bitflash binary; | ||
| 54 | 3. `tor.exe` or `tor` on `PATH`. | ||
| 55 | |||
| 56 | Windows releases can also be built as `Bitflash-*-windows-with-tor.zip`. That | ||
| 57 | package includes the official Tor Expert Bundle under `tor/`, so the first path | ||
| 58 | above exists immediately after extraction and `bitflash -managedtor` needs no | ||
| 59 | separate Tor install. | ||
| 60 | |||
| 61 | The bundled-Tor package is created by: | ||
| 62 | |||
| 63 | ```bash | ||
| 64 | make windows-tor | ||
| 65 | ``` | ||
| 66 | |||
| 67 | The packaging script verifies a pinned SHA256 for the downloaded Tor Expert | ||
| 68 | Bundle before copying it into the release directory. When `gpg` is available it | ||
| 69 | also verifies the matching `.asc` signature against the pinned Tor Browser | ||
| 70 | Developers signing-key fingerprint. Release builds should use | ||
| 71 | `TOR_VERIFY_GPG=required make windows-tor` to make that second check mandatory. | ||
| 72 | The Tor binary is bundled; the runtime data directory, hidden-service key, and | ||
| 73 | control cookie are still created under the user's Bitflash data directory. | ||
| 74 | |||
| 75 | To point at a specific Tor executable: | ||
| 76 | |||
| 77 | ```bash | ||
| 78 | bitflash -managedtor=/opt/tor/bin/tor | ||
| 79 | bitflash -managedtor=C:\Tor Browser\Browser\TorBrowser\Tor\tor.exe | ||
| 80 | ``` | ||
| 81 | |||
| 82 | Managed Tor writes its state under the Bitflash data directory: | ||
| 83 | |||
| 84 | ```text | ||
| 85 | managed-tor/ | ||
| 86 | torrc | ||
| 87 | data/ | ||
| 88 | onion-service/ | ||
| 89 | ``` | ||
| 90 | |||
| 91 | The private key for the onion address lives in `managed-tor/onion-service/`. | ||
| 92 | Back up that directory if you want the same onion address after moving the node. | ||
| 93 | Delete it only if you intentionally want a fresh onion endpoint. | ||
| 94 | |||
| 95 | `-managedtor` takes precedence over `-tor` and `-socks`, because it must choose | ||
| 96 | the local SOCKS port it starts. Diagnostics report the current managed Tor | ||
| 97 | state, including the SOCKS port and onion endpoint once Tor has generated it. | ||
| 98 | |||
| 28 | When Tor mode is enabled: | 99 | When Tor mode is enabled: |
| 29 | 100 | ||
| 30 | - outbound Nostr relay dials use Tor; | 101 | - outbound Nostr relay dials use Tor; |
| 31 | - outbound `.btf` rendezvous relay dials use Tor; | 102 | - outbound `.btf` rendezvous relay dials use Tor; |
| 103 | - outbound direct peer dials prefer signed `.onion` endpoints when a peer | ||
| 104 | advertises one; | ||
| 32 | - destination names are sent to Tor as SOCKS5 domain-name CONNECT requests, so | 105 | - destination names are sent to Tor as SOCKS5 domain-name CONNECT requests, so |
| 33 | the local DNS resolver does not see them; | 106 | the local DNS resolver does not see them; |
| 34 | - direct `.onion` dials are refused unless a SOCKS5/Tor proxy is enabled; | 107 | - direct `.onion` dials are refused unless a SOCKS5/Tor proxy is enabled; |
| 35 | - plain HTTP external-IP probes are skipped. | 108 | - plain HTTP external-IP probes are skipped. |
| 36 | 109 | ||
| 110 | Tor mode affects outbound discovery and relay dials. It does not encrypt | ||
| 111 | `wallet.dat`, hide mining rewards on-chain, or change the `.btf` identity. A | ||
| 112 | relay still sees the TCP client that connected to it; with Tor mode that client | ||
| 113 | is the Tor exit or onion circuit endpoint instead of the user's direct IP. | ||
| 114 | |||
| 115 | ## What to test | ||
| 116 | |||
| 117 | Use the public status page only as a rough health check. To prove the local node | ||
| 118 | is using Tor/SOCKS, test from the node machine: | ||
| 119 | |||
| 120 | ```bash | ||
| 121 | bitflash -tor -debug | ||
| 122 | ``` | ||
| 123 | |||
| 124 | Then confirm in `debug.log` that Nostr and rendezvous dials report the proxy | ||
| 125 | path. A DNS leak test should show no local resolver lookup for relay hostnames: | ||
| 126 | the SOCKS5 request uses the domain-name form and asks the proxy to resolve it. | ||
| 127 | |||
| 37 | ## Onion rendezvous relay | 128 | ## Onion rendezvous relay |
| 38 | 129 | ||
| 39 | An operator can expose a Bitflash rendezvous relay as a Tor hidden service. The | 130 | An operator can expose a Bitflash rendezvous relay as a Tor hidden service. The |
| @@ -69,16 +160,72 @@ bitflash -operator -tor \ | |||
| 69 | -announcerelay=exampleexampleexampleexampleexampleexampleexampleexample.onion:8434 | 160 | -announcerelay=exampleexampleexampleexampleexampleexampleexampleexample.onion:8434 |
| 70 | ``` | 161 | ``` |
| 71 | 162 | ||
| 72 | ## Full-node hidden service | 163 | ## Manual direct full-node hidden service |
| 73 | 164 | ||
| 74 | The legacy P2P listener still binds to port `8433`. You can expose it through | 165 | Managed Tor is the preferred path for ordinary nodes. Operators can still expose |
| 75 | Tor manually: | 166 | the P2P listener through a separately managed Tor service: |
| 76 | 167 | ||
| 77 | ```text | 168 | ```text |
| 78 | HiddenServiceDir /var/lib/tor/bitflash-node/ | 169 | HiddenServiceDir /var/lib/tor/bitflash-node/ |
| 79 | HiddenServicePort 8433 127.0.0.1:8433 | 170 | HiddenServicePort 8433 127.0.0.1:8433 |
| 80 | ``` | 171 | ``` |
| 81 | 172 | ||
| 82 | Current automatic peer discovery is `.btf`/rendezvous based, not onion-address | 173 | After restarting Tor, read the generated onion name: |
| 83 | gossip. Treat direct full-node onion access as an operator/admin tool unless a | 174 | |
| 84 | future release adds first-class onion peer advertisements. | 175 | ```bash |
| 176 | sudo cat /var/lib/tor/bitflash-node/hostname | ||
| 177 | ``` | ||
| 178 | |||
| 179 | Then start Bitflash with Tor outbound enabled and advertise that hidden service: | ||
| 180 | |||
| 181 | ```bash | ||
| 182 | bitflash -tor \ | ||
| 183 | -onionservice=exampleexampleexampleexampleexampleexampleexampleexample.onion:8433 | ||
| 184 | ``` | ||
| 185 | |||
| 186 | That onion endpoint is signed into this node's self-certifying `.btf` | ||
| 187 | descriptor. New peers that resolve the descriptor and also run with `-tor` try | ||
| 188 | the `.onion:8433` P2P connection first. If it fails, they fall back to the | ||
| 189 | ordinary encrypted rendezvous path. Older nodes ignore the onion field and still | ||
| 190 | use the rendezvous relay because the descriptor keeps the legacy signature too. | ||
| 191 | |||
| 192 | If the node has `-onionservice` but has not registered at any rendezvous relay | ||
| 193 | yet, it can still publish an onion-capable descriptor with | ||
| 194 | `meeting_node = rendezvous-pending`. Tor-capable peers can dial the hidden | ||
| 195 | service directly from that descriptor. Rendezvous becomes a fallback, not a | ||
| 196 | precondition for being discoverable. | ||
| 197 | |||
| 198 | This means rendezvous relays stop being the only way to reach a node that has | ||
| 199 | published a hidden service. They remain useful bootstrap/fallback infrastructure, | ||
| 200 | but an already-discovered onion-capable peer can be reached directly over Tor. | ||
| 201 | |||
| 202 | Tor owns the private key under `HiddenServiceDir`; back it up if you want the | ||
| 203 | onion name to stay stable. | ||
| 204 | |||
| 205 | ## Release smoke test | ||
| 206 | |||
| 207 | After building a Windows binary with managed Tor support, run the two-node smoke | ||
| 208 | test before cutting a release: | ||
| 209 | |||
| 210 | ```powershell | ||
| 211 | powershell -ExecutionPolicy Bypass -File scripts\smoke-managed-tor-onion.ps1 ` | ||
| 212 | -Bitflash .\src\bitflash.exe ` | ||
| 213 | -Tor C:\path\to\tor.exe | ||
| 214 | ``` | ||
| 215 | |||
| 216 | The script creates two temporary datadirs, starts node A with `-managedtor`, | ||
| 217 | waits for it to publish its `.btf` identity and generated `.onion` endpoint, | ||
| 218 | then starts node B with its own `-managedtor`, `-oniononly`, and | ||
| 219 | `-connectbtf=<node-a>`. It passes only when node B logs that it connected to | ||
| 220 | node A `via direct onion`; rendezvous fallback cannot satisfy the test. | ||
| 221 | |||
| 222 | By default the script waits 120 seconds after node A's descriptor self-resolves | ||
| 223 | before it starts node B. Tor can create the local `hostname` before the hidden | ||
| 224 | service descriptor is visible enough for client circuits, and early dials often | ||
| 225 | fail with SOCKS5 reply `0x01`. Override this with `-OnionWarmupSec N` when | ||
| 226 | testing a slow or already-warmed Tor environment. | ||
| 227 | |||
| 228 | This test still uses Nostr for descriptor discovery, because that is how nodes | ||
| 229 | learn each other's signed `.btf` descriptors. It does not require a Bitflash | ||
| 230 | rendezvous relay to carry the P2P connection: once the descriptor includes the | ||
| 231 | signed onion endpoint, the actual node connection goes directly through Tor. | ||