diff --git a/docs/spec.md b/docs/spec.md new file mode 100644 index 0000000..fb79304 --- /dev/null +++ b/docs/spec.md @@ -0,0 +1,1025 @@ +# Specification: NATSBridge + +**Version**: 1.0.0 +**Date**: 2026-03-13 +**Status**: Active +**Ground Truth**: [`src/NATSBridge.jl`](../src/NATSBridge.jl) +**Specification Format**: JSON Schema + AsyncAPI + +--- + +## Executive Summary + +This document defines the **technical contract** for NATSBridge - the cross-platform bi-directional data bridge that enables seamless communication between **Julia**, **JavaScript**, **Python**, and **MicroPython** applications using NATS as the message bus. + +This specification serves as the single source of truth for: +- **Inputs**: What data structures are accepted by `smartsend()` +- **Outputs**: What data structures are returned by `smartreceive()` +- **Data Shapes**: Exact field names, types, and constraints +- **Error Codes**: Standardized error responses for failure scenarios + +--- + +## Specification Versioning + +| Component | Version | Notes | +|-----------|---------|-------| +| Specification | 1.0.0 | Initial release | +| Protocol | v1 | Message envelope protocol version | + +--- + +## Message Envelope Schema + +### Envelope Structure (JSON) + +```json +{ + "correlation_id": "string (UUID)", + "msg_id": "string (UUID)", + "timestamp": "string (ISO 8601 UTC)", + "send_to": "string", + "msg_purpose": "string", + "sender_name": "string", + "sender_id": "string (UUID)", + "receiver_name": "string", + "receiver_id": "string (UUID)", + "reply_to": "string", + "reply_to_msg_id": "string", + "broker_url": "string", + "metadata": "object", + "payloads": [ + { + "id": "string (UUID)", + "dataname": "string", + "payload_type": "string", + "transport": "string", + "encoding": "string", + "size": "integer", + "data": "string or URL", + "metadata": "object" + } + ] +} +``` + +### Field Definitions + +| Field | Type | Required | Validation | Description | +|-------|------|----------|------------|-------------| +| `correlation_id` | `string` | Yes | UUID v4 format | Track message flow across distributed systems | +| `msg_id` | `string` | Yes | UUID v4 format | Unique identifier for this specific message | +| `timestamp` | `string` | Yes | ISO 8601 UTC | Message publication timestamp (e.g., `2026-03-13T07:02:50.443Z`) | +| `send_to` | `string` | Yes | Non-empty string | NATS subject/topic to publish the message to | +| `msg_purpose` | `string` | Yes | Enum | Purpose of the message (see `msg_purpose` enum) | +| `sender_name` | `string` | Yes | Non-empty string | Name of the sender application | +| `sender_id` | `string` | Yes | UUID v4 format | Unique identifier for the sender | +| `receiver_name` | `string` | Yes | Any string | Name of the receiver (empty = broadcast) | +| `receiver_id` | `string` | Yes | Any string | UUID of the receiver (empty = broadcast) | +| `reply_to` | `string` | Yes | Any string | Topic where receiver should reply (empty = no reply expected) | +| `reply_to_msg_id` | `string` | Yes | Any string | Message ID this message is replying to | +| `broker_url` | `string` | Yes | Valid URL | NATS broker URL | +| `metadata` | `object` | No | Any JSON object | Message-level metadata | +| `payloads` | `array` | Yes | Non-empty array | List of payload objects | + +--- + +## Payload Schema + +### Payload Structure (JSON) + +```json +{ + "id": "string (UUID)", + "dataname": "string", + "payload_type": "string", + "transport": "string", + "encoding": "string", + "size": "integer", + "data": "string or URL", + "metadata": "object" +} +``` + +### Payload Field Definitions + +| Field | Type | Required | Validation | Description | +|-------|------|----------|------------|-------------| +| `id` | `string` | Yes | UUID v4 format | Unique identifier for this payload | +| `dataname` | `string` | Yes | Non-empty string | Name of the payload (e.g., `login_image`, `user_data`) | +| `payload_type` | `string` | Yes | Enum | Type of payload (see `payload_type` enum) | +| `transport` | `string` | Yes | Enum | Transport method: `direct` or `link` | +| `encoding` | `string` | Yes | Enum | Encoding method (see `encoding` enum) | +| `size` | `integer` | Yes | Positive integer | Size of the payload in bytes | +| `data` | `string` or `URL` | Yes | Base64 string or URL | Payload data (base64 for direct, URL for link) | +| `metadata` | `object` | No | Any JSON object | Payload-level metadata | + +--- + +## Enumerations + +### `msg_purpose` Enum + +| Value | Description | +|-------|-------------| +| `ACK` | Acknowledgment of successful message processing | +| `NACK` | Negative acknowledgment of message processing failure | +| `updateStatus` | Status update message | +| `shutdown` | Graceful shutdown request | +| `chat` | Chat/message payload | +| `command` | Command payload | +| `event` | Event payload | + +### `payload_type` Enum + +| Value | Description | Supported Platforms | Encoding Options | +|-------|-------------|---------------------|------------------| +| `text` | Plain text string | All | `base64` | +| `dictionary` | JSON object/dictionary | All | `base64`, `json` | +| `arrowtable` | Apache Arrow IPC table | Desktop (Julia/JS/Python) | `base64`, `arrow-ipc` | +| `jsontable` | JSON array of objects | All | `base64`, `json` | +| `image` | Binary image data | All | `base64` | +| `audio` | Binary audio data | All | `base64` | +| `video` | Binary video data | All | `base64` | +| `binary` | Generic binary data | All | `base64` | + +### `transport` Enum + +| Value | Description | Data Format | Use Case | +|-------|-------------|-------------|----------| +| `direct` | Payload sent directly via NATS | Base64-encoded string | Payloads < size_threshold | +| `link` | Payload uploaded to file server | HTTP URL | Payloads ≥ size_threshold | + +### `encoding` Enum + +| Value | Description | Payload Types | +|-------|-------------|---------------| +| `none` | No additional encoding | Link transport URLs | +| `base64` | Base64 encoding | Text, binary, image, audio, video | +| `json` | JSON encoding | Dictionary, jsontable | +| `arrow-ipc` | Apache Arrow IPC format | Arrowtable | + +--- + +## Transport Protocols + +### Direct Transport Protocol + +When `transport = "direct"`, the `data` field contains a Base64-encoded string of the serialized payload. + +**Flow**: +1. Serialize payload according to `payload_type` +2. Encode serialized bytes as Base64 +3. Include Base64 string in `data` field + +**Example**: +```json +{ + "transport": "direct", + "encoding": "base64", + "size": 11, + "data": "SGVsbG8gV29ybGQ=" +} +``` + +### Link Transport Protocol + +When `transport = "link"`, the `data` field contains a URL pointing to the uploaded payload. + +**Flow**: +1. Serialize payload according to `payload_type` +2. Upload to HTTP file server (e.g., Plik) +3. Include returned URL in `data` field + +**Example**: +```json +{ + "transport": "link", + "encoding": "none", + "size": 1000000, + "data": "http://localhost:8080/file/3F62E/4AgGT/data.zip" +} +``` + +--- + +## Size Thresholds + +### Desktop Platforms (Julia/JS/Python) + +| Platform | Size Threshold | Notes | +|----------|----------------|-------| +| Desktop | 500,000 bytes (0.5MB) | Default threshold | + +### MicroPython Platform + +| Platform | Size Threshold | Maximum Payload | Notes | +|----------|----------------|-----------------|-------| +| MicroPython | 100,000 bytes (100KB) | 50,000 bytes | Hard limit due to memory constraints | + +--- + +## NATS Subject Convention + +### Subject Naming Pattern + +``` +/// +``` + +**Examples**: +- `/agent/wine/api/v1/prompt` - AI agent prompt endpoint +- `/chat/user/v1/message` - User chat message +- `/system/worker/v1/status` - Worker status update + +### Subject Wildcards + +| Wildcard | Description | Example | +|----------|-------------|---------| +| `*` | Single-level wildcard | `/chat/user/v1/*` matches `/chat/user/v1/message` | +| `>` | Multi-level wildcard | `/chat/user/v1/>` matches all `/chat/user/v1/*` subjects | + +--- + +## Error Handling + +### Error Response Format + +```json +{ + "error": { + "code": "string", + "message": "string", + "details": "object" + } +} +``` + +### Error Codes + +| Code | HTTP Status | Description | Recovery | +|------|-------------|-------------|----------| +| `INVALID_ENVELOPE` | 400 | Message envelope validation failed | Fix envelope structure | +| `INVALID_PAYLOAD_TYPE` | 400 | Unsupported payload type | Use supported payload_type | +| `INVALID_TRANSPORT` | 400 | Unsupported transport type | Use `direct` or `link` | +| `UPLOAD_FAILED` | 500 | File server upload failed | Retry or use direct transport | +| `DOWNLOAD_FAILED` | 503 | File server download failed | Retry with exponential backoff | +| `NATS_CONNECTION_FAILED` | 503 | NATS connection failed | Check NATS server availability | +| `DESERIALIZATION_ERROR` | 500 | Payload deserialization failed | Check payload_type matches data | +| `SIZE_EXCEEDED` | 413 | Payload exceeds maximum size | Split payload or use link transport | + +### Exception Handling + +| Scenario | Handler | Retry Policy | +|----------|---------|--------------| +| File server unavailable | Retry up to 5 times | Exponential backoff (100ms → 5000ms) | +| NATS publish failure | Connection auto-reconnect | TCP-level reconnection | +| Deserialization error | Log correlation ID and throw | No retry (data corruption) | +| Memory overflow (MicroPython) | Reject payloads >50KB | No retry (client-side check) | + +--- + +## Serialization Rules + +### Text Serialization + +| Platform | Input Type | Serialization | Encoding | +|----------|------------|---------------|----------| +| All | `String` | UTF-8 bytes | Base64 | + +### Dictionary Serialization + +| Platform | Input Type | Serialization | Encoding | +|----------|------------|---------------|----------| +| All | `Object`/`Dict` | JSON string | Base64 or direct JSON | + +### Arrow Table Serialization + +| Platform | Input Type | Serialization | Encoding | +|----------|------------|---------------|----------| +| Desktop | `DataFrame` | Arrow IPC stream | Base64 or arrow-ipc | +| Desktop | `Arrow.Table` | Arrow IPC stream | Base64 or arrow-ipc | +| MicroPython | ❌ | Not supported | N/A | + +### JSON Table Serialization + +| Platform | Input Type | Serialization | Encoding | +|----------|------------|---------------|----------| +| All | `Vector{Dict}`/`Array` | JSON array | Base64 or direct JSON | + +### Binary Serialization + +| Platform | Input Type | Serialization | Encoding | +|----------|------------|---------------|----------| +| All | `Uint8Array`/`Buffer`/`bytes` | Raw bytes | Base64 | + +--- + +## API Contract + +### `smartsend` Function Signature + +#### Julia + +```julia +function smartsend( + subject::String, + data::AbstractArray{Tuple{String, Any, String}}; + broker_url::String = "nats://localhost:4222", + fileserver_url::String = "http://localhost:8080", + fileserver_upload_handler::Function = plik_oneshot_upload, + size_threshold::Int = 500_000, + correlation_id::String = string(uuid4()), + msg_purpose::String = "chat", + sender_name::String = "NATSBridge", + receiver_name::String = "", + receiver_id::String = "", + reply_to::String = "", + reply_to_msg_id::String = "", + is_publish::Bool = true, + NATS_connection::Union{NATS.Connection, Nothing} = nothing, + msg_id::String = string(uuid4()), + sender_id::String = string(uuid4()) +)::Tuple{msg_envelope_v1, String} +``` + +#### Python + +```python +async def smartsend( + subject: str, + data: List[Tuple[str, Any, str]], + broker_url: str = "nats://localhost:4222", + fileserver_url: str = "http://localhost:8080", + fileserver_upload_handler: Callable = plik_oneshot_upload, + size_threshold: int = 500_000, + correlation_id: str = None, + msg_purpose: str = "chat", + sender_name: str = "NATSBridge", + receiver_name: str = "", + receiver_id: str = "", + reply_to: str = "", + reply_to_msg_id: str = "", + is_publish: bool = True, + nats_connection: Any = None, + msg_id: str = None, + sender_id: str = None +) -> Tuple[Dict, str]: +``` + +#### JavaScript (Node.js) + +```typescript +async function smartsend( + subject: string, + data: Array<[string, any, string]>, + options?: { + broker_url?: string; + fileserver_url?: string; + fileserver_upload_handler?: Function; + size_threshold?: number; + correlation_id?: string; + msg_purpose?: string; + sender_name?: string; + receiver_name?: string; + receiver_id?: string; + reply_to?: string; + reply_to_msg_id?: string; + is_publish?: boolean; + nats_connection?: NATS.Connection; + msg_id?: string; + sender_id?: string; + } +): Promise<[Object, string]>; +``` + +#### JavaScript (Browser) + +```typescript +async function smartsend( + subject: string, + data: Array<[string, any, string]>, + options?: { + broker_url?: string; + fileserver_url?: string; + fileserver_upload_handler?: Function; + size_threshold?: number; + correlation_id?: string; + msg_purpose?: string; + sender_name?: string; + receiver_name?: string; + receiver_id?: string; + reply_to?: string; + reply_to_msg_id?: string; + is_publish?: boolean; + nats_connection?: NATS.Connection; + msg_id?: string; + sender_id?: string; + } +): Promise<[Object, string]>; +``` + +#### MicroPython + +```python +def smartsend( + subject: str, + data: List[Tuple[str, Any, str]], + **kwargs +) -> Tuple[Dict, str]: +``` + +### `smartreceive` Function Signature + +#### Julia + +```julia +function smartreceive( + msg::NATS.Msg; + fileserver_download_handler::Function = _fetch_with_backoff, + max_retries::Int = 5, + base_delay::Int = 100, + max_delay::Int = 5000 +)::JSON.Object{String, Any} +``` + +#### Python + +```python +async def smartreceive( + msg: Any, + fileserver_download_handler: Callable = fetch_with_backoff, + max_retries: int = 5, + base_delay: int = 100, + max_delay: int = 5000 +) -> Dict[str, Any]: +``` + +#### JavaScript (Node.js) + +```typescript +async function smartreceive( + msg: Object, + options?: { + fileserver_download_handler?: Function; + max_retries?: number; + base_delay?: number; + max_delay?: number; + } +): Promise; +``` + +#### JavaScript (Browser) + +```typescript +async function smartreceive( + msg: Object, + options?: { + fileserver_download_handler?: Function; + max_retries?: number; + base_delay?: number; + max_delay?: number; + } +): Promise; +``` + +#### MicroPython + +```python +def smartreceive(msg: Any, **kwargs) -> Dict[str, Any]: +``` + +--- + +## File Server Interface + +### Upload Handler Contract + +**Function Signature**: +```julia +function fileserver_upload_handler( + file_server_url::String, + dataname::String, + data::Vector{UInt8} +)::Dict{String, Any} +``` + +**Return Format**: +```json +{ + "status": 200, + "uploadid": "string", + "fileid": "string", + "url": "string" +} +``` + +**Required Keys**: +| Key | Type | Description | +|-----|------|-------------| +| `status` | `integer` | HTTP response status code | +| `uploadid` | `string` | Upload session identifier | +| `fileid` | `string` | File identifier within session | +| `url` | `string` | Full download URL | + +### Download Handler Contract + +**Function Signature**: +```julia +function fileserver_download_handler( + url::String, + max_retries::Int, + base_delay::Int, + max_delay::Int, + correlation_id::String +)::Vector{UInt8} +``` + +**Retry Policy**: +- Initial delay: `base_delay` milliseconds +- Maximum delay: `max_delay` milliseconds +- Multiplier: 2x per retry +- Maximum retries: `max_retries` + +--- + +## Platform-Specific Constraints + +### Desktop (Julia/JS/Python) + +| Feature | Status | Notes | +|---------|--------|-------| +| Arrow IPC | ✅ Supported | Requires Arrow.jl/pyarrow | +| JSON table | ✅ Supported | Human-readable format | +| File server upload | ✅ Supported | HTTP/HTTPS | +| File server download | ✅ Supported | HTTP/HTTPS | +| Size threshold | 500KB | Configurable | + +### MicroPython + +| Feature | Status | Notes | +|---------|--------|-------| +| Arrow IPC | ❌ Not supported | Memory constraints | +| JSON table | ⚠️ Limited | Only direct transport | +| File server upload | ❌ Not implemented | Placeholder only | +| File server download | ❌ Not implemented | Placeholder only | +| Size threshold | 100KB | Hard limit enforced | +| Max payload | 50KB | Hard limit enforced | + +--- + +## Message Flow + +### Sending Flow + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 1. User calls smartsend(subject, data) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 2. For each payload: │ +│ - Serialize data according to payload_type │ +│ - Calculate serialized size │ +└─────────────────────────────────────────────────────────────────┘ + │ + ├─ Size < Threshold ────────────────►┐ + │ │ + ▼ ▼ +┌─────────────────────────────────────────────────────────────────┐ │ +│ 3. Direct Transport: │ │ +│ - Encode as Base64 │ │ +│ - Include in payload.data │ │ +└─────────────────────────────────────────────────────────────────┘ │ + │ │ + ▼ │ +┌─────────────────────────────────────────────────────────────────┐ │ +│ 4. Build envelope with metadata │ │ +│ - correlation_id, msg_id, timestamp │ │ +│ - sender/receiver info │ │ +│ - payloads array │ │ +└─────────────────────────────────────────────────────────────────┘ │ + │ │ + ▼ │ +┌─────────────────────────────────────────────────────────────────┐ │ +│ 5. Convert envelope to JSON string │ │ +│ 6. Publish to NATS subject │ │ +└─────────────────────────────────────────────────────────────────┘ │ + │ +┌─────────────────────────────────────────────────────────────────┐ │ +│ 7. Return envelope and JSON string to caller │ │ +└─────────────────────────────────────────────────────────────────┘ │ + │ +``` + +### Receiving Flow + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 1. NATS message arrives │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 2. Parse JSON envelope │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 3. For each payload: │ +│ - Check transport type │ +└─────────────────────────────────────────────────────────────────┘ + │ + ├─ transport == "direct" ──────────►┐ + │ │ + ▼ ▼ +┌─────────────────────────────────────────────────────────────────┐ │ +│ 4. Direct Transport: │ │ +│ - Extract Base64 data │ │ +│ - Decode Base64 │ │ +│ - Deserialize based on payload_type │ │ +└─────────────────────────────────────────────────────────────────┘ │ + │ │ + ▼ │ +┌─────────────────────────────────────────────────────────────────┐ │ +│ 5. Link Transport: │ │ +│ - Extract URL from data │ │ +│ - Fetch with exponential backoff │ │ +│ - Deserialize based on payload_type │ │ +└─────────────────────────────────────────────────────────────────┘ │ + │ │ + ▼ │ +┌─────────────────────────────────────────────────────────────────┐ │ +│ 6. Replace payloads array with deserialized tuples │ │ +│ - [(dataname, data, type), ...] │ │ +└─────────────────────────────────────────────────────────────────┘ │ + │ +┌─────────────────────────────────────────────────────────────────┐ +│ 7. Return envelope with processed payloads │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Validation Rules + +### Envelope Validation + +| Rule | Condition | Error Code | +|------|-----------|------------| +| Required fields present | `correlation_id`, `msg_id`, `timestamp`, `send_to`, `payloads` | `INVALID_ENVELOPE` | +| Valid UUID format | `correlation_id`, `msg_id`, `sender_id`, `receiver_id` | `INVALID_ENVELOPE` | +| Valid timestamp format | ISO 8601 UTC | `INVALID_ENVELOPE` | +| Non-empty payloads array | `length(payloads) > 0` | `INVALID_ENVELOPE` | + +### Payload Validation + +| Rule | Condition | Error Code | +|------|-----------|------------| +| Valid payload_type | Must be in `payload_type` enum | `INVALID_PAYLOAD_TYPE` | +| Valid transport | Must be `direct` or `link` | `INVALID_TRANSPORT` | +| Valid encoding | Must match payload_type and transport | `INVALID_TRANSPORT` | +| Positive size | `size > 0` | `INVALID_PAYLOAD` | +| Valid Base64 for direct | `data` matches Base64 pattern | `DESERIALIZATION_ERROR` | +| Valid URL for link | `data` matches HTTP(S) URL pattern | `DOWNLOAD_FAILED` | + +--- + +## Test Contracts + +### Unit Test Validation + +| Test | Input | Expected Output | +|------|-------|-----------------| +| Text round-trip | `("msg", "Hello", "text")` | `("msg", "Hello", "text")` | +| Dictionary round-trip | `("data", {"key": "value"}, "dictionary")` | `("data", {"key": "value"}, "dictionary")` | +| Arrow table round-trip | `("table", DataFrame(...), "arrowtable")` | `("table", DataFrame(...), "arrowtable")` | +| Mixed payloads | `[("text", "Hello", "text"), ("img", bytes, "image")]` | `[("text", "Hello", "text"), ("img", bytes, "image")]` | +| Large payload | `("data", rand(10_000_000), "arrowtable")` | `("data", URL, "arrowtable")` with link transport | + +### Integration Test Scenarios + +| Scenario | Platforms | Expected Result | +|----------|-----------|-----------------| +| Julia ↔ JavaScript | Text, dictionary | Round-trip successful | +| Python ↔ Julia | Arrow table | Arrow IPC round-trip | +| JavaScript ↔ Python | Mixed content | All payloads preserved | +| Large file transfer | All platforms | File server upload/download | + +--- + +## Dependencies + +### Required Dependencies by Platform + +| Platform | Package | Version | Purpose | +|----------|---------|---------|---------| +| Julia | NATS.jl | Latest | NATS client | +| Julia | JSON.jl | Latest | JSON serialization | +| Julia | Arrow.jl | Latest | Arrow IPC support | +| Julia | HTTP.jl | Latest | HTTP file server | +| Julia | UUIDs.jl | Latest | UUID generation | +| Node.js | nats | Latest | NATS client | +| Node.js | node-fetch | Latest | HTTP file server | +| Python | nats-py | Latest | NATS client | +| Python | aiohttp | Latest | HTTP file server | +| Python | pyarrow | Latest | Arrow IPC support | +| MicroPython | builtin | N/A | Limited implementation | + +### Optional Dependencies + +| Platform | Package | Purpose | +|----------|---------|---------| +| Julia | DataFrames.jl | DataFrame support | +| Python | pandas | DataFrame support | + +--- + +## Change Log + +| Date | Version | Changes | +|------|---------|---------| +| 2026-03-13 | 1.0.0 | Initial specification | +| - | - | Message envelope schema defined | +| - | - | Payload schema with transport modes | +| - | - | Enumerations for payload_type, transport, encoding | +| - | - | Size thresholds for desktop/MicroPython | +| - | - | Error codes and validation rules | +| - | - | API contracts for all platforms | + +--- + +## References + +- [`docs/requirements.md`](./requirements.md) - Business requirements and user stories +- [`docs/architecture.md`](./architecture.md) - System architecture diagrams +- [`docs/implementation.md`](./implementation.md) - Implementation details +- [`src/NATSBridge.jl`](../src/NATSBridge.jl) - Ground truth implementation +- [`README.md`](../README.md) - Project overview + +--- + +## Appendix + +### A. Complete JSON Schema + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "NATSBridge Envelope", + "type": "object", + "properties": { + "correlation_id": { + "type": "string", + "pattern": "^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$", + "description": "UUID v4 format for tracking message flow" + }, + "msg_id": { + "type": "string", + "pattern": "^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$", + "description": "Unique message identifier" + }, + "timestamp": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$", + "description": "ISO 8601 UTC timestamp" + }, + "send_to": { + "type": "string", + "minLength": 1, + "description": "NATS subject to publish to" + }, + "msg_purpose": { + "type": "string", + "enum": ["ACK", "NACK", "updateStatus", "shutdown", "chat", "command", "event"], + "description": "Purpose of the message" + }, + "sender_name": { + "type": "string", + "minLength": 1, + "description": "Sender application name" + }, + "sender_id": { + "type": "string", + "pattern": "^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$", + "description": "Sender UUID" + }, + "receiver_name": { + "type": "string", + "description": "Receiver name (empty = broadcast)" + }, + "receiver_id": { + "type": "string", + "pattern": "^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$|^$", + "description": "Receiver UUID (empty = broadcast)" + }, + "reply_to": { + "type": "string", + "description": "Topic for reply messages" + }, + "reply_to_msg_id": { + "type": "string", + "description": "Message ID being replied to" + }, + "broker_url": { + "type": "string", + "pattern": "^nats://[^\\s]+$", + "description": "NATS broker URL" + }, + "metadata": { + "type": "object", + "description": "Message-level metadata" + }, + "payloads": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/Payload" + } + } + }, + "required": ["correlation_id", "msg_id", "timestamp", "send_to", "msg_purpose", "sender_name", "sender_id", "receiver_name", "receiver_id", "reply_to", "reply_to_msg_id", "broker_url", "payloads"], + "definitions": { + "Payload": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$" + }, + "dataname": { + "type": "string", + "minLength": 1 + }, + "payload_type": { + "type": "string", + "enum": ["text", "dictionary", "arrowtable", "jsontable", "image", "audio", "video", "binary"] + }, + "transport": { + "type": "string", + "enum": ["direct", "link"] + }, + "encoding": { + "type": "string", + "enum": ["none", "base64", "json", "arrow-ipc"] + }, + "size": { + "type": "integer", + "minimum": 1 + }, + "data": { + "anyOf": [ + { + "type": "string", + "pattern": "^(https?://[^\\s]+)$" + }, + { + "type": "string", + "pattern": "^[A-Za-z0-9+/]+=*$" + } + ] + }, + "metadata": { + "type": "object" + } + }, + "required": ["id", "dataname", "payload_type", "transport", "encoding", "size", "data"] + } + } +} +``` + +### B. AsyncAPI Specification (NATS) + +```yaml +asyncapi: '2.6.0' +info: + title: NATSBridge API + version: '1.0.0' + description: Cross-platform bi-directional data bridge using NATS + contact: + name: NATSBridge Team + url: https://github.com/your-org/NATSBridge + license: + name: MIT + url: https://opensource.org/licenses/MIT +channels: + /agent/{service}/api/v{version}/{operation}: + address: /agent/{service}/api/v{version}/{operation} + parameters: + service: + schema: + type: string + version: + schema: + type: string + enum: ['v1'] + operation: + schema: + type: string + publish: + summary: Publish message to NATS + operationId: publishMessage + message: + $ref: '#/components/message' + subscribe: + summary: Subscribe to NATS messages + operationId: subscribeMessage + message: + $ref: '#/components/message' +components: + message: + payload: + $ref: '#/components/schemas/Envelope' + schemas: + Envelope: + type: object + properties: + correlation_id: + type: string + format: uuid + msg_id: + type: string + format: uuid + timestamp: + type: string + format: date-time + send_to: + type: string + msg_purpose: + type: string + enum: [ACK, NACK, updateStatus, shutdown, chat, command, event] + sender_name: + type: string + sender_id: + type: string + format: uuid + receiver_name: + type: string + receiver_id: + type: string + format: uuid + reply_to: + type: string + reply_to_msg_id: + type: string + broker_url: + type: string + metadata: + type: object + payloads: + type: array + items: + $ref: '#/components/schemas/Payload' + required: + - correlation_id + - msg_id + - timestamp + - send_to + - msg_purpose + - sender_name + - sender_id + - receiver_name + - receiver_id + - reply_to + - reply_to_msg_id + - broker_url + - payloads + Payload: + type: object + properties: + id: + type: string + format: uuid + dataname: + type: string + payload_type: + type: string + enum: [text, dictionary, arrowtable, jsontable, image, audio, video, binary] + transport: + type: string + enum: [direct, link] + encoding: + type: string + enum: [none, base64, json, arrow-ipc] + size: + type: integer + minimum: 1 + data: + type: string + metadata: + type: object + required: + - id + - dataname + - payload_type + - transport + - encoding + - size + - data +``` + +--- + +*This specification is versioned and maintained in git alongside the codebase. All implementations must adhere to this specification.*