net-mesh 0.36.0

High-performance, schema-agnostic, backend-agnostic event bus
Documentation
# `net` — Net mesh plugin for Hermes

A first-party [Hermes](https://github.com/NousResearch/hermes-agent) plugin that
lets an agent reach capabilities running on your **other machines** — the ones
you published there with `net wrap` — as five `net_*` tools, with local consent
and pin approval.

It embeds a **first-class Net node in-process** via `net-mesh-sdk` (no daemon,
no MCP shim): the node joins your mesh, and the tools drive the SDK's
consent-gated capability gateway and the machine-shared pin store. All
consent / validation / pin logic lives once in the Rust SDK — the plugin is a
thin, public-API-only view over it.

This is the native tier of `HERMES_INTEGRATION_PLAN.md` Phase 1. (The zero-code
tier — pointing Hermes's stock MCP client at `net mcp serve` — needs no plugin
at all; this plugin is what native integration adds.)

## Tools (toolset `net`)

| Tool | What it does |
|---|---|
| `net_search_capabilities` | Search the **mesh** index across your nodes. **Not** Hermes's local `tool_search` — the descriptions say so explicitly, so the model doesn't misroute. |
| `net_describe_capability` | Full input schema + credential status + whether it needs approval. |
| `net_invoke_capability` | Invoke through the consent gate. Returns structured `status`: `ok` / `requires_approval` / `validation_error` / `denied` / `not_found` / … — never raises. |
| `net_list_pinned_capabilities` | Approved + pending pins from the shared store. |
| `net_request_pin` | Records a **pending** approval request (grants nothing; a human approves out of band). |

## Enable

```yaml
# ~/.hermes/config.yaml
plugins:
  enabled: [net]
```

Then join your mesh via environment (see `node.py` for the full list):

| Var | Meaning |
|---|---|
| `NET_MESH_PSK` | 64-hex pre-shared key of your mesh (required to join a real mesh; unset ⇒ isolated dev node — tools still load, search is empty). |
| `NET_MESH_PEERS` | JSON `[{"addr","pubkey","node_id"}]` of the machines running `net wrap`. |
| `NET_MESH_IDENTITY_SEED` | 32-byte hex seed for a stable node identity. |
| `NET_MESH_PIN_STORE` | Pin-store path; defaults to the machine-shared file `net mcp pin` uses. |

A capability is invocable only once its pin is **approved** — from anywhere on
the machine (`net mcp pin approve <cap_id>`, another SDK client, or the shim).
Approved anywhere ⇒ approved everywhere, because it's one locked store.

## Install / distribution

Installed via Hermes's clone-install: `hermes plugins install hermes-pro/net`.
That mirror is a **build output** of this directory in the Net main tree
(`net/crates/net/integrations/hermes/`), not a dev repo — source-of-truth lives
here; CI syncs release-tagged builds one-way, re-pinning `net-mesh-sdk` in
`requirements.txt` per release.

## Known follow-ups

- **Meta-tools always-load.** The plan wants the five meta-tools exempt from
  `tool_search` deferral. Hermes keys never-defer off a tool *name* being in
  `toolsets._HERMES_CORE_TOOLS` (a core file) — there's no per-tool flag — so
  making them always-load needs a config mechanism or an upstream public hook
  (never a core patch, per H6). Until then they are ordinary deferrable plugin
  tools; five small defs is cheap.
- **Pin promotion (Phase 2) — done, subscription-based.** `pins.py` promotes an
  approved pin to a first-class typed tool and retires it on unpin, diff-based
  like `tools/mcp_tool.py`, driven by the SDK's pin-change **subscription**
  (`AsyncPinStore.snapshot_and_watch` — an OS file watcher, not polling), so a
  cross-process `net mcp pin approve` promotes within ~1s. Remaining refinements:
  SDK-side canonical name allocation (names are deterministic client-side for
  now) and `dynamic_schema_overrides` wired to the live descriptor (schema is
  captured at promote time).
- **Delegated identity (Phase 3) — Slice A done.** `delegation.py` derives the
  `root -> machine -> gateway` chain at node init from `NET_MESH_IDENTITY_SEED`
  (the user root) via the SDK (`net.DelegationChain` / `RevocationRegistry` /
  `derive_child_identity`), and `check_fn` gates on it — a revoked or expired
  gateway delegation removes the tools. No root seed ⇒ un-delegated dev node
  (tools still load). Remaining (Slice B): carry the chain on the invoke so the
  remote provider audits *which* gateway/subagent called — a cross-cutting
  wrap-protocol change. See the plan's Phase 3 status.
- **Streaming/folds (Phase 6)**, etc. — see the plan.

## Tests

`tests/` load the plugin package (under a non-`net` name to avoid colliding
with the `net` wheel) and exercise registration + the async handlers against an
isolated node. Run from `net/crates/net/bindings/python` with the built wheel:

```sh
.venv/Scripts/python -m pytest ../../integrations/hermes/tests -q
```

`tests/real_hermes_loader_check.py` is a **manual** validation (not collected
by pytest) that drives the plugin through Hermes's *real* `PluginContext` ->
`tools.registry` -> `get_definitions` in a Hermes checkout — see its docstring
for the recipe (needs an ABI-matched `net-mesh` wheel). Verified passing: the
five tools register into the real registry and survive Hermes's model-facing
assembly, and the embedded node builds + tears down in Hermes's interpreter.