# em_pop

[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE.md)

**Population Protocol federation layer for [Emergence](https://github.com/EmergenceSystem).**
em_pop is the gossip substrate every Emergence agent runs: a node holds a peer
table, advertises a **semantic capability vector**, and continuously exchanges
state with its peers over HTTP. Any node can then ask *"which peers best match
this query vector?"* and route work to them — the mesh self-assembles, with no
central registry.

## Install

```erlang
%% rebar.config
{deps, [{em_pop, "0.2.0"}]}.
```

## Usage

```erlang
%% start a node: it listens for gossip and advertises MyCapabilityVector
{ok, Node} = em_pop:start_link(#{
    port            => 9100,
    vector          => MyCapabilityVector,   %% f32 flat binary
    stale_timeout   => 30000,                %% optional, default 30s
    gossip_interval => 5000                  %% optional, default 5s (0 = manual)
}),

%% seed from a known peer; from then on gossip discovers the rest
ok = em_pop:add_peer(Node, "other-host", 9101),

%% (a background loop gossips every gossip_interval; or drive it yourself)
ok = em_pop:gossip_tick(Node),

%% route: the top-3 peers whose capability vector is closest to QueryVec
[{PeerMap, Score} | _] = em_pop:peers_for(Node, QueryVec, 3).
```

## API

| Function                        | Purpose                                              |
|---------------------------------|------------------------------------------------------|
| `start_link/1`                  | Start a node (`#{port, vector, stale_timeout, gossip_interval}`) |
| `stop/1`                        | Stop a node and free its port                        |
| `id/1`                          | This node's unique binary id                         |
| `vector/1`                      | The capability vector the node advertises            |
| `add_peer/3`                    | Seed a peer by `Host, Port`                           |
| `peers/1`                       | All currently known peers                            |
| `peers_for/3`                   | Top-K peers by cosine similarity to a query vector   |
| `trust/2`                       | Trust score for a given peer                         |
| `gossip_tick/1`                 | Run one gossip round synchronously                   |

## How it works

- **Gossip.** Each node periodically POSTs its peer table to a known peer
  (`POST /pop/gossip`, bidirectional state exchange). New peers propagate
  transitively, so seeding from one well-connected node joins the whole network.
- **Semantic routing.** Peers advertise an f32 capability vector; `peers_for/3`
  ranks them by cosine similarity (via [kvex](https://hex.pm/packages/kvex)), so a
  query goes to the peers most likely to answer it.
- **Stale eviction.** Peers not heard from within `stale_timeout` are dropped.

The gen_server never blocks on HTTP: gossip is asynchronous.

## Requirements

- Erlang/OTP 27+, [rebar3](https://rebar3.org).

## Build & test

```bash
rebar3 compile
rebar3 ct
```

## License

Apache License 2.0 — see [LICENSE](LICENSE.md).
