Skip to main content

Installation

The node software is the program that sits in front of your inference backend, advertises your catalog, and speaks the network's wire protocol. It's distributed as prebuilt binaries from the zs-node repository under the Functional Source License (FSL-1.1-ALv2), which converts to Apache 2.0 two years after each release; the source is not published. This page covers what you need, the three ways to install the node binary, wiring it to a backend, and running it as a long-lived service behind HTTPS.

For what the node does once it's running, see How the network works and the concept pages. For registering on-chain, which you do before the node can serve, see Registering on-chain.

Supported platforms & hardware

The node does not run models. It sits in front of an inference backend, and which machine that backend runs on is what decides your hardware bill. So size two things separately:

What it isWhat it costs
The node processThe zs-node binary — envelopes, admission, settlement, built-in toolsSmall and bounded. 1–2 vCPU, 2 GB RAM. Measured; see Sizing the node process.
The inference backendWhatever actually runs the modelEverything else. Zero if the model runs somewhere you don't pay for.

What decides the second bill is where the backend runs, not which provider you name:

DeploymentConfigThis host needs
Backend runs elsewhere — a hosted gateway, OpenAI, Vertexopenai_passthrough with a remote base_url, or vertexaiNo GPU. 1–2 vCPU, 2 GB RAM.
Backend runs on this box — vLLM, SGLang, Ollamaopenai_passthrough with a local base_urlThe node's 2 GB plus what the backend needs.
Backend runs on this box, node-supervisedlocal (llama.cpp), or llamacpp / lmstudio / kronk pointed at a sibling processSame, plus the node derives the backend's concurrency flags — see KV cache.
Image generationimage_llm.provider: comfyuiIts own VRAM, on top of any text model — see Image models.
Relay-onlyzs.relay_only: true1 vCPU, 256 MB. No models, no tools, no settlement.
warning

openai_passthrough is on both sides of that table, and it's the most common way to run your own GPU. vLLM and SGLang speak the OpenAI API, so a node in front of your own vLLM is openai_passthrough with base_url: http://127.0.0.1:8000/v1not provider: local, which means specifically "node-supervised llama.cpp". Naming the provider tells you nothing about whether you need a GPU; the base_url does.

Only local is a child process the node supervises. lmstudio, llamacpp, kronk, a same-box vLLM, and ComfyUI are all separate processes with their own memory — the node's GOMEMLIMIT and container limits do not restrain them.

If your backend runs elsewhere, you're done sizing: the node is a small VM that never loads a weight. Skip to Install paths; the only other page you need is Sizing the node process.

What the inference backend needs

Two terms, and the second is the one that surprises people:

VRAM ≈ model weights + KV cache

Weights are a property of the model and the quantization you pull. Model names usually carry the format — …-nvfp4, …-fp8-dynamic, …-Q4_K_M — and it changes the answer by up to 4×:

Format≈ GB per 1B paramsRuntimeGPU floor
BF16 / FP162.0anythingany
FP8 / fp8-dynamic1.0vLLM, SGLangHopper or newer (H100/H200/Blackwell)
NVFP4~0.6vLLMBlackwell (B100/B200, RTX 50-series)
MXFP4~0.6vLLM, llama.cppwider than NVFP4
GGUF Q4_K_M~0.6llama.cpp, LM Studio, Ollamaany

For a mixture-of-experts model that multiplier applies to total parameters, not active ones — every expert stays resident even though only a few fire per token. A "3B active" model still costs you all 26B of weights.

warning

FP8 and NVFP4 have a hardware floor, and missing it is expensive. The A100 has no FP8 tensor cores — vLLM will either refuse --dtype fp8 or silently fall back to FP16, doubling the memory you budgeted. NVFP4 needs Blackwell. Check the format against your card before you pull 60 GB of weights.

KV cache is a property of your config, and it scales with concurrency:

KV bytes/token = 2 × layers × kv_heads × head_dim × bytes_per_element
KV total = context_window × parallel_slots × KV bytes/token

That second line is the one to internalize. parallel_slots: 0 — the default — resolves to zs.max_active_tickets, so raising your ticket concurrency multiplies your VRAM requirement. A config that boots at max_active_tickets: 2 can fail to allocate at 16 with nothing else changed.

Real numbers

KV is computed from each model's published config.json at f16 cache; exact per-token figures are given where the architecture is plain attention, and marked small where sliding-window or compressed attention makes the cache far cheaper than the parameter count suggests. Total is weights + KV at context_window: 32768 with 4 slots, and assumes nothing else is on the card. Treat these as planning figures — your runtime, format and context all move them.

ModelParamsFormatWeightsKV / tokenKV @ 32k × 4TotalRealistic card
Gemma 4 E4B4.5B eff.Q4_K_M~3 GBsmall (SWA)~1 GB~4 GB8 GB, or CPU
Gemma 4 12B12BQ4_K_M~7.5 GBsmall (SWA)~2 GB~10 GB16 GB
Gemma 4 26B-A4B26B MoE (3.8B active)NVFP4~16 GBsmall (SWA)~2 GB~18 GB24 GB Blackwell
Qwen3 14B14BQ4_K_M~8.3 GB160 KB21 GB~29 GB32 GB / 2× 24 GB
Qwen3 30B-A3B30B MoE (3B active)Q4_K_M~17 GB96 KB~13 GB~30 GB32 GB / 2× 24 GB
MedGemma 27B27BFP8~27 GBsmall (SWA)~2 GB~29 GB40 GB Hopper+
Qwen3 32B32BQ4_K_M~19 GB256 KB34 GB~53 GB2× 48 GB
gpt-oss-120b120B MoE (5B active)MXFP4~61 GB~36 KB~5 GB~66 GB1× 80 GB (H100)
DeepSeek V4 Flash284B MoE (13B active)FP8~284 GBsmall (compressed)~290 GB4× 80 GB
Kimi K32.8T MoEMXFP4~1.5 TBmulti-node cluster

Four things that table is trying to tell you:

  • Parameter count doesn't predict what fits. gpt-oss-120b has nearly 4× the parameters of Qwen3 32B and one seventh its KV cost per token — 36 layers of 8 KV heads at head-dim 64, half of them sliding-window, against Qwen3's 64 full-attention layers at head-dim 128. Gemma's sliding-window attention does the same thing. Architecture decides the KV bill.
  • The format in the model name is half the answer. Gemma 4 26B-A4B at NVFP4 is a 24 GB card; MedGemma 27B at FP8 is barely smaller in parameters and needs 40 GB and a Hopper-class GPU. "27B" on its own tells you almost nothing — read the suffix.
  • KV cache can exceed the weights. Qwen3 32B is 19 GB of weights and 34 GB of cache at four slots. Sizing a card off the weights column alone is how operators end up at Out of VRAM at startup.
  • The top of the range is what passthrough is for. Kimi K3 is not a single-box model at any quantization. Serving it means openai_passthrough to someone who runs the cluster — which is a completely legitimate way to operate a node, and needs no GPU at all.

If it doesn't fit

In rough order of what you give up:

  1. Lower context_window. Linear on KV, and it's the per-session budget you advertise — so it's a real product decision, not a free win.
  2. Lower zs.max_active_tickets (or pin llm.local.parallel_slots). Linear on KV. Costs concurrency, not capability.
  3. Quantize the KV cacheextra_args: ["--cache-type-k", "q8_0", "--cache-type-v", "q8_0"] roughly halves it. The node derives -c, --parallel and --cont-batching itself and rejects those in extra_args, but the cache-type flags are yours to set.
  4. Lower gpu_layers to spill layers to CPU — it fits, and it's slow.
  5. Smaller quantization, or a smaller model.
warning

Nothing checks this for you. The node reads no GGUF metadata and never looks at your VRAM — gpu_layers is passed to llama-server verbatim. The one guard is a sanity ceiling of 4M tokens on context_window × parallel_slots, which is expressed in tokens and so can't tell a 24 GB card from a 192 GB one.

The failure mode is therefore at startup, in the backend: llama-server fails to allocate, the supervisor restarts it five times in sixty seconds, then gives up permanently. Do the arithmetic before you deploy, not after.

Image models

Image generation is a separate provider (image_llm.provider) and a separate process. Its VRAM does not share the text model's budget — if you serve both on one box, they add. There is no KV cache here; the cost is the transformer plus the VAE, text encoder and activations, so it's near-constant per model rather than scaling with concurrency.

ModelFormatVRAMNotes
SDXLBF168+ GB16+ GB unified memory on Apple Silicon
Z-Image Turbo (6B)BF1614–16 GB~12 GB transformer + VAE + text encoder + activations
Z-Image Turbo (6B)FP8~8 GBGGUF variants go to ~6 GB
flux-schnellBF1616+ GB32+ GB unified memory on Apple Silicon (q4)

ComfyUI is operator-installed and not packaged with the node — it runs as its own process, so its memory sits outside the node's limits and GOMEMLIMIT. On Linux + AMD, install the ROCm extras: SDXL works, flux is hit-or-miss. Setup is in Serving models & pricing.

GPU and platform notes

  • NVIDIA driver ≥ 535 (CUDA 12.x runtime) on any GPU host.
  • Multi-GPU works without NVLink. llama.cpp splits by layer across cards, and layer-split is usually faster than row-split on non-NVLink pairs. NVLink helps tensor-parallel runtimes (vLLM) far more than it helps llama.cpp.
  • Apple Silicon shares one pool between weights, KV cache and the OS — apply the same arithmetic to unified memory and leave several GB for macOS. Fine for development; see the OS table below for why it isn't recommended in production.
  • Confidential compute forces llm.provider: local, so a TEE node always owns its GPU — and the supported parts are narrower (H100 / H200 / Blackwell; A100 is not supported).

Sizing the node process

On a dedicated machine you can skip this — the inference engine dwarfs the node and there's headroom to spare. It matters when the node gets a memory limit of its own: a container, a cgroup, a VM sized to the pod rather than the box.

The node idles in the low hundreds of MB, but it is not flat. The one path that spikes is web read: converting a page to markdown costs roughly 20–70× the page's own size in peak memory, because the readability pass builds and scores a document tree of the whole page. The multiple depends on how the page is built, so there's no single number — a hostile page is at the top of that range, and the caller chooses the page.

The node bounds this for you: zs.builtin_tools.web_read.max_concurrent (default 4) caps how many pages it converts at once, so a burst of large reads queues instead of multiplying. Roughly:

peak ≈ max_download × (20–70) × max_concurrent

Measured on the shipped defaults with 16 concurrent readers: ~270 MB on pages the size of the largest real article we found (~2 MB), ~560 MB when every reader fetches a page that nearly fills the 4 MiB ceiling. Those are ordinary page structures; a pathological one costs several times more per byte, which is what the top of the 20–70× range buys you. Budget for it.

DeploymentRequestLimitGOMEMLIMITConfig
Serving node (inference elsewhere)256Mi2Gi1700MiBdefaults
Serving node, tighter256Mi1Gi850MiBmax_concurrent: 1
Serving node, small container128Mi512Mi430MiBmax_concurrent: 1, max_download: 1048576
Relay-only128Mi256Mi200MiBn/a — no tools

max_concurrent is the right lever for a tight box, because it trades latency, not capability: reads queue rather than fail, and every page stays readable. Measured effect, same load, defaults otherwise — it does what you'd expect:

max_concurrent1248
peak live heap153 MB220 MB345 MB513 MB

Lowering max_download also cuts memory, but by making large pages permanently unreadable — reach for it only when you also want that (the 512Mi row above accepts exactly that trade).

A relay-only node runs no built-in tools, no settlement, and no oracle — it forwards traffic and answers discovery. Nothing on it converts a page, so it has no spike to budget for.

warning

Set GOMEMLIMIT, not just a memory limit — a limits.memory on its own does not restrain the node, it only decides when the kernel kills it.

Go's collector lets the heap grow well past what's live before running, so a conversion burst can push the process over a cgroup limit while the live heap was never near it — you get OOM-killed for memory you weren't really using. GOMEMLIMIT is a soft limit: as the runtime approaches it the collector works harder to stay under, trading CPU for survival. Set it to roughly 85% of your limit, leaving room for stacks and allocator overhead the Go heap doesn't count.

Don't assume the runtime does this for you — it does for CPU, not memory. The node is built with Go 1.26, which reads your cgroup's CPU limit on its own (so GOMAXPROCS needs no setting, and setting it disables the automatic behavior). Go has no equivalent for memory: the runtime never reads your cgroup memory limit, in any released version. The proposal to add it (golang/go#75164) is still open and unimplemented. Until it lands, GOMEMLIMIT is the only thing telling the Go runtime how much memory it's allowed to use.

Kubernetes, serving node:

resources:
requests:
memory: 256Mi
cpu: 500m
limits:
memory: 2Gi
env:
- name: GOMEMLIMIT
value: "1700MiB"

If that's more than you want to give it, the levers, in order of bluntness: web_read.max_concurrent: 1 (queues reads, keeps every page readable — start here), then web_read.max_download (the spike scales with it, but large pages stop being readable at all), then web_read.enabled: false and the spike goes away entirely. See Built-in tools.

Operating systems:

OSNotes
Linux (recommended for production)Ubuntu 22.04+ or any modern x86_64 distro. The node's SQLite is pure-Go (modernc.org/sqlite), so there's no libsqlite to install.
WindowsRuns natively or under WSL2; both work, using the prebuilt CUDA llama.cpp binary.
macOSSupported as a dev environment (Metal). Not recommended for production because of lower concurrent throughput.

To build from source, Go 1.26+. (GPU hosts also need an NVIDIA driver ≥ 535 — see GPU and platform notes above.)

Network:

  • Outbound HTTPS to your algod provider, to CoinGecko (for ALGO/USD pricing), and to your upstream LLM if you use OpenAI-compatible passthrough or Vertex AI.
  • Inbound HTTPS to the node's public listener, exposed through your reverse proxy or the node's own TLS — see Exposing the endpoint over HTTPS.
info

You should already have an operator_id before you install. Registration is done through the operator dashboard ("Register Operator" form) with your owner wallet; it returns the operator_id you'll put in the node config. See Registering on-chain, and Encryption & keys for which keys the node holds.

Install paths

Pick one. A release binary (install script / Homebrew / Scoop / direct download) is the quickest; Docker is simplest for production; the bare-metal binary builds from source; building inside Docker is for custom images.

The quickest path — a prebuilt binary. macOS builds are Developer ID–signed and notarized, so there's no Gatekeeper prompt on first run.

Install script (Linux / macOS):

curl -fsSL https://zerosignal.ai/install.sh | sh -s -- zs-node

It detects your OS and architecture, verifies the download's SHA-256 against the release's own checksums.txt, and installs to /usr/local/bin if you can write there or ~/.local/bin otherwise. It never uses sudo and never starts anything. Read it first with curl -fsSL https://zerosignal.ai/install.sh | less; pin a release with ZS_VERSION=X.Y.Z or choose the directory with ZS_INSTALL_DIR. Re-run it to upgrade — it reinstalls into the same directory, which matters because the service unit records the binary's path.

Homebrew (macOS):

brew install txnlab/tap/zs-node

Scoop (Windows):

scoop bucket add txnlab https://github.com/txnlab/scoop-bucket
scoop install zs-node

Direct download (Linux / any): grab the archive for your platform from the latest release, verify its checksum against checksums.txt, and put the binary on your PATH:

PlatformArchive
Linux x86_64zs-node_<version>_linux_amd64.tar.gz
Linux arm64zs-node_<version>_linux_arm64.tar.gz
macOS (universal Intel + Apple Silicon)zs-node_<version>_darwin_all.tar.gz
Windows x86_64zs-node_<version>_windows_amd64.zip
Windows arm64zs-node_<version>_windows_arm64.zip
sha256sum -c checksums.txt --ignore-missing # verify
tar -xzf zs-node_<version>_linux_amd64.tar.gz
sudo install -m 755 zs-node /usr/local/bin/zs-node

Then run it as a service — see Running as a service. zs-node install-service generates and enables the unit for you (the Homebrew / Scoop installs already put zs-node on your PATH).

Wiring the node to an inference backend

The node doesn't run models itself — it forwards decrypted prompts to an inference backend you choose. You select the backend with the llm.provider key (one provider per node):

ProviderWhat it does
openai_passthroughAny OpenAI-compatible base URL (vLLM, Ollama, LM Studio, hosted gateways, or a real OpenAI-compatible endpoint).
localThe node supervises a llama-server child process over loopback. The recommended path for a self-hosted GPU.
lmstudio, llamacppPoint at a separately-running LM Studio or llama-server.
kronkPoint at a separately-running Kronk server — a multi-model llama.cpp pool in one process.
vertexaiGoogle Vertex AI's OpenAI-compatible surface (no static API key; uses Application Default Credentials).

Full configuration for each provider — base URLs, API keys, GGUF model paths, gpu_layers, slots, image generation, and multi-model layouts — lives in Serving models & pricing and Configuration. Choose your provider there before you finalize config.yaml.

tip

You usually don't have to pick by hand. Once your backend is running, point the wizard at it and it works the rest out — which provider to use, the real API root, which endpoints exist, and what each model can do:

zs-node init --base-url=http://127.0.0.1:8080/v1

See Generate it: zs-node init.

llama.cpp setup (the local provider)

If you run provider: "local", install llama-server once; the node starts, supervises, and restarts it for you. On Linux with NVIDIA CUDA:

# 1. Driver — aim for >= 535 (CUDA 12.x runtime).
sudo apt install -y nvidia-driver-550
sudo reboot
nvidia-smi # confirm GPU(s) visible

# 2. Prebuilt llama.cpp CUDA binary. Pin to a known-good tag.
LLAMA_TAG=b4500 # replace with a tag you've smoke-tested
curl -L -o llama.zip \
https://github.com/ggerganov/llama.cpp/releases/download/${LLAMA_TAG}/llama-${LLAMA_TAG}-bin-ubuntu-cuda-x64.zip
unzip llama.zip -d /tmp/llama
sudo install -m 755 /tmp/llama/build/bin/llama-server /usr/local/bin/llama-server
sudo cp /tmp/llama/build/bin/lib*.so /usr/local/lib/
sudo ldconfig
llama-server --version

# 3. Pull a GGUF that fits weights AND KV cache — see "What the inference
# backend needs" above. Budget ~0.6 GB per billion params for weights, then
# add context_window x parallel_slots x KV-bytes-per-token on top.
mkdir -p /srv/models
huggingface-cli download bartowski/Qwen_Qwen3-14B-GGUF \
Qwen3-14B-Q4_K_M.gguf --local-dir /srv/models

The matching config block:

llm:
provider: "local"
local:
binary_path: "/usr/local/bin/llama-server" # default; /opt/homebrew/bin/llama-server on macOS
parallel_slots: 0 # 0 = derive from max_active_tickets
models:
- id: "qwen3-14b"
model_path: "/srv/models/Qwen3-14B-Q4_K_M.gguf"
context_window: 32768 # x parallel_slots = your KV budget
gpu_layers: 99
warning

context_window: 32768 with the default parallel_slots: 0 means the KV cache is sized at 32768 × zs.max_active_tickets tokens. On this model that is ~5.2 GB per slot — so four slots need ~21 GB of cache on top of the ~8.3 GB of weights. Check that arithmetic against your card before you start it, or lower one of the two. See What the inference backend needs.

info

The local provider supervises one llama-server instance, so it serves a single model per node by design — a multi-entry models[] is a config error. The node derives the concurrency flags (--parallel, -c, continuous batching) from max_active_tickets, so don't set those in extra_args — they're rejected. To serve several local models on one host, run one node per model, each with its own config.yaml, public listen port, and settlement DB; they can share one operator identity and signing mnemonic. See Serving models & pricing.

Exposing the endpoint over HTTPS

Clients and relays reach you at the public HTTPS base URL you registered on-chain (e.g. https://node.example.com). The node is meant to face the public internet directly — bind server.listen to all interfaces (:9090 — dual-stack IPv4+IPv6; 0.0.0.0 would be IPv4-only) and give it HTTPS one of two ways:

  • The node's own TLS (simplest — no extra moving parts). Set server.tls.mode to acme and the node provisions and renews a Let's Encrypt certificate automatically via your operator's NFD DNS, or manual if you supply your own cert_path + key_path. The node serves HTTPS itself; nothing sits in front of it.
  • A TLS-terminating reverse proxy. If you already run one, terminate TLS there and forward to the node. This is a single TLS front door for a single node — it is not a load balancer. Do not fan requests across multiple node instances (see below).
warning

Do not put the node behind a load balancer. A node id is one process with one signing key and per-process admission/settlement state; multiple instances of the same node id corrupt accounting. To add capacity, register additional nodes (each its own node id, key, and URL) under your operator and let clients load-balance across them — see Registration. Also: the responses route streams, so a reverse proxy in front of a node must forward streamed responses without buffering the whole reply — and its own connection-draining window has to outlive the node's shutdown drain, or it will cut responses mid-stream every time you restart. See Graceful shutdown.

The full TLS and DNS configuration — tls.mode options, ACME prerequisites, NFD records, and IP sync — is covered in Configuration and Endpoint reference.

Public vs private listeners

The node binds two listeners with different jobs:

ListenerDefaultServesTLS?
Public (server.listen)127.0.0.1:9090 (loopback; bind :9090 to serve)The protocol routes — reserve, responses, image routes, details, relay. Serve over HTTPS.tls.mode: acme/manual, or one reverse proxy
Private (server.private_listen)127.0.0.1:9091/healthz (readiness), /livez (liveness), and /metrics (Prometheus).Never — loopback-only

The private listener is never TLS-wrapped and is meant to stay on loopback so your ops routes don't sit behind whatever fronts the public port. Set private_listen to the empty string ("") to colocate /healthz, /livez and /metrics on the public port instead. In YAML, quote ":9091" — the leading colon is otherwise a mapping marker.

Running as a service

Keep the node up across crashes and reboots.

systemd (bare-metal install). The quickest path is to let the node write and enable the unit for you:

sudo zs-node install-service

On first run it drops a 0600 secrets template at /etc/zerosignal/secrets.env and stops so you can add your mnemonic; run it again to install, enable, and start the service. sudo zs-node uninstall-service removes it. It generates exactly the unit below (which you can equivalently write by hand):

# /etc/systemd/system/zs-node.service
# Generated by `zs-node install-service`. Re-run to regenerate.
[Unit]
Description=ZeroSignal node
Documentation=https://docs.zerosignal.ai/operators
After=network-online.target
Wants=network-online.target
# Bound a crash loop (e.g. a bad config): 5 restarts / 60s, then give up.
StartLimitIntervalSec=60
StartLimitBurst=5

[Service]
Type=simple
DynamicUser=yes
StateDirectory=zs-node
EnvironmentFile=/etc/zerosignal/secrets.env
ExecStart=/usr/local/bin/zs-node --config /etc/zerosignal/config.yaml
# Graceful stop: on SIGTERM the node stops accepting new work, then finishes the
# inference it already accepted before exiting. TimeoutStopSec MUST exceed the
# node's whole drain budget (server.drain_grace + drain_timeout +
# shutdown_timeout — defaults 20s + 5m + 30s = 350s) or systemd SIGKILLs it
# mid-drain, truncating paid inference and stranding payer escrow. Raise this if
# you raise drain_timeout. See docs.zerosignal.ai/operators/operations.
KillSignal=SIGTERM
TimeoutStopSec=400
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

DynamicUser=yes runs the node under a transient system user systemd allocates at start — no useradd, no user to manage — and StateDirectory=zs-node creates and owns /var/lib/zs-node for it. Point your settlement DB there in config, since ProtectSystem=strict makes it the one writable path.

EnvironmentFile=/etc/zerosignal/secrets.env (mode 0600) holds your secrets — notably the signing mnemonic the node refuses to start without:

OPERATOR_SIGNING_MNEMONIC=word1 word2 ... word25
NODE_LLM_OPENAI_API_KEY=sk-...

If you wrote the unit by hand, enable it (the install-service command does this for you):

sudo systemctl daemon-reload
sudo systemctl enable --now zs-node
sudo journalctl -u zs-node -f

Docker: the --restart unless-stopped flag in the Docker install gives you the same always-up behavior.

info

The signing mnemonic is the only secret the node must have at startup — it's the on-chain identity that signs your tickets and receipts. Any env var ending in _MNEMONIC (such as OPERATOR_SIGNING_MNEMONIC) is picked up automatically, or you can load it from a cloud secret manager via ZS_MNEMONIC_URLS. See Encryption & keys for the full keystore options.

Your algod must observe the mempool

The node admits a paid request by checking the payer's escrow open() transaction while it's still pending — before it confirms — via algod.PendingTransactionInformation. So whatever algod you point the node at must see incoming pending transactions in the mempool, or admission will never succeed.

  • A public RPC (Nodely / AlgoNode) satisfies this out of the box — nothing to configure.
  • A self-hosted algod must either be participating in consensus or have ForceFetchTransactions: true set in its config.json. A non-participating archival node with default settings does not observe the mempool and will silently fail admission.
info

Privacy: your algod provider learns payer addresses. To admit a request the node also looks up the payer's account — an escrow opt-in check, and a free-tier allowance simulate — both of which send the payer's Algorand address to whatever algod you use. A shared public RPC therefore sees which payer is reserving on your node. That address is a stable pseudonym, never the prompt, and there's no protocol fix for it today. If withholding it from a third party matters to you or your payers, run your own algod instead of a shared RPC.

You wire algod with the algod config block (network, plus optional endpoint and token, preferring NODE_ALGOD_TOKEN for the token). Full Algorand wiring, including the escrow app id defaults, is in Configuration.