Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions F5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
NIP-F5
======

Population Census
------------------

`draft` `optional` `client`

This NIP defines an event kind that lets a distributed network of nodes
estimate the number of currently online peers through probabilistic,
self-reported sampling.

## Motivation

Distributed networks (mesh P2P overlays, federated services, etc.) often
need to know how many nodes are online in a given scope. No single relay
or indexer sees the whole network, so a central count is impossible. This
NIP describes a mechanism where each node independently decides whether to
publish a census event during a window, and readers combine the received
events with a Horvitz-Thompson estimator to recover the population size.

## Event

`30789` addressable-range event. Published once per node per time window
(at most), with an inclusion probability `p`.

The `content` is a JSON document. The only field defined by this NIP is:

- `p`: the inclusion probability, a number in `(0, 1]`, optionally clamped
to a network-defined minimum.

Implementations MAY add further fields to `content` (for example a
timestamp, or application-specific identity material). How a node's
identity is established and confirmed is intentionally left to each
implementation; this NIP does not define it. See [Identity](#identity).

### Tags

The event carries:

- `t` = a fixed network-scope topic label (hashtag) identifying the census
scope. Each network chooses its own label (for example `my-network`).
- `x` = the fixed value `census`.

There is no `d` tag. In the `30000 <= n < 40000` range a missing `d` tag is
treated by relays as an empty-string address ([NIP-01](01.md)), so the
events are addressable per `(kind, pubkey, "")`.

```jsonc
{
"kind": 30789,
"tags": [["t", "my-network"], ["x", "census"]],
"content": "{\"p\":0.5}"
}
```

## Identity

How a node proves it is the one behind a census packet is deliberately not
specified here, because different networks use different identity models.
Common approaches include:

- relying on the event's `pubkey` itself (the event author is the node);
- wrapping `p` (or the whole packet) in an application-level signature under
the network's own key scheme, carried in additional `content` fields;
- any other scheme the network deems appropriate.

A reader MUST NOT assume a particular identity scheme. At minimum, an event
must be a valid NIP-01 event — its `sig` verifies against its `pubkey` —
before its packet may be counted.

## Publishing

Each node runs a loop over time windows of fixed length `W` (for example 10
minutes). Every node keeps an inclusion probability `p`.

1. Count the distinct census events observed during the current window,
excluding the node's own event: `E`.
2. Update the inclusion probability toward a target number of events per
window `T`:

- if `E == 0`, grow the probability: `p' = p * g` with `g > 1` (for
example `1.5`);
- otherwise `p' = p * (T / E)`;
- clamp `p'` to `[p_min, 1]` with `p_min > 0` (for example `0.001`).

3. With probability `p'`, publish a census event containing the updated `p`.

Initial `p` is typically `0.5`. The exact values of `W`, `T`, `g`, `p_min`
and the initial `p` are network-specific parameters.

## Reading

A reader that receives a set of valid census events estimates the number of
online nodes with the Horvitz-Thompson estimator:

```
M = sum(1 / p_i) over all received events (excluding the reader's own event)
+ 1 for the reader itself, which is known to be online
```

Deduplication is per node identity, however the network defines it (see
[Identity](#identity)). Events older than a freshness window `W` (or a
network-defined TTL) are ignored, as are events that fail the network's
identity confirmation.

## Validation

Ingress packets are untrusted and MUST be checked before being counted:

- the event is a valid NIP-01 event (`sig` verifies against `pubkey`);
- `p` is a finite number in `(0, 1]` (and above the network minimum if one
is defined);
- the network's identity confirmation (if any) succeeds.

## Notes

- The census relies on all participants publishing to (and subscribing from)
a shared relay set, so events are visible to the whole network scope.
- Because participation is probabilistic, the estimator is unbiased but
noisy for small networks; larger `T` reduces variance at the cost of more
events.
- This mechanism is orthogonal to [NIP-45](45.md) (relay-side event counts)
and to relay discovery ([NIP-66](66.md)); it estimates the number of
*nodes* that are online, not the number of stored events.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ NIPs listed here are not a protocol checklist. Nothing forces any software to im
- [NIP-CC: Geocaching](CC.md)
- ~~[NIP-EE: E2EE Messaging using MLS Protocol](EE.md) --- **unrecommended**: superseded by the [Marmot Protocol](https://github.com/marmot-protocol/marmot)~~
- [NIP-F4: Podcasts](F4.md)
- [NIP-F5: Population Census](F5.md)

## Event Kinds

Expand Down Expand Up @@ -275,6 +276,7 @@ This table is not exhaustive. For a machine-readable registry of all known event
| `30403` | Draft Classified Listing | [99](99.md) |
| `30617` | Repository announcements | [34](34.md) |
| `30618` | Repository state announcements | [34](34.md) |
| `30789` | Population Census | [F5](F5.md) |
| `30818` | Wiki article | [54](54.md) |
| `30819` | Redirects | [54](54.md) |
| `31234` | Draft Event | [37](37.md) |
Expand Down