Skip to main content

Go SDK

Go client for the beachcomber shell-state daemon. Communicates over a Unix domain socket using newline-delimited JSON. Idiomatic Go with standard error returns and no external dependencies.

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() booltrue when OK && Data != nil
IsMiss() booltrue 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() []byteFull 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. $XDG_RUNTIME_DIR/beachcomber/sock (if XDG_RUNTIME_DIR is set)
  3. /tmp/beachcomber-<uid>/sock

This mirrors the daemon's bind path; $TMPDIR is not consulted.