Skip to content

[Bug] Server always binds to 0.0.0.0 on IPv6-only deployments — ALL_INTERFACES_BIND_HOST hardcoded in @paperclipai/shared #3832

@Plectar

Description

@Plectar

Environment

  • Paperclip version: v2026.416.0 (regression from v2026.403.0)
  • Deployment: Proxmox LXC container (unprivileged)
  • Network: IPv6-only (no IPv4 address assigned, single eth0 with DHCPv6)
  • OS: Ubuntu 24.04
  • Node.js: managed via npx / PM2
  • net.ipv6.bindv6only: 0 (dual-stack kernel setting)

Description

On an IPv6-only host, Paperclip v2026.416.0 always binds to 0.0.0.0:3100 instead of :::3100, making the server completely unreachable. This is a regression — v2026.403.0 worked correctly on the same host.

The root cause is that ALL_INTERFACES_BIND_HOST is hardcoded to "0.0.0.0" in @paperclipai/shared/dist/network-bind.js:

// @paperclipai/shared/dist/network-bind.js
export const ALL_INTERFACES_BIND_HOST = "0.0.0.0";  // ← hardcoded IPv4

This constant is used in resolveRuntimeBind() for the lan bind preset:

case "lan":
    return { bind, host: ALL_INTERFACES_BIND_HOST, customBindHost, errors: [] };

And is re-exported via @paperclipai/shared/dist/index.js and consumed by @paperclipai/server/dist/config.js:

import { resolveRuntimeBind, ... } from "@paperclipai/shared";

This means that regardless of what is set in config.json ("host": "::") or environment variables (HOST=::, PAPERCLIP_BIND=lan, PAPERCLIP_DEPLOYMENT_MODE=authenticated), the resolved host is always overwritten with "0.0.0.0" by resolveRuntimeBind().

On a dual-stack host, 0.0.0.0 works because the kernel maps IPv4 to IPv6 sockets. On an IPv6-only host (no IPv4 interface), 0.0.0.0 binds to nothing reachable and the server is inaccessible.


Steps to Reproduce

  1. Set up an IPv6-only host (no IPv4 address on any interface)
  2. Install Paperclip: npx paperclipai onboard --yes
  3. Configure as authenticated/public with bind: lan
  4. Start: npx paperclipai run
  5. Observe: server binds to 0.0.0.0:3100 instead of :::3100

Verify with:

ss -tlnp | grep 3100
# Output: LISTEN 0.0.0.0:3100   ← IPv4 only, unreachable on IPv6-only host

Expected Behavior

When bind is set to lan on an IPv6-only host, the server should bind to :::3100 (all interfaces, IPv6). The ALL_INTERFACES_BIND_HOST constant should be "::" to cover both IPv4 (via dual-stack) and pure IPv6 environments.

On dual-stack hosts, :: with net.ipv6.bindv6only=0 also accepts IPv4 connections, so this change is fully backwards compatible.


Actual Behavior

[INFO]: Server listening on 0.0.0.0:3100

Server is unreachable from any client because the host has no IPv4 address.


Workaround

Manually patch the compiled file in the npx cache:

sed -i 's/ALL_INTERFACES_BIND_HOST = "0\.0\.0\.0"/ALL_INTERFACES_BIND_HOST = "::"/' \
    ~/.npm/_npx/<hash>/node_modules/@paperclipai/shared/dist/network-bind.js

Note: this patch is lost whenever the npx cache is cleared or the package is reinstalled.


Proposed Fix

In packages/shared/src/network-bind.ts (or equivalent source):

// Before
export const ALL_INTERFACES_BIND_HOST = "0.0.0.0";

// After
export const ALL_INTERFACES_BIND_HOST = "::";

On Linux with net.ipv6.bindv6only=0 (the default), a socket bound to :: accepts both IPv4 and IPv6 connections, making this a safe change for all deployment environments. On systems with net.ipv6.bindv6only=1, operators can use PAPERCLIP_BIND_HOST=0.0.0.0 to override explicitly.


Additional Context

Node.js correctly supports IPv6 binding on this host — verified with:

const http = require('http');
const s = http.createServer((req, res) => res.end('ok'));
s.listen(9999, '::', () => console.log('listening:', s.address()));
// Output: listening: { address: '::', family: 'IPv6', port: 9999 }

The issue is exclusively in Paperclip's resolveRuntimeBind() overriding the configured host with the hardcoded 0.0.0.0 constant.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions