Port Interface Reference
Complete reference for port definitions in TinySystems components.
Source of truth: module/node.go in the SDK (github.com/tiny-systems/module).
Port Structure
type Port struct {
// true = this port is a SOURCE of data (output port)
Source bool
// Which side of the node displays this port
Position Position
// Lower-case programmatic name
Name string
// Human-readable name (capital cased)
Label string
// Request configuration/schema struct
Configuration interface{}
// Response configuration/schema struct (blocking request/response ports)
ResponseConfiguration interface{}
// Raw JSON schema override; published verbatim instead of reflecting Configuration
Schema json.RawMessage
}
Fields
Source
Indicates port direction.
Type: bool
| Value | Direction | Description |
|---|---|---|
true | Output | The port is a source of data — the component emits messages from it |
false | Input | The port receives messages (the zero value; usually omitted) |
Examples:
// Output port
Source: true
// Input port (default — typically omitted)
Source: false
This matches TinyModuleComponentPort.Source in the CRD API: "Source is true for output ports, false for input ports."
Position
Visual position on the node in the flow editor.
Type: module.Position (an int)
Constants:
| Constant | Value | Typical Use |
|---|---|---|
module.Top | 0 | Settings, Control (the zero value/default) |
module.Right | 1 | Output ports |
module.Bottom | 2 | Errors, Status |
module.Left | 3 | Input ports |
const (
Top Position = iota // 0
Right // 1
Bottom // 2
Left // 3
)
Visual Layout:
Top (Settings, Control)
|
Left ---+--- Right
(inputs) | (outputs)
|
Bottom (Errors, Status)
Name
Unique identifier for the port within the component.
Type: string
Requirements:
- Must be unique within the component
- Lower-case programmatic name
- Names starting with
_are reserved for system ports
Reserved Names (constants in api/v1alpha1/consts.go):
| Name | Constant | Purpose |
|---|---|---|
_settings | v1alpha1.SettingsPort | Component settings |
_control | v1alpha1.ControlPort | Dashboard/control messages |
_reconcile | v1alpha1.ReconcilePort | Node reconciliation |
_client | v1alpha1.ClientPort | Kubernetes client access |
_identity | v1alpha1.IdentityPort | Node identity (name, namespace, flow, project) |
Examples:
Name: "input"
Name: "output"
Name: "request"
Name: "result"
Name: "error"
Label
Human-readable display label for the UI.
Type: string
Guidelines:
- Use title case
- Keep concise (1-3 words)
Examples:
Label: "Input"
Label: "Output"
Label: "Settings"
Label: "HTTP Request"
Configuration
Struct instance that defines the port's schema and default values.
Type: interface{}
Purpose:
- Defines the JSON Schema for the port (via reflection over struct tags)
- Provides default/example values
- Configures UI rendering
Example:
Configuration: Request{
Array: []Item{"first", "second", "third"},
Index: 1,
}
The live values are materialized into the published schema as defaults, so the simulator and editor mock real example data.
ResponseConfiguration
Struct instance describing the synchronous response of a blocking request/response port (the value that flows back through the Handler return chain).
Type: interface{}
Leave nil for fire-and-forget ports.
Schema
Raw JSON schema published verbatim instead of reflecting Configuration.
Type: json.RawMessage
Use it for forms whose shape is only known at runtime — a component that receives a schema as data and presents it to a human has no compile-time Go type to reflect. Components with a Go type for their port keep returning Configuration alone; reflection stays the default.
Note: do not rely on key order to order the rendered fields — set propertyOrder on each field in the schema JSON; that is what the editor sorts on.
Common Port Patterns
Real-world convention (see common-module components): inputs omit Source, outputs set Source: true.
Settings Port (input)
{
Name: v1alpha1.SettingsPort,
Label: "Settings",
Configuration: Settings{},
}
Delivered via SettingsHandler.OnSettings — never via Handle.
Simple Input/Output
// Input
{
Name: "input",
Label: "Input",
Position: module.Left,
Configuration: Input{},
}
// Output
{
Name: "output",
Label: "Output",
Source: true,
Position: module.Right,
Configuration: Output{},
}
Result and Error Outputs
{
Name: "result",
Label: "Result",
Source: true,
Position: module.Right,
Configuration: Result{},
},
{
Name: "error",
Label: "Error",
Source: true,
Position: module.Bottom,
Configuration: module.ErrorMessage{},
}
Control Port
{
Name: v1alpha1.ControlPort,
Label: "Control",
Source: true, // publishes current state to the dashboard widget
Configuration: c.getControl(),
}
Incoming control messages are delivered via ControlHandler.OnControl. Returning a different Go type per state (e.g. ControlRunning vs ControlStopped) is a supported pattern for state-dependent forms.
HTTP Server Ports
// Request output (emits incoming HTTP requests downstream)
{
Name: "request",
Label: "Request",
Source: true,
Position: module.Right,
Configuration: Request{},
ResponseConfiguration: Response{}, // synchronous reply flowing back
}
Dynamic Ports
Ports() is called again after settings change, so ports can be generated from configuration.
func (c *Component) Ports() []module.Port {
ports := []module.Port{
{
Name: v1alpha1.SettingsPort,
Label: "Settings",
Configuration: Settings{},
},
{
Name: "input",
Label: "Input",
Position: module.Left,
Configuration: Input{},
},
}
// Dynamic output ports based on configured routes
for _, route := range c.settings.Routes {
ports = append(ports, module.Port{
Name: "out_" + route.Name,
Label: route.Label,
Source: true,
Position: module.Right,
Configuration: Output{},
})
}
return ports
}
Schema Configuration
The Configuration field uses struct tags to define JSON Schema:
Configuration: struct {
// Basic types
StringField string `json:"stringField" title:"String Field"`
IntField int `json:"intField" title:"Integer Field"`
BoolField bool `json:"boolField" title:"Boolean Field"`
// Validation
Required string `json:"required" required:"true"`
MinMax int `json:"minMax" minimum:"0" maximum:"100"`
Pattern string `json:"pattern" pattern:"^[a-z]+$"`
// UI hints (formats supported by the editor)
Textarea string `json:"textarea" format:"textarea"`
Config string `json:"config" format:"code" language:"yaml"`
// Data mapping
Data any `json:"data" configurable:"true" title:"Data"`
// Defaults
WithDefault string `json:"withDefault" default:"hello"`
}{}
See the Struct Tags Reference for the full supported set.
Best Practices
1. Consistent Naming
// Good
"input", "output", "request", "result", "error"
// Avoid
"Input1", "OUTPUT", "my port"
2. Clear Labels
// Good
Label: "HTTP Request"
Label: "Result"
// Avoid
Label: "out"
Label: "data"
3. Logical Positioning
// Inputs on the left
Position: module.Left
// Outputs on the right
Position: module.Right
// Errors on the bottom
Position: module.Bottom
4. Meaningful Defaults
Configuration: Settings{
Timeout: "30s",
MaxRetries: 3,
}
5. Canonical Error Payloads
// Error ports should use the SDK's shape
Configuration: module.ErrorMessage{}