No description
Find a file
Dirk Hoyer 58630abb4a feat: Add KV Store service methods for feature parity
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>
2026-01-09 21:34:00 +01:00
sdk feat: Add KV Store service methods for feature parity 2026-01-09 21:34:00 +01:00
go.mod feat: Migrate to Cap'n Proto serialization (Mission I140) 2025-12-24 19:39:46 +01:00
go.sum feat: Migrate to Cap'n Proto serialization (Mission I140) 2025-12-24 19:39:46 +01:00
LICENSE feat(sdk-go): Add MIT license and update documentation 2025-10-23 08:38:46 +02:00
README.md feat: Add KV Store service methods for feature parity 2026-01-09 21:34:00 +01:00

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.