syntax = "proto3";
package meshllm.node.v1;
// Stream 0x01 — Gossip
message GossipFrame {
uint32 gen = 1;
repeated PeerAnnouncement peers = 2;
bytes sender_id = 3; // must be exactly 32 bytes; must match QUIC peer identity
}
message PeerAnnouncement {
bytes endpoint_id = 1; // exactly 32 bytes
NodeRole role = 2;
optional uint32 http_port = 3;
optional string version = 4;
optional string gpu_name = 5; // Deprecated in v0.60.0: prefer hardware.gpus[].name
optional string hostname = 6; // Deprecated in v0.60.0: prefer hardware.hostname
optional bool is_soc = 7; // Deprecated in v0.60.0: prefer hardware.is_soc; true for system-on-chip / unified-memory hosts such as Apple Silicon and Jetson
optional string gpu_vram = 8; // Deprecated in v0.60.0: prefer hardware.gpus[].vram_bytes
repeated string available_models = 9; // Deprecated in v0.55.0 for protobuf gossip; compatibility surface only
repeated string serving_models = 10; // Deprecated in v0.55.0: prefer served_model_identities and served_model_runtime
repeated string requested_models = 11;
repeated CompactModelMetadata available_model_metadata = 12;
optional ExpertsSummary experts_summary = 13;
optional uint32 rtt_ms = 14;
repeated string catalog_models = 15; // GGUF model names (mesh catalog contribution; was JSON `models`)
uint64 vram_bytes = 16; // GPU VRAM in bytes
optional string model_source = 17; // how to obtain this node's model
optional string primary_serving = 18; // primary serving model (backward compat; was JSON `serving`)
optional string mesh_id = 19; // stable mesh identity (self entry only)
repeated ModelDemandEntry demand = 20; // demand map entries (self entry only)
map<string, uint64> available_model_sizes = 21; // file sizes (bytes) per GGUF model name
bytes serialized_addr = 22; // JSON-serialized EndpointAddr (includes network addresses for peer discovery)
repeated string hosted_models = 23; // actually routable / healthy
optional bool hosted_models_known = 24; // false/absent when relayed from legacy JSON peers
repeated ServedModelDescriptor served_model_descriptors = 25;
repeated ServedModelIdentity served_model_identities = 26;
repeated ModelRuntimeDescriptor served_model_runtime = 27;
optional SignedNodeOwnership owner_attestation = 28;
optional string gpu_mem_bandwidth_gbps = 29; // Deprecated in v0.60.0: prefer hardware.gpus[].mem_bandwidth_gbps
optional string gpu_compute_tflops_fp32 = 30; // Deprecated in v0.60.0: prefer hardware.gpus[].compute_tflops_fp32
optional string gpu_compute_tflops_fp16 = 31; // Deprecated in v0.60.0: prefer hardware.gpus[].compute_tflops_fp16
optional string gpu_reserved_bytes = 32; // Deprecated in v0.60.0: prefer hardware.gpus[].reserved_bytes
optional HardwareInfo hardware = 33; // Introduced in v0.60.0; preferred structured hardware inventory
optional uint64 first_joined_mesh_ts = 34;
repeated string explicit_model_interests = 35; // Advisory canonical refs this node wants the mesh to consider
reserved 36;
reserved "artifact_transfer_supported";
repeated MeshSubprotocol subprotocols = 37; // Additive feature discovery for admitted subsystem protocols
optional uint32 latency_ms = 40;
optional LatencySource latency_source = 41;
optional uint32 latency_age_ms = 42;
optional bytes latency_observer_id = 43;
repeated AdvertisedModelThroughput advertised_model_throughput = 44;
enum LatencySource {
LATENCY_SOURCE_UNSPECIFIED = 0;
LATENCY_SOURCE_DIRECT = 1; // Measured directly by this node
LATENCY_SOURCE_ESTIMATED = 2; // Estimated via transitive gossip
LATENCY_SOURCE_UNKNOWN = 3; // Unknown source (e.g. propagated with no origin)
}
}
message AdvertisedModelThroughput {
string model_name = 1;
uint64 avg_tokens_per_second_milli = 2;
uint64 throughput_samples = 3;
}
message MeshSubprotocol {
string name = 1; // e.g. "skippy-stage"
uint32 major = 2; // subprotocol major version
repeated string features = 3; // e.g. "artifact-transfer"
}
// Stream 0x0d — generic admitted subsystem stream.
// Mesh owns this open frame. The remaining bytes on the stream are opaque to
// mesh and are owned by the selected subsystem protocol.
message MeshSubprotocolOpen {
uint32 gen = 1;
string name = 2; // e.g. "skippy-stage"
uint32 major = 3; // subprotocol major version
}
message HardwareInfo {
optional bool is_soc = 1; // True for system-on-chip / unified-memory hosts such as Apple Silicon and Jetson
optional string hostname = 2;
repeated GpuInfo gpus = 3;
}
message GpuInfo {
optional string name = 1;
optional string vram_bytes = 2;
optional string reserved_bytes = 3;
optional string mem_bandwidth_gbps = 4;
optional string compute_tflops_fp32 = 5;
optional string compute_tflops_fp16 = 6;
}
message SignedNodeOwnership {
uint32 version = 1;
string cert_id = 2;
string owner_id = 3;
bytes owner_sign_public_key = 4; // 32 bytes
bytes node_endpoint_id = 5; // 32 bytes
uint64 issued_at_unix_ms = 6;
uint64 expires_at_unix_ms = 7;
optional string node_label = 8;
optional string hostname_hint = 9;
bytes signature = 10; // Ed25519 signature over canonical claim bytes
}
message ServedModelDescriptor {
ServedModelIdentity identity = 1;
ModelCapabilities capabilities = 2;
optional ModelTopology topology = 3;
optional bool capabilities_known = 4;
}
message ServedModelIdentity {
string model_name = 1;
bool is_primary = 2;
ModelSourceKind source_kind = 3;
optional string canonical_ref = 4;
optional string repository = 5;
optional string revision = 6;
optional string artifact = 7;
optional string local_file_name = 8;
optional string identity_hash = 9;
}
message ModelCapabilities {
CapabilityLevel vision = 1;
CapabilityLevel reasoning = 2;
CapabilityLevel tool_use = 3;
bool moe = 4;
bool multimodal = 5;
CapabilityLevel audio = 6;
}
enum CapabilityLevel {
CAPABILITY_LEVEL_UNSPECIFIED = 0;
CAPABILITY_LEVEL_NONE = 1;
CAPABILITY_LEVEL_LIKELY = 2;
CAPABILITY_LEVEL_SUPPORTED = 3;
}
message ModelTopology {
optional ModelMoeInfo moe = 1;
}
message ModelRuntimeDescriptor {
string model_name = 1;
optional string identity_hash = 2;
optional uint32 context_length = 3;
bool ready = 4;
}
message ModelMoeInfo {
uint32 expert_count = 1;
uint32 used_expert_count = 2;
optional uint32 min_experts_per_node = 3;
optional string source = 4;
optional string ranking_source = 5;
repeated uint32 ranking = 6;
optional uint32 ranking_prompt_count = 7;
optional uint32 ranking_tokens = 8;
optional string ranking_layer_scope = 9;
optional string ranking_origin = 10;
}
message CompactModelMetadata {
string model_key = 1;
uint32 context_length = 2;
uint32 vocab_size = 3;
uint32 embedding_size = 4;
uint32 head_count = 5;
uint32 layer_count = 6;
uint32 feed_forward_length = 7;
uint32 key_length = 8;
uint32 value_length = 9;
string architecture = 10;
string tokenizer_model_name = 11;
repeated SpecialToken special_tokens = 12;
float rope_scale = 13;
float rope_freq_base = 14;
bool is_moe = 15;
uint32 expert_count = 16;
uint32 used_expert_count = 17;
string quantization_type = 18; // GGUF quantization type string (e.g. "Q4_K_M")
}
message SpecialToken {
string name = 1;
uint32 token_id = 2;
}
message ExpertsSummary {
uint32 total_experts = 1;
uint32 expert_count_used = 2;
repeated uint32 top_expert_ids = 3;
}
enum ModelSourceKind {
MODEL_SOURCE_KIND_UNSPECIFIED = 0;
MODEL_SOURCE_KIND_CATALOG = 1;
MODEL_SOURCE_KIND_HUGGING_FACE = 2;
MODEL_SOURCE_KIND_LOCAL_GGUF = 3;
MODEL_SOURCE_KIND_DIRECT_URL = 4;
MODEL_SOURCE_KIND_UNKNOWN = 5;
}
message ModelDemandEntry {
string model_name = 1;
uint64 last_active = 2;
uint64 request_count = 3;
}
// Stream 0x03 — Tunnel Map
message TunnelMap {
bytes owner_peer_id = 1;
repeated TunnelEntry entries = 2;
}
message TunnelEntry {
bytes target_peer_id = 1;
optional bytes relay_peer_id = 2;
uint32 tunnel_port = 3;
}
// Stream 0x05 — Route Table
message RouteTableRequest {
bytes requester_id = 1; // 0 or exactly 32 bytes (passive callers may omit)
uint32 gen = 2; // must equal NODE_PROTOCOL_GENERATION; rejected otherwise
}
message RouteTable {
repeated RouteEntry entries = 1;
optional string mesh_id = 2; // stable mesh identity — passive/client callers learn this here
uint32 gen = 3; // must equal NODE_PROTOCOL_GENERATION; caller rejects mismatches
}
message RouteEntry {
bytes endpoint_id = 1; // exactly 32 bytes (peer public key)
string model = 2; // model being served (empty if node is not actively serving)
}
// Stream 0x06 — Peer Down
message PeerDown {
bytes peer_id = 1; // exactly 32 bytes; the peer being reported as unreachable
uint32 gen = 2; // must equal NODE_PROTOCOL_GENERATION; rejected otherwise
}
// Stream 0x07 — Peer Leaving
message PeerLeaving {
bytes peer_id = 1; // exactly 32 bytes; must match the QUIC sender identity
uint32 gen = 2; // must equal NODE_PROTOCOL_GENERATION; rejected otherwise
}
// Shared enum
enum NodeRole {
NODE_ROLE_UNSPECIFIED = 0;
WORKER = 1;
HOST = 2;
CLIENT = 3;
}
message NodeConfigSnapshot {
uint32 version = 1; // config schema version (currently 1)
NodeGpuConfig gpu = 2;
repeated NodeModelEntry models = 3;
repeated NodePluginEntry plugins = 4;
optional string config_toml = 5; // canonical persisted config payload for additive owner-control roundtrip
}
enum GpuAssignment {
GPU_ASSIGNMENT_UNSPECIFIED = 0;
GPU_ASSIGNMENT_AUTO = 1;
GPU_ASSIGNMENT_PINNED = 2;
}
message NodeGpuConfig {
GpuAssignment assignment = 1;
}
message ConfiguredModelRef {
string declared_ref = 1;
optional string source_kind = 2;
optional string revision = 3;
}
message NodeModelEntry {
string model = 1;
optional string mmproj = 2;
optional uint32 ctx_size = 3;
optional string gpu_id = 4;
ConfiguredModelRef model_ref = 5;
ConfiguredModelRef mmproj_ref = 6;
}
message NodePluginEntry {
string name = 1;
optional bool enabled = 2;
optional string command = 3;
repeated string args = 4;
}
enum ConfigApplyMode {
CONFIG_APPLY_MODE_UNSPECIFIED = 0;
CONFIG_APPLY_MODE_STAGED = 1;
CONFIG_APPLY_MODE_LIVE = 2;
CONFIG_APPLY_MODE_NOOP = 3;
}
message OwnerControlEnvelope {
uint32 gen = 1; // must equal NODE_PROTOCOL_GENERATION
OwnerControlHandshake handshake = 2;
OwnerControlRequest request = 3;
OwnerControlResponse response = 4;
OwnerControlError error = 5;
}
message OwnerControlHandshake {
SignedNodeOwnership ownership = 1;
}
message OwnerControlRequest {
uint64 request_id = 1;
OwnerControlGetConfigRequest get_config = 2;
OwnerControlWatchConfigRequest watch_config = 3;
OwnerControlApplyConfigRequest apply_config = 4;
OwnerControlRefreshInventoryRequest refresh_inventory = 5;
}
message OwnerControlResponse {
uint64 request_id = 1;
OwnerControlGetConfigResponse get_config = 2;
OwnerControlWatchConfigResponse watch_config = 3;
OwnerControlApplyConfigResponse apply_config = 4;
OwnerControlRefreshInventoryResponse refresh_inventory = 5;
}
message OwnerControlError {
OwnerControlErrorCode code = 1;
string message = 2;
optional uint64 request_id = 3;
optional uint64 current_revision = 4;
}
enum OwnerControlErrorCode {
OWNER_CONTROL_ERROR_CODE_UNSPECIFIED = 0;
OWNER_CONTROL_ERROR_CODE_BAD_REQUEST = 1;
OWNER_CONTROL_ERROR_CODE_UNAUTHORIZED = 2;
OWNER_CONTROL_ERROR_CODE_REVISION_CONFLICT = 3;
OWNER_CONTROL_ERROR_CODE_CONTROL_UNSUPPORTED = 4;
OWNER_CONTROL_ERROR_CODE_CONTROL_ENDPOINT_REQUIRED = 5;
OWNER_CONTROL_ERROR_CODE_CONTROL_UNAVAILABLE = 6;
OWNER_CONTROL_ERROR_CODE_UNKNOWN_COMMAND = 7;
OWNER_CONTROL_ERROR_CODE_LEGACY_JSON_UNSUPPORTED = 8;
OWNER_CONTROL_ERROR_CODE_INVALID_HANDSHAKE = 9;
OWNER_CONTROL_ERROR_CODE_TARGET_NODE_MISMATCH = 10;
}
message OwnerControlGetConfigRequest {
bytes requester_node_id = 1; // exactly 32 bytes
bytes target_node_id = 2; // exactly 32 bytes
}
message OwnerControlGetConfigResponse {
OwnerControlConfigSnapshot snapshot = 1;
}
message OwnerControlWatchConfigRequest {
bytes requester_node_id = 1; // exactly 32 bytes
bytes target_node_id = 2; // exactly 32 bytes
bool include_snapshot = 3;
}
message OwnerControlWatchConfigResponse {
OwnerControlWatchAccepted accepted = 1;
OwnerControlConfigSnapshot snapshot = 2;
OwnerControlConfigUpdate update = 3;
}
message OwnerControlWatchAccepted {
bytes target_node_id = 1; // exactly 32 bytes
}
message OwnerControlApplyConfigRequest {
bytes requester_node_id = 1; // exactly 32 bytes
bytes target_node_id = 2; // exactly 32 bytes
uint64 expected_revision = 3;
NodeConfigSnapshot config = 4;
}
message OwnerControlApplyConfigResponse {
bool success = 1;
uint64 current_revision = 2;
bytes config_hash = 3;
optional string error = 4;
ConfigApplyMode apply_mode = 5;
}
message OwnerControlRefreshInventoryRequest {
bytes requester_node_id = 1; // exactly 32 bytes
bytes target_node_id = 2; // exactly 32 bytes
}
message OwnerControlRefreshInventoryResponse {
OwnerControlConfigSnapshot snapshot = 1;
}
message OwnerControlConfigSnapshot {
bytes node_id = 1; // exactly 32 bytes
uint64 revision = 2;
bytes config_hash = 3; // SHA-256 of canonical proto bytes (32 bytes)
NodeConfigSnapshot config = 4;
optional string hostname = 5;
}
message OwnerControlConfigUpdate {
bytes node_id = 1; // exactly 32 bytes
uint64 revision = 2;
bytes config_hash = 3; // SHA-256 of canonical proto bytes (32 bytes)
NodeConfigSnapshot config = 4;
}