No description
Find a file
Dirk Hoyer e1d1e53e2e fix: use correct TextList/DataList ToPtr() accessor for capnp v3
TextList and DataList are type aliases over List in the capnp v3 Go
library, not structs with a .List field. Use tl.ToPtr() and dl.ToPtr()
directly instead of tl.List.ToPtr() and dl.List.ToPtr().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 21:28:27 +01:00
examples/services-demo fix: Use canonical capability namespace in services-demo example 2026-02-19 12:16:00 +01:00
ixt-public@eaf4205d72 chore: Update ixt-public submodule and sync BlobStore schemas 2026-02-23 19:45:17 +01:00
sdk fix: use correct TextList/DataList ToPtr() accessor for capnp v3 2026-03-20 21:28:27 +01:00
.gitignore feat: Source IPC schemas from ixt-public submodule 2026-02-17 14:56:02 +01:00
.gitmodules feat: Source IPC schemas from ixt-public submodule 2026-02-17 14:56:02 +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 fix: imdl.toml -> ixt.imdl manifest filename in README 2026-03-19 17:10:59 +01:00
sync-schemas.sh feat: Source IPC schemas from ixt-public submodule 2026-02-17 14:56:02 +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) {
    // Tip: Use `ixt codegen schema.toml` to generate typed service proxies
    // with methods for each operation. See examples/codegen/ for details.
    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

# ixt.imdl
schema_version = "3.0"

[module]
name = "my-module"
version = "1.0.0"
description = "My IXT module"
runtime_targets = ["process"]

[provides]
services = ["my.service"]
events = []

# Multi-method services are also supported:
# [services."my.service".methods.process]
# input = "ProcessRequest"
# output = "ProcessResponse"

[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.