Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b166beb
hydrated: Go evidence for bitwire decision 0019 (not for merge)
jm9e Oct 7, 2026
5ea0bc2
hydrated: TypeScript mirror and Go/TypeScript interop for 0019 (not f…
jm9e Oct 7, 2026
3adb89c
hydrated(ts): extract the observation harness for consumer suites
jm9e Oct 7, 2026
b4f842a
Specify consumer composition and lifetime acceptance
jm9e Oct 7, 2026
9ce2db4
Qualify compositional Cell adapters and endpoint lifetimes
jm9e Oct 7, 2026
04e4c9c
hydrated: answer Wire & Runtime's 0019 review in Go and TypeScript (n…
jm9e Oct 7, 2026
2110f73
hydrated: README lists the TypeScript observations now covered
jm9e Oct 7, 2026
61c3bb6
Merge remote-tracking branch 'origin/evidence/hydrated-0019-go' into …
jm9e Oct 7, 2026
63c5918
hydrated: one counting domain for value bounds in both directions
jm9e Oct 7, 2026
4217f1b
Merge remote-tracking branch 'origin/evidence/hydrated-0019-go' into …
jm9e Oct 7, 2026
8a52138
hydrated: reference paths are byte material, not depth (D8)
jm9e Oct 7, 2026
476bf52
hydrated: no construction path bypasses projection (D1, D7)
jm9e Oct 7, 2026
3cf6348
Merge consumer composition conformance (PR43, reviewed at 4217f1b)
jm9e Oct 7, 2026
ab21b3e
hydrated: pin the accepted 0019 vectors (bitwire#83 at 6e33fb3)
jm9e Oct 7, 2026
3a4bad3
hydrated: align with bitwire's public hydrated surface; production do…
jm9e Oct 7, 2026
7e6c5c9
hydrated: join bitwire's public declarations and shared codec
jm9e Oct 7, 2026
982f64c
hydrated: gate the hydrated interop; retire the superseded spike
jm9e Oct 7, 2026
dcb26dd
hydrated: bound live-to-data conversion before allocation; iterative …
jm9e Oct 7, 2026
9ea405f
hydrated: bound conversion allocation for wide tuples and repeated re…
jm9e Oct 7, 2026
7910a94
release: prepare bitruntime 0.7.0 (hydrated layer over bitwire 0.6.0)
jm9e Oct 7, 2026
9b66d69
deps: pin bitwire 0.6.0 (public hydrated declarations and shared codec)
jm9e Oct 7, 2026
63c53fb
docs: reconcile the 0.7.0 release documentation with the hydrated layer
jm9e Oct 7, 2026
6817e40
hydrated(ts): correct the observation-suite header
jm9e Oct 7, 2026
ff1c8ea
hydrated: the first termination is final; 0.6.0 baseline and hydrated…
jm9e Oct 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ jobs:
- run: npm test
- run: node scripts/package-smoke.mjs
- run: node scripts/interop.mjs
- run: node scripts/hydrated-interop.mjs
27 changes: 0 additions & 27 deletions .github/workflows/hydration-spike.yml

This file was deleted.

1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
- run: npm ci --ignore-scripts && npm run check && npm run build && npm test
- run: node scripts/package-smoke.mjs
- run: node scripts/interop.mjs
- run: node scripts/hydrated-interop.mjs
- run: node scripts/go-smoke.mjs "$GITHUB_REF_NAME"
- name: Pack release
run: |
Expand Down
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Working here as an agent

Read [CHARTER.md](CHARTER.md), [the current realization](docs/WIRE-RUNTIME.md), and
Read [CHARTER.md](CHARTER.md), [the baseline realization](docs/WIRE-RUNTIME.md), [the hydrated layer](hydrated/README.md), and
bitwire decision 0015 with its wire/carrier contracts at the pinned dependency.
Read [the composition plan](docs/COMPOSITION.md) before adding routing across
instances, multiplexing, wire export or stream carriers. Its pending protocol
instances, multiplexing or stream carriers; wire export is the hydrated layer
(bitwire decision 0019). Its pending protocol
decisions are prerequisites, not implemented APIs or permission to infer laws.

- Implement addressless Endpoints in carriers and one reusable addressed layer above them. Wire grants sending; Endpoint adds receive/close ownership; WireNode adds complete structure. Change missing or incorrect laws in bitwire first.
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## 0.7.0

- Require bitwire 0.6.0, with its public hydrated declarations and shared pure
hydrated codec.
- Add `hydrated/{go,ts}` (TypeScript subpath `/hydrated`), the realization of
bitwire decision 0019, the hydrated wire protocol's first edition.
- Messages may carry live Wires. References are `(owner path, scope, id)`,
with 16-octet unpredictable ids, valid end to end in one namespace.
- Exports are bound to a local Endpoint's lifetime. An endpoint travels only
as its sending face, on every path. Liveness is judged on send.
- Received context is delivered beside the value.
- Endpoint termination is bitwire's `Termination`. A receiver failure ends
the endpoint and its export.
- The shared codec owns the grammar and bounds. Conversion counts each
occurrence and refuses before allocating, so a compact value with shared
subtrees, a wide tuple or a repeated reference path cannot grow past the
bounds.
- CI and release run the Go/TypeScript hydrated interoperability. The package
and Go smokes exercise the hydrated layer from installed artifacts.
- Add the composition conformance suite (Cell, Text, Counter).
- Remove the hydration spike.

## 0.6.0

- Restore addressless Wire sending and Endpoint ownership under bitwire 0.5.0.
Expand Down
4 changes: 2 additions & 2 deletions LAYOUT.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Repository layout

Use component first, language second: `<component>/<lang>/`, with two-letter codes such as `go` and `ts`. The current implementation is `core/{go,ts}` and `websocket/{go,ts}`. TypeScript source and tests use `src/` and `test/` within each component; Go tests sit beside their packages.
Use component first, language second: `<component>/<lang>/`, with two-letter codes such as `go` and `ts`. The current implementation is `core/{go,ts}`, `websocket/{go,ts}` and `hydrated/{go,ts}`. TypeScript source and tests use `src/` and `test/` within each component; Go tests sit beside their packages.

The root `go.mod` and `package.json` describe one release unit. Shared documentation stays under `docs/`. The generic interoperability peer is test-only at `conformance/interop/go`; its runner is `scripts/interop.mjs`. Test fixtures live with the tests that use them. Do not add empty packages for planned components.
The root `go.mod` and `package.json` describe one release unit. Shared documentation stays under `docs/`. The generic interoperability peer is test-only at `conformance/interop/go`; its runner is `scripts/interop.mjs`. The hydrated peer is `conformance/interop/hydrated`, run by `scripts/hydrated-interop.mjs`. Test fixtures live with the tests that use them. Do not add empty packages for planned components.

Move imports, tests, manifests, build/release tooling and documentation together whenever a component moves. See [RELEASING.md](RELEASING.md) for the exported coordinates.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# bitruntime

Go and TypeScript implementations of [bitwire 0.5.0](https://github.com/Bitspark/bitwire/tree/v0.5.0): addressless endpoints, addressed access, complete byte-keyed trees, local pairs and WebSocket carriers.
Go and TypeScript implementations of [bitwire 0.6.0](https://github.com/Bitspark/bitwire/tree/v0.6.0): addressless endpoints, addressed access, complete byte-keyed trees, local pairs and WebSocket carriers, and the hydrated layer of [decision 0019](https://github.com/Bitspark/bitwire/blob/v0.6.0/docs/decisions/0019-hydrated-wire-protocol.md) ([hydrated/README.md](hydrated/README.md)).

Wire grants sending ground ontos values. Endpoint adds one detachable receive owner, termination and closure. AddressedWire adds a separate path parameter; an AddressedEndpoint presents that layer over an existing endpoint. WireNode is a complete deixis tree whose own values are send capabilities.

Expand All @@ -9,7 +9,7 @@ Wire grants sending ground ontos values. Endpoint adds one detachable receive ow
Install the release package and shared contract:

```sh
npm install --allow-remote=root --@bitspark:registry=https://registry.npmjs.org @bitspark/bitwire@0.5.0 https://github.com/Bitspark/bitruntime/releases/download/v0.6.0/bitspark-bitruntime-0.6.0.tgz
npm install --allow-remote=root --@bitspark:registry=https://registry.npmjs.org @bitspark/bitwire@0.6.0 https://github.com/Bitspark/bitruntime/releases/download/v0.7.0/bitspark-bitruntime-0.7.0.tgz
```

```ts
Expand All @@ -27,15 +27,17 @@ A raw endpoint can send an atom directly. The addressed facade works on the same

`/core` exports `pair`, `addressed`, `bind`, `under`, `asAddressed`, `compose`, `select` and `route`. Binding captures a path and returns send authority only; `under` captures a prefix. `asAddressed` selects a WireNode node and invokes its own sender once. MissingPathError differs from refusal by that sender. `route(tree,path,message)` does the analogous explicit selection for receiver trees. Structural construction and selection invoke no own capabilities.

`/hydrated` realizes bitwire's hydrated layer (decision 0019): `Scope`, `Endpoint`, `Namespace`, `hydratedTuple` and `items`. A message may carry live Wires. A participant's dispatcher hands its `Scope` each value addressed to its path, with the received context. An `Endpoint`'s `wire` is the only exportable local Wire, and closing the endpoint ends its export.

`/websocket` exports `connectWebSocket`, `listenWebSocket` and `bindWebSocketServer`. A binding owns accepted endpoints and leaves a supplied HTTP/HTTPS server open. A convenience listener owns its HTTP server too. WebSocket negotiates `bitwire.ontos.v2` and carries whole ontos-codec-v1 values. The optional addressed layer encodes `bitwire/addressed/1` as an ordinary value above any carrier.

## Go

```sh
go get github.com/Bitspark/bitruntime@v0.6.0
go get github.com/Bitspark/bitruntime@v0.7.0
```

Use `core/go.NewPair` and `websocket/go.Dial`, `Accept`, `NewServer` or `Listen`. They return bitwire Endpoints. `core/go.Addressed`, `Bind`, `Under` and `AsAddressed` implement the same layering as TypeScript; absent tree paths return ErrMissingPath. A receiver runs on its endpoint's dispatcher. Close does not wait for or cancel dispatched application work.
Use `core/go.NewPair` and `websocket/go.Dial`, `Accept`, `NewServer` or `Listen`. They return bitwire Endpoints. `core/go.Addressed`, `Bind`, `Under` and `AsAddressed` implement the same layering as TypeScript; absent tree paths return ErrMissingPath. `hydrated/go` provides `NewScope`, `NewEndpoint`, `NewNamespace` and `NewTuple` over bitwire's hydrated declarations. A receiver runs on its endpoint's dispatcher. Close does not wait for or cancel dispatched application work.

## Limits and ownership

Expand All @@ -60,7 +62,8 @@ npm ci --ignore-scripts
npm run check
npm test
node scripts/interop.mjs
node scripts/hydrated-interop.mjs
node scripts/package-smoke.mjs
```

Tests use bitwire-owned observations, independent bytes, native carrier failures, TLS, resource release and cross-language peers. Packed-consumer tests install into a fresh project. [RELEASING.md](RELEASING.md) defines the public Go tag and GitHub package asset process. [The realization record](docs/WIRE-RUNTIME.md) states the contract and evidence.
Tests use bitwire-owned observations, independent bytes, native carrier failures, TLS, resource release and cross-language peers. Packed-consumer tests install into a fresh project. [RELEASING.md](RELEASING.md) defines the public Go tag and GitHub package asset process. [The realization record](docs/WIRE-RUNTIME.md) states the 0.6.0 baseline's contract and evidence; [hydrated/README.md](hydrated/README.md) states the hydrated layer's.
8 changes: 4 additions & 4 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Releasing

One root `vX.Y.Z` tag versions the Go module and TypeScript package together. bitruntime 0.6.0 implements bitwire 0.5.0: raw ontos-codec-v1 messages, the optional `bitwire/addressed/1` layer and WebSocket `bitwire.ontos.v2`.
One root `vX.Y.Z` tag versions the Go module and TypeScript package together. bitruntime 0.7.0 implements bitwire 0.6.0: raw ontos-codec-v1 messages, the optional `bitwire/addressed/1` layer, WebSocket `bitwire.ontos.v2` and the hydrated layer `bitwire/hydrated/1` (decision 0019).

- Go module: `github.com/Bitspark/bitruntime`, packages `core/go` and `websocket/go`.
- TypeScript package: `@bitspark/bitruntime`, subpaths `/core` and `/websocket`.
- Go module: `github.com/Bitspark/bitruntime`, packages `core/go`, `websocket/go` and `hydrated/go`.
- TypeScript package: `@bitspark/bitruntime`, subpaths `/core`, `/websocket` and `/hydrated`.
- TypeScript distribution: GitHub release asset `bitspark-bitruntime-<version>.tgz`, covered by `SHA256SUMS`. This is an npm-compatible tarball, not a registry publication. URL installs with npm 12 require `--allow-remote=root`.

Before tagging, land a reviewed green PR on main. CI requires Go formatting/vet/race tests, TypeScript checking/build/tests, bitwire conformance observations, a fresh installed package consumer and Go/TypeScript WebSocket interoperability. Run the same checks locally where supported. Confirm bitwire's exact dependency release is publicly installable, with no file dependencies, module replacements or private registries. Run `node scripts/go-smoke.mjs <pushed-commit>` before tagging.
Before tagging, land a reviewed green PR on main. CI requires Go formatting/vet/race tests, TypeScript checking/build/tests, bitwire conformance observations, a fresh installed package consumer, and Go/TypeScript WebSocket and hydrated interoperability. Run the same checks locally where supported. Confirm bitwire's exact dependency release is publicly installable, with no file dependencies, module replacements or private registries. Run `node scripts/go-smoke.mjs <pushed-commit>` before tagging.

Keep package.json, package-lock.json and docs/RELEASE.md consistent. Create and push an annotated version tag on the verified main commit. The tag workflow repeats checks, verifies a fresh public Go consumer, packs the TypeScript artifact and publishes the GitHub release. Verify the workflow, remote commit/tag, downloaded artifact checksum and fresh installed consumers before reporting completion.

Expand Down
171 changes: 171 additions & 0 deletions conformance/interop/hydrated/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Command hydrated is the Go peer of the hydrated interoperability check
// (scripts/hydrated-interop.mjs), evidence for bitwire decision 0019. Test-only.
//
// hydrated serve listen at participant [s]; on connection, expose an echo
// endpoint and send its reference payload at ["bootstrap"];
// print the URL; exit when stdin closes
// hydrated client <url> participant [c]: call "continue" with a reply HydratedWire, send a
// third HydratedWire through the returned continuation, print "ok"
// when the third HydratedWire receives the value
//
// The echo: (op, arg, reply) replies (arg, continuation); the continuation sends
// the value it receives to the HydratedWire it carries, then closes.
package main

import (
"bufio"
"context"
"fmt"
"net/http"
"os"
"time"

core "github.com/Bitspark/bitruntime/core/go"
hydrated "github.com/Bitspark/bitruntime/hydrated/go"
websocket "github.com/Bitspark/bitruntime/websocket/go"
ontos "github.com/Bitspark/bitwire/ontos/go/core"
wire "github.com/Bitspark/bitwire/wire/go"
)

var (
ns = hydrated.NewNamespace("interop")
bootstrap = wire.Path{ontos.NewAtom([]byte("bootstrap"))}
)

func text(s string) ontos.Atom { return ontos.NewAtom([]byte(s)) }

func fail(err error) {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

// participant composes a scope at path over an addressed link; bootstrap values
// go to onBootstrap, everything else to the scope with the link as context.
func participant(link wire.AddressedEndpoint, path wire.Path, onBootstrap func(ontos.Value)) *hydrated.Scope {
scope, err := hydrated.NewScope(ns, path, link, hydrated.DefaultLimits)
if err != nil {
fail(err)
}
if _, err := link.Receive(func(p wire.Path, v ontos.Value) {
if wire.PathEqual(p, bootstrap) {
onBootstrap(v)
return
}
if err := scope.Deliver(p, v, "link"); err != nil {
fmt.Fprintln(os.Stderr, "diagnostic:", err)
}
}); err != nil {
fail(err)
}
return scope
}

func echo(v wire.HydratedValue, _ wire.ReceivedContext) {
xs, _ := hydrated.Items(v)
cont := hydrated.NewEndpoint(1)
_, _ = cont.Receive(func(next wire.HydratedValue, _ wire.ReceivedContext) {
ys, _ := hydrated.Items(next)
_ = ys[1].(wire.HydratedWire).Send(ys[0])
_ = cont.Close()
})
r, _ := hydrated.NewTuple(xs[1], cont)
_ = xs[2].(wire.HydratedWire).Send(r)
}

func serve() {
server, err := websocket.Listen("127.0.0.1:0", websocket.Options{}, func(e *websocket.Endpoint, _ *http.Request) {
link := core.Addressed(e)
scope := participant(link, wire.Path{text("s")}, func(ontos.Value) {})
service := hydrated.NewEndpoint(16)
_, _ = service.Receive(echo)
ref, err := scope.Expose(service)
if err != nil {
fail(err)
}
payload, err := ref.Payload()
if err != nil {
fail(err)
}
if err := link.Send(bootstrap, payload); err != nil {
fail(err)
}
})
if err != nil {
fail(err)
}
fmt.Println(server.URL())
_, _ = bufio.NewReader(os.Stdin).ReadString(0)
_ = server.Close()
}

func client(url string) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
e, err := websocket.Dial(ctx, url, websocket.Options{})
if err != nil {
fail(err)
}
refs := make(chan ontos.Value, 1)
scope := participant(core.Addressed(e), wire.Path{text("c")}, func(v ontos.Value) { refs <- v })
var payload ontos.Value
select {
case payload = <-refs:
case <-time.After(10 * time.Second):
fail(fmt.Errorf("no bootstrap reference"))
}
ref, err := hydrated.ReadReference(payload)
if err != nil {
fail(err)
}
reply := hydrated.NewEndpoint(1)
replies := make(chan wire.HydratedValue, 1)
_, _ = reply.Receive(func(v wire.HydratedValue, _ wire.ReceivedContext) { replies <- v })
req, _ := hydrated.NewTuple(text("continue"), text("hello"), reply)
if err := scope.Connect(ref).Send(req); err != nil {
fail(err)
}
var r wire.HydratedValue
select {
case r = <-replies:
case <-time.After(10 * time.Second):
fail(fmt.Errorf("no reply"))
}
xs, _ := hydrated.Items(r)
if !xs[0].(ontos.Atom).Equal(text("hello")) {
fail(fmt.Errorf("wrong reply"))
}
third := hydrated.NewEndpoint(1)
thirds := make(chan wire.HydratedValue, 1)
_, _ = third.Receive(func(v wire.HydratedValue, _ wire.ReceivedContext) { thirds <- v })
next, _ := hydrated.NewTuple(text("third HydratedWire"), third)
if err := xs[1].(wire.HydratedWire).Send(next); err != nil {
fail(err)
}
select {
case v := <-thirds:
if !v.(ontos.Atom).Equal(text("third HydratedWire")) {
fail(fmt.Errorf("wrong third value"))
}
case <-time.After(10 * time.Second):
fail(fmt.Errorf("third HydratedWire not reached"))
}
fmt.Println("ok")
_ = e.Close()
}

func main() {
if len(os.Args) < 2 {
fail(fmt.Errorf("usage: hydrated serve | client <url>"))
}
switch os.Args[1] {
case "serve":
serve()
case "client":
if len(os.Args) < 3 {
fail(fmt.Errorf("client needs a url"))
}
client(os.Args[2])
default:
fail(fmt.Errorf("unknown mode %q", os.Args[1]))
}
}
Loading
Loading