diff --git a/config/nginx.conf.template b/config/nginx.conf.template index 638bc2d..771586c 100644 --- a/config/nginx.conf.template +++ b/config/nginx.conf.template @@ -239,6 +239,64 @@ http { proxy_pass_header Access-Control-Expose-Headers; } + # DAG import endpoint for CAR-batched UXF Profile pushes + # (sphere-sdk #370 Part 1). Replaces the per-block /dag/put loop + # with a SINGLE multipart POST of the full CAR — eliminates the + # burst of N parallel /dag/put requests during §D.1 pre-clear + # snapshots. Capability-probed once per process by the SDK at + # startup; absence here (404 or 405) makes the SDK transparently + # fall back to the per-block /dag/put path above. + # + # client_max_body_size: 100M — CAR-batched bundles carry the + # full pin payload in a single body (vs ~50KB-1MB per individual + # block on /dag/put). Sized generously above the typical Profile + # snapshot (~few MB) to avoid 413 truncation on large wallets. + # + # Timeouts: 300s — Kubo's internal pin of every block in a + # large CAR takes several seconds end-to-end; allow headroom + # over the typical ~1-2s import time so a slow inner CAR walk + # doesn't surface as a spurious gateway-side timeout that + # triggers the SDK's legacy fallback unnecessarily. + location /api/v0/dag/import { + client_max_body_size 100M; + proxy_pass http://127.0.0.1:5001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_pass_header Access-Control-Allow-Origin; + proxy_pass_header Access-Control-Allow-Methods; + proxy_pass_header Access-Control-Allow-Headers; + proxy_pass_header Access-Control-Expose-Headers; + } + + # DAG export endpoint for CAR-batched UXF Profile fetches + # (sphere-sdk #370 Part 1). Replaces the per-block BFS walk + # (one /api/v0/block/get per block) with a single CAR stream + # rooted at the requested CID. Capability-probed by the SDK; + # absence here transparently falls the SDK back to the BFS + # path above. + # + # proxy_buffering off: response CARs can be tens of MB and + # are content-addressed (verified end-to-end by the receiver). + # Buffering provides no value and would pile bytes up in + # nginx's worker memory under concurrent fetches. + # + # No client_max_body_size needed — the request body is the + # query string (`?arg=`); the bytes flow the other way. + location /api/v0/dag/export { + proxy_pass http://127.0.0.1:5001; + proxy_buffering off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_pass_header Access-Control-Allow-Origin; + proxy_pass_header Access-Control-Allow-Methods; + proxy_pass_header Access-Control-Allow-Headers; + proxy_pass_header Access-Control-Expose-Headers; + } + # Safe API proxy for content preloading (refs triggers fetch via bitswap) location /api/v0/refs { client_max_body_size 10M; diff --git a/scripts/configure-ipfs.sh b/scripts/configure-ipfs.sh index b5bded1..6f8a9f4 100644 --- a/scripts/configure-ipfs.sh +++ b/scripts/configure-ipfs.sh @@ -42,26 +42,26 @@ su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config Addresses.API /ip4/ # Bind Gateway to all interfaces (within container) su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config Addresses.Gateway /ip4/0.0.0.0/tcp/8080" -# Enable relay client and service with resource caps +# Relay: keep CLIENT (used in case we're behind NAT), DISABLE SERVICE (we don't +# forward other peers' traffic — that's pure overhead for a wallet gateway). su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.RelayClient.Enabled true" -su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.RelayService '{ - \"Enabled\": true, - \"ConnectionDurationLimit\": \"2m0s\", - \"ConnectionDataLimit\": 131072, - \"ReservationTTL\": \"60m\", - \"MaxReservations\": 64, - \"MaxCircuits\": 16, - \"BufferSize\": 4096, - \"MaxReservationsPerIP\": 8, - \"MaxReservationsPerASN\": 32 -}'" - -# Connection manager: cap connections with graceful trimming -# LowWater/HighWater ratio kept gentle (75%) to avoid connection storm churn +su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.RelayService.Enabled false" + +# Connection manager: aggressive cap for "API-first, light p2p" profile. +# LowWater 16 / HighWater 64 = node maintains a small mesh for occasional +# cross-peer bitswap, doesn't try to be a public IPFS supernode. +# GracePeriod 30s = ConnMgr prunes new peers 4× faster than the default 2m. su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.ConnMgr.Type '\"basic\"'" -su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.ConnMgr.LowWater 96" -su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.ConnMgr.HighWater 128" -su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.ConnMgr.GracePeriod '\"2m\"'" +su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.ConnMgr.LowWater 16" +su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.ConnMgr.HighWater 64" +su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.ConnMgr.GracePeriod '\"30s\"'" + +# DHT: leave as "auto" (Kubo modern default: server when publicly reachable, +# client otherwise). Earlier experiments with "dhtclient" broke the public +# `/ipfs/` gateway route — content provider discovery for non-local +# CIDs timed out because client mode degrades the findProviders walk. The +# auto-mode load is small enough on this host (~5-15% CPU steady state). +su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config Routing.Type auto" # Add Unicity bootstrap peers echo "[IPFS] Adding Unicity bootstrap peers..." @@ -70,6 +70,15 @@ su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD bootstrap add /dns4/unicit su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD bootstrap add /dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6" || true su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD bootstrap add /dns4/unicity-ipfs3.dyndns.org/tcp/4003/wss/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6" || true +# === DATASTORE: storage ceiling === +# Kubo's default StorageMax is 10 GB — far below the working set of a real +# gateway. Once RepoSize exceeds StorageMax, kubo enters a continuous GC +# reclaim loop that fights every pin write (and can cause "silent pin drops" +# where the API reports success but bytes are GC'd before the next reader +# can fetch them). 750 GB matches the operator's disk headroom while +# keeping the cap as a defensive ceiling against runaway growth. +su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config Datastore.StorageMax 750GB" + # === RESOURCE MANAGER: cap system-wide resources === # Kubo 0.19+ removed Swarm.ResourceMgr.Limits — use libp2p-resource-limit-overrides.json instead echo "[IPFS] Configuring resource manager limits..." @@ -78,12 +87,12 @@ su -s /bin/sh ipfs -c "IPFS_PATH=/data/ipfs $IPFS_CMD config --json Swarm.Resour cat > /data/ipfs/libp2p-resource-limit-overrides.json << 'RESLIMITS' { "System": { - "Conns": 512, - "ConnsInbound": 256, - "ConnsOutbound": 256, - "Streams": 2048, - "StreamsInbound": 1024, - "StreamsOutbound": 1024, + "Conns": 192, + "ConnsInbound": 128, + "ConnsOutbound": 64, + "Streams": 1024, + "StreamsInbound": 512, + "StreamsOutbound": 512, "FD": 4096, "Memory": 1610612736 }