9. rysh-proto — Reference Schemas
rysh-proto (module github.com/rysh-ai/rysh-proto, proto3) defines the message shapes used across Rysh. Important: these are reference schemas only. The live NATS wire format is JSON, and the canonical serialization types are the Go structs in rysh-shared/msg. The proto files document intent and shape; they are not used for serialization at runtime.
Package naming. Every
.protodeclares the protobuf packagerysh.v1, but each file'sgo_packageoption (and the generated Go) uses a distinct per-directory package —conversationpb,panemsgpb,memorypb,envelopepb. There is no single Go import package calledrysh.v1.
The module contains both .proto sources and generated .pb.go files. As of the May 2026 repo split, rysh-proto is now a standalone git repository (git@github.com:rysh-ai/rysh-proto.git) and is .gitignored from the monorepo root — the directory is still physically present and is still referenced by go.work via a local replace directive, so it remains a workspace member at build time. (The same externally-managed-repo treatment applies to rysh-shared and rysh-chrome-plugin.) The runtime simply mirrors these definitions as hand-maintained Go structs.
9.1 Why proto-as-documentation?
graph LR
Proto[".proto files
(reference schemas)"] -.documents shape.-> GoStructs["rysh-shared/msg
Go structs (canonical)"]
GoStructs -->|JSON| Envelope["NATSEnvelope (JSON)"]
Envelope -->|publish| NATS["NATS subjects"]
Proto -.->|generated| PbGo[".pb.go (not on the wire)"]
The team chose JSON-on-the-wire (simpler debugging, no codegen coupling for the TS extension and dynamic codec) but kept proto as a single source of truth for the message contracts. Note the enum names differ in case from the Go constants (proto CONVERSATION_TYPE_SHELL/_AI vs Go "shell"/"ai").
The "reference only" status is documented in Go, not in proto. The proto files carry only ordinary descriptive comments (e.g.
envelope.protosayspayloadis the "protobuf-serialized inner message"). The statement that proto is reference-only and the wire is JSON comes from comments inrysh-shared/msg/conversation.go. So the proto and the runtime have genuinely diverged: the proto describes a protobuf encoding that the runtime does not use.
9.2 The four packages
conversationpb/conversation.proto — the core schema
Verbatim (package rysh.v1; syntax = "proto3";):
enum ConversationType {
CONVERSATION_TYPE_UNSPECIFIED = 0; CONVERSATION_TYPE_SHELL = 1; CONVERSATION_TYPE_AI = 2;
CONVERSATION_TYPE_RYSH = 3; CONVERSATION_TYPE_CHAT = 4; CONVERSATION_TYPE_EMAIL = 5;
CONVERSATION_TYPE_SLACK = 6; CONVERSATION_TYPE_CHATBOT = 7;
}
enum TurnType { TURN_TYPE_UNSPECIFIED = 0; TURN_TYPE_QUESTION = 1; TURN_TYPE_ANSWER = 2; }
enum InputType { INPUT_TYPE_UNSPECIFIED = 0; INPUT_TYPE_SHELL = 1; INPUT_TYPE_PROMPT = 2;
INPUT_TYPE_COMMAND = 3; INPUT_TYPE_APPROVAL = 4; INPUT_TYPE_MESSAGE = 5; }
enum MessageSource { MESSAGE_SOURCE_UNSPECIFIED = 0; MESSAGE_SOURCE_HUMAN = 1; MESSAGE_SOURCE_AI = 2;
MESSAGE_SOURCE_EXTERNAL = 3; MESSAGE_SOURCE_AGENT = 4; MESSAGE_SOURCE_SUBAGENT = 5;
MESSAGE_SOURCE_HUMANOID = 6; MESSAGE_SOURCE_SYSTEM = 7; }
message ConversationMessage {
string turn_id = 1; TurnType turn_type = 2;
ConversationType conversation_type = 3; InputType input_type = 4;
MessageSource message_source = 5; string content = 6;
int64 timestamp_ms = 7; bool sensitive = 8;
bool subject_to_share = 9; string role = 10; // visitor, assistant, human, system
bool streaming = 11; // partial streaming chunk
}
This single type replaces 12 legacy per-mode message types. (The Go struct adds an Origin field absent from proto.)
panemsgpb/pane_messages.proto
MsgConversationAppend{ConversationMessage message}→ output topics.MsgConversationHistoryAppend{ConversationMessage message}→ history topics.
These replace six per-mode MsgPane*OutputAppend and six MsgPane*HistoryAppend types.
memorypb/memory.proto
MemoryEntry{id, summary, turn_ids[], mode, created_at_ms, turn_count}.MemoryState{pane_id, mode, entries[], total_summarized_turns}.
envelopepb/envelope.proto
// NATSEnvelope is the wire format for all inter-actor messages on NATS.
message NATSEnvelope {
string type_tag = 1; // message type discriminator (e.g. "MsgConversationAppend")
string reply_to = 2; // NATS reply subject; empty for fire-and-forget
bytes payload = 3; // protobuf-serialized inner message
}
This is where proto and runtime diverge most visibly. The proto declares full field names (type_tag/reply_to/payload) and a bytes (protobuf) payload. The runtime Go NATSEnvelope instead uses short JSON keys (t/r/p) with a JSON payload (json.RawMessage), and the Chrome extension uses base64-encoded JSON. None of these are the protobuf encoding the proto describes.
9.3 Relationship to the runtime
| Proto type | Runtime Go type (rysh-shared/msg) |
On the wire |
|---|---|---|
ConversationMessage |
ConversationMessage (+ extra Origin field) |
JSON inside envelope p |
MsgConversationAppend / …HistoryAppend |
same names | JSON, tag in envelope t |
MemoryEntry / MemoryState |
same names | JSON |
NATSEnvelope{type_tag, reply_to, bytes payload} |
NATSEnvelope{t, r, json.RawMessage p} |
JSON (proto says protobuf) |
Note also that the TypeTags the system actually uses on the wire (MsgAgenticPrompt, MsgApprovalResponse, MsgBrowserActionRequest, …) are not defined in proto at all — only the two MsgConversation* wrappers are. The full tag set lives in rysh-shared/msg (§3.5).
The Go ConversationMessage adds an Origin *MessageOrigin field (not in proto) used for tab-mirroring provenance. See §3.3.
9.4 Caveat
Do not assume protobuf binary encoding anywhere in Rysh. Treat rysh-proto as living documentation of the message contracts; change the Go structs in rysh-shared/msg to change actual behavior, and keep the proto in sync for clarity.