Source profileQuality 87/100Review permissions

freenet/freenet-agent-skills/skills/local-dev/SKILL.md

local-dev

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.

Source repository stars
23
Declared platforms
0
Static risk flags
2
Last source update
2026-08-04
Source checked
2026-08-04

Decision brief

What it does—and where it fits

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

Best for

  • 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 deplo…

Not for

  • Tasks that require unconfirmed production actions or broad system permissions.
  • Environments where the pinned source and install steps cannot be inspected.

Compatibility matrix

Platform support, with evidence labels

PlatformStatusEvidenceWhat to check
CodexNot declaredNo explicit evidencePortability before use
Claude CodeNot declaredNo explicit evidencePortability before use
CursorNot declaredNo explicit evidencePortability before use
Gemini CLINot declaredNo explicit evidencePortability before use
Open the compatibility checker

Installation

Inspect first. Install second.

The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

Source-detected install commandSource
npx skills add https://github.com/freenet/freenet-agent-skills --skill "skills/local-dev"
Safe inspection promptEditorial

Inspect the Agent Skill "local-dev" from https://github.com/freenet/freenet-agent-skills/blob/294f4ad2d7c185f8e7fa4c1ec10802de5673616c/skills/local-dev/SKILL.md at commit 294f4ad2d7c185f8e7fa4c1ec10802de5673616c. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

Workflow

What the source asks the agent to do

  1. 01

    Single-process test network (no real networking)

    fdev test --gateways 2 --nodes 10 --events 100 --seed 0xDEADBEEF single-process

    fdev test --gateways 2 --nodes 10 --events 100 --seed 0xDEADBEEF single-process
  2. 02

    Prerequisites

    Review the “Prerequisites” section in the pinned source before continuing.

    Review and apply the “Prerequisites” source section.
  3. 03

    Architecture

    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 (…

    Connection status, peer count, own ring locationPeer table: address, ring location, type (Peer/Gateway), bytes sent/received, connected durationExternal address (NAT traversal result), NAT statistics
  4. 04

    Ports & Services

    Review the “Ports & Services” section in the pinned source before continuing.

    Review and apply the “Ports & Services” source section.
  5. 05

    HTTP Endpoints

    Review the “HTTP Endpoints” section in the pinned source before continuing.

    Review and apply the “HTTP Endpoints” source section.

Permission review

Static risk signals and limitations

Network access

medium · line 44

The documentation includes network, browsing, or remote request actions.

curl -s http://localhost:7509/ | grep -o 'own-loc[^<]*<[^>]*>[^<]*'

Network access

medium · line 47

The documentation includes network, browsing, or remote request actions.

curl -s http://localhost:7509/ | grep -o 'peer-row[^}]*'

Runs scripts

medium · line 405

The documentation asks the agent to run terminal commands or scripts.

# Node status page

Runs scripts

medium · line 501

The documentation asks the agent to run terminal commands or scripts.

docker-compose up

Evidence record

Why each signal appears

EvidenceSourceComputedTestedEditorial
SignalValueEvidence typeMeaning
Quality score87/100ComputedDocumentation, specificity, maintenance, and trust rules
Repository stars23SourceRepository attention, not individual Skill quality
Compatibility0 platformsSourceDeclared in the catalog source record
Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

Pinned source

Provenance and original SKILL.md

Repository
freenet/freenet-agent-skills
Skill path
skills/local-dev/SKILL.md
Commit
294f4ad2d7c185f8e7fa4c1ec10802de5673616c
License
LGPL-3.0
Collected
2026-08-04
Default branch
main
View the original SKILL.md

Freenet Local Development & Node Interaction

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

Prerequisites

which freenet fdev
rustup target add wasm32-unknown-unknown

Architecture

Ports & Services

ServiceDefault PortFlagPurpose
Network (P2P)31337--network-portPeer-to-peer connections
WebSocket API7509--ws-api-portClient API (UI, CLI tools, fdev)

HTTP Endpoints

EndpointPurpose
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=nativeWebSocket API v1
WS /v2/contract/command?encodingProtocol=nativeWebSocket 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:

# 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

PlatformDefault 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:

# 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.

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 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:

# 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:

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.

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.

# 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.

When a publish runs through cargo-make (e.g. a publish-* task) rather than fdev directly, there is no --port flag to pass — the task hardcodes the default port internally. Override it with the WS_API_PORT environment variable instead:

# Targets the isolated test node on 7510 instead of the default 7509
WS_API_PORT=7510 cargo make publish-myapp

The failure mode for a misdirected cargo-make publish is the unhelpful put failed after 4 attempts, which gives no hint that the port was wrong — so set WS_API_PORT whenever the target node isn't on 7509.

--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:

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

# 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 \
  --gateway "127.0.0.1:31337,${GATEWAY_KEY}" \
  --skip-load-from-network \
  --data-dir ~/freenet-local-peer/data \
  --log-dir ~/freenet-local-peer/logs \
  --log-level debug

Mobile testing (phone on same WiFi)

# Bind WebSocket API to all interfaces (--ws-api-address 0.0.0.0)
freenet network \
  --ws-api-address 0.0.0.0 \
  --ws-api-port 7510 \
  --network-port 31338 \
  --is-gateway \
  --skip-load-from-network \
  --data-dir ~/freenet-mobile-test/data \
  --public-network-address 127.0.0.1 \
  --log-dir ~/freenet-mobile-test/logs \
  --log-level debug

# Phone opens: http://{YOUR_LAN_IP}:7510/v1/contract/web/{CONTRACT_ID}/
# Find LAN IP:
#   macOS:  ifconfig en0 | grep "inet "
#   Linux:  ip addr show wlan0

Multi-instance deployment (10 peers + gateway)

# Uses deploy-local-gateway.sh from freenet-core
cd /path/to/freenet-core
scripts/deploy-local-gateway.sh --all-instances

Publishing Contracts Locally

Using fdev

IMPORTANT: fdev argument order matters. --code and --parameters go before the contract subcommand. --port goes before execute.

# Publish a contract with webapp
fdev --port 7510 execute put \
  --code target/wasm32-unknown-unknown/release/my_contract.wasm \
  --parameters params.bin \
  contract \
  --webapp-archive target/webapp/webapp.tar.xz \
  --webapp-metadata target/webapp/webapp.metadata

# Get contract ID without publishing
fdev get-contract-id \
  --code target/wasm32-unknown-unknown/release/my_contract.wasm \
  --parameters params.bin

Targeting a specific node

Override the WebSocket port for fdev:

fdev --port 7510 execute put --code ... contract ...

Querying Node State

# List connected peers and subscriptions
fdev query

# Get detailed node diagnostics
fdev diagnostics

# Get diagnostics for specific contracts
fdev diagnostics --contract <base58_contract_id>

WebSocket API

Connection

ws://127.0.0.1:7509/v1/contract/command?encodingProtocol=native
  • Encoding: native (bincode) or flatbuffers
  • Auth: Send ClientRequest::Authenticate { token } after connecting
  • Tokens: Generated per-connection, base58-encoded 32 bytes. Invalidated on node restart (error prefix: AUTH_TOKEN_INVALID).

Request Types

pub enum ClientRequest {
    ContractOp(ContractRequest),   // GET, PUT, UPDATE, Subscribe
    DelegateOp(DelegateRequest),   // Delegate operations
    Authenticate { token },         // Auth token
    NodeQueries(NodeQuery),         // Queries (see below)
    Disconnect { cause },           // Close with reason
    Close,                          // Graceful close
}

NodeQuery Variants

QueryResponseData
ConnectedPeersConnectedPeers { peers }Vec<(peer_id, socket_addr)>
SubscriptionInfoNetworkDebug(info)Subscriptions + connected peers
NodeDiagnostics { config }NodeDiagnostics(response)Configurable (see below)
ProximityCacheInfoProximityCache(info)Proximity cache state for update propagation

NodeDiagnostics Config

NodeDiagnosticsConfig {
    include_node_info: bool,           // Peer ID, location, uptime
    include_network_info: bool,        // Active connections, peer list
    include_subscriptions: bool,       // Active subscriptions
    contract_keys: Vec<ContractKey>,   // Specific contracts (empty = all)
    include_system_metrics: bool,      // Connection count, hosting contracts
    include_detailed_peer_info: bool,  // Full peer details
    include_subscriber_peer_ids: bool, // Peer IDs of subscribers per contract
}

Wire-format change (stdlib v0.7.0): NodeDiagnosticsResponse.contract_states is now HashMap<String, ContractState> where the key is the Base58-encoded ContractKey::Display form (the instance field, not the full struct). Previously it was HashMap<ContractKey, ContractState> but that serialization broke JSON because the key was a struct. To map back to a typed ContractKey, decode the Base58 string and reconstruct. This is a bidirectional bincode break — match node and tooling versions.

for (key_str, state) in diag.contract_states.iter() {
    // key_str is the Base58 ContractKey instance id
}

Config File Reference

mode = "network"                      # "network" or "local"
network-address = "0.0.0.0"
network-port = 54761                  # UDP port for peer traffic
ws-api-address = "0.0.0.0"
ws-api-port = 7509                    # HTTP + WebSocket port
min-number-of-connections = 25
max-number-of-connections = 100
transient-budget = 2048               # Max concurrent transient connections (gateway)
transient-ttl-secs = 30              # TTL for unpromoted transient connections
token-ttl-seconds = 86400            # Auth token lifetime
token-cleanup-interval-seconds = 300
log_level = "info"
is_gateway = false

Ring Distance

Each peer has a ring location in [0.0, 1.0). Distance between two locations:

distance = min(|a - b|, 1.0 - |a - b|)

Max distance is 0.5. Use the dashboard peer table to get locations and compute distances.

Debugging

Check node status

# Node status page
curl -s http://127.0.0.1:7510/

# Active WebSocket connections
lsof -i :7510 -P | grep ESTABLISHED     # macOS
ss -tnp | grep 7510                      # Linux

Check node logs

# Follow logs (use your --log-dir path)
tail -f "$LOG_DIR"/freenet.$(date +%Y-%m-%d-%H).log

# Filter for contract/delegate events
tail -f "$LOG_DIR"/freenet.*.log | grep -i "delegate\|contract\|websocket\|error\|sign"

Each node instance should use --log-dir pointing to a unique directory so logs don't interleave.

Debugging node logs

Key patterns to search for:

# Follow all delegate + contract activity
grep -i "delegate\|sign\|update\|put\|subscribe" "$LOG_DIR"/freenet.*.log | tail -50

# Track a specific operation by transaction ID
grep "01KK70QEAR" "$LOG_DIR"/freenet.*.log

# WebSocket lifecycle
grep -i "websocket\|connection\|disconnect\|client" "$LOG_DIR"/freenet.*.log | tail -20

Debugging with Playwright (automated browser testing)

Use the Playwright MCP tools to test the full UI flow without manual interaction:

1. browser_navigate → open the contract URL
2. browser_snapshot → see the DOM state
3. browser_click / browser_fill_form → interact with the UI
4. browser_console_messages → check for WASM panics or JS errors

Especially useful for reproducing mobile issues on desktop, where console output is visible. If a flow works in Playwright but not on mobile, the issue is likely WebSocket suspension or browser caching.

Mobile-specific debugging

Browser caching: Mobile browsers aggressively cache WASM bundles. After republishing, use a cache-busting URL parameter:

http://{IP}:7510/v1/contract/web/{CONTRACT_ID}/?_v={timestamp}

Or clear browser cache / force close and reopen. Firefox mobile is particularly aggressive about caching.

WebSocket suspension: Mobile browsers suspend WebSocket connections when:

  • Screen locks
  • Tab goes to background
  • Browser switches to another app
  • Heavy WASM computation starves the event loop

Your app should handle reconnection when the tab becomes visible again. Consider implementing a visibilitychange listener that re-establishes the WebSocket connection.

Common issues

SymptomCauseFix
"Gateway nodes must specify a public network address"Missing --public-network-addressAdd --public-network-address 127.0.0.1
Signing key lost after node restartUsed --id (ephemeral temp dir)Use --data-dir for persistent data
"Auth token not found"Stale cached pageHard refresh or clear browser cache
"delegate not found in store"Legacy delegate migrationExpected on fresh node, non-blocking
"Connection reset by peer"Browser killed WebSocketCheck if page is in background tab
"peer connection dropped" on putPublishing to live node failedUse isolated test node (--skip-load-from-network)
Contract not foundNot published to this nodePublish with fdev --port {PORT} (see Isolation pitfalls)
"Signature verification failed" on a fresh publishfdev defaulted to port 7509 and hit the system nodePass fdev --port {TEST_PORT} explicitly
Test node joins public network despite --data-dirgateways.toml is read from global config, not --data-dirOverride HOME to a sandbox dir with gateways = [] (see Isolation pitfalls)
Blank page (cached old WASM)Mobile browser caches aggressivelyClear cache, force close browser, or use ?_v=timestamp
sed -i fails on macOSBSD sed requires backup extensionUse build tools directly instead of sed
cargo make targets LinuxCross-compilation for web-container-toolBuild natively: cargo build --release -p web-container-tool

Other Infrastructure

Docker containers (freenet-core)

# Gateway container
cd /path/to/freenet-core/docker/freenet-gateway
docker-compose up

# Node container
cd /path/to/freenet-core/docker/freenet-node
docker-compose up

fdev simulation testing

# Single-process test network (no real networking)
fdev test --gateways 2 --nodes 10 --events 100 --seed 0xDEADBEEF single-process

# With fault injection
fdev test --message-loss 0.1 --latency-min 50 --latency-max 200 single-process

Related Skills

  • dapp-builder: Design and architect new Freenet dApps
  • telemetry-monitor: Analyze network telemetry from the central collector
  • release: Publish production releases

Alternatives

Compare before choosing

Computed 921,835

wondelai/skills

improve-code-quality

Guided journey from a working-but-untested vibe-coded prototype to a production-ready product with tests, clean structure, a business-rules boundary, and resilience at scale. Orchestrates nine skills phase by phase - working-with-legacy-code, clean-code, refactoring-patterns, software-design-philosophy, clean-architecture, pragmatic-programmer, release-it, system-design, ddia-systems - asking the user questions at every decision point and recording results in the project docs/ folder (TESTING.md

Computed 921,835

wondelai/skills

remove-technical-debt

Guided journey from a large aged codebase everyone fears to touch to one that is safe to change, legible, bounded, and resilient - paid down in place without a rewrite. Orchestrates eight skills phase by phase - working-with-legacy-code, refactoring-patterns, clean-code, software-design-philosophy, clean-architecture, pragmatic-programmer, release-it, domain-driven-design - asking the user questions at every decision point and recording results in the project docs/ folder (TESTING.md, TECH-DEBT.

Computed 9033,026

anthropics/claude-plugins-official

build-mcp-app

This skill should be used when the user wants to build an "MCP app", add "interactive UI" or "widgets" to an MCP server, "render components in chat", build "MCP UI resources", make a tool that shows a "form", "picker", "dashboard" or "confirmation dialog" inline in the conversation, or mentions "apps SDK" in the context of MCP. Use AFTER the build-mcp-server skill has settled the deployment model, or when the user already knows they want UI widgets.

Computed 90423

jabrena/plinth

802-regulations-dora

Use when reviewing, designing, or modifying Java enterprise systems that may support financial entities, critical ICT services, third-party ICT provider integrations, or operational resilience obligations under DORA. This should trigger for requests such as Review a Java platform for DORA ICT risk controls; Design operational resilience evidence for a financial service; Add incident, continuity, backup, recovery, or third-party ICT controls; Assess resilience testing and monitoring before produc