Esc

    Go SDK

    Idiomatic Go client for InferaDB.

    Coming soon. The Go SDK is under active development. The API surface shown here is based on the Rust SDK and may change before release.

    Typed, context-aware client for InferaDB. Requires Go 1.22+.

    Installation

    go get github.com/inferadb/go
    

    Authentication

    Three authentication methods:

    Method Use Case Security
    Client Credentials (Ed25519 JWT) Service-to-service High
    Bearer Token User sessions, OAuth Medium
    API Key Testing, simple integrations Basic
    package main
    
    import (
        "log"
        "os"
    
        inferadb "github.com/inferadb/go"
    )
    
    func main() {
        key, err := inferadb.Ed25519PrivateKeyFromPEMFile("client.pem")
        if err != nil {
            log.Fatal(err)
        }
    
        client, err := inferadb.NewClient(
            inferadb.WithURL("https://engine.inferadb.com"),
            inferadb.WithClientCredentials("my-client", key, "cert-id"),
        )
        if err != nil {
            log.Fatal(err)
        }
        defer client.Close()
    }
    

    Bearer Token

    client, err := inferadb.NewClient(
        inferadb.WithURL("https://engine.inferadb.com"),
        inferadb.WithBearerToken(os.Getenv("INFERADB_TOKEN")),
    )
    

    API Key

    client, err := inferadb.NewClient(
        inferadb.WithURL("https://engine.inferadb.com"),
        inferadb.WithAPIKey(os.Getenv("INFERADB_API_KEY")),
    )
    

    Permission Checks

    vault := client.Organization("my-org").Vault("production")
    ctx := context.Background()
    
    allowed, err := vault.Check(ctx, "user:alice", "can_edit", "document:readme")
    

    With ABAC Context

    allowed, err := vault.Check(ctx, "user:alice", "can_view", "document:readme",
        inferadb.WithContext(map[string]any{"ip_address": "10.0.0.1"}),
    )
    

    Require — Returns Error on Deny

    err := vault.Require(ctx, "user:alice", "can_edit", "document:readme")
    if errors.Is(err, inferadb.ErrAccessDenied) {
        // permission denied
    }
    

    With Consistency Token

    allowed, err := vault.Check(ctx, "user:alice", "can_view", "document:readme",
        inferadb.AtLeastAsFresh(revisionToken),
    )
    

    Batch Check

    results, err := vault.CheckBatch(ctx, []inferadb.CheckRequest{
        {Subject: "user:alice", Permission: "can_edit", Resource: "document:readme"},
        {Subject: "user:bob", Permission: "can_view", Resource: "document:readme"},
    })
    if results.AllAllowed() {
        // all checks passed
    }
    

    Relationships

    Write

    // Returns a revision token
    token, err := vault.Relationships().Write(ctx, inferadb.Relationship{
        Resource: "document:readme",
        Relation: "editor",
        Subject:  "user:alice",
    })
    

    Batch Write

    err := vault.Relationships().WriteBatch(ctx, []inferadb.Relationship{
        {Resource: "document:readme", Relation: "editor", Subject: "user:alice"},
        {Resource: "document:readme", Relation: "viewer", Subject: "user:bob"},
    })
    

    List

    rels, err := vault.Relationships().List(ctx,
        inferadb.FilterByResource("document:readme"),
    )
    

    Delete

    err := vault.Relationships().DeleteWhere(ctx,
        inferadb.FilterByResource("document:readme"),
        inferadb.FilterByRelation("viewer"),
        inferadb.FilterBySubject("user:bob"),
    )
    

    Lookups

    // What resources can Alice view?
    resources, err := vault.Resources().AccessibleBy(ctx, "user:alice",
        inferadb.LookupPermission("can_view"),
        inferadb.LookupResourceType("document"),
    )
    
    // Who can edit this document?
    subjects, err := vault.Subjects().WithPermission(ctx, "can_edit",
        inferadb.LookupResource("document:readme"),
    )
    

    Testing

    MockClient (Fastest)

    Stub specific check results. Use defer client.AssertExpectations(t) to verify all stubs were hit.

    import inferadbtesting "github.com/inferadb/go/testing"
    
    client := inferadbtesting.NewMockClient().
        OnCheck("user:alice", "can_edit", "document:readme").Allow().
        OnCheck("user:bob", "can_edit", "document:readme").Deny().
        OnCheckAnySubject("can_view", "document:readme").Allow().
        DefaultDeny().
        Build()
    
    defer client.AssertExpectations(t)
    

    InMemoryClient (Full Policy Evaluation)

    import inferadbtesting "github.com/inferadb/go/testing"
    
    client, err := inferadbtesting.NewInMemoryClient(
        `type document {
            relation viewer
            relation editor
            relation can_view = viewer | editor
        }`,
        []inferadb.Relationship{
            {Resource: "document:readme", Relation: "editor", Subject: "user:alice"},
            {Resource: "document:readme", Relation: "viewer", Subject: "user:bob"},
        },
    )
    

    TestVault (Real Instance)

    Cleanup registered automatically via t.Cleanup.

    import inferadbtesting "github.com/inferadb/go/testing"
    
    vault := inferadbtesting.NewTestVault(t, org,
        inferadbtesting.WithSchema(schemaIPL),
    )
    

    Error Handling

    allowed, err := vault.Check(ctx, "user:alice", "can_edit", "document:readme")
    if err != nil {
        var inferaErr *inferadb.Error
        if errors.As(err, &inferaErr) {
            if inferaErr.IsRetriable() {
                time.Sleep(inferaErr.RetryAfter)
                // retry
            }
            log.Error("authorization error",
                "kind", inferaErr.Kind,
                "request_id", inferaErr.RequestID,
            )
        }
    }
    

    ErrorKind constants: ErrKindUnauthorized, ErrKindForbidden, ErrKindNotFound, ErrKindRateLimited, ErrKindSchemaViolation, ErrKindUnavailable, ErrKindTimeout, ErrKindInvalidArgument.

    Framework Integrations

    net/http Middleware (Go 1.22+)

    type ctxKey struct{}
    var UserIDKey = ctxKey{}
    
    func AuthorizationMiddleware(vault *inferadb.VaultClient) func(http.Handler) http.Handler {
        return func(next http.Handler) http.Handler {
            return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                userID, _ := r.Context().Value(UserIDKey).(string)
                docID := r.PathValue("id") // Go 1.22+ stdlib routing
    
                if err := vault.Require(r.Context(),
                    "user:"+userID, "can_view", "document:"+docID,
                ); err != nil {
                    http.Error(w, "Forbidden", http.StatusForbidden)
                    return
                }
    
                next.ServeHTTP(w, r)
            })
        }
    }
    

    gRPC Interceptor

    import "google.golang.org/grpc/metadata"
    
    func AuthUnaryInterceptor(vault *inferadb.VaultClient) grpc.UnaryServerInterceptor {
        return func(ctx context.Context, req any, info *grpc.UnaryServerInfo,
            handler grpc.UnaryHandler) (any, error) {
    
            md, ok := metadata.FromIncomingContext(ctx)
            if !ok {
                return nil, status.Errorf(codes.Unauthenticated, "missing metadata")
            }
            values := md.Get("x-user-id")
            if len(values) == 0 {
                return nil, status.Errorf(codes.Unauthenticated, "missing user id")
            }
            userID := values[0]
    
            if err := vault.Require(ctx, "user:"+userID, "access", "rpc:"+info.FullMethod); err != nil {
                return nil, status.Errorf(codes.PermissionDenied, "access denied")
            }
    
            return handler(ctx, req)
        }
    }