Claude
Skills
Sign in
Back

local-dev

Included with Lifetime
$97 forever

Set up and manage local Freenet development environments and interact with a running node. Use when the user wants to test contract changes locally, debug UI issues, run a local node, query connections/diagnostics, inspect the dashboard, use the WebSocket API, or iterate on a Freenet application without deploying to the live network.

Design

What this skill does


# Freenet Local Development & Node Interaction

Guidance for running local Freenet nodes, publishing contracts, querying node state, and debugging dApps during development.

## Prerequisites

```bash
which freenet fdev
rustup target add wasm32-unknown-unknown
```

## Architecture

### Ports & Services

| Service | Default Port | Flag | Purpose |
|---------|-------------|------|---------|
| Network (P2P) | 31337 | `--network-port` | Peer-to-peer connections |
| WebSocket API | 7509 | `--ws-api-port` | Client API (UI, CLI tools, fdev) |

### HTTP Endpoints

| Endpoint | Purpose |
|----------|---------|
| `GET /` | Home dashboard (auto-refreshes every 5s) |
| `GET /peer/{address}` | Peer detail page |
| `GET /v1/contract/web/{key}` | Contract web interface |
| `WS /v1/contract/command?encodingProtocol=native` | WebSocket API v1 |
| `WS /v2/contract/command?encodingProtocol=native` | WebSocket API v2 |

### Dashboard

The home dashboard at `http://localhost:7509/` shows:
- Connection status, peer count, own ring location
- Peer table: address, ring location, type (Peer/Gateway), bytes sent/received, connected duration
- External address (NAT traversal result), NAT statistics
- Contract counts (hosted, subscribed, managed)
- Operation stats (GET/PUT/UPDATE/SUBSCRIBE success/failure counts)

Scraping peer data:
```bash
# Get own location
curl -s http://localhost:7509/ | grep -o 'own-loc[^<]*<[^>]*>[^<]*'

# Get peer rows (address, location, type, sent, recv, uptime)
curl -s http://localhost:7509/ | grep -o 'peer-row[^}]*'
```

### Node Data Locations

| Platform | Default Data Path |
|----------|-------------------|
| macOS | `~/Library/Application Support/The-Freenet-Project-Inc.Freenet/` |
| Linux | `~/.local/share/freenet/` |

Contents: `contracts/` (WASM), `delegates/`, `secrets/`, `db/`, `config.toml`

### Log Directory Convention

Choose an appropriate log directory for your OS:
- **macOS**: `~/Library/Logs/freenet-test-node`
- **Linux**: `~/.local/share/freenet-test-node/logs`

The examples below use `$LOG_DIR` as a placeholder. Set it once:
```bash
# macOS:
LOG_DIR=~/Library/Logs/freenet-test-node
# Linux:
LOG_DIR=~/.local/share/freenet-test-node/logs

mkdir -p "$LOG_DIR"
```

## Running Local Nodes

### Single node (simplest)

Your existing node on port 7509 works. Publish test contracts to it directly.

### Isolated test node (won't affect your running node)

**IMPORTANT:** Gateway nodes require `--public-network-address`. Always use
`--log-dir` to isolate logs from your main node.

```bash
freenet network \
  --network-port 31338 \
  --ws-api-port 7510 \
  --ws-api-address 0.0.0.0 \
  --is-gateway \
  --skip-load-from-network \
  --data-dir ~/freenet-test-node/data \
  --public-network-address 127.0.0.1 \
  --log-dir "$LOG_DIR" \
  --log-level debug
```

Persistent data lives in `--data-dir` and logs in `--log-dir`, but the
**gateway bootstrap list is NOT isolated** by `--data-dir` — see
[Isolation pitfalls](#isolation-pitfalls) below before assuming the node
is offline-only. Likewise, `fdev` defaults to port 7509 and will silently
target whichever node owns that port (often the system service, not your
test node).

**WARNING:** Do NOT use `--id` for local dev. It creates ephemeral temp directories
that get wiped on restart, destroying delegate secrets (signing keys, app data).
Use `--data-dir` for persistent isolation instead.

### Isolation pitfalls

#### `--data-dir` does NOT isolate the gateway bootstrap list

`freenet` reads `gateways.toml` from the global config directory regardless
of `--data-dir`:

- **macOS:** `~/Library/Application Support/The-Freenet-Project-Inc.Freenet/gateways.toml`
- **Linux:** `~/.config/freenet/gateways.toml`

On a machine with an existing Freenet install, a "local" test node will
dial real public gateways (e.g. `nova.locut.us`, `vega.locut.us`) and
attempt NAT traversal to live peers — silently joining the public network.

To fully isolate, override `HOME` so the node sees an empty gateway list:

```bash
# macOS
mkdir -p ~/iso-home/Library/Application\ Support/The-Freenet-Project-Inc.Freenet
printf 'gateways = []\n' > ~/iso-home/Library/Application\ Support/The-Freenet-Project-Inc.Freenet/gateways.toml

# Linux
mkdir -p ~/iso-home/.config/freenet
printf 'gateways = []\n' > ~/iso-home/.config/freenet/gateways.toml

# Then launch with HOME overridden. For an isolated *gateway* node
# (--is-gateway, no --gateway flags), expect 0 bootstrap gateways:
HOME=~/iso-home freenet network --is-gateway --skip-load-from-network ...

# For an isolated *peer* node, pass your local gateway(s) explicitly:
HOME=~/iso-home freenet network --gateway "127.0.0.1:31337,$GATEWAY_PUBKEY" ...
```

Note: an empty `gateways.toml` will fail with `missing field 'gateways'`.
The file must contain `gateways = []`.

**Verification:** grep the node log for the initial-join line and confirm
the gateway count matches what you passed:

```bash
grep "Starting initial join procedure" "$LOG_DIR"/freenet.*.log
# Expect: "...with N gateways" where N == number of --gateway flags
# For an isolated gateway node (no --gateway flags), N must be 0.
# If N is higher than expected, isolation is broken.
```

Upstream tracking: [freenet/freenet-core#3980](https://github.com/freenet/freenet-core/issues/3980).

#### `fdev` defaults to port 7509

`fdev` targets `ws://127.0.0.1:7509` unless `--port` is passed. On a dev
machine running a system Freenet service (which owns 7509), `fdev publish ...`
without `--port` silently goes to that node, not your isolated test node.

```bash
# WRONG: silently targets whichever node owns 7509 (often the system service)
fdev publish --code ... contract ...

# RIGHT: always pass --port when targeting a non-default test node
fdev --port 7510 publish --code ... contract ...
```

Symptom of a misdirected publish: `"Signature verification failed: signature error"`
on a fresh publish to the test node, because the system node has stale
contract state from a previous run signed by a different key. If you see
this on a "fresh" test, check which node `fdev` actually hit.

#### `--data-dir` does NOT isolate `config.toml` either — use `--config-dir` per node

Two `freenet` processes on the same host that pass the same (or default)
config directory share `config.toml` AND `secrets/transport-keypair.pem`.
Symptoms: second node fails to bind its UDP port, or both nodes use
identical peer IDs and the network refuses the duplicate connection.

For a deterministic multi-node harness on one host (gateway + peer + …),
pass `--config-dir` explicitly to each node, NOT just `--data-dir`:

```bash
freenet network --config-dir /tmp/iso-net/gw/config   --data-dir /tmp/iso-net/gw/data   ...
freenet network --config-dir /tmp/iso-net/peer/config --data-dir /tmp/iso-net/peer/data ...
```

**CI gotcha:** on Linux runners that set `XDG_CONFIG_HOME` (e.g. ubicloud,
sometimes GitHub Actions images), `dirs::config_dir()` returns
`$XDG_CONFIG_HOME` regardless of `HOME` — so the `HOME=~/iso-home …`
trick from the previous section is bypassed. `--config-dir` is the only
flag that wins against `XDG_CONFIG_HOME`. Use it any time the harness
must run identically on dev laptops and CI.

A working reference harness lives at
`scripts/run-isolated-nodes.sh` in the freenet/mail repo — covers up /
down / wipe / status, full state wipe between test runs (avoids day-1
AFT cap carryover in repeated E2E runs), and `FREENET_E2E_KEEP=1` to
leave nodes up for post-mortem.

### Two-node local network

```bash
# Terminal 1: Gateway
freenet network \
  --network-port 31337 \
  --ws-api-port 7509 \
  --is-gateway \
  --skip-load-from-network \
  --data-dir ~/freenet-local-gw/data \
  --public-network-address 127.0.0.1 \
  --log-dir ~/freenet-local-gw/logs \
  --log-level debug

# Terminal 2: Peer (get gateway pubkey first)
GATEWAY_KEY=$(cat ~/.config/Freenet/secrets/local-gw/transport.pub 2>/dev/null || echo "CHECK_PUBKEY")
freenet network \
  --network-port 31338 \
  --ws-api-port 7510 
Files: 1
Size: 17.8 KB
Complexity: 28/100
Category: Design

Related in Design