Skip to main content

Go SDK

Go client for the beachcomber shell-state daemon. A binding over the shared libbeachcomber C ABI, using purego (vendored) for pure-Go dynamic library loading — no cgo, no wire-protocol code in this SDK. Idiomatic Go with standard error returns.

Requirements​

  • Go 1.21+
  • A running comb daemon

Installation​

go get github.com/NavistAu/beachcomber/sdks/go

Quick start​

package main

import (
"fmt"
"log"

beachcomber "github.com/NavistAu/beachcomber/sdks/go"
)

func main() {
c, err := beachcomber.NewClient()
if err != nil {
log.Fatal(err)
}

result, err := c.Get("git.branch", "/path/to/repo")
if err != nil {
log.Fatal(err)
}
if result.IsHit() {
branch, _ := result.GetString("")
fmt.Printf("branch: %s (age %dms)\n", branch, result.AgeMs)
}
}

Sessions​

Open a persistent connection for multiple queries to avoid per-call socket overhead:

sess, err := c.Session()
if err != nil {
log.Fatal(err)
}
defer sess.Close()

sess.SetContext("/path/to/repo")

branchResult, err := sess.Get("git.branch", "")
dirtyResult, err := sess.Get("git.dirty", "")

API reference​

Client​

c, err := beachcomber.NewClient()                       // auto-discover socket
c := beachcomber.NewClientWithPath("/custom/path/sock") // explicit socket path

c.Get(key, path) (*Result, error)​

Read a cached value. path may be "" to omit it.

result, err := c.Get("git.branch", "/path/to/repo")
result, err := c.Get("hostname", "") // global provider
result, err := c.Get("git", "/path/to/repo") // full provider object

c.Refresh(key, path) error​

Force the daemon to recompute a provider.

c.Status() ([]CacheRow, error)​

Return cache rows as typed structs (one per warm cache entry).

c.Hello() (*HelloInfo, error)​

Handshake — returns daemon version, protocol version, and build info.

c.Put(key string, data interface{}, ttl, path string) error​

Write a value into the cache as a virtual provider.

c.Introspect(subject IntrospectSubject, durationSecs uint64) (*IntrospectResponse, error)​

Inspect a daemon subsystem ("daemon", "providers", "config", "cache", "lifecycle", "watches", "timers", "demand", "procs").

c.Watch(key, path string) (*WatchStream, error)​

Subscribe to live cache updates. Returns a WatchStream; call NextEvent() in a loop and Close() when done.

c.Session() (*Session, error)​

Open a persistent connection.

Session​

MethodDescription
Get(key, path string) (*Result, error)Read a cached value
Refresh(key, path string) errorForce recomputation
SetContext(path string) errorSet default path for subsequent queries
GetWithFlags(key, path string, force, wait bool) (*Result, error)Read with force/wait flags
Hello() (*HelloInfo, error)Handshake — returns daemon and protocol versions
Put(key string, data interface{}, ttl, path string) errorWrite a virtual provider entry
Introspect(subject IntrospectSubject, durationSecs uint64) (*IntrospectResponse, error)Inspect a daemon subsystem
Status() ([]CacheRow, error)Return typed cache rows
Close() errorClose the connection

Result​

Field / methodTypeDescription
OKbooltrue when the daemon returned success
Datainterface{}Decoded payload (string, map, etc.)
AgeMsuint64Cache age in milliseconds
StaleboolWhether the value is stale
IsHit() bool—true when OK && Data != nil
IsMiss() bool—true when OK && Data == nil
GetString(field string) (string, bool)—Extract a string (pass "" for scalar results)
GetInt(field string) (int64, bool)—Extract an integer
GetFloat(field string) (float64, bool)—Extract a float
GetBool(field string) (bool, bool)—Extract a boolean
RawJSON() []byte—Full raw JSON response

For full provider queries, pass a field name to the typed accessors:

result, _ := c.Get("git", "/repo")
branch, _ := result.GetString("branch") // "main"
dirty, _ := result.GetBool("dirty") // false

Errors​

ErrorWhen returned
ErrDaemonNotRunningUnix socket cannot be reached
*ServerErrorDaemon responded with ok: false
*ProtocolErrorResponse could not be parsed

Wire protocol reference​

All operations follow the wire contract defined in docs/protocol-spec.md.

Socket discovery​

The SDK discovers the daemon socket at:

  1. $BEACHCOMBER_SOCKET (if set and non-empty)
  2. /tmp/beachcomber-<uid>/sock

This mirrors the daemon's bind path; no session-scoped environment ($TMPDIR, $XDG_RUNTIME_DIR) is consulted, so every shell of one user reaches the same daemon.