TNT/docs/MODULE_PROTOCOL.md
m1ngsama 2402c70d6f feat: add module foundation runtime
Add validated input buffering, shared JSON helpers, the tnt.module.v1 protocol helpers, and an opt-in external-process module runtime behind TNT_MODULE_PATHS.

Closes #52
2026-06-04 22:48:21 +08:00

4.3 KiB

TNT Module Protocol

This document defines the compatibility contract for external TNT modules. The first implementation target is external-process modules that exchange JSON Lines with TNT over stdin/stdout. Keeping modules out of the server address space makes the community extension surface easier to audit, restart, rate-limit, and disable.

The protocol is intentionally separate from messages.log v1. TNT 1.x keeps the persisted public history format stable. Module-generated content must always provide a plain-text fallback that can be stored and rendered by older or less capable clients.

TNT core should stay conservative: text-first, terminal-compatible, and easy to deploy over plain SSH. Modules are the extension surface for personalized workflow features, rich rendering, terminal-specific visuals, and other experience experiments. Integrating a module with TNT must not make plain terminal users lose the basic chat path.

Compatibility

  • Protocol version: tnt.module.v1
  • Transport: UTF-8 JSON Lines
  • Framing: one complete JSON object per line
  • Direction: TNT sends events to module stdin; modules write responses to stdout
  • Error stream: modules should write diagnostics to stderr
  • License: module protocol examples and official community modules should use the same license as TNT unless a module states stricter terms

Modules are disabled unless TNT_MODULE_PATHS is set. The value is a colon-separated list of module directories, each containing tnt-module.json and the declared executable entrypoint.

TNT may add optional fields to existing messages. Modules must ignore unknown fields. TNT must ignore unknown response fields unless the response type explicitly requires them.

Manifest

Each module directory should include tnt-module.json:

{
  "protocol": "tnt.module.v1",
  "name": "echo",
  "version": "0.1.0",
  "description": "Echoes public messages for testing",
  "entrypoint": "./echo-module.sh",
  "permissions": ["message:read", "message:create"],
  "events": ["message.created"]
}

Required fields:

  • protocol: protocol compatibility string
  • name: stable module id, lowercase ASCII, a-z, 0-9, and -
  • version: module version
  • entrypoint: executable path relative to the manifest directory
  • permissions: explicit capabilities requested by the module
  • events: event names the module wants to receive

Handshake

TNT starts a module process and writes a handshake event:

{"type":"handshake","protocol":"tnt.module.v1","server":{"name":"tnt","version":"1.0.1"}}

The module should answer:

{"type":"handshake.ok","protocol":"tnt.module.v1","module":{"name":"echo","version":"0.1.0"}}

If the module cannot run, it should answer:

{"type":"error","code":"unsupported_protocol","message":"requires tnt.module.v2"}

Events

Message-created event:

{
  "type": "message.created",
  "message": {
    "id": "local-00000001",
    "timestamp": "2026-06-04T12:00:00Z",
    "sender": "alice",
    "kind": "text",
    "plain_text": "hello",
    "metadata": {}
  }
}

The plain_text field is mandatory for every user-visible message. Future rich content, images, and terminal-specific render hints must be represented as optional metadata or attachment records with a plain-text fallback.

Responses

Create a public message:

{"type":"message.create","plain_text":"echo: hello"}

No-op acknowledgement:

{"type":"event.ok"}

Module error:

{"type":"error","code":"bad_request","message":"missing plain_text"}

Security Rules

  • Modules are untrusted external processes.
  • TNT should enforce per-module permissions before delivering events or accepting responses.
  • TNT should cap stdout line length, startup time, event handling time, and total queued output.
  • TNT should disable a module after repeated invalid JSON, protocol errors, or timeout failures.
  • Modules must never receive private messages unless they request and are granted an explicit private-message permission.

Rendering Rules

Every module-created message must be renderable as plain text. Terminal image protocols such as Kitty graphics or Sixel are optional renderer capabilities, not message requirements. A module may provide attachment metadata later, but TNT must be able to fall back to a link, filename, digest, or short label.