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 |
Client Credentials (Recommended)
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)
}
}