Files
buzz/docs/nips/NIP-AO.md
T
cls 9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
feat: import Chinese-localized Buzz source snapshot
Signed-off-by: cls_宁波本机 <908705107@qq.com>
2026-08-13 18:34:25 +08:00

9.7 KiB
Raw Blame History

NIP-AO

Agent Observability

draft optional

This NIP defines ephemeral, encrypted event kinds for streaming internal session telemetry between AI agent processes and their owners' desktop clients via Nostr relays.

Motivation

AI agent harnesses execute long-running sessions that invoke tools, send protocol frames to models, and emit intermediate reasoning. Owners need real-time visibility into this activity for debugging, auditing, and control — without that telemetry being stored on any relay or visible to third parties.

Kind 24200 provides a dedicated, encrypted, ephemeral channel for this purpose. It is strictly scoped to the agent↔owner relationship and carries no durable state.

Definitions

  • Agent: An AI process with its own Nostr keypair, executing a session on behalf of an owner.
  • Owner: The human (or system) whose pubkey the agent was provisioned under.
  • Observer Frame: A single kind 24200 event carrying one unit of telemetry or control.
  • Session: A bounded agent execution correlated by a shared sessionId.

Event Kinds

Kind Name Direction
24200 Agent Observer Frame agent↔owner (both)

Kind 24200 falls in the ephemeral range (2000029999) defined by NIP-01. Relays MUST NOT persist it.

Event Structure

{
  "kind": 24200,
  "pubkey": "<sender_pubkey>",
  "created_at": <unix_timestamp>,
  "content": "<NIP-44 v2 ciphertext>",
  "tags": [
    ["p",     "<recipient_pubkey>"],
    ["agent", "<agent_pubkey>"],
    ["frame", "telemetry" | "control"]
  ]
}

Events MUST have exactly one p tag, exactly one agent tag, and exactly one frame tag.

Telemetry (agent → owner): pubkey=agent, p=owner, agent=agent. Control (owner → agent): pubkey=owner, p=agent, agent=agent (target).

frame MUST be "telemetry" or "control". Relays SHOULD silently drop events with unrecognized frame values (returning OK to the publisher for forward compatibility). Clients MUST ignore events with unrecognized frame values. An h tag MAY be included when the session runs within a NIP-29 group context.

Encryption

All content fields MUST be encrypted with NIP-44 v2 (XChaCha20-Poly1305 over a secp256k1 ECDH shared secret).

  • Telemetry: encrypted with (agent_privkey, owner_pubkey)
  • Control: encrypted with (owner_privkey, agent_pubkey)

Plaintext SHOULD be zeroized from memory immediately after encrypt/decrypt. Decrypted payload MUST NOT exceed 65,535 bytes.

Decrypted Payload

Telemetry (frame=telemetry)

The content field decrypts to an ObserverEvent JSON object:

{
  "seq":         <monotonic_integer>,
  "timestamp":   "<rfc3339_string>",
  "kind":        "<frame_kind>",
  "agentIndex":  <integer> | null,
  "channelId":   "<channel_uuid>" | null,
  "sessionId":   "<session_id>" | null,
  "turnId":      "<turn_id>" | null,
  "payload":     { ... }
}

seq, timestamp, kind, and payload are REQUIRED. agentIndex, channelId, sessionId, and turnId are OPTIONAL — they MAY be null when the value is not yet known (e.g., sessionId before session establishment). Clients MUST handle null values gracefully.

seq is monotonically increasing per session (drop detection). timestamp is an RFC 3339 datetime string with sub-second precision (e.g., "2026-04-29T12:00:41.500Z"). agentIndex identifies the agent in multi-agent scenarios. sessionId/turnId correlate frames across a session and turn. payload is kind-specific (MAY be {}). Unknown kind values MUST be ignored.

Frame Kinds

kind Description
acp_read Inbound ACP protocol frame (model → harness)
acp_write Outbound ACP protocol frame (harness → model)
turn_started A new agent turn has begun
session_resolved Session completed or terminated

Control (frame=control)

The content field decrypts to:

{
  "type":      "cancel_turn",
  "channelId": "<channel_uuid>"
}

The only defined control type is cancel_turn. Implementations MUST ignore events with unrecognized type values.

Ephemerality Contract

  • Relays MUST NOT persist kind 24200 events to any durable storage.
  • Relays MUST NOT include kind 24200 events in search indexes.
  • Relays MUST NOT include kind 24200 events in audit logs.
  • Relays SHOULD fan out kind 24200 events only via in-memory pub/sub, never via a database write path.
  • Clients SHOULD subscribe with since=<now>; historical replay is not supported.
  • Clients SHOULD buffer received events in a bounded in-memory ring buffer.

Authorization

Telemetry (agent → owner):

  • event.pubkey MUST equal the agent pubkey.
  • p tag MUST equal the owner pubkey.
  • Relay MUST verify is_agent_owner(agent, owner) via authenticated ownership lookup.

Control (owner → agent):

  • event.pubkey MUST equal the owner pubkey.
  • p tag MUST equal the agent pubkey.
  • Relay MUST verify is_agent_owner(agent, owner) where agent is resolved from the agent tag.

Both directions require relay confirmation of the agent-owner relationship via database lookup. #p tag matching alone is insufficient. Unauthorized publish or subscribe attempts MUST be rejected with AUTH required.

Relay Behavior

On receiving a kind 24200 event, a relay MUST:

  1. Validate the event signature per NIP-01.
  2. Verify authorization per the rules above.
  3. Fan out to matching subscribers via in-memory pub/sub.
  4. NOT invoke the normal event ingestion or persistence path.

Relays SHOULD enforce a rate limit of 100 events/second per agent pubkey. Relays are RECOMMENDED to reject events whose created_at falls outside a ±5-minute freshness window to prevent replay of captured events.

Client Behavior

Clients subscribe with:

{"kinds": [24200], "#p": ["<own_pubkey>"], "since": <now>}

On receiving an event, a client MUST:

  1. Verify the event signature.
  2. Decrypt content using own secret key and event.pubkey.
  3. Parse the decrypted payload and dispatch on kind (telemetry) or type (control).
  4. Ignore unknown kind/type values.

Clients SHOULD verify that the agent tag matches a known/trusted agent pubkey before decrypting.

Clients SHOULD buffer events in a bounded ring buffer (RECOMMENDED maximum: 800 events). Clients MUST NOT request historical kind 24200 events (no since in the past, no until, no ids queries).

Security Considerations

Metadata leakage. Routing tags (p, agent, frame, created_at) are cleartext. A relay operator can observe that agent X is streaming to owner Y at what rate. For maximum metadata privacy, implementors MAY wrap events in NIP-59 gift wrap.

No forward secrecy. NIP-44 does not provide forward secrecy; compromise of the agent's private key allows decryption of any captured ciphertext.

Replay attacks. A captured, signed event could be replayed without a freshness check. Relays are RECOMMENDED to enforce a created_at freshness window.

Rogue relays. The ephemerality contract is relay policy, not cryptography. NIP-44 encryption ensures stored events remain opaque to the relay operator absent key compromise.

Best-effort delivery. Control frames can be dropped during reconnect or queue overflow. Control commands SHOULD be treated as advisory with idempotent semantics. Agents MUST NOT rely on guaranteed delivery of control frames.

Operational persistence vectors. Telemetry may transiently exist in process memory, crash dumps, and application logs. Implementations SHOULD minimize logging of decrypted payloads and MUST NOT log it at INFO level or above.

Relationship to Other NIPs

  • NIP-01: Kind 24200 is in the ephemeral range (2000029999); standard event structure and signature rules apply.
  • NIP-42: Recommended for relay-side authentication gating.
  • NIP-44: Required encryption algorithm for all content fields.
  • NIP-29: An h tag MAY be included when the agent session is scoped to a NIP-29 group.
  • NIP-XX (PR #2226): NIP-XX defines the agent output plane; this NIP defines the observability plane (internal agent activity). They are complementary and non-overlapping.

Examples

1. Telemetry Event — acp_write frame

Wire event (encrypted):

{
  "id":         "a1b2c3d4...",
  "kind":       24200,
  "pubkey":     "agent_pubkey_hex",
  "created_at": 1777464041,
  "content":    "<NIP-44 v2 ciphertext>",
  "tags": [
    ["p",     "owner_pubkey_hex"],
    ["agent", "agent_pubkey_hex"],
    ["frame", "telemetry"]
  ],
  "sig": "..."
}

Decrypted payload:

{
  "seq":        42,
  "timestamp":  "2026-04-29T12:00:41.500Z",
  "kind":       "acp_write",
  "agentIndex": 0,
  "channelId":  "52a85618-0f8f-4542-94ec-599e6e1c6f2e",
  "sessionId":  "a1b2c3d4",
  "turnId":     "e5f6g7h8",
  "payload": {
    "jsonrpc": "2.0",
    "method":  "tools/call",
    "params":  { "name": "shell", "arguments": { "command": "ls -la" } }
  }
}

2. Control Event — cancel_turn frame

Wire event (encrypted):

{
  "id":         "e5f6a7b8...",
  "kind":       24200,
  "pubkey":     "owner_pubkey_hex",
  "created_at": 1777464042,
  "content":    "<NIP-44 v2 ciphertext>",
  "tags": [
    ["p",     "agent_pubkey_hex"],
    ["agent", "agent_pubkey_hex"],
    ["frame", "control"]
  ],
  "sig": "..."
}

Decrypted payload:

{
  "type":      "cancel_turn",
  "channelId": "52a85618-0f8f-4542-94ec-599e6e1c6f2e"
}

Reference Implementation

block/sprout PR #421