Plugin protocol

Sunscreen plugins are external binaries that speak two transport layers:

  • stdio JSON-RPC (default, used today).
  • gRPC (defined in proto/plugin.proto, planned for richer plugins).

Manifest

Each plugin ships a plugin.toml next to its binary:

[plugin]
name = "yellowstone-indexer"
version = "0.3.0"
description = "Scaffold a Yellowstone gRPC indexer slice."
entry = "./bin/plugin"

[permissions]
workspace_write = true       # default: false
network = false              # if true, prompts user at install time
declared_paths = []          # extra read-only paths outside workspace

[commands.scaffold]
indexer = "Scaffold a Yellowstone indexer slice"
listener = "Scaffold an event listener"

[commands.hook]
after_build = "Update indexer config when IDL changes"

JSON-RPC methods

Sunscreen calls these on the plugin via stdin/stdout. One JSON object per line.

commands

Request:

{"jsonrpc":"2.0","id":1,"method":"commands"}

Response:

{"jsonrpc":"2.0","id":1,"result":{
  "scaffold": {"indexer": "…", "listener": "…"},
  "hook": {"after_build": "…"}
}}

run

Request:

{"jsonrpc":"2.0","id":2,"method":"run","params":{
  "command": "scaffold:indexer",
  "args": ["Trades"],
  "flags": {"program": "app"},
  "workspace_root": "/abs/path/to/workspace",
  "config": {"version": 1, "name": "my-app", "..." : "..."}
}}

Response (success):

{"jsonrpc":"2.0","id":2,"result":{
  "files_written": ["programs/app/src/instructions/index_trades.rs", "..."],
  "summary": "scaffolded indexer Trades"
}}

Response (error):

{"jsonrpc":"2.0","id":2,"error":{
  "code": 4,
  "message": "instruction index_trades already exists",
  "data": {"next_step": "pass --force or pick a different name"}
}}

hook

Request:

{"jsonrpc":"2.0","id":3,"method":"hook","params":{
  "name": "after_build",
  "context": {"workspace_root": "...", "idl": {...}}
}}

Response: same shape as run.

Sandbox

Sunscreen enforces:

CapabilityDefaultToggle
Read workspace filesallowedalways on
Write workspace filesdeniedpermissions.workspace_write = true
Read outside workspacedenieddeclare each path in permissions.declared_paths
Network accessdeniedpermissions.network = true (user-approved at install)
Spawn subprocessesdeniednot toggleable in current version

Sandbox violations terminate the session and return exit 9 to the caller.

gRPC contract

proto/plugin.proto (in the sunscreen repo) defines the streaming-friendly equivalent of the JSON-RPC surface. Use when:

  • Plugin needs bidirectional streaming (live progress, log forwarding).
  • Plugin is implemented in a non-stdio-friendly runtime (e.g. JVM).

The gRPC transport is wire-defined but not yet end-to-end implemented in the runtime.

Conformance tests

Reference plugins under sunscreen-apps/ exercise the full protocol surface and serve as templates. The spl-token-2022 plugin is the canonical example.