DePIN Network Coverage: The 482 Networks With No Free Public Feed

At Kairos Signal, we index Decentralized Physical Infrastructure Networks (DePINs) for a living. Our ingestion pipeline currently catalogs 745 distinct DePIN networks across compute, storage, wireless, sensor, and energy verticals. That number sounds impressive on a pitch deck. The reality beneath it is far less so.

Of those 745 networks:

| Category | Count | Share | |---|---|---| | Networks with live, accessible data feeds | 263 | 35.3% | | Networks with first-party supply telemetry (node counts, geographic distribution, uptime) | 171 | 22.9% | | Networks with no free public feed of any kind | 482 | 64.7% |

This post is about that last row—the 482 networks that are, from a data perspective, dark matter. We cannot see them. We cannot verify them. We refuse to pretend otherwise.

---

The Coverage Function

Let us define the problem formally. A DePIN network $N_i$ is a tuple:

$$N_i = \langle \mathcal{S}_i, \mathcal{D}_i, \mathcal{A}_i, \mathcal{T}_i \rangle$$

Where:

For 263 networks, $\mathcal{T}_i$ is non-empty: there exists at least one endpoint, subgraph, or stream from which we can reconstruct $\mathcal{S}_i$ and $\mathcal{D}_i$ with bounded staleness $\delta_t$.

For 171 of those, we can additionally access first-party supply telemetry—meaning the protocol itself exposes node-level metadata (geolocation, hardware type, version, uptime ratio) through official APIs or on-chain registries. These are the gold standard.

For the remaining 482:

$$\mathcal{T}_i = \emptyset$$

No public RPC that returns node registries. No subgraph indexing operator state. No REST endpoint. No WebSocket stream. No CSV dump on a governance forum. Nothing.

---

Why We Do Not Estimate

The industry temptation is to fill these gaps with models. Given partial data from the 263 observable networks, one could train a regressor—say, a gradient-boosted model over protocol type, token market cap, chain, and age—to predict node counts for the 482 dark networks.

We refuse. Here is the mathematical argument.

The Estimation Trap

Let $\hat{n}_i$ be the estimated node count for network $N_i$ where $\mathcal{T}_i = \emptyset$. The estimator is trained on observable networks:

$$\hat{n}_i = f(N_i \mid \theta), \quad \theta^* = \arg\min_\theta \sum_{j \in \mathcal{O}} \mathcal{L}(n_j, f(N_j \mid \theta))$$

Where $\mathcal{O}$ is the set of observable networks and $\mathcal{L}$ is some loss function. This is standard supervised learning. The problem is distribution shift: the observable set $\mathcal{O}$ is not drawn iid from the population $\mathcal{P}$ of all DePIN networks. Networks that expose telemetry are systematically different from those that do not.

Specifically, networks with $\mathcal{T}_i = \emptyset$ are more likely to be:

The conditional distribution $P(N \mid \mathcal{T} = \emptyset)$ differs structurally from $P(N \mid \mathcal{T} \neq \emptyset)$. Any estimator trained on $\mathcal{O}$ will have unquantifiable bias on the complement set $\mathcal{P} \setminus \mathcal{O}$:

$$\text{Bias}(\hat{n}_i) = \mathbb{E}[\hat{n}_i] - n_i, \quad i \in \mathcal{P} \setminus \mathcal{O}$$

This bias is unbounded because we have no ground truth for $n_i$ in the dark set. Cross-validation on $\mathcal{O}$ tells us nothing about generalization to $\mathcal{P} \setminus \mathcal{O}$.

Publishing $\hat{n}_i$ as if it were data creates a category error: it conflates observation with interpolation across a distributional chasm. Downstream consumers—researchers, investors, regulators—will treat these estimates as measurements, and decisions made on fictional precision are worse than decisions made on acknowledged ignorance.

---

The not_covered Map

Instead of estimation, we build a not_covered map: a formal, machine-readable specification of what our data does NOT contain.

Schema

# not_covered.toml — Kairos Signal Coverage Boundary Specification

Each entry represents a network where T_i = ∅

[[not_covered]] network_id = "depin-00417" name = "Helium IoT (legacy chain, pre-migration)" chain = "solana" category = "wireless_iot" reason = "no_public_rpc" last_attempted = "2025-01-08T14:32:00Z" attempt_method = "rpc_node_registry" notes = "Legacy Helium IoT entity registry inaccessible; migration to Solana incomplete for historical data"

[[not_covered]] network_id = "depin-00582" name = "Xnet WiFi" chain = "ethereum" category = "wireless_wifi" reason = "no_telemetry_endpoint" last_attempted = "2025-01-10T09:17:00Z" attempt_method = "subgraph_query, rest_api_probe" notes = "Token contract exists; no operator registry or supply API found"

[[not_covered]] network_id = "depin-00691" name = "WeatherXM" chain = "polygon" category = "sensor_weather" reason = "gated_api" last_attempted = "2025-01-12T16:45:00Z" attempt_method = "api_docs_review, swagger_probe" notes = "API requires API key; free tier does not expose node-level supply data"

Each entry is an assertion of ignorance with metadata about the attempt to resolve it. This is not a bug tracker; it is an epistemic boundary.

The Coverage Set Formalization

We define the coverage set $\mathcal{C}$ and its complement $\bar{\mathcal{C}}$:

$$\mathcal{C} = \{N_i \in \mathcal{P} : \mathcal{T}_i \neq \emptyset \land \text{staleness}(\mathcal{T}_i) \leq \delta_t\}$$

$$\bar{\mathcal{C}} = \mathcal{P} \setminus \mathcal{C} = \{N_i \in \mathcal{P} : \mathcal{T}_i = \emptyset \lor \text{staleness}(\mathcal{T}_i) > \delta_t\}$$

Our data products carry an explicit coverage_mask:

from dataclasses import dataclass
from enum import Enum
from typing import Optional
from datetime import datetime

class CoverageReason(Enum): NO_PUBLIC_RPC = "no_public_rpc" NO_TELEMETRY_ENDPOINT = "no_telemetry_endpoint" GATED_API = "gated_api" PRIVATE_CHAIN = "private_chain" ABANDONED = "abandoned" STALE_DATA = "stale_data" RATE_LIMITED = "rate_limited"

@dataclass class CoverageBoundary: network_id: str reason: CoverageReason last_attempt: datetime attempt_method: str notes: Optional[str] = None

def to_null_marker(self) -> dict: """Convert to a null marker for downstream pipeline filtering.""" return { "network_id": self.network_id, "node_count": None, # Explicitly NULL, not 0 "node_count_ci_lower": None, "node_count_ci_upper": None, "coverage_status": "not_covered", "reason": self.reason.value, "epistemic_type": "null_observation" # NOT "missing_data" }

Note the distinction: node_count is None, not 0. A zero count is a measurement ("we counted zero nodes"). A None is an assertion ("we cannot count"). These are fundamentally different propositions, and conflating them is the most common data sin in DePIN analytics.

---

Why not_covered Is More Useful Than Estimates

For Researchers

A researcher studying DePIN geographic distribution needs to know where data infrastructure is missing, not where we have fabricated numbers. Consider a study mapping wireless coverage density across Sub-Saharan Africa:

-- WRONG: Includes estimates that conflate observation with interpolation
SELECT 
    region,
    SUM(node_count) AS total_nodes,
    COUNT(*) AS network_count
FROM depin_supply_estimates
WHERE category = 'wireless'
GROUP BY region;

-- RIGHT: Separates observed from unobserved SELECT region, SUM(CASE WHEN coverage_status = 'covered' THEN node_count ELSE 0 END) AS observed_nodes, SUM(CASE WHEN coverage_status = 'covered' THEN 1 ELSE 0 END) AS observable_networks, SUM(CASE WHEN coverage_status = 'not_covered' THEN 1 ELSE 0 END) AS dark_networks, COUNT(*) AS total_networks, ROUND( SUM(CASE WHEN coverage_status = 'not_covered' THEN 1 ELSE 0 END)::FLOAT / COUNT(*)::FLOAT, 3 ) AS darkness_ratio FROM depin_supply_with_coverage_mask WHERE category = 'wireless' GROUP BY region;

The second query tells you: "In West Africa, we observe 340 nodes across 12 networks, but there are