Typed runtime · Go 1.23+

The runtime for composable work

A lightweight, typed Go execution library for building reusable actions, resilient pipelines, and deterministic DAG workflows. Independent of concrete transports, persistence implementations, AI vendor SDKs, and identity providers.

Kernel surface

Hot path zero-alloc
Types generic, inferred
Concurrency safe by design
Streaming iter.Seq2 · O(1) memory
License Apache 2.0
Core Principle

Everything composes around typed actions

An action owns its request, response, execution, and lifecycle. The Kernel owns lifecycle, concurrency safety, and execution semantics. Extensions implement backend drivers and explicit middleware layers; they do not fork the builder or alter execution semantics.

action.New · builder · typed Do user.lookup.go
type UserRequest struct { ID string `json:"id"` }
type User       struct { ID, Name string }

lookupUser := action.New("user.lookup",
    func(ctx context.Context, req UserRequest) (User, error) {
        return User{ID: req.ID, Name: "Alice"}, nil
    }).
    Timeout(2 * time.Second).
    Retry(2, action.ExponentialBackoff(25*time.Millisecond, 2*time.Second)).
    Build()

user, err := lookupUser.Do(ctx, UserRequest{ID: "usr-42"})

Request and response types are inferred from the handler signature without reflection on the hot path. The builder chain compiles middleware at initialization time, so per-call cost is a constant: iterator closure, hook wrappers, panic recovery.

Core packages

A focused kernel surface

Seven packages, each with one job. No framework-shaped dependency maze — the Kernel does not bundle concrete database drivers, transport libraries, or vendor SDKs.

Package
Purpose
action
Typed builders, hooks, middleware, composition, retries, timeouts, concurrency limits, caching, idempotency, deduplication, coalescing, streaming, sagas, and resilience policies.
observe
Vendor-neutral lifecycle events, bounded memory history, metrics aggregation, Prometheus, JSONL, and slog sinks.
ai/dag
Typed directed acyclic graphs with deterministic compilation, layered parallel execution, nested graphs, state snapshots, Mermaid rendering, and durable checkpoint/resume (HIL) semantics.
xctx
Typed request context values, request scopes, correlation data, trace data, roles, permissions, features, and scope pooling.
xerr
Categorized errors for validation, authorization, conflicts, not-found cases, transient failures, internal failures, and panic recovery.
stream
Domain-neutral, typed lazy stream operators: Map, Filter, Take, Batch, Reduce, FlatMap, WithContext. No per-item allocation.
ringbuf
Cache-line-aligned lock-free MPMC ring buffer for bounded telemetry, hot-path metrics, and lifecycle events.
Reflection-free bridge

Dynamic dispatch without losing types

The Kernel supports type-erased and dynamic execution boundaries (AI tool-calling agents, plugin registries, generic HTTP routers) without weakening typed internal handlers.

DoAny · InvokeAny · Coerce · Dynamic dynamic.go
act := action.New("user.lookup", lookupHandler).Build()

// 1. Direct type-erased call: 0 heap allocations on matching Req or *Req pointer
result, err := act.DoAny(ctx, UserRequest{ID: "usr-42"})

// 2. Universal Invoker: automatically decodes and coerces dynamic payloads
result, err = action.InvokeAny(ctx, act, map[string]any{"id": "usr-42"})

// 3. Coerce: converts dynamic structures (JSON, maps, strings) into target types
req, err := action.Coerce[UserRequest](untypedInput)

// 4. Dynamic lifting: lifts AnyAction into *Builder[any, any] for composition
dynamicAct := action.Dynamic(act).Build()
Composition primitives

Pipelines without adapter soup

Seven composition primitives connect typed actions without inventing intermediate adapter structs. Each one compiles to a single *BuiltAction that is safe for concurrent use.

01

Pipe · PipeWith

Connect two actions sequentially (A → B). PipeWith inserts an inline transform function so you do not write a wrapper struct just to bridge two shapes.

A → B
02

Chain

Execute same-typed actions sequentially, piping output of one into the input of the next. Useful for layered validators and reducers.

T → T → T
03

Branch · BranchAny

Route execution conditionally using a router function. Branch keeps types; BranchAny works on type-erased registries.

router → A | B
04

FirstSuccess

Run a fallback chain. The first non-error result wins; remaining actions are cancelled. Inputs must be idempotent.

A || B || C
05

Parallel

Execute typed actions concurrently over the same request (scatter-gather). Order is preserved in the returned slice.

scatter / gather
06

ParallelNamed · ParallelMap

Run heterogeneous actions concurrently and collect results into a map[string]any keyed by branch name.

named map
07

FanOut · Race

FanOut executes a slice of requests concurrently with bounded maxConcurrency. Race returns the first successful result and cancels losing attempts.

bounded · first-wins
PipeWith · ParallelNamed composition.go
// Pipeline with zero-allocation inline field transform
pipeline := action.PipeWith("user.process",
    fetchUserAct,
    func(ctx context.Context, u User) (string, error) { return u.Email, nil },
    sendEmailAct,
).Build()

// Heterogeneous concurrent execution
reviewer := action.ParallelNamed[string]("review.suite", map[string]action.AnyAction{
    "security": checkSecurityAct,
    "linter":   runLinterAct,
}).Build()

results, err := reviewer.Do(ctx, "package main...")
Resilience, concurrency, traffic shields

Production hardening

Retries, backoff, circuit breaking, dedup, coalesce, rate limiting, load shedding, distributed locks, sagas, and state machines. Each is a one-line builder method; each is independently testable.

R1

Retries · Predicates

Retry(n, backoff) retries only transient errors via xerr.IsTransient. RetryIf takes a custom predicate; RetryAll retries on any non-nil error for idempotent batch jobs.

Retry · RetryIf · RetryAll
R2

Backoff strategies

ExponentialBackoff(base, max) doubles per attempt. ExponentialJitter adds ±30% random jitter to mitigate thundering-herd spikes. Plus LinearBackoff(step) and ConstantBackoff(d).

Exp · Jitter · Linear · Const
R3

Adaptive circuit breaker

Adaptive(AdaptiveConfig{}) applies a breaker with dynamic timeout scaling and half-open state recovery. SmartResilience / InferredResilient drive retry + breaker automatically from xerr.Kind, fast-failing 4xx errors without consuming retries.

Adaptive · SmartResilience
R4

Dedup · Coalesce

Dedup(keyFn) singleflight collapses concurrent identical calls. Coalesce(coalescer, keyFn) shares in-flight results; a canceled waiter may exit without terminating the background execution for other callers.

singleflight
R5

Concurrency · RateLimit

ConcurrencyLimit(n) enforces an in-process ceiling with atomic counters. RateLimit, RateLimitWithKey, and RateLimitDistributed provide token-bucket admission control.

token bucket
R6

AdaptiveLoadShedding

Evaluates CPU percentage and goroutine count against priority tiers (PriorityCritical, PriorityNormal, PriorityLow), dropping non-essential traffic when thresholds are exceeded.

priority tiers
R7

Fenced distributed locks

ExclusiveFenced(mutex, ttl, keyFn) acquires a LockLease with a monotonically increasing fence counter. An internal ticker renews the lease at ttl / 3. Lease loss cancels the action context immediately to abort in-flight work.

split-brain safe
R8

Sagas · State machines

NewSaga[Req, Res] coordinates distributed transactions with compensating Undo functions in reverse LIFO order. NewStateMachine guards entity lifecycle transitions, rejecting disallowed ones with xerr.KindConflict.

compensation · lifecycle
resilience chaining · all on one builder resilience.go
act := action.New("fetch.data", coreHandler).
    Timeout(2 * time.Second).
    Retry(3, action.ExponentialJitter(100*time.Millisecond, 2*time.Second)).
    Idempotent().
    Cache(30*time.Second, keyFn, cacheStore).
    Dedup(keyFn).
    ConcurrencyLimit(10).
    RateLimit(100, 50).
    ExclusiveFenced(distLock, 10*time.Second, keyFn).
    Build()

Fencing contract notice: fencing is only effective if the downstream database or storage operation validates the token (e.g. UPDATE ... WHERE fence >= @fence). The Kernel coordinates lease lifecycle and cancellation; it cannot enforce fencing on storage engines that do not verify the token.

Multi-layer cache · idempotency contracts

Coordination without stampedes

Cache, idempotency, and Once give you safe coordination under load. Every miss is singleflight-protected; every back-fill is automatic.

C1

Multi-layer read-through cache

Cache(ttl, keyFn, layers...) coordinates L1 (local memory) through LN (distributed). Cache hits return without invoking the handler. Slower-layer hits back-fill faster layers. With no layers passed, an internal thread-safe cache with a background janitor is created.

L1 → LN · singleflight
C2

Once · memoize forever

Once() memoizes the result or error of the first execution indefinitely. Ideal for static configuration loaders that must run exactly once per process.

memoize
C3

Idempotency contracts

.Idempotent() / .IdempotentWithConfig(cfg) register metadata. IdempotencyCoordinator defines atomic claim semantics — Claim, Complete, Release — with leased in-progress locks and request hash validation to detect duplicate payload conflicts.

Claim · Complete · Release
C4

MemoryIdempotencyStore

In-memory reference implementation of the coordinator. Swap in Redis, Postgres, or any durable store by implementing the same three-method contract.

reference impl
Typed streams · O(1) memory

Make concurrency legible

Streams are lazy, cancellable, and composable. The consumer decides when work is pulled; the action owns lifecycle hooks and terminal behavior. Per-item cost is zero allocations — only a constant per-call overhead (iterator closure, hook wrappers, panic recovery).

action.NewStream · iter.Seq2 · ctx.Err respected telemetry.stream.go
stream := action.NewStream("telemetry.stream",
    func(ctx context.Context, req Query) (iter.Seq2[Record, error], error) {
        return func(yield func(Record, error) bool) {
            for i := 0; i < req.Limit; i++ {
                if ctx.Err() != nil {
                    yield(Record{}, ctx.Err())
                    return
                }
                if !yield(Record{ID: i}, nil) {
                    return // Consumer stopped early
                }
            }
        }, nil
    })

seq, err := stream.Do(ctx, Query{Limit: 100})
for record, err := range seq {
    // Zero heap allocation streaming
}

CollectStream materializes an iterator into an in-memory slice when streaming is not required. The stream package adds composable operators — Map, Filter, Take, Batch, Reduce, FlatMap, WithContext — none of which allocate per item.

Deterministic DAG workflows · ai/dag

Compile graphs, not wishful thinking

The ai/dag package compiles directed acyclic graphs into deterministic, concurrent execution layers. Cycle detection via Kahn's algorithm. Namespaced outputs. Pooled state. Sub-graph nesting. Mermaid export. Durable checkpoint/resume for human-in-the-loop.

dag.New · AddNode · AddEdge · Compile · Execute order.workflow.go
graph, err := dag.New("order.workflow").
    AddNode("validate", "val_out", validateAction).
    AddNode("reserve",  "res_out", reserveAction).
    AddNode("charge",   "chg_out", chargeAction).
    AddEdge("validate", "reserve").
    AddEdge("validate", "charge").
    Compile()

initialState := dag.AcquireState()
defer initialState.Release()

finalState, err := graph.Execute(ctx, initialState)
defer finalState.Release()

res, err := dag.GetNodeOutput[ReserveResult](finalState, "reserve")
D1

Cycle detection

Compiles layers using Kahn's algorithm. Circular dependencies return xerr.KindConflict at Compile() time — never at runtime.

topological sort
D2

Namespaced outputs

Node outputs are stored under tasks.<node_id>.output to prevent key collisions during parallel execution. No accidental overwrite between concurrent branches.

tasks.<id>.output
D3

Pooled state

State instances utilize sync.Pool. Read operations during layer execution are zero-lock. Callers must call state.Release() when finished.

sync.Pool · zero-lock
D4

Sub-graph nesting

A compiled DAG can be converted into an action via .AsAction() and embedded directly as a node in a parent DAG. Hierarchical composition without copy-paste.

.AsAction()
D5

Mermaid export

graph.ToMermaid() exports deterministic, formatted Mermaid flowchart diagrams. Wire it into your CI to keep diagrams in lock-step with the executed graph.

graph → diagram
D6

HIL · ErrSuspended

Workflows requiring asynchronous human intervention (approvals, manual reviews, external callbacks) halt via dag.ErrSuspended. Partial state is preserved. Resume later by re-executing with a populated State; completed nodes are skipped automatically.

checkpoint · resume

When a node returns dag.ErrSuspended, Execute returns (partialState, dag.ErrSuspended) without purging the state from memory. When re-executing with an existing state, any node whose output key is already present in State is skipped — ensuring non-idempotent upstream actions do not re-run.

Context guards · categorized errors

Security at the boundary

Context guards inspect metadata attached to context.Context. Authentication (decoding JWTs, session lookups, certificate validation) is handled at transport boundaries; the Kernel enforces authorization, quotas, immutability, and audit at the action edge.

S1

RBAC guards

RequireAuth(), RequireTenant(), RequireRole(role), RequireAnyRole(roles...), RequirePermission(perm) (with wildcard *), RequireFeature(flags...), RequireAnyFeature(flags...).

RBAC · permissions
S2

Quotas & immutability

RequireCreationLimit(resource, checkFn) checks resource quotas on entity creation (ID == 0). ImmutableWhen(guardFn, reason) aborts execution if an entity is in a locked or terminal state.

limits · locked
S3

Compliance & audit

TrackPIIAccess(tracker, purpose) records personal data access reasons for regulatory compliance. Audited(logger, category, detailsFn) records audit events on successful execution. Transactional(runner) wraps execution inside an application-provided TxRunner.

PII · audit · tx
S4

Categorized errors

xerr categories: BadRequest, Unauthorized, Forbidden, NotFound, Conflict, Validation, TooManyRequests, Timeout, Unavailable, Internal, CircuitBreaker. IsTransient(err) drives retry decisions; Public(requestID) returns sanitized API responses.

xerr.Kind*
S5

Request scopes · async cloning

xctx.NewScope(parent) returns a pooled *RequestScope. Typed accessors: WithUserID, WithTenantID, WithRoles, WithPermissions, WithFeatures, WithTraceID. CloneForAsync(ctx) clones metadata into a detached context — preserving tracing while decoupling from the parent cancellation tree.

pooled · detached
S6

Sprint · safe traces

xerr.Sprint(err) renders error trees with sanitized user stack traces in development (ENV=dev) and structured one-line outputs in production. Never leaks internal causes or stack frames to end users.

dev vs prod
xerr categories · IsTransient · Public errors.go
// Categories: BadRequest, Unauthorized, Forbidden, NotFound, Conflict,
// Validation, TooManyRequests, Timeout, Unavailable, Internal, CircuitBreaker.
return xerr.NotFound("user not found")
return xerr.Unavailable("database connection dropped", originalErr)

if xerr.IsTransient(err) {
    // safe to retry — the kernel's Retry policy already checks this
}

appErr := xerr.From(err)
// Public returns a sanitized ErrorResponse suitable for API responses
// without leaking internal causes or stack traces.
writeJSON(w, appErr.Public(requestID))
Vendor-neutral telemetry · observe

Observe without rebuilding handlers

The optional observe package emits vendor-neutral lifecycle events. Sinks are concurrency-safe and receive structured observe.Event records with action Duration and complete Request/Response payload references for latency and token/cost attribution. Nil hooks and no-op sinks evaluate with zero heap allocations on the hot path.

fanoutSink · Slog · Metrics · Prometheus · Memory · JSONL observe.go
type fanoutSink struct{ sinks []observe.Sink }
func (f *fanoutSink) Emit(ctx context.Context, e observe.Event) {
    for _, s := range f.sinks { s.Emit(ctx, e) }
}

sink := &fanoutSink{sinks: []observe.Sink{
    observe.NewSlogSink(slog.Default()),
    observe.NewMetricsSink(),
    observe.NewPrometheusSink(),
    observe.NewMemorySink(100),
    observe.NewJSONLSink(os.Stdout, 4096),
}}

act := action.New("user.find", findUser).
    AnyHook(observe.Hook(sink)).
    Build()
Event Kind
Emitted When
Relevant Fields
executed
Action completes successfully
Request, Response, Duration
error
Action fails with an error
Error, Duration
retry
Retry middleware initiates another attempt
Attempt, Error, Duration
cache_hit
A cache layer serves the response
Request, Response, Duration
cache_miss
No cache layer contains the key
Request, Duration
canceled
Action observes caller context cancellation
Request, Duration
panic
Action recovers an unhandled panic
Recovered, Request, Duration
deduplicated
Concurrent duplicate waits on an existing flight
Request, Duration
coalesced
Concurrent caller receives a coalesced result
Request, Duration

Every event includes Time, Duration, Kind, Action, and available correlation identifiers (ExecutionID, TraceID, SpanID). Context cancellations emit canceled and are not duplicated as error events. Recovered panics emit a panic event followed by an error event containing the resulting xerr.KindInternal error.

Architectural boundaries

The Kernel ownsies execution. You own the rest.

The Kernel provides execution semantics and safety contracts. Applications supply the external integrations. This boundary is what keeps the Kernel small and your business logic yours.

Concern
Who owns it
Storage & state
Cache backends, distributed locks, idempotency stores, and database transactions are supplied via interfaces. The Kernel does not bundle concrete database drivers.
Telemetry ingestion
observe captures lifecycle events and execution duration locally. Forwarding events to OpenTelemetry collectors, Datadog, or external brokers belongs to application sinks.
HIL & workflows
The Kernel manages suspension (dag.ErrSuspended), partial state preservation, and memoized resumption. Applications supply the human interfaces (webhooks, email dispatch, approval dashboards) and durable persistence for state snapshots.
Security decisions
Context guards inspect metadata attached to context.Context. Authentication (decoding JWTs, session lookups, certificate validation) is handled at transport boundaries.
Resource ownership
Pooled objects (dag.State, xctx.RequestScope) must be explicitly released by the caller to prevent resource leaks.
Runnable examples

Twelve examples, each one go run-able

Complete, runnable examples are located in the examples/ directory of the repository. Each one demonstrates a specific Kernel capability end-to-end.

Performance & verification

No reflection on hot paths

The Kernel avoids reflection on execution paths, relies on immutable structures post-build, and compiles middleware chains at initialization time. Streaming is O(1) in memory. Per-item cost is zero allocations; only a constant per-call overhead is paid.

format · vet · race · bench verify.sh
# Verify formatting
gofmt -l .

# Run static analysis
go vet ./...

# Run test suite with race detector
go test -race ./...

# Benchmark core dispatch and allocation profiles
go test ./... -run '^$' -bench=. -benchmem
Nexss ecosystem

One idea. Many layers.

Nexss is not a collection of unrelated packages. Each layer has one job and a precise boundary: the Kernel executes, Flow composes, and domain packages give the work meaning.

Layer
Owns
Connects to
Why it matters
Kernel
Actions, streams, lifecycle, resilience, DAG
Go code
Small, typed execution core
Flow / nflow
Graphs, state, control flow
AnyAction / AnyStreamAction
Readable orchestration without moving logic
AI
Models, tools, schemas
Typed actions and streams
Structured output becomes a contract
Transport
HTTP, NATS, CLI, cron, workers
Stable action boundaries
Distribution without rewriting handlers
Domain packages
Files, billing, search, agents
Typed request / response
Your business stays yours
Strategic position

Build your next system

Start with a typed action. Add a stream when the work becomes incremental. Compile a DAG when the graph becomes the product. Nexss gives every layer a place to live.