No description
Add KvGet, KvSet, KvExists, KvDelete methods to ModuleRunner for complete KV Store service integration. Now matches Python and Node.js SDK feature set. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
||
|---|---|---|
| sdk | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| README.md | ||
IXT Go SDK
Go SDK for building IXT modules that run as separate processes and communicate with the IXT runtime via Cap'n Proto IPC.
Features
- Simple interface-based module development
- Cap'n Proto serialization for efficient IPC
- Automatic connection handling and lifecycle management
- Type-safe service request/response handling
- Health check support
- JSON encoding helpers
- Idiomatic Go error handling
Installation
go get git.h-dv.de/h-dv/ixt-sdk-go
Quick Start
1. Create Your Module
package main
import (
"os"
"git.h-dv.de/h-dv/ixt-sdk-go/sdk"
)
type MyModule struct {
sdk.BaseModule
dbURL string
}
func (m *MyModule) OnInit(config map[string]any) error {
// Initialize module with configuration
if url, ok := config["database_url"].(string); ok {
m.dbURL = url
} else {
m.dbURL = "sqlite:///data.db"
}
return nil
}
func (m *MyModule) OnHandle(message *sdk.Message, context *sdk.Context) (*sdk.Response, error) {
switch message.Topic {
case "my.service.process":
// Process the request
result := map[string]any{"processed": true}
return sdk.SuccessJSON(result)
default:
return sdk.Error("UNKNOWN_TOPIC", "Unknown topic: "+message.Topic), nil
}
}
func (m *MyModule) OnShutdown() error {
// Clean up resources
return nil
}
func main() {
m := &MyModule{}
if err := sdk.Run(m); err != nil {
os.Exit(1)
}
}
2. Create Manifest
# imdl.toml
schema_version = "3.0"
[module]
name = "my-module"
version = "1.0.0"
description = "My IXT module"
runtime_targets = ["process"]
[provides]
services = ["my.service.process"]
events = []
[requires]
services = []
events = []
[artifacts.process]
path = "my-module"
sha256 = "0000000000000000000000000000000000000000000000000000000000000000"
size_bytes = 1024
3. Build and Run
# Build
go build -o my-module .
# For development (IXT runtime spawns automatically)
export IXT_IPC_SOCKET=/tmp/ixt-module.sock
export IXT_MODULE_ID=my-module:1.0.0/instance-001
./my-module
API Reference
Module Interface
type Module interface {
OnInit(config map[string]any) error
OnHandle(message *Message, context *Context) (*Response, error)
OnShutdown() error
OnHealthCheck() (*HealthStatus, error)
}
BaseModule
Embed BaseModule to get default health check implementation:
type MyModule struct {
sdk.BaseModule
// your fields
}
Message Types
// Message - incoming request
type Message struct {
Payload []byte
Topic string
TraceID string
SenderID string
}
// Context - request context
type Context struct {
RequestID string
TraceID string
Timestamp int64
TimeoutMs int64
}
// Response - handler response
type Response struct {
Success bool
Data []byte
ErrorCode string
ErrorMessage string
}
// HealthStatus - health check response
type HealthStatus struct {
Healthy bool
Status string
UptimeSeconds int64
ActiveRequests int32
}
Response Helpers
// Success response with raw bytes
sdk.Success([]byte("data"))
// Success response with JSON encoding
sdk.SuccessJSON(map[string]any{"result": "ok"})
// Error response
sdk.Error("CODE", "message")
Service Requests (Calling Other Services)
func (m *MyModule) OnHandle(message *sdk.Message, context *sdk.Context) (*sdk.Response, error) {
// Get the module runner to make service requests
runner := sdk.GetRunner()
// Call another service
request := map[string]any{"query": "SELECT * FROM users"}
requestBytes, _ := json.Marshal(request)
response, err := runner.SendServiceRequest("database.query", requestBytes, 5000)
if err != nil {
return sdk.Error("SERVICE_ERROR", err.Error()), nil
}
return sdk.Success(response.Data), nil
}
KV Store
Persist and retrieve key-value data across module restarts:
// Get a value (returns nil if not found)
value, err := runner.KvGet("user:123:session")
if err != nil {
return sdk.Error("KV_ERROR", err.Error()), nil
}
if value != nil {
// Use the value
}
// Set a value
err = runner.KvSet("user:123:session", []byte(`{"token":"abc"}`))
if err != nil {
return sdk.Error("KV_ERROR", err.Error()), nil
}
// Check if key exists
exists, err := runner.KvExists("user:123:session")
if err != nil {
return sdk.Error("KV_ERROR", err.Error()), nil
}
// Delete a key (returns true if it existed)
existed, err := runner.KvDelete("user:123:session")
if err != nil {
return sdk.Error("KV_ERROR", err.Error()), nil
}
Custom Health Checks
Override OnHealthCheck for custom health reporting:
func (m *MyModule) OnHealthCheck() (*sdk.HealthStatus, error) {
// Check dependencies, connections, etc.
healthy := m.checkDatabaseConnection()
return &sdk.HealthStatus{
Healthy: healthy,
Status: "running",
UptimeSeconds: m.uptime(),
ActiveRequests: m.activeCount(),
}, nil
}
Environment Variables
| Variable | Description |
|---|---|
IXT_IPC_SOCKET |
Unix socket path for IPC communication |
IXT_MODULE_ID |
Module identifier in format name:version/instance |
Error Handling
The SDK uses Go's standard error handling patterns:
func (m *MyModule) OnHandle(message *sdk.Message, context *sdk.Context) (*sdk.Response, error) {
result, err := m.processRequest(message.Payload)
if err != nil {
// Return error response (not Go error)
return sdk.Error("PROCESSING_ERROR", err.Error()), nil
}
return sdk.SuccessJSON(result)
}
Return a Go error only for unrecoverable errors that should crash the module.
Requirements
- Go 1.21+
- capnproto.org/go/capnp/v3
License
MIT License - see LICENSE file for details.