Help us shape the future of Go! Take the 2026 Go Developer Survey

nightseam

module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2026 License: Apache-2.0

README

Nightseam

ci Go Reference npm License

Declare a duplex API once, in tiers of JSON. Get a typed server and a typed client in Go and TypeScript — every one of them typed in both directions, since a client serves what the server calls — all speaking one wire profile: nightseam.duplex/1, JSON frames carrying requests, responses, events and cancellation over a WebSocket, a tunnel channel or an in-memory pipe.

Duplex means both ends call. The server calls the client with the machinery the client calls the server with, declared in the same file and typed the same way — which is what a browser, an agent and a forwarder need, and what a request-and-response contract has no way to state.

The declaration language separates data, RPC and live levels. Go and TypeScript generate and validate unions, nullable and literal expressions, inline shapes, type and family parameters, and inherited operations. The live tier adds callable values — functions that may take or return functions, and generic data containers applied to them — with scoped export, import and release. The changelog records the implemented forms and the removal of the governed session layer. Generic callables and live types drawn through family parameters remain unsupported; the generated surface distinguishes those limits.

Declare it

api/contracts/probe/protocol.json

{
  "profile": "nightseam.duplex/1",
  "server": {
    "methods": {"echo": {"request": "Payload", "result": "Payload"}},
    "events": {"changed": {"type": "Payload"}}
  },
  "client": {
    "methods": {"reverse": {"request": "Payload", "result": "Payload"}}
  },
  "errors": {"denied": "The caller is denied."}
}
Write the behavior

Go, the server side. Generated code is never edited by hand; behavior goes in a file of your own, which nightseam init probe writes once:

type Probe struct{}

func (Probe) Echo(ctx context.Context, remote *binding.Remote, p protocol.Payload) (protocol.Payload, error) {
	return remote.Reverse(ctx, p) // the server calls the client, typed, inside the request
}

// wherever you serve:
handler, err := binding.NewHandler(Probe{}, runtime.ServerOptions{})
http.Handle("/probe", handler)
Call it

TypeScript, the client side:

const client = await Client.dial('wss://example.test/probe', {}, {
  reverse: ({ text, count }) => ({ text: [...text].reverse().join(''), count }),
}, {
  changed: p => console.log('changed', p.text),
});

const payload = await client.echo({ text: 'hello', count: 1 });

What you never write: the envelope, the correlation of a response to its request, cancellation, backpressure, the validator that holds every frame to the declaration — or the second language's copy of any of it.

Status

Pre-1.0. Published packages, the generator's runtime dependency version and Go module tags move in lockstep. The 0.5.0 work replaces the governed session layer directly, with no compatibility shim; this is the current release policy, not a settled compatibility policy for a mature ecosystem. CHANGELOG.md says what each version holds. The conformance suite under conformance/ holds every language's seam, runtime, tunnel, live and generated packages to Go's over a real socket, scenario by scenario, so a peer of any language is held to the reference before it is released; conformance/matrix.json is the last run's standing of each language in each profile.

Languages

A language is in Nightseam when it is in this table, and what it promises is its assigned tier: 1 promises every profile with no lag, 2 promises core and generator always and every other profile within a minor release, and 3 and 4 are one band — reference-held — that the generator column tells apart. docs/languages/tiers.md says what each promise and each profile is.

Go and TypeScript provide generated clients and server bindings, including typed reverse calls, events and live-value conversion. TypeScript's serve accepts a connection supplied by the host; authentication and socket listening stay with the application. The role inventory maps the generated socket scenarios to each server operation and language pairing.

language tier core generator tunnel live observability verdict
go (reference) 1 ✓ ✓ ✓ ✓ ✓ ok
typescript 1 ✓ ✓ ✓ ✓ ✓ ok

Planned, with no testee yet: cpp, haskell, python, rust at tier 2; java, swift at tier 4.

The table is the last conformance run, rendered from conformance/matrix.json by node scripts/matrix-table.mjs; CI fails a pull request whose table has drifted from the matrix, as nightseam check fails one whose generated output is stale. ok means the gate found neither failures nor skips in profiles required by that language's tier. Required-profile failures and skips follow the tier's release policy; failures elsewhere follow its lag policy, while optional-profile skips remain informational. What CI runs is the star — every language against the Go reference on both sides, which is the gate a language passes to have joined; the full matrix of every language against every other runs nightly, and a scenario two non-reference languages disagree about becomes an issue against the scenario, since the reference decides.

Install

npm install @nightseam/runtime @nightseam/tunnel           # a generated RPC client
npm install @nightseam/live                               # when its family has a live tier
go get github.com/Bitspark/nightseam                       # the Go runtime packages
go get -tool github.com/Bitspark/nightseam/cmd/nightseam   # the generator, as a Go tool

The generator is development tooling. Generated packages depend on their protocol types and the runtime components they use, including the tunnel for clients and the live runtime for live values. Generated data-only packages need no live runtime.

The packages

npm Go what it is
@nightseam/duplex duplex/go the seam: ordered frames both ways, an explicit close with a code and a reason, a WebSocket adapter and an in-memory pipe
@nightseam/runtime runtime/go the peer of the profile: correlation, cancellation, backpressure, presence, trace context, the wire validator, the observer and its console and slog adapters
@nightseam/tunnel tunnel/go channels multiplexed over one peer, each one a connection of the seam, with per-channel credit
@nightseam/live live/go callable values across one connection: a scope over a peer, exported bindings, imported references, release and forwarding
@nightseam/otel otel/go the OpenTelemetry adapter: a propagator over W3C trace context and an observer that opens a span per request. The four components above pull in no telemetry backend; their external Go dependencies provide WebSocket transport and strict JSON decoding.
— cmd/nightseam the generator

Every published component exists in both languages and both are held to one suite; the suite's own testees live at conformance/<lang>, private. A third language is <component>/<lang> for each of these, a target under internal/targets/ (Go's is golang, and markdown is a writer of the specification, a target that is no language), and a testee under conformance/<lang>; nothing else moves, and docs/languages/onboarding.md is the order to do it in.

Using it

A family is a directory of tier files, api/contracts/<family>/ — the types, the protocol over them, and what each target names otherwise than the convention does. The generator renders it into packages it owns wholesale: api/go/<f>-protocol, -binding and -client, api/ts/<f>-client and -binding, and the family's specification as Markdown at api/spec/<f>/README.md.

go tool nightseam validate            # every diagnostic of every family
go tool nightseam generate            # render what is stale
go tool nightseam init probe          # the handlers you implement, written once
go tool nightseam check               # in CI: fail if the checked-in output is stale
go tool nightseam version             # which version of the tool is running

The three blocks above, made to run: examples/ — one family, a Go server and a TypeScript client, installed from what is published rather than from this tree, which is also how a release finds out whether what it publishes can be used.

Documentation

docs/README.md is the map. The reference is in sets by who reads it:

set for what
docs/goals/ a reviewer, a designer the north stars: what Nightseam is for, in eight respects, at the limit — abstract, never done, and what a review measures the tree against
docs/wire/ a runtime in any language what crosses the wire: the profile nightseam.duplex/1, the tunnel's operations, and the test that says where something new on the wire belongs
docs/runtime/ a consumer of the packages the surface of the peer, the tunnel, the live layer and the observer, and what a consumer composes out of them, Go and TypeScript side by side
docs/declaration/ a consumer declaring a family the tier files, a family generic in others, the generator's commands, what the generated packages export, and the pipeline for whoever changes it
docs/languages/ a consumer choosing a language, a contributor bringing one the four promises, profiles and tiers, and how a language joins
docs/decisions/ anyone asking why the record: one page per decision — the question, what was decided, what the alternative cost, since when
conformance/DRIVER.md a testee's author the conformance suite: the protocol a language's testee speaks to the runner, every op

Working on Nightseam

go test -short ./...                              # the fast tier: Go alone, seconds
go test ./...                                     # the full tier: both languages, the conformance suite
pnpm install && pnpm -r check && pnpm -r build && pnpm -r test
(cd otel/go && go vet ./... && go test ./...)     # the nested module, which ./... does not enter
node scripts/matrix-table.mjs --check             # the README's Languages table against the matrix
node scripts/links.mjs                            # every link in every page resolves to the tree

The full tier needs Go, Node 22.12 or later and the TypeScript compiler pnpm install brings, and fails rather than skips when one is missing.

COLLABORATION.md says how work is organized here — the boundary rule, parity between the languages, the two tiers, the golden discipline, lanes, and working in one tree. SECURITY.md says how to report a vulnerability, CODE_OF_CONDUCT.md what is expected of everyone here, and RELEASING.md what is published and how a release is cut.

License

Apache License, Version 2.0: LICENSE, with NOTICE beside it.

Directories

Path Synopsis
auth
go module
cmd
nightseam command
nightseam renders a checkout's families into generated packages.
nightseam renders a checkout's families into generated packages.
nightseam-surface command
nightseam-surface compares the generated Go API shared by two checkouts.
nightseam-surface compares the generated Go API shared by two checkouts.
conformance
go
Package conformance is the runner of Nightseam's conformance suite — the repository's own, importable because a Go module has no private path, and no API a consumer is meant to build on: nothing in it is held stable.
Package conformance is the runner of Nightseam's conformance suite — the repository's own, importable because a Go module has no private path, and no API a consumer is meant to build on: nothing in it is held stable.
go/testee command
The Go testee: Nightseam's Go runtime and tunnel under the control of the conformance runner, over the protocol of conformance/DRIVER.md.
The Go testee: Nightseam's Go runtime and tunnel under the control of the conformance runner, over the protocol of conformance/DRIVER.md.
duplex
go
Package duplex is the seam beneath every protocol: a frames duplex connection.
Package duplex is the seam beneath every protocol: a frames duplex connection.
go/duplextest
Package duplextest holds every transport to what a frames duplex connection promises.
Package duplextest holds every transport to what a frames duplex connection promises.
go/ws
Package ws carries a frames duplex connection over a WebSocket.
Package ws carries a frames duplex connection over a WebSocket.
internal
analysis
Package analysis derives the facts about a family that checks and renderings read, within the world it is rendered in: the families it imports, resolved; the two types every family with a protocol carries; each declaration's tier; the fields a record carries through inheritance; and what is generic in the family — which parameter each type draws on, at which of the bound family's types.
Package analysis derives the facts about a family that checks and renderings read, within the world it is rendered in: the families it imports, resolved; the two types every family with a protocol carries; each declaration's tier; the fields a record carries through inheritance; and what is generic in the family — which parameter each type draws on, at which of the bound family's types.
check
Package check holds the rules a family is held to before any target sees it: one function per tier over the facts analysis derived, and one over the override files.
Package check holds the rules a family is held to before any target sees it: one function per tier over the facts analysis derived, and one over the override files.
compose
Package compose is the composition root: the only place a target is named.
Package compose is the composition root: the only place a target is named.
diag
Package diag is the vocabulary every other package reports problems in: a diagnostic locates one problem in a family by the tier file and the JSON pointer of what is wrong, names it by a code, and says what to write instead.
Package diag is the vocabulary every other package reports problems in: a diagnostic locates one problem in a family by the tier file and the JSON pointer of what is wrong, names it by a code, and says what to write instead.
doc
Package doc is the specification as a document: every fact of a family a reader wants, computed once from render and held in one structure that writers render and never derive again — the types with their fields and what inherits them, the two sides with their operations and errors, the parameters a family is generic in, and the families and built-ins it draws on.
Package doc is the specification as a document: every fact of a family a reader wants, computed once from render and held in one structure that writers render and never derive again — the types with their fields and what inherits them, the two sides with their operations and errors, the parameters a family is generic in, and the families and built-ins it draws on.
emit
Package emit is what every target writes source with: a writer that keeps the indentation, an import set an emitter declares at the site that needs it, and a namespace that holds the identifiers a rendering declares so that a collision is found before a line is written.
Package emit is what every target writes source with: a writer that keeps the indentation, an import set an emitter declares at the site that needs it, and a namespace that holds the identifiers a rendering declares so that a collision is found before a line is written.
kernel
Package kernel drives generation: it reads a checkout's families, gives each its world, holds it to the model's and every concern's rules, and — once every target is composed with it — asks each target to check and render what it consumes.
Package kernel drives generation: it reads a checkout's families, gives each its world, holds it to the model's and every concern's rules, and — once every target is composed with it — asks each target to check and render what it consumes.
load
Package load reads a checkout's families from their tier files into the typed model.
Package load reads a checkout's families from their tier files into the typed model.
model
Package model is the typed declaration of one family across its tiers: the types of its model, the sides, operations and errors of its protocol, and the raw override files of its targets.
Package model is the typed declaration of one family across its tiers: the types of its model, the sides, operations and errors of its protocol, and the raw override files of its targets.
model/builtin
Package builtin holds the families Nightseam declares of itself: the profile's `duplex` and the tunnel's `tunnel`, written as ordinary tier files in the declaration language and carried in the binary.
Package builtin holds the families Nightseam declares of itself: the profile's `duplex` and the tunnel's `tunnel`, written as ordinary tier files in the declaration language and carried in the binary.
model/modeltest
Package modeltest builds families from tier files written inline, for the tests of the packages above model that may not read a checkout: the loader's work without its schemas.
Package modeltest builds families from tier files written inline, for the tests of the packages above model that may not read a checkout: the loader's work without its schemas.
naming
Package naming derives identifiers from the names a contract declares, by one convention every target shares: a name's alphanumeric words, spelled in upper or lower camel case, with the initialisms a language keeps in capitals.
Package naming derives identifiers from the names a contract declares, by one convention every target shares: a name's alphanumeric words, spelled in upper or lower camel case, with the initialisms a language keeps in capitals.
oracle
Package oracle is test support: the left path of the diagram a generic rendering must commute with.
Package oracle is test support: the left path of the diagram a generic rendering must commute with.
pattern
Package pattern shares declaration syntax checks with runtime descriptors.
Package pattern shares declaration syntax checks with runtime descriptors.
render
Package render is the family as a target sees it: every fact a rendering needs, computed once from analysis after the neutral checks passed, and nothing target-specific.
Package render is the family as a target sees it: every fact a rendering needs, computed once from analysis after the neutral checks passed, and nothing target-specific.
scalarjson
Package scalarjson guards Unicode before encoding/json can replace it.
Package scalarjson guards Unicode before encoding/json can replace it.
spi
Package spi is the seam between the kernel and the targets it renders with.
Package spi is the seam between the kernel and the targets it renders with.
surface
Package surface describes the exported declarations of generated Go files.
Package surface describes the exported declarations of generated Go files.
targets/atlas
Package atlas writes a checkout's document as one self-contained, interactive HTML page.
Package atlas writes a checkout's document as one self-contained, interactive HTML page.
targets/golang
Package golang renders a family as Go: a protocol package of wire types with their validator, a binding package for a server, a client package for a caller.
Package golang renders a family as Go: a protocol package of wire types with their validator, a binding package for a server, a client package for a caller.
targets/markdown
Package markdown writes the document as Markdown: one page per family, README.md under api/spec/<family>, which renders where the tree is read — the types with their fields and constraints, an example of each and where it is used, the two sides with their operations and errors and each operation as the frames of the profile carry it, and the parameters a family is generic in — so that what a family means is written once, in the declaration, and the page is never behind it.
Package markdown writes the document as Markdown: one page per family, README.md under api/spec/<family>, which renders where the tree is read — the types with their fields and constraints, an example of each and where it is used, the two sides with their operations and errors and each operation as the frames of the profile carry it, and the parameters a family is generic in — so that what a family means is written once, in the declaration, and the page is never behind it.
targets/typescript
Package typescript renders a family as TypeScript: a client package with shared types and validators, and a binding package for the server.
Package typescript renders a family as TypeScript: a client package with shared types and validators, and a binding package for the server.
live
go
Package live carries callable values across one connection: a scope over a peer, bindings exported from it, and references that name them inside an ordinary payload.
Package live carries callable values across one connection: a scope over a peer, bindings exported from it, and references that name them inside an ordinary payload.
go/livetest
Package livetest is the live layer's shared suite: the behavior a scope promises, written once and run by both languages against their own runtime.
Package livetest is the live layer's shared suite: the behavior a scope promises, written once and run by both languages against their own runtime.
otel
go module
runtime
go
Package runtime implements the nightseam.duplex/1 profile over a frames duplex connection (duplex): JSON text frames carrying requests, responses, events and cancellations.
Package runtime implements the nightseam.duplex/1 profile over a frames duplex connection (duplex): JSON text frames carrying requests, responses, events and cancellations.
go/slogobserver
Package slogobserver writes what a peer tells an observer to a slog.Logger: one record per event, at a level per kind, with the event's fields as attributes.
Package slogobserver writes what a peer tells an observer to a slog.Logger: one record per event, at a level per kind, with the event's fields as attributes.
tunnel
go
Package tunnel multiplexes channels over one peer of the nightseam.duplex/1 profile: a third transport beneath the seam, after the WebSocket and the pipe.
Package tunnel multiplexes channels over one peer of the nightseam.duplex/1 profile: a third transport beneath the seam, after the WebSocket and the pipe.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL