Part II · Protocol & Services · Chapter 7

Hyphanet

Commands only. What each step does, why it is built this way, and the judgment behind it are in the book.

LAB 7.1

A node in a friend mesh

Step 1, Bring up the friend mesh

Ubuntu
# a few Hyphanet nodes, opennet disabled, wired as mutual darknet friends
# on an isolated network — they never reach the public network
$ ./lab up hyphanet
$ ./lab stop hyphanet  # stop — containers kept; ./lab up hyphanet resumes them
$ ./lab down hyphanet  # delete — removes containers (add --volumes to drop the seed)
Expected, the nodes start and pair as friends
darkweb-fn-n1     started
darkweb-fn-n2     started
darkweb-fn-n3     started
darkweb-fn-n4     started
hyphanet testnet: 4 nodes, opennet OFF (darknet only), isolated network
seeding darknet peer references between nodes... done (settle takes minutes)

Step 2, Open a node's web interface

Ubuntu
# FProxy — the node's built-in web UI — for node n1, proxied to your host
$ ./lab fn console n1
  FProxy for darkweb-fn-n1 → http://127.0.0.1:8888

Step 3, What your node is actually doing

Verify in the Docker host
Ubuntu
$ ./lab check 7.1
  • The nodes are running on an isolated network with a custom, opennet-disabled config
  • A node's FProxy interface is reachable and reports darknet peers
  • The node has a datastore initialized (it is contributing storage)
LAB 7.2

Insert, retrieve, and outlive the publisher

Step 1, Insert content and get a CHK

Ubuntu
# insert a small file into the store from node n1; you get a content-hash key
$ ./lab fn insert n1 ./sample.txt
Expected, a CHK is returned (this can take a while)
inserting sample.txt (2.1 KB) via n1 ... this may take 30-90s
CHK@8Xq...c7A,Kd2...9pE,AAMC8mI
inserted: the key is derived from the content itself

Step 2, Retrieve it from a different node

Ubuntu
# fetch the same key from n3 — which never saw the file directly
$ ./lab fn fetch n3 'CHK@8Xq...c7A,Kd2...9pE,AAMC8mI'
Expected, the content comes back, verified by its key
requesting CHK@8Xq...c7A via n3 ... routing toward the key's location
retrieved 2.1 KB; hash matches the key  (integrity verified)
"the quick brown fox..."

Step 3, Take the publisher offline

Ubuntu
# stop the node that inserted the file, then fetch from a node that never had it
$ docker stop darkweb-fn-n1
$ ./lab fn fetch n4 'CHK@8Xq...c7A,Kd2...9pE,AAMC8mI'
Expected, the content is still there, with its publisher gone
requesting CHK@8Xq...c7A via n4 ... (n1 is offline)
retrieved 2.1 KB; hash matches the key  (integrity verified)
the publisher is gone; the content is not
Verify in the Docker host
Ubuntu
$ ./lab check 7.2
  • A CHK insert returns a content-derived key, and a fetch from another node verifies against it
  • The content-hash-key logic self-tests (address = hash of content; tampering is detected)
  • You can explain why the content survives the inserting node going offline
LAB 7.3

Mutable content: SSK, USK, freesites

Step 1, Create a signed subspace (SSK)

Ubuntu
$ ./lab fn genkey n1
  SSK public : SSK@ Math...RnQ,Cf8...2wd,AQACAAE/
  SSK private: SSK@ math...RnQ,Cf8...2wd,AQECAAE/   (keep this to publish updates)

Step 2, Publish a freesite, then update it

Ubuntu
# insert a small static site as a USK (a versioned SSK), then insert v2
$ ./lab fn putsite n1 ./freesite/   # -> USK@math...RnQ,.../labsite/1/
$ ./lab fn fetch n3 'USK@math...RnQ,.../labsite/-1/'   # -1 = "latest"

Step 3, The dangerous convenience: KSK

Verify in the Docker host
Ubuntu
$ ./lab check 7.3
  • A signed subspace (SSK) keypair can be generated on a node
  • A freesite is inserted under a USK and fetched by version from another node
  • You can explain why KSK naming is unauthenticated and squattable
LAB 7.4

Opennet vs darknet

Step 1, See how your node is connected

Ubuntu
$ ./lab fn peers n1
Expected, darknet peers only, opennet disabled
node n1  opennet: DISABLED   darknet peers: 3
  n2   connected   trust: manual (friend)
  n3   connected   trust: manual (friend)
  n4   connected   trust: manual (friend)
no seednodes, no stranger connections

Step 2, Why the topology is the threat model

Step 3, The exposure that remains

Verify in the Docker host
Ubuntu
$ ./lab check 7.4
  • The node runs in darknet mode with opennet disabled and only manual friend peers
  • The small-world routing model self-tests (greedy key routing converges on the friend graph)
  • You can state how darknet changes the adversary from "can join" to "must be trusted"
LAB 7.5

Deniability and the datastore

Step 1, Inspect the datastore

Ubuntu
$ ./lab fn store n1
Expected, an opaque, encrypted store
datastore : 512 MB allocated, ~318 MB used
blocks    : encrypted; keys not held for cached/relayed content
readable  : only what this node inserted or explicitly requested
note      : the node cannot enumerate the plaintext of most blocks it holds

Step 2, Why that is the deniability argument

Step 3, The forgetful store

Verify in the Docker host
Ubuntu
$ ./lab check 7.5
  • The node has an initialized datastore of a configured size
  • You can explain why cached blocks are not evidence of intent (encrypted, keys not held)
  • You can explain why unrequested content is evicted and what that means for collection
LAB 7.6

Threat model vs Tor and I2P

Step 1, Re-place the adversaries

Step 2, Score it, darknet publisher

Worked model, darknet Hyphanet publisher (1 low … 5 high)
threat                          L   I   E   weight   vs onion / eepsite
-----------------------------   -   -   -   ------   ---------------------------------
application leak in content      4   5   2    40      unchanged — inserted content betrays you
operator OPSEC mistake           4   5   2    40      unchanged — persona, stylometry, reuse
exit reads destination/content   1   1   1     1      absent — there is no exit
service host located/seized      1   3   1     3      absent — there is no host to seize
routing/correlation adversary    2   4   2    16      opennet risk; small in a trusted darknet
compromised/coerced friend       3   5   3    45      NEW — the mesh is only as safe as its members
keyword (KSK) squat/poison       2   3   3    18      NEW-ish — purest naming chokepoint (7.3)
content evicted (forgetful)      3   2   4    24      NEW framing — availability, not exposure

Step 3, The headline, and the throughline

Verify in the Docker host
Ubuntu
$ ./lab check 7.6
  • The Hyphanet threat-model worksheet ships with the vanished and new rows pre-listed
  • The testnet the model reasons about is up and in darknet mode
  • You produced a weighted model for a specific Hyphanet activity, not "Hyphanet in general"
LAB 7.7

The complete overlay decision

Step 1, Three networks, one table

Tor vs I2P vs Hyphanet, by property
property           Tor                     I2P                      Hyphanet
----------------   ---------------------   ----------------------   ------------------------
what it is         anonymity routing to    anonymity routing to     a distributed datastore;
                   the clearnet & onions   in-network services      you fetch keys, not hosts
trust root         9 dir authorities       Kademlia NetDB           content-addressed keys;
                                           (floodfills)             friend graph (darknet)
path / delivery    one bidirectional       four unidirectional      key-routed request through
                   circuit                 tunnels                  the store; store-and-forward
is there a host?   yes (onion service)     yes (eepsite)            no — content outlives its
                                                                    publisher
clearnet exit      yes                     no (outproxy = a         no
                                           separate choice)
naming             onion addr = key        addressbook + jump       CHK/SSK/USK; KSK is the
                                           services                 squattable convenience
adversary must     run guard+exit;         grind floodfills near    be your friend (darknet) or
                   correlate both ends     a destination            run many opennet nodes
persistence        while host is up        while host is up         while requested; evicted if
                                                                    not (forgetful store)
latency            low                     low-ish                  high (store-and-forward)
reach for it to    browse/serve the        run a block-resistant    publish/store durably &
                   clearnet anonymously    in-network service       deniably; survive takedown

Step 2, Let the task choose

Step 3, What Part II leaves you with

Verify in the Docker host
Ubuntu
$ ./lab check 7.7
  • The overlay matrix is populated for all three networks (the Hyphanet stub is filled)
  • The matrix names the decision procedure and each network's "reach for it to" case
  • You produced a defensible network choice for a specific task and adversary