This guide explains how to create HighBar clients in languages beyond the maintained Python and F# clients. HighBar uses ordinary proto3 and gRPC, so any language with support for client-side streaming, server-side streaming, unary RPCs, and plaintext UDS or loopback TCP can interoperate.
Read this with Proto Reference, which defines the service surface and message contracts.
| Client type | Required service | Typical capabilities |
|---|---|---|
| Observer | HighBarProxy |
Hello, StreamState, counters. |
| AI policy | HighBarProxy |
Observer flow plus token-authenticated SubmitCommands, validation, callbacks, save/load. |
| Admin/test harness | HighBarAdmin |
Capabilities, dry-run validation, pause/speed/spawn/resource/transfer actions. |
| Coordinator | HighBarCoordinator |
Plugin dial-out topology, state fan-out, command channel. |
Start with an observer. It exercises transport, schema compatibility, and stream handling without mutating the match.
The supported Python package lives in clients/python.
cd clients/python
python -m pip install -e '.[dev]'
make codegen
make testGenerated stubs are committed under
clients/python/highbar_client/highbar/. Useful modules:
highbar_client.channel: endpoint construction.highbar_client.session:Hello, token reading, schema checks.highbar_client.state_stream: stream recording and invariants.highbar_client.commands: command batch helpers and diagnostics.highbar_client.admin: admin action builders.highbar_client.live_topology: standard Python launcher for live Recoil/BAR topologies and BNV demos.
The F# wrapper lives in clients/fsharp.
HighBar.Proto.csprojgenerates C# stubs fromproto/highbar/*.proto.HighBar.Client.fsprojwraps channel, session, command, state, and admin helpers.- Samples are in
clients/fsharp/samples/Observerandclients/fsharp/samples/AiClient.
Create a local generation template for your language instead of editing
proto/buf.gen.yaml unless you intend to commit generated outputs for
that language.
Example buf.gen.go.yaml:
version: v2
plugins:
- remote: buf.build/protocolbuffers/go
out: gen/go
opt: paths=source_relative
- remote: buf.build/grpc/go
out: gen/go
opt: paths=source_relativeRun:
buf generate proto --template buf.gen.go.yamlFor languages without a Buf remote plugin, install the standard protoc
plugin and call protoc directly.
Every generator needs proto as its import root:
protoc -I proto \
--<language>_out=gen/<language> \
--grpc-<language>_out=gen/<language> \
proto/highbar/*.protoThe generated package/namespace comes from package highbar.v1 unless
your generator has its own namespace options.
-
Create a plaintext gRPC channel to UDS or loopback TCP.
-
Call
HighBarProxy.Hello:schema_version = "1.0.0"client_id = "your-client/name-version"role = ROLE_OBSERVER
-
Verify the response schema equals your generated schema.
-
Start
StreamState(StreamStateRequest{resume_from_seq: 0}). -
Require monotonic
StateUpdate.seq. -
Treat the first
snapshotas baseline and apply laterdeltaevents in order.
-
Read the token file with retry/backoff.
-
Call
Hellowithrole = ROLE_AIand metadatax-highbar-ai-token. -
Start
StreamState. -
Call
GetCommandSchemaonce after handshake. -
For a unit, call
GetUnitCapabilitiesbefore building strict batches. -
Use
ValidateCommandBatchduring client development and before uncertain commands. -
Open
SubmitCommandsand streamCommandBatchmessages. -
Reconcile both:
- immediate
CommandAck.results - later
CommandDispatchEventdeltas
- immediate
-
Call
GetAdminCapabilities. -
Build
AdminActionwith action sequence, client action id, conflict policy, basis fields when available, and reason. -
Call
ValidateAdminActionfor dry-run checks. -
Call
ExecuteAdminActionwith metadata:x-highbar-ai-tokenx-highbar-admin-rolex-highbar-client-id
-
Verify success behaviorally through state stream, snapshot/delta, or engine-log evidence. Do not treat
ADMIN_ACTION_EXECUTEDalone as proof.
Use plaintext HTTP/2 over loopback:
127.0.0.1:50511
Do not expose the service on non-loopback interfaces. HighBar has no TLS or cross-host auth model.
Use the gRPC runtime's Unix-domain-socket support.
Endpoint strings vary by language:
| Runtime | Typical endpoint |
|---|---|
Python grpcio |
unix:/tmp/highbar.sock |
Go grpc-go |
custom dialer with network="unix" |
.NET Grpc.Net.Client |
SocketsHttpHandler.ConnectCallback |
| Java grpc-netty | NettyChannelBuilder.forAddress(new DomainSocketAddress(path)) with native transport |
Rust tonic |
Endpoint::connect_with_connector and tokio::net::UnixStream |
Node @grpc/grpc-js |
use TCP; UDS support depends on runtime and path form |
When UDS support is awkward, use loopback TCP.
Generate stubs with protoc-gen-go and protoc-gen-go-grpc. Use
stream APIs directly:
client.StreamState(ctx, req)returns a receive stream.client.SubmitCommands(ctx)returns a send stream followed byCloseAndRecv.
For UDS, use grpc.DialContext with a custom context dialer that calls
net.Dialer.DialContext(ctx, "unix", path).
Use prost and tonic. Add the proto files to build.rs, include
proto as the import path, and generate client modules for
highbar.v1. StreamState is an inbound stream; SubmitCommands
requires an outbound stream such as tokio_stream::iter.
For UDS, build a tonic::transport::Endpoint with a custom connector
backed by tokio::net::UnixStream, or use loopback TCP.
Use protobuf-gradle-plugin with grpc-java. Generated service clients
are:
HighBarProxyGrpcHighBarAdminGrpcHighBarCoordinatorGrpc
For TCP, use ManagedChannelBuilder.forAddress("127.0.0.1", port) .usePlaintext(). UDS requires Netty native epoll/kqueue domain socket
support; otherwise prefer TCP.
Use either static generation (grpc-tools, ts-proto) or dynamic
loading (@grpc/proto-loader plus @grpc/grpc-js). Make sure oneof
fields are preserved; HighBar relies heavily on oneof command/action
arms.
Use loopback TCP unless your stack has verified UDS support.
The native plugin already builds C++ stubs into build/gen. Separate C++
tools can also generate from proto/highbar/*.proto using
grpc_cpp_plugin. Avoid linking those tools into the Recoil process
unless they use the same protobuf/gRPC ABI as the native plugin.
Every client should enforce:
HelloResponse.schema_version == "1.0.0".StateUpdate.seqnever decreases or repeats on one stream.StateUpdate.payloadoneof is recognized; unknown payloads are logged and skipped.- Command batches set
client_command_id,based_on_frame, andbased_on_state_seqonce the client has observed state. - Retried command batches use a fresh
batch_seq; keepclient_command_idstable if the retry represents the same intent. - Admin success requires behavioral evidence.
UNAVAILABLEis recoverable only by reconnecting and resuming the state stream.
Pseudo-code independent of generated language names:
latest = wait_for_snapshot_or_delta()
unit = choose own unit from latest.own_units
cmd = AICommand {
move_unit: {
unit_id: unit.unit_id
options: 0
timeout: 0
to_position: { x: 1024, y: 0, z: 1024 }
}
}
batch = CommandBatch {
batch_seq: next_batch_seq()
target_unit_id: unit.unit_id
commands: [cmd]
client_command_id: next_client_command_id()
based_on_frame: latest.frame
based_on_state_seq: latest.seq
conflict_policy: COMMAND_CONFLICT_REPLACE_CURRENT
}
result = ValidateCommandBatch(batch)
if result.status is acceptable:
send batch on SubmitCommands
action = AdminAction {
action_seq: next_action_seq()
client_action_id: next_client_action_id()
based_on_frame: latest.frame
based_on_state_seq: latest.seq
conflict_policy: ADMIN_CONFLICT_REJECT_IF_CONTROLLED
reason: "demo speed change"
global_speed: { speed: 2.0 }
}
result = ExecuteAdminAction(action, metadata)
assert result.status == ADMIN_ACTION_EXECUTED
observe "Speed set to 2.0" in engine log or speed-equivalent state evidence
Documentation-only changes should not require generated code, but API or client changes do. Run the hosted workflow equivalents before pushing:
buf lint proto
cd proto && buf generate
cd ../clients/python
python -m pip install -e '.[dev]'
make codegen
make testAlso run shell syntax checks when touching tests/headless or
tests/bench:
bash -n tests/headless/*.sh tests/bench/*.shIf .proto files change, commit regenerated Python stubs and update
this guide plus Proto Reference. Do not edit
generated *_pb2.py files manually.
- Calling mutating RPCs before
Hello. - Forgetting
x-highbar-ai-tokenon AI/admin RPCs. - Building command batches without a target unit or basis fields.
- Treating
CommandAckas final execution evidence; dispatch happens on the engine thread and is reflected later. - Holding the state stream receive loop on the UI thread.
- Assuming UDS endpoint syntax is identical across language runtimes.
- Adding generated code for a new language without updating
.gitignoreand CI expectations.