API Contract
Complete reference for the Shiro module interface — Go types, subprocess stdin/stdout protocol, and HTTP microservice API.
Module Interface
All Shiro modules — whether compiled-in Go, subprocess, or HTTP — ultimately implement this interface. Compiled Go modules must implement it directly.
// internal/modules/module.go
type Module interface {
// Run executes the module for a single workflow step.
// stepCtx contains resolved outputs from upstream steps.
// step is the workflow.Step struct; extract Config via reflection.
// Returns output map accessible as {{steps.<id>.output.*}} in downstream steps.
Run(ctx context.Context, stepCtx interface{}, step interface{}) (map[string]interface{}, error)
// Metadata returns the module's schema for documentation and validation.
Metadata() ModuleMetadata
}ModuleMetadata & SchemaField
type ModuleMetadata struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema map[string]SchemaField `json:"input_schema"`
OutputSchema map[string]SchemaField `json:"output_schema"`
}
type SchemaField struct {
Type string `json:"type"` // "string" | "number" | "boolean" | "array" | "object"
Description string `json:"description"`
Required bool `json:"required"`
Default interface{} `json:"default,omitempty"`
}Copy these structs into your module package. Do not import them from internal/modules — that creates circular dependencies.
Registry
The registry maps module type names to Module instances. Compiled-in modules register themselves at startup via generated code in internal/cli/registry.go.
type Registry struct { /* ... */ }
func NewRegistry() *Registry
// Register adds a module by type name. Returns error if already registered.
func (r *Registry) Register(moduleType string, module Module) error
// Get retrieves a module by type name.
func (r *Registry) Get(moduleType string) (Module, error)
// List returns all registered type names.
func (r *Registry) List() []string
// RegisterHTTPModule adds an HTTP-backed module.
func (r *Registry) RegisterHTTPModule(moduleType string, config *HTTPModuleConfig) errorRegistry config file format (auto-generated by shiro add module):
modules:
mymodule:
name: mymodule
type: builtin # builtin | http | subprocess
package: github.com/your-org/shiro-mymodule
factory: NewMyModule # exported constructor function name
version: "1.0.0"
description: "My module"
source: https://github.com/your-org/shiro-mymodule
added_at: "2025-01-01T00:00:00Z"Subprocess Protocol
Subprocess modules receive a JSON request on stdin and must write a JSON response to stdout. The binary must be named shiro-<name> and placed in .shiro/plugins/ or on PATH.
Request (stdin)
{
"action": "operation_name", // step type or specific operation
"config": { // step.config map
"key": "value"
},
"context": { // resolved upstream step outputs
"steps": {
"step_id": { "output": { ... } }
}
}
}Response (stdout)
{
"output": { // returned as steps.<id>.output.*
"result": "...",
"success": true
},
"error": "" // non-empty string causes step failure
}Metadata action
Shiro calls the binary with "action": "__metadata__" to fetch schema. Respond with a ModuleMetadata JSON object:
// Shiro sends:
{ "action": "__metadata__" }
// Binary responds:
{
"name": "mymodule",
"description": "Does useful things",
"input_schema": {
"key": { "type": "string", "description": "...", "required": true }
},
"output_schema": {
"result": { "type": "string", "description": "..." }
}
}Go types
// SubprocessRequest — sent to the binary via stdin
type SubprocessRequest struct {
Action string `json:"action"`
Config map[string]interface{} `json:"config"`
Context map[string]interface{} `json:"context,omitempty"`
}
// SubprocessResponse — read from the binary's stdout
type SubprocessResponse struct {
Output map[string]interface{} `json:"output"`
Error string `json:"error,omitempty"`
}HTTP Module API
HTTP modules are language-agnostic servers that implement two endpoints. Register them in the registry with type: http.
/runExecute a step. Body is the same JSON as the subprocess stdin request.
// Request body
{
"action": "operation",
"config": { "key": "value" },
"context": { "steps": { ... } }
}
// Response body (200 OK)
{
"output": { "result": "..." },
"error": ""
}/metadataReturn module metadata. Response body is a ModuleMetadata JSON object.
// Response body (200 OK)
{
"name": "mymodule",
"description": "...",
"input_schema": { ... },
"output_schema": { ... }
}Shiro supports multiple endpoints per HTTP module for load balancing — specify them as a list under endpoints in the registry.
AI Provider Interface
The ai.generate module resolves providers from .shiro/config.yaml. Built-in providers are openai and ollama.
// internal/ai/provider.go
type Provider interface {
Generate(ctx context.Context, req *GenerateRequest) (*GenerateResponse, error)
Stream(ctx context.Context, req *GenerateRequest) (<-chan StreamChunk, error)
Close() error
}
type GenerateRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
System string `json:"system,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
TopP float64 `json:"top_p,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
type Message struct {
Role string `json:"role"` // "system" | "user" | "assistant"
Content string `json:"content"`
}
type GenerateResponse struct {
Content string `json:"content"`
FinishReason string `json:"finish_reason"`
Usage *Usage `json:"usage,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
type Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
}
type ProviderConfig struct {
Type string `json:"type"` // "ollama" | "openai"
BaseURL string `json:"base_url"`
APIKey string `json:"api_key,omitempty"`
Model string `json:"model"`
Headers map[string]string `json:"headers,omitempty"`
Timeout int `json:"timeout"`
SkipTLSVerify bool `json:"skip_tls_verify,omitempty"`
}