UPnP Manual Testing Guide
This guide walks through testing BrightChain’s UPnP port mapping with a real router. UPnP allows the node to automatically forward ports on your router so it’s reachable from the internet without manual configuration.
Validates: Requirement 8.2 — Integration testing with real UPnP-enabled router.
Prerequisites
Before you begin, make sure you have:
- A UPnP-enabled router — most consumer routers support UPnP (check your router’s admin page).
- UPnP enabled in your router settings — some routers ship with UPnP disabled by default. Look for a “UPnP” or “NAT-PMP” toggle in your router’s admin panel (usually under Advanced / NAT / Firewall settings).
- The BrightChain server running on a machine connected to the router’s LAN (wired or Wi-Fi).
- A second device or network for external verification (e.g. a phone on mobile data, a VPS, or a friend’s machine).
curlor a web browser available on the external device.
1. Enable UPnP in the Environment
Copy the example env file if you haven’t already, then enable UPnP:
# From the brightchain-api project directory
cp src/.env.example src/.env
Edit src/.env and set the UPnP variables:
# ─── UPnP Configuration ─────────────────────────────────────────────────
# Enable UPnP automatic port forwarding
UPNP_ENABLED=true
# HTTP/Express port to map externally (default: 3000)
UPNP_HTTP_PORT=3000
# WebSocket port to map externally (default: 3000, same as HTTP)
UPNP_WEBSOCKET_PORT=3000
# Port mapping time-to-live in seconds (default: 3600 = 1 hour, min: 60)
UPNP_TTL=3600
# Mapping refresh interval in milliseconds (default: 1800000 = 30 minutes)
UPNP_REFRESH_INTERVAL=1800000
# UPnP protocol: upnp | natpmp | auto (default: auto)
UPNP_PROTOCOL=auto
# Number of retry attempts on failure (default: 3, range: 1-10)
UPNP_RETRY_ATTEMPTS=3
# Delay between retries in milliseconds (default: 5000, range: 1000-60000)
UPNP_RETRY_DELAY=5000
For a quick smoke test you can lower the TTL and refresh interval:
UPNP_TTL=120
UPNP_REFRESH_INTERVAL=60000
This makes the mapping expire in 2 minutes and refresh every 1 minute, so you can observe the refresh cycle faster.
2. Start the Server and Verify UPnP Initialization
Start the API server:
npx nx serve brightchain-api
Watch the console output for UPnP log messages. A successful initialization looks like:
[UPnP] Initializing UPnP port mapping...
[UPnP] External IP: 203.0.113.42
[UPnP] HTTP port mapping created — external 203.0.113.42:3000 → internal :3000
[UPnP] WebSocket using same port as HTTP (3000), no additional mapping needed
[UPnP] Refresh timer started (interval: 1800000ms)
[UPnP] Initialization complete
Key things to confirm:
- External IP is logged — this is your public IP as reported by the router.
- HTTP port mapping created — the router accepted the port forward.
- Refresh timer started — periodic renewal is active.
- If HTTP and WebSocket use different ports, you’ll see a second mapping line for WebSocket.
If initialization fails, you’ll see:
[UPnP] Initialization failed: <error message>
[UPnP] UPnP not available. Manual port forwarding required:
Forward external port 3000 to internal port 3000
Protocol: TCP
Description: BrightChain Node HTTP
The server continues running — UPnP failure is non-fatal. See the Troubleshooting section below.
3. Verify the Port Mapping on Your Router
Open your router’s admin page (typically http://192.168.1.1 or http://192.168.0.1) and navigate to the UPnP or port forwarding section. You should see an entry similar to:
| External Port | Internal IP | Internal Port | Protocol | Description |
|---|---|---|---|---|
| 3000 | 192.168.1.x | 3000 | TCP | BrightChain Node HTTP |
If you configured a separate WebSocket port, you’ll see a second entry for “BrightChain Node WebSocket”.
4. Verify External Accessibility
Option A: curl from an external network
From a machine outside your LAN (VPS, phone hotspot, etc.), run:
curl -v http://<your-external-ip>:3000/
Replace <your-external-ip> with the IP logged during initialization. You should get a response from the BrightChain API server (the exact response depends on your routes — a connection refusal or timeout means the mapping isn’t working).
Option B: Online port checker
Use an online port checking tool such as:
Enter your external IP and port 3000. The tool should report the port as open.
Option C: WebSocket connectivity
If testing WebSocket accessibility, use a WebSocket client (e.g. wscat):
npx wscat -c ws://<your-external-ip>:3000
A successful connection confirms the WebSocket port mapping is working.
5. Verify the Refresh Cycle
If you lowered the TTL and refresh interval (see step 1), wait for the refresh interval to elapse and watch the logs:
[UPnP] Refresh complete (1 mapping(s) active)
This confirms the mapping was renewed before the TTL expired. If refresh fails, you’ll see:
[UPnP] Refresh failed (attempt 1): <error message>
[UPnP] Scheduling backoff refresh in 5000ms (failure #1)
The manager uses exponential backoff on repeated failures (up to 8x the retry delay).
6. Test Graceful Shutdown
Graceful shutdown should remove all port mappings from the router before the process exits.
Send SIGTERM
In a separate terminal:
kill -SIGTERM <pid>
Or press Ctrl+C in the terminal running the server (sends SIGINT).
Expected log output
[UPnP] Received SIGTERM, shutting down UPnP...
[UPnP] Shutting down...
[UPnP] Refresh timer stopped
[UPnP] All mappings removed and service closed
Verify mapping removal
Go back to your router’s admin page and confirm the UPnP port mapping entry for BrightChain is gone. Also re-run the external port check from step 4 — the port should now be closed.
7. Troubleshooting
UPnP not available
Symptom: [UPnP] Initialization failed: ... followed by manual port forwarding instructions.
Possible causes:
- UPnP is disabled on your router. Log into the router admin page and enable it.
- Your router doesn’t support UPnP. Try setting
UPNP_PROTOCOL=natpmpif you have an Apple AirPort or other NAT-PMP-capable router. - You’re behind a double NAT (e.g. ISP modem + your router). UPnP only works on the directly connected router. You may need to put the ISP modem in bridge mode.
- A firewall on the host machine is blocking UPnP discovery (SSDP uses UDP port 1900). Ensure your OS firewall allows outbound UDP 1900.
Port conflicts
Symptom: Mapping creation fails with a conflict error.
Possible causes:
- Another device or application already has a UPnP mapping for the same port. Check your router’s UPnP table and remove the conflicting entry, or change
UPNP_HTTP_PORTto a different port. - A previous BrightChain instance didn’t shut down cleanly and left a stale mapping. Restart the router’s UPnP service or wait for the old mapping’s TTL to expire.
Timeout / slow router
Symptom: [UPnP] Initialization failed: ... timeout or repeated retry messages.
Possible causes:
- The router is slow to respond. Increase
UPNP_RETRY_ATTEMPTS(up to 10) andUPNP_RETRY_DELAY(up to 60000ms). - Network congestion between the host and router. Try a wired connection.
- The router’s UPnP implementation is buggy. Some older routers have unreliable UPnP. Try a firmware update or fall back to manual port forwarding.
External port check shows closed
Possible causes:
- Your ISP blocks incoming connections on the port. Try a higher port (e.g. 8080, 8443). Some ISPs block ports below 1024 or common ports like 80/443.
- The mapping was created but your OS firewall is blocking incoming traffic. Allow TCP traffic on the configured port.
- Double NAT — see “UPnP not available” above.
Mappings not removed on shutdown
Possible causes:
- The process was killed with
SIGKILL(kill -9) which cannot be caught. Always useSIGTERMorCtrl+Cfor graceful shutdown. - The router was unreachable during shutdown (network disconnected). The mapping will expire when its TTL runs out.
- To manually clean up stale mappings, use your router’s admin page to delete them.
8. Fallback: Manual Port Forwarding
If UPnP doesn’t work in your environment, you can manually configure port forwarding on your router:
- Open your router’s admin page.
- Navigate to Port Forwarding (sometimes called “Virtual Server” or “NAT Rules”).
- Create a new rule:
- External Port: 3000 (or your configured
UPNP_HTTP_PORT) - Internal IP: Your machine’s LAN IP (e.g.
192.168.1.100) - Internal Port: 3000 (same as external)
- Protocol: TCP
- Description: BrightChain Node HTTP
- External Port: 3000 (or your configured
- If using a separate WebSocket port, create a second rule for that port.
- Save and apply the changes.
- Set
UPNP_ENABLED=falsein your.envto skip UPnP initialization. - Verify external accessibility using the methods in step 4.
Tip: To find your machine’s LAN IP, run
ifconfig(macOS/Linux) oripconfig(Windows) and look for the address on your LAN interface (usually192.168.x.xor10.x.x.x).