Kimberlite Coding

Go Client

On this page

Status: Deferred post-v0.4 launch. The Go SDK has skeleton code in sdks/go/ but is not functionally wired up yet. The API sketch below shows the intended surface; treat it as a proposal, not an installable package.

For production use today, see the Rust, Python, or TypeScript clients. If you need Go specifically, please open an issue so we can prioritise it based on demand.

Build Kimberlite applications in Go with simplicity and performance.

Prerequisites (planned)

  • Go 1.21 or later
  • Running Kimberlite cluster (see Start)

Install (planned — not yet published)

# Not yet available. Tracked in ROADMAP.md.
# go get github.com/kimberlitedb/kimberlite-go

Quick Verification

Create main.go:

package main

import (
    "fmt"
    kimberlite "github.com/kimberlitedb/kimberlite-go"
)

func main() {
    fmt.Println("Kimberlite Go client imported successfully!")
    fmt.Printf("Version: %s\n", kimberlite.Version)
}

Run it:

go run main.go

Sample Projects

Basic: Create Table and Query Data

package main

import (
    "context"
    "fmt"
    "log"

    kimberlite "github.com/kimberlitedb/kimberlite-go"
)

func main() {
    ctx := context.Background()

    // Connect to cluster
    client, err := kimberlite.Connect(ctx, &kimberlite.Config{
        Addresses: []string{"localhost:3000"},
    })
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Create table
    _, err = client.Exec(ctx, `
        CREATE TABLE users (
            id INT PRIMARY KEY,
            email TEXT NOT NULL,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    `)
    if err != nil {
        log.Fatal(err)
    }

    // Insert data
    _, err = client.Exec(ctx,
        "INSERT INTO users (id, email) VALUES (?, ?)",
        1, "alice@example.com",
    )
    if err != nil {
        log.Fatal(err)
    }

    // Query data
    rows, err := client.Query(ctx, "SELECT * FROM users")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var email string
        var createdAt time.Time
        if err := rows.Scan(&id, &email, &createdAt); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("User %d: %s\n", id, email)
    }
}

Compliance: Enable RBAC and Test Access Control

package main

import (
    "context"
    "fmt"
    "log"

    kimberlite "github.com/kimberlitedb/kimberlite-go"
)

func main() {
    ctx := context.Background()

    // Connect as admin
    adminClient, err := kimberlite.Connect(ctx, &kimberlite.Config{
        Addresses: []string{"localhost:3000"},
        User:      "admin",
        Password:  "admin-password",
    })
    if err != nil {
        log.Fatal(err)
    }
    defer adminClient.Close()

    // Create role with limited permissions
    _, err = adminClient.Exec(ctx, `
        CREATE ROLE data_analyst;
        GRANT SELECT ON patients TO data_analyst;
    `)
    if err != nil {
        log.Fatal(err)
    }

    // Create user with role
    _, err = adminClient.Exec(ctx, `
        CREATE USER analyst1
        WITH PASSWORD 'analyst-password'
        WITH ROLE data_analyst;
    `)
    if err != nil {
        log.Fatal(err)
    }

    // Connect as analyst
    analystClient, err := kimberlite.Connect(ctx, &kimberlite.Config{
        Addresses: []string{"localhost:3000"},
        User:      "analyst1",
        Password:  "analyst-password",
    })
    if err != nil {
        log.Fatal(err)
    }
    defer analystClient.Close()

    // This works (SELECT granted)
    rows, err := analystClient.Query(ctx, "SELECT * FROM patients")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    count := 0
    for rows.Next() {
        count++
    }
    fmt.Printf("Found %d patients\n", count)

    // This fails (no INSERT permission)
    _, err = analystClient.Exec(ctx,
        "INSERT INTO patients VALUES (99, 'Unauthorized', '000-00-0000')",
    )
    if err != nil {
        if kimberlite.IsPermissionDenied(err) {
            fmt.Printf("Access denied: %v\n", err)
        } else {
            log.Fatal(err)
        }
    }
}

Multi-Tenant: Tenant Isolation Example

package main

import (
    "context"
    "fmt"
    "log"

    kimberlite "github.com/kimberlitedb/kimberlite-go"
)

func main() {
    ctx := context.Background()

    // Connect to tenant 1
    tenant1Client, err := kimberlite.Connect(ctx, &kimberlite.Config{
        Addresses: []string{"localhost:3000"},
        TenantID:  1,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer tenant1Client.Close()

    // Create data for tenant 1
    _, err = tenant1Client.Exec(ctx, `
        CREATE TABLE orders (id INT, customer TEXT, amount DECIMAL);
        INSERT INTO orders VALUES (1, 'Alice', 99.99);
    `)
    if err != nil {
        log.Fatal(err)
    }

    // Connect to tenant 2
    tenant2Client, err := kimberlite.Connect(ctx, &kimberlite.Config{
        Addresses: []string{"localhost:3000"},
        TenantID:  2,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer tenant2Client.Close()

    // Tenant 2 cannot see tenant 1's data
    rows, err := tenant2Client.Query(ctx, "SELECT * FROM orders")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    count := 0
    for rows.Next() {
        count++
    }
    fmt.Printf("Tenant 2 sees %d orders\n", count) // 0

    // Create separate data for tenant 2
    _, err = tenant2Client.Exec(ctx, "INSERT INTO orders VALUES (1, 'Bob', 149.99)")
    if err != nil {
        log.Fatal(err)
    }

    rows, err = tenant2Client.Query(ctx, "SELECT * FROM orders")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    count = 0
    for rows.Next() {
        count++
    }
    fmt.Printf("Tenant 2 sees %d orders\n", count) // 1
}

Compliance: Data Classification and Masking

package main

import (
    "context"
    "fmt"
    "log"

    kimberlite "github.com/kimberlitedb/kimberlite-go"
)

func main() {
    ctx := context.Background()

    client, err := kimberlite.Connect(ctx, &kimberlite.Config{
        Addresses: []string{"localhost:3000"},
    })
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Create table with PHI data
    _, err = client.Exec(ctx, `
        CREATE TABLE patients (
            id INT PRIMARY KEY,
            name TEXT NOT NULL,
            ssn TEXT NOT NULL,
            diagnosis TEXT
        );
    `)
    if err != nil {
        log.Fatal(err)
    }

    // Classify sensitive columns
    _, err = client.Exec(ctx,
        "ALTER TABLE patients MODIFY COLUMN ssn SET CLASSIFICATION 'PHI'",
    )
    if err != nil {
        log.Fatal(err)
    }

    _, err = client.Exec(ctx,
        "ALTER TABLE patients MODIFY COLUMN diagnosis SET CLASSIFICATION 'MEDICAL'",
    )
    if err != nil {
        log.Fatal(err)
    }

    // Insert data
    _, err = client.Exec(ctx, `
        INSERT INTO patients VALUES
            (1, 'Alice Johnson', '123-45-6789', 'Hypertension'),
            (2, 'Bob Smith', '987-65-4321', 'Diabetes');
    `)
    if err != nil {
        log.Fatal(err)
    }

    // Create masking rule
    _, err = client.Exec(ctx, "CREATE MASK ssn_mask ON patients.ssn USING REDACT")
    if err != nil {
        log.Fatal(err)
    }

    // Query - SSN is automatically masked
    rows, err := client.Query(ctx, "SELECT * FROM patients")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var name, ssn, diagnosis string
        if err := rows.Scan(&id, &name, &ssn, &diagnosis); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%s: SSN=%s\n", name, ssn) // SSN shows as ****
    }

    // View classifications
    rows, err = client.Query(ctx, "SHOW CLASSIFICATIONS FOR patients")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    for rows.Next() {
        var column, classification string
        if err := rows.Scan(&column, &classification); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%s: %s\n", column, classification)
    }
}

Time-Travel: Query Historical State

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    kimberlite "github.com/kimberlitedb/kimberlite-go"
)

func main() {
    ctx := context.Background()

    client, err := kimberlite.Connect(ctx, &kimberlite.Config{
        Addresses: []string{"localhost:3000"},
    })
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Insert initial data
    _, err = client.Exec(ctx, `
        CREATE TABLE inventory (product_id INT, quantity INT);
        INSERT INTO inventory VALUES (1, 100);
    `)
    if err != nil {
        log.Fatal(err)
    }

    // Wait a moment
    time.Sleep(2 * time.Second)
    checkpoint := time.Now()

    // Update inventory
    _, err = client.Exec(ctx, "UPDATE inventory SET quantity = 75 WHERE product_id = 1")
    if err != nil {
        log.Fatal(err)
    }

    // Query current state
    rows, err := client.Query(ctx, "SELECT * FROM inventory WHERE product_id = 1")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    if rows.Next() {
        var productID, quantity int
        if err := rows.Scan(&productID, &quantity); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Current quantity: %d\n", quantity) // 75
    }

    // Query historical state
    rows, err = client.Query(ctx,
        "SELECT * FROM inventory AS OF TIMESTAMP ? WHERE product_id = 1",
        checkpoint,
    )
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    if rows.Next() {
        var productID, quantity int
        if err := rows.Scan(&productID, &quantity); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Historical quantity: %d\n", quantity) // 100
    }
}

API Reference

Creating a Client

import kimberlite "github.com/kimberlitedb/kimberlite-go"

// Basic connection
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
    Addresses: []string{"localhost:3000"},
})

// With authentication
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
    Addresses: []string{"localhost:3000"},
    User:      "username",
    Password:  "password",
})

// With tenant isolation
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
    Addresses: []string{"localhost:3000"},
    TenantID:  1,
})

// With TLS
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
    Addresses:  []string{"localhost:3000"},
    TLSEnabled: true,
    TLSCACert:  "/path/to/ca.pem",
})

// Full configuration
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
    Addresses:     []string{"localhost:3000", "localhost:3001", "localhost:3002"},
    User:          "admin",
    Password:      "password",
    TenantID:      1,
    TLSEnabled:    true,
    TLSCACert:     "/path/to/ca.pem",
    TLSClientCert: "/path/to/client.pem",
    TLSClientKey:  "/path/to/client-key.pem",
    Timeout:       5 * time.Second,
    MaxRetries:    3,
})

Executing Queries

// DDL (CREATE, ALTER, DROP)
_, err := client.Exec(ctx, `
    CREATE TABLE products (
        id INT PRIMARY KEY,
        name TEXT NOT NULL,
        price DECIMAL
    )
`)

// DML (INSERT, UPDATE, DELETE)
result, err := client.Exec(ctx,
    "INSERT INTO products VALUES (?, ?, ?)",
    1, "Widget", 19.99,
)
rowsAffected, _ := result.RowsAffected()
fmt.Printf("Inserted %d rows\n", rowsAffected)

Querying Data

// Simple query
rows, err := client.Query(ctx, "SELECT * FROM products")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
    var id int
    var name string
    var price float64
    if err := rows.Scan(&id, &name, &price); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s: $%.2f\n", name, price)
}

// Parameterized query
rows, err := client.Query(ctx,
    "SELECT * FROM products WHERE price > ?",
    25.0,
)

// QueryRow for single result
var count int
err := client.QueryRow(ctx, "SELECT COUNT(*) FROM products").Scan(&count)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Product count: %d\n", count)

Transactions

// Begin transaction
tx, err := client.Begin(ctx)
if err != nil {
    log.Fatal(err)
}

// Execute in transaction
_, err = tx.Exec(ctx, "UPDATE accounts SET balance = balance - 100 WHERE id = 1")
if err != nil {
    tx.Rollback(ctx)
    log.Fatal(err)
}

_, err = tx.Exec(ctx, "UPDATE accounts SET balance = balance + 100 WHERE id = 2")
if err != nil {
    tx.Rollback(ctx)
    log.Fatal(err)
}

// Commit transaction
if err := tx.Commit(ctx); err != nil {
    log.Fatal(err)
}

Error Handling

import "github.com/kimberlitedb/kimberlite-go"

_, err := client.Exec(ctx, "INSERT INTO users VALUES (1, 'alice@example.com')")
if err != nil {
    switch {
    case kimberlite.IsConnectionError(err):
        fmt.Println("Failed to connect to cluster")
    case kimberlite.IsAuthenticationError(err):
        fmt.Println("Invalid credentials")
    case kimberlite.IsPermissionDenied(err):
        fmt.Println("No permission for this operation")
    case kimberlite.IsConstraintViolation(err):
        fmt.Printf("Constraint violation: %v\n", err)
    case kimberlite.IsQueryError(err):
        fmt.Printf("Query error: %v\n", err)
    default:
        fmt.Printf("Error: %v\n", err)
    }
}

Prepared Statements

// Prepare statement
stmt, err := client.Prepare(ctx,
    "INSERT INTO logs (timestamp, message) VALUES (?, ?)",
)
if err != nil {
    log.Fatal(err)
}
defer stmt.Close()

// Execute multiple times
_, err = stmt.Exec(ctx, time.Now(), "User logged in")
if err != nil {
    log.Fatal(err)
}

_, err = stmt.Exec(ctx, time.Now(), "User logged out")
if err != nil {
    log.Fatal(err)
}

Working with Types

import (
    "database/sql"
    "time"
    "github.com/shopspring/decimal"
)

// Insert with proper types
_, err := client.Exec(ctx, `
    INSERT INTO transactions (id, amount, timestamp, metadata)
    VALUES (?, ?, ?, ?)
`,
    1,
    decimal.NewFromFloat(99.99),
    time.Now(),
    `{"source": "web", "ip": "192.168.1.1"}`,
)

// Query with typed extraction
rows, err := client.Query(ctx, "SELECT * FROM transactions WHERE id = 1")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

if rows.Next() {
    var id int
    var amount decimal.Decimal
    var timestamp time.Time
    var metadata string

    if err := rows.Scan(&id, &amount, &timestamp, &metadata); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Amount: %s\n", amount)
    fmt.Printf("Timestamp: %s\n", timestamp)
    fmt.Printf("Metadata: %s\n", metadata)
}

Testing

Use the standard Go testing package:

package main

import (
    "context"
    "testing"

    kimberlite "github.com/kimberlitedb/kimberlite-go"
)

func setup(t *testing.T) *kimberlite.Client {
    ctx := context.Background()

    client, err := kimberlite.Connect(ctx, &kimberlite.Config{
        Addresses: []string{"localhost:3000"},
    })
    if err != nil {
        t.Fatal(err)
    }

    // Clean database
    rows, err := client.Query(ctx, "SHOW TABLES")
    if err != nil {
        t.Fatal(err)
    }
    defer rows.Close()

    for rows.Next() {
        var name string
        if err := rows.Scan(&name); err != nil {
            t.Fatal(err)
        }
        _, err = client.Exec(ctx, "DROP TABLE IF EXISTS "+name)
        if err != nil {
            t.Fatal(err)
        }
    }

    return client
}

func TestCreateTable(t *testing.T) {
    client := setup(t)
    defer client.Close()

    ctx := context.Background()

    _, err := client.Exec(ctx, `
        CREATE TABLE test_table (
            id INT PRIMARY KEY,
            name TEXT NOT NULL
        )
    `)
    if err != nil {
        t.Fatal(err)
    }

    rows, err := client.Query(ctx, "SHOW TABLES")
    if err != nil {
        t.Fatal(err)
    }
    defer rows.Close()

    found := false
    for rows.Next() {
        var name string
        if err := rows.Scan(&name); err != nil {
            t.Fatal(err)
        }
        if name == "test_table" {
            found = true
            break
        }
    }

    if !found {
        t.Error("Table test_table not found")
    }
}

func TestInsertAndQuery(t *testing.T) {
    client := setup(t)
    defer client.Close()

    ctx := context.Background()

    _, err := client.Exec(ctx, "CREATE TABLE users (id INT, email TEXT)")
    if err != nil {
        t.Fatal(err)
    }

    _, err = client.Exec(ctx,
        "INSERT INTO users VALUES (?, ?)",
        1, "test@example.com",
    )
    if err != nil {
        t.Fatal(err)
    }

    rows, err := client.Query(ctx, "SELECT * FROM users WHERE id = 1")
    if err != nil {
        t.Fatal(err)
    }
    defer rows.Close()

    if !rows.Next() {
        t.Fatal("Expected 1 row, got 0")
    }

    var id int
    var email string
    if err := rows.Scan(&id, &email); err != nil {
        t.Fatal(err)
    }

    if email != "test@example.com" {
        t.Errorf("Expected email 'test@example.com', got '%s'", email)
    }
}

Examples

Complete example applications are available in the repository:

  • examples/go/basic/ - Simple CRUD application
  • examples/go/compliance/ - HIPAA-ready healthcare app
  • examples/go/multi-tenant/ - Multi-tenant SaaS application
  • examples/go/http/ - HTTP REST API with net/http
  • examples/go/grpc/ - gRPC service

Next Steps

Further Reading