Skip to main content

Quickstart

Get a Spine publisher and subscriber talking to each other locally. This is the part of PreciCore that's actually implemented and runnable today — it doesn't cover Purifier, Kinematics Engine, or CrackHead as a connected pipeline, since those pieces aren't wired together as running nodes yet (see Architecture for what's real vs. planned).

Prerequisites

DependencyNotes
Go 1.24+For spine-go
Zig 0.16Only needed if you're building spined from source

spined is optional for everything in this guide — Spine falls back to "local-only mode" automatically if it can't connect to the daemon. You'll only need it if you want a central place to see what's registered.

1 — (Optional) Start spined

cd spined
zig build run

This listens on a Unix domain socket at /tmp/spine/spined. If you skip this step, every spine.CreateNode(...) call below still works — it just logs a warning and proceeds without registering anywhere.

2 — Install spine-go

go get github.com/poisnoir/spine-go

3 — Publisher

package main

import (
"context"
"log/slog"
"os"
"time"

"github.com/poisnoir/spine-go"
)

func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
ctx := context.Background()

// "common" is the only namespace spined currently supports.
node, _ := spine.CreateNode("common", "publisher_sample", ctx, logger)

pub, _ := spine.NewPublisher[uint32](node, "temperature")

var temp uint32 = 0
for {
pub.Publish(temp)
temp++
time.Sleep(15 * time.Millisecond)
}
}
go run main.go

4 — Subscriber

In a second terminal:

package main

import (
"context"
"fmt"
"log/slog"
"os"

"github.com/poisnoir/spine-go"
)

func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
ctx := context.Background()

node, _ := spine.CreateNode("common", "subscriber_sample", ctx, logger)
sub, _ := spine.NewSubscriber[uint32](node, "temperature")

for {
fmt.Println(sub.Get())
}
}
go run main.go

You should see the subscriber print an increasing number. There's no discovery step to wait on — the subscriber dials the publisher's Unix socket at a well-known path (/tmp/spine/publisher/common/temperature) the moment it's created, retrying with backoff until the publisher exists.

What this doesn't cover

  • Services/RPC — see the Examples page for a NewService/NewServiceCaller pair.
  • spine-zig — this walkthrough is Go-only; see spine-zig for the same steps in Zig.
  • Cross-machine setups — not possible yet; see Spine Overview.
  • Namespaces other than common — rejected by spined today; see Troubleshooting.
  • Purifier — no code exists yet; see Purifier. Kinematics Engine and CrackHead, by contrast, are running end-to-end today against a live input source and a live simulator — see Kinematics Engine.

Next steps

  • Architecture — what's built vs. designed across the whole system
  • Spine Overview — the full picture of what Spine does and doesn't do today
  • MAD — the serialization format underneath every message