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

nightseam

module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 19, 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 a typed client in 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 session, an agent and a relay need, and what a request-and-response contract has no way to state.

Version 0.4.0 releases the consumer improvements described in the changelog. Its checker and specification renderer support the new declaration forms; complete Go/TypeScript generation and value validation for those forms, and the remaining typed session operations, continue in 0.5.0. Until implemented, the code targets report unrendered_form for those forms.

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. The declaration language, the generated surface and the profile move with minor versions; CHANGELOG.md says what each version holds. What is already held fixed is the agreement between the languages: the conformance suite under conformance/ holds every language's seam, runtime, tunnel, session 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 tier: 1 guarantees every profile with no lag, 2 guarantees 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.

language tier core generator tunnel session observability verdict
go (reference) 1 ✓ ✓ ✓ ✓ ✓ ok
typescript 1 ✓ ✓ 4 skipped ✓ ✓ ✓ 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. A red cell in a profile the language's tier guarantees refuses a release; elsewhere it is what the tier's lag allows. 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           # what a generated TypeScript client needs
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

Nightseam is developer tooling and never a runtime dependency of its own generator: a generated package depends on the protocol types, the runtime and the tunnel, and on nothing else.

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/session session/go a session over a tunnel's channels: the relay, the registry, the holder of control, the log, and the changes it reports
@nightseam/otel otel/go the OpenTelemetry adapter, the one component a consumer opts into: a propagator over the W3C trace context propagator and an observer that opens a span per request, so that the four above pull in no telemetry backend — on npm they depend on nothing at all, and in Go on one third-party module, the WebSocket transport
— 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 spec is 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, how a session of them is governed, 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 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, the session's rules and vocabulary, 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 session and the observer, 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, tunnel and session under the control of the conformance runner, over the protocol of conformance/DRIVER.md.
The Go testee: Nightseam's Go runtime, tunnel and session 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 session families a parameter may bind; 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 session families a parameter may bind; 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.
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, the governance of its session, 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, the governance of its session, and the raw override files of its targets.
model/builtin
Package builtin holds the families Nightseam declares of itself: the profile's `duplex`, the tunnel's `tunnel` and the session's `session`, 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`, the tunnel's `tunnel` and the session's `session`, 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.
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.
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/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/spec
Package spec renders a family as its specification: one Markdown document per family, from every tier it declares — the types with their fields and constraints, the two sides with their operations and errors, the parameters it is generic in, the governance of a session of it — so that what a family means is written once, in the declaration, and the document is never behind it.
Package spec renders a family as its specification: one Markdown document per family, from every tier it declares — the types with their fields and constraints, the two sides with their operations and errors, the parameters it is generic in, the governance of a session of it — so that what a family means is written once, in the declaration, and the document is never behind it.
targets/typescript
Package typescript renders a family as TypeScript: one client package with the wire types, their validator, the client class and the handler of what the server sends.
Package typescript renders a family as TypeScript: one client package with the wire types, their validator, the client class and the handler of what the server sends.
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.
session
go
Package session is a session as a thing with an identity that outlives connections: one up connection, the machine's; any number of down connections, the consumers', each in a role; one holder of control; an ask routed to the holder; and a log of every frame in one order, replayed from a sequence.
Package session is a session as a thing with an identity that outlives connections: one up connection, the machine's; any number of down connections, the consumers', each in a role; one holder of control; an ask routed to the holder; and a log of every frame in one order, replayed from a sequence.
go/sessiontest
Package sessiontest holds a session component to what a relay promises.
Package sessiontest holds a session component to what a relay promises.
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