Nostr Surface Gap — Codebase vs Live Relay

- Uses nostr-sdk Rust - Publishes custom kinds: raw_txxm 30000-30099, knot_commitment 30300-30399, governance 30400-30499, braid_sync 30500-30599 - Subscribes to: `Kind::Custom30000`, `Kind::Custom300

Nostr Surface Gap — Codebase vs Live Relay

The Disconnect

Codebase (kapnet-nostr crate):

  • Uses nostr-sdk (Rust)
  • Publishes custom kinds: raw_txxm (30000-30099), knot_commitment (30300-30399), governance (30400-30499), braid_sync (30500-30599)
  • Subscribes to: Kind::Custom(30000), Kind::Custom(30078), Kind::Custom(30100), Kind::Custom(30300), Kind::Custom(30500), Kind::Custom(30501)
  • Assumes: Kapnet-aware relays accept custom kinds

Live (what we’re doing now):

  • Uses nostr-tools (Node.js)
  • Publishes: kind-1 (text), kind-30078 (custom data)
  • Subscribes to: #kapnet hashtag, mentions of our npub
  • Reality: Public relays REJECT custom kinds (30000+)

Conflict

If we start the kapnetd daemon with its built-in Nostr adapter:

  • It will subscribe to Kind::Custom(30000) etc.
  • Public relays will NOT send those events
  • The daemon’s Nostr sync will appear “dead” — no events received
  • Meanwhile, our Node.js listener is receiving kind-1 and kind-30078 just fine

Resolution Options

Option A: Bimap Adapter (Recommended)

Build a thin adapter that:

  1. Takes internal Kapnet TXXM events (custom kinds 30000+)
  2. Wraps them as kind-30078 with structured content + #kapnet #[type] tags
  3. Publishes to public relay
  4. On receive: unwraps kind-30078 content back to TXXM types
Kapnet Internal          Public Nostr Bridge              Public Relay
Kind::Custom(30001)  →  kind-30078 content JSON     →  stored as-is
  raw_txxm                { type: "raw_txxm", ... }
                          + t: ["kapnet", "raw_txxm"]

Kind::Custom(30301)  →  kind-30078 content JSON     →  stored as-is
  knot_commitment         { type: "knot_commitment", ... }
                          + t: ["kapnet", "knot_commitment"]

kind-30078 receive   ←  unwrap content JSON        ←  received event
  → dispatch to KapnetTxxm::RawTxxm / KnotCommitment etc.

Option B: Patch kapnet-nostr to use public kinds

Change the subscribe filter to include kind-1 + kind-30078 with #kapnet tag.

Option C: Run a local Kapnet-aware relay

Run strfry with custom kind acceptance. Then kapnetd uses it directly.

Recommended: Option A

Build a lightweight bimap in Node.js:

// kapnet-nostr-bridge.mjs

const KAPNET_KIND_MAP = {
  // raw_txxm: 30000-30099 → kind-30078 + tag "raw_txxm"
  raw_txxm: { kind: 30078, tag: "raw_txxm" },
  // knot_commitment: 30300-30399 → kind-30078 + tag "knot_commitment"  
  knot_commitment: { kind: 30078, tag: "knot_commitment" },
  // governance: 30400-30499 → kind-30078 + tag "governance"
  governance: { kind: 30078, tag: "governance" },
  // braid_sync: 30500-30599 → kind-30078 + tag "braid_sync"
  braid_sync: { kind: 30078, tag: "braid_sync" },
};

// Publish: wrap Kapnet TXXM → public Nostr
function publishTxxm(txxmEvent) {
  const mapping = KAPNET_KIND_MAP[txxmEvent.type];
  const content = JSON.stringify(txxmEvent);
  const tags = [
    ["t", "kapnet"],
    ["t", mapping.tag],
    ["k", String(txxmEvent.kind)],  // preserve original kind
    ["d", txxmEvent.id],
  ];
  // Sign and publish to relay
}

// Subscribe: unwrap public Nostr → Kapnet Txxm
function onEvent(event) {
  if (event.kind === 30078) {
    try {
      const txxm = JSON.parse(event.content);
      const originalKind = event.tags.find(t => t[0] === "k")?.[1];
      // Dispatch to appropriate Kapnet handler
      dispatchTxxm(txxm);
    } catch(e) { /* not our event */ }
  }
}

This lets:

  • kapnetd publish TXXMs via our bridge (Node.js nostr-tools)
  • Other Kapnet nodes receive via our bridge
  • Public relay stores everything as kind-30078
  • Full Kapnet semantics preserved in content + tags

Action Items

  1. Build kapnet-nostr-bridge.mjs (Node.js bridge)
  2. Integrate with kapnetd startup
  3. Test round-trip: publish → relay → receive → unwrap

Write a comment
No comments yet.