GO NATIVE.OPEN SOURCE. MIT LICENSE.
An event bus with a decision layer

Fast events.
Intelligent decisions.

Route with rules. Remember with cache.
Let Jev handle the ambiguous.
Keep execution in Go.

01 / DECIDE02 / DISPATCH
THE ROUTING ENGINEONE PIPELINE. THREE WAYS THROUGH.
goeventbus / routing.pipeline
EXPLORE A PATH
INCOMING STATE
event.json

“The property at 1 Main St
was sold for $500,000.”

unstructured input
01 / DETERMINISTIC

Rules

no match
02 / REMEMBERED

Cache

cache miss
03 / INTELLIGENT

Jev

decision selected
GOEVENTBUS
EventStorering buffer → handlers
SELECTED EVENTproperty.sold
No matching rule. No cached decision. Jev selects from your candidates.
ILLUSTRATIVE DEMO

Your rules run first. Your cache comes next.
Only the unresolved reaches the model.

DECISION LAYER ≠ EXECUTION LAYER
SMALL CORE. CONNECTED SYSTEMS.
Go 1.23+Redis StreamsRabbitMQ✳ Jev optional
01 / THE PRINCIPLESEPARATE THE CHOICE FROM THE WORK

Inference stays
out of the
hot path.

Choose the event before you enqueue it. Then let a bounded MPMC ring buffer and Go handlers do what they do best.

Already know the event type? Call Subscribe directly. The decision layer is entirely optional.

Explore the architecture
execution.engine
SELECTED EVENTstore.Subscribe(ctx, event)
BOUNDED MPMC RINGFIFO
store.Publish()dispatch pending events
Fan-out
Ordered
Batch
YOUR HANDLERS. YOUR EXECUTION.
DETERMINISM WHERE YOU WANT IT.INTELLIGENCE WHERE YOU NEED IT.
02 / BUILT INTO THE BUSFROM ONE HANDLER TO MANY

The plumbing,
already in place.

From local events to concurrent pipelines.
The execution layer stays under your control.

[ 01 ]

One event. Many handlers.

Fan out to independent handlers. One handler’s error doesn’t keep the others from doing their work.

dispatcher.Register(...)
[ 02 ]

Order where it matters.

Preserve FIFO delivery per ordering key in async mode, while unrelated entities run concurrently.

store.RegisterOrdered(...)
[ 03 ]

Work in batches.

Collect pending events into chunks during a publish cycle. A natural fit for bulk writes and metrics.

store.RegisterBatch(...)
[ 04 ]

Pressure, with a policy.

Choose what happens when the buffer fills: drop the oldest event, wait for capacity, or return an error.

DropOldest · Block · ReturnError
[ 05 ]

Observe. Recover. Replay.

Add middleware and lifecycle hooks. Capture handler failures and recovered panics in a replayable dead-letter queue.

store.DLQ.Replay(ctx, store)
[ 06 ]

Beyond one process.

Bring events in with Redis Streams or RabbitMQ providers. Keep the local EventStore as your execution engine.

store.Consume(ctx, provider)
03 / CORE DISPATCH BENCHMARKS

Built for
the hot path.

Low latency. Predictable execution.
A Go event bus for real systems.

BenchmarkPublish

3.91 ns/op

Publish

Intelligent routing stays outside
the core dispatch benchmark.

go test -bench . -benchtime=3s APPLE M-SERIES
Selected core dispatch benchmarks from the README, run on Apple M-series. Latency in nanoseconds per operation; lower is better. Bars share a 0–500 ns/op scale.
BENCHMARK RESULTSLATENCY (ns/op)
BenchmarkSubscribeParallel38.42 ns/op
BenchmarkSubscribe40.37 ns/op
BenchmarkFastHTTPSync163.8 ns/op
EVENT STORE (SYNC + ASYNC)
BenchmarkEventStore_Sync428.5 ns/op
BenchmarkEventStore_Async436.5 ns/op

LOWER IS BETTER BARS: 0–500 ns/op

All 11 README benchmarks
Complete benchmark results reported in the README
BenchmarkIterationsns/op
BenchmarkSubscribe27,080,37640.37
BenchmarkSubscribeParallel26,418,99938.42
BenchmarkPublish295,661,4643.91
BenchmarkPublishAfterPrefill252,943,5264.59
BenchmarkSubscribeLargePayload1,613,017771.5
BenchmarkPublishLargePayload296,434,2253.91
BenchmarkEventStore_Async2,816,988436.5
BenchmarkEventStore_Sync2,638,519428.5
BenchmarkFastHTTPSync6,275,112163.8
BenchmarkFastHTTPAsync1,954,884662.0
BenchmarkFastHTTPParallel4,489,274262.3
View benchmarks in the README

Benchmarks run on Apple M-series
go test -bench . -benchtime=3s

Rules, cache, and Jev have different latency profiles and are intentionally not part of these core dispatch benchmarks.

04 / LESS SETUP. MORE GO.GO 1.23+

Start simple.
Add intelligence.

Register a handler. Enqueue an event. Publish.
Bring in Jev when your input needs a decision.

go get github.com/Protocol-Lattice/GoEventBus
01

Make it yours.
Use string or typed projections for local dispatch.

02

Keep the choice bounded.
Define the candidates. Jev selects between them.

03

Bring your own control.
Plug in your selectors and cache implementations.

Browse all runnable examples
main.go
package main

import (
    "context"
    "fmt"
    "log"

    bus "github.com/Protocol-Lattice/GoEventBus"
)

func main() {
    ctx := context.Background()
    dispatcher := bus.Dispatcher{}

    dispatcher.Register("order.created", func(
        _ context.Context, ev bus.Event,
    ) (bus.Result, error) {
        fmt.Println("Received:", ev.Data)
        return bus.Result{}, nil
    })

    store := bus.NewEventStore(
        &dispatcher, 1024, bus.Block,
    )
    defer store.Close(ctx)

    if err := store.Subscribe(ctx, bus.Event{
        ID:         "evt-1",
        Projection: "order.created",
        Data:       "Hello, GoEventBus",
    }); err != nil {
        log.Fatal(err)
    }

    store.Publish()
}
05 / GOOD QUESTIONS

A few things
worth knowing.

Do I need Jev to use GoEventBus?

No. Use Subscribe and Publish for a conventional event bus. You can add deterministic rules, a decision cache, or a Jev fallback whenever your workload needs them.

When does the model actually get called?

With RuleCacheSelector, rules are evaluated first. If none match, the cache is checked. The fallback selector runs only when neither resolves the decision. Successful fallback decisions can then be cached.

How does the Jev integration work?

JevSelector calls the OpenRouter Decisions API using the Go standard library. Set OPENROUTER_API_KEY in your server environment and supply a bounded list of event candidates. It returns the selected choice, confidence, and probabilities. Read the integration guide.

Can I check confidence before dispatch?

Yes. Call SelectEvent, check the returned decision, and then enqueue it with Subscribe. The convenience method DecideAndSubscribe selects and enqueues immediately; it doesn’t apply a confidence threshold for you.

Can events travel between services?

Yes. Redis Streams and RabbitMQ providers move events between processes, while the local EventStore runs your handlers. Remote providers need string projections for stable broker routing names.

YOUR NEXT EVENT STARTS HERE.[ GO ]

Good things
happen in Go.

Keep the fast path fast.
Give the uncertain a way forward.

Build with GoEventBus