- Go 96.1%
- Shell 3.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Generated stubs (buf + protoc-gen-go/-go-grpc, managed go_package) for the vendored spada.v1 surface with pinned provenance, wrapped: Connect with TLS+token, idempotent writes with Flush as the visible durability barrier and its own longer deadline, a byte-and-count-bounded Ingest stream that flushes last, Search with the receipt as a first-class type (EXACT is a proof; UnitsDropped surfaced), and S-150 typed error details decoded through the STOCK grpc-go status machinery — which doubles as live proof the server's RpcStatus mirror is byte-identical to google.rpc.Status. Classify + Policy give the table-driven retry answer, with HomeReleased as the one retryable precondition and SealerFailed as operator-action. Verified against a live server: quickstart (write, dedup replay, EXACT receipt, passages) and typederrors (LimitExceeded naming its setting key as a field) both run clean. go build + go vet clean. Claude-Session: https://claude.ai/code/session_01JrTbCU5Da5hJ19XUrYYxq9 |
||
| examples | ||
| gen/spada/v1 | ||
| proto | ||
| buf.gen.yaml | ||
| client.go | ||
| errors.go | ||
| generate.sh | ||
| go.mod | ||
| go.sum | ||
| query.go | ||
| README.md | ||
| sync-protos.sh | ||
| write.go | ||
spada-sdk-go — Go client for the spada search engine
The official Go SDK for spada's
public spada.v1 gRPC surface: an idiomatic wrapper over generated stubs,
with the semantics the wire cannot express encoded in the API — durability,
coverage receipts, and table-driven retry policy.
go get git.teixos.net/yannick/spada-sdk-go
Quickstart
import (
"context"
spada "git.teixos.net/yannick/spada-sdk-go"
)
client, err := spada.Connect("127.0.0.1:50051", spada.Options{})
if err != nil { /* … */ }
defer client.Close()
ctx := context.Background()
// Idempotent, flushed write: the ack is a durability barrier.
out, err := client.Write(ctx, "articles", []spada.Doc{
{DocID: "munich", Text: "# Munich\n\nBayern beats the rain.",
Fields: map[string]string{"lang": "en"}},
}, spada.WriteOptions{MutationID: "ingest-0001", Flush: true})
// Query — and READ THE RECEIPT: EXACT is a proof, not a mood.
answer, err := client.Search(ctx, "articles", spada.Query{
Terms: []string{"bayern"}, K: 10,
})
fmt.Println("receipt:", answer.Receipt()) // e.g. "EXACT"
for _, hit := range answer.Hits {
fmt.Printf("%s %.3f\n", hit.DocId, hit.Score)
}
Runnable versions: examples/quickstart and
examples/typederrors — start a dev server with
spada serve --data-dir /tmp/demo --listen 127.0.0.1:50051, then
go run ./examples/quickstart.
The three contracts
1. Durability: Flush is the barrier, SealTriggered is not
A non-flush write's ack means accepted and query-visible. The durable
checkpoint completes in the background (spada seals through a pipelined
worker pool — the response's flag means a seal was triggered). When "this
call returning" must mean "on disk", set WriteOptions.Flush. A flush
drains the server's seal pool and can block for seconds; the SDK gives
flushing calls the longer FlushTimeout (default 120 s) automatically.
Client.Ingest streams a corpus over one WriteStream in batches bounded
by both count and bytes, flushing on the last message — its report means
durable. Note WriteStream's contract: strictly serial, one response, no
cross-message transaction — a mid-stream failure leaves prior messages
applied.
2. Receipts: EXACT is a proof, everything else is a disclosure
r := answer.Receipt()
if !r.IsExact() {
log.Printf("not certified: %s", r) // APPROXIMATE [stages] / UNPROVEN (why)
}
if r.UnitsDropped() > 0 {
// A referenced segment was unreadable: the answer is PARTIAL.
}
Watch for the stage "query-embed-skipped": a hybrid query silently
degraded to lexical-only because no embedding model was available. If your
application must never act on an uncertified answer, set
Query.ExactnessRequired — the server refuses (typed) instead of degrading.
3. Errors: the detail type is the retry policy
Since spada S-150, structured refusals ride the standard
grpc-status-details-bin trailer as a genuine google.rpc.Status — this
SDK decodes it through the stock google.golang.org/grpc/status machinery,
no spada-specific transport code. Never parse error messages:
if _, err := client.Search(ctx, ns, q); err != nil {
if e, ok := spada.Classify(err); ok {
switch e.Policy() {
case spada.RetryWithBackoff: // transient: back off, retry here
case spada.RetryElsewhere: // re-resolve routing, retry there
case spada.OperatorAction: // page someone; do NOT spin
case spada.RetryNever: // fix the request
}
if l, ok := e.Detail.(*pb.LimitExceeded); ok {
log.Printf("raise %s (limit %d, asked %d)", l.SettingKey, l.Limit, l.Actual)
}
}
}
The two details that matter most: HomeReleased — the one
FAILED_PRECONDITION that IS retryable (the namespace moved home;
re-resolve and retry there) — and SealerFailed — the server fail-stopped
writes; retrying can never succeed and even backing off is wrong; surface
it to an operator (reads keep working). A status with no detail is normal
(the code alone yields the safe policy), and an unknown detail type from a
newer server degrades to that same safe reading: spada.v1 is
additive-only, machine-enforced upstream.
Idempotent writes
Set WriteOptions.MutationID on any write you might retry. A retry with
the same id returns the original result verbatim (Deduplicated) and
writes nothing. The horizon is finite (write.dedup_history server-side):
past it a retry is refused typed, never silently re-executed — bound your
retry window well inside it. And cite DocVersions for identity, never
DocID (a mutable attribute over a succession of versions).
Beyond the wrapper
client.Raw() exposes the full generated spada.v1 client (Export,
QueryStream, MultiQuery, BatchQuery, schema, recall evaluation), with
client.WithAuth(ctx) to carry the bearer token. Two rules when you drop
down: always read a QueryStream to its terminal receipt frame (stopping
early leaves you holding an unverified result), and never set the
x-spada-forwarded metadata key (reserved; setting it suppresses routing).
TLS and auth
ca, _ := os.ReadFile("ca.pem")
client, err := spada.Connect("spada.example:50051", spada.Options{
TLSCAPEM: ca,
Token: os.Getenv("SPADA_TOKEN"),
})
Client.Health returns DevModeNoAuth — if true, the server runs without
authorization and must not be exposed to a network. Surface it.
Protos, codegen, compatibility
The spada.v1 protos are vendored under proto/ with their
source commit pinned in proto/PROVENANCE.md.
Regenerate with ./generate.sh (buf + protoc-gen-go/protoc-gen-go-grpc);
refresh from a spada checkout with ./sync-protos.sh <path>. The surface
is frozen additive-only and machine-enforced upstream. Only the public
package is vendored — spada.internal.v1 and spada.model.v1 carry no
compatibility promise and a client must never speak them.
License
Apache-2.0.