Skip to content

Repository files navigation

CrossByte

CrossByte logo

CrossByte is a cross-platform Haxe framework for networked, event-driven, and systems-oriented applications.

It is built for projects that want a strong runtime foundation without dragging in a giant engine shape: sockets, HTTP, RPC, timers, workers, files, crypto, compression, IPC, and a set of practical data structures all live in one coherent core.

CrossByte aims to stay modular. The core provides portable behavior first, while optional sibling haxelibs can add native-backed integrations when they are worth the extra dependency.

Install

haxelib install crossbyte

Or track the repository:

haxelib git crossbyte https://github.com/dimensionscapeorg/crossbyte.git

Native targets

Native (hxcpp) builds need the production branch of the dimensionscape/hxcpp fork:

haxelib git hxcpp https://github.com/dimensionscape/hxcpp.git production

It carries the socket, poll and TLS corrections CrossByte depends on, ALPN for HTTP/2, TLS session resumption and the bundled MySQL client, and it exposes hxcpp's mbedTLS to libraries, which CrossByte's RSA and ECDSA build against. hxcpp 4.3.2, the release on haxelib, has none of these and cannot build CrossByte's crypto. CI builds against the same branch.

The JVM, Node, HashLink, Neko and the interpreter need nothing else to install; Node needs hxnodejs. What a target cannot do it says at the member: the interpreter, for one, cannot start a child process (NativeProcess.isSupported is false there).

Install the published CrossByte task runner with:

haxelib install aedifex

What CrossByte Is Good At

  • evented applications and services
  • TCP, WebSocket, and reliable datagram networking
  • peer-to-peer connections through NAT, including WebRTC data channels to and from browsers
  • HTTP clients and lightweight HTTP server flows, HTTP/1.1 and HTTP/2
  • request/response RPC over live connections
  • file, byte, and stream-heavy workflows
  • headless runtimes, tools, and backend infrastructure
  • cross-target foundations that still leave room for native extensions

Core Surface

CrossByte currently includes:

  • async runtime and timer scheduling
  • event system and typed event classes
  • HTTP and middleware
  • URL loading and request utilities
  • TCP, WebSocket, and RUDP transport layers
  • NAT traversal and WebRTC:
    • PeerConnection and DataChannel -- the full stack, ICE to SCTP over DTLS, interoperable with a browser's RTCPeerConnection in either signalling direction (CI proves both against headless Chrome)
    • StunClient and per-connection reflexive gathering, so a peer behind NAT can learn the address the world sees, and RFC 5780's tests (classifyMapping, classifyFiltering) for what kind of NAT is in the way
    • TurnClient and relayed candidates for the peers no direct path reaches, verified in CI against an independent TURN server
    • hole punching on the reliable datagram sockets, for the same problem without the browser, with a TURN relay to fall back on where punching fails (ReliableDatagramServerSocket.allocateRelay)
  • RPC sessions, commands, handlers, and typed responses -- see the RPC guide
  • IPC primitives such as LocalConnection, SharedChannel, and SharedObject, natively (cpp) on Windows, Linux and macOS; on every other target they say so with isSupported and throw when used
  • file APIs, ByteArray, ByteArrayInput, and ByteArrayOutput
  • compression:
    • DEFLATE
    • GZIP
    • LZ4
    • Brotli
  • crypto:
    • natively (cpp) only, from libsodium and BLAKE3 compiled in: Aead (XChaCha20-Poly1305), KeyExchange and X25519, GenericHash (BLAKE2b), HKDF, Ed25519 and Blake3; and from mbedTLS, RSA and ECDSA signatures (PublicKeySignature, SignatureKey). Elsewhere isAvailable() is false and they throw
    • Argon2id natively and on Node 24.7 or later; BCrypt everywhere, in Haxe
    • secure random bytes natively, on the jvm, on Node, in a browser and on PHP; not on the interpreter, neko or HashLink
  • workers, task pools, and NativeProcess, which starts a child process and reads its output natively, on the jvm, HashLink, Neko and Node; not on the interpreter, whose process calls hold every thread while they wait, nor in a browser
  • data structures and utility packages
  • database surfaces for:
    • SQLite
    • MySQL and MariaDB: natively through hxcpp's bundled client, which logs in with caching_sha2_password or mysql_native_password, uses TLS when the server offers it (MySQLConfig.sslMode), bounds its waits and can cancel() a statement; on the jvm through Connector/J on the class path, without the TLS or limit settings
    • PostgreSQL: natively through libpq, loaded at run time, with bound parameters, statement and connect timeouts and cancel(); on php through PDO; no other target
    • MongoDB, through its wire protocol (OP_MSG, SCRAM, TLS, cursors, transactions) on hxcpp, the jvm, the interpreter, hl and neko; not on JavaScript, which cannot block

Timers

Every CrossByte runtime schedules its timers with a min-heap, and for almost everything that is the end of the story. It orders timers exactly, a one-millisecond delay costs what a six-hour delay costs, and thirty thousand recurring timers still leave it using well under a millisecond per frame. You should not have to think about it.

The exception is a runtime holding thousands of short timers that it re-arms constantly — a deadline per connection, a cooldown per entity. That is where the heap's O(log n) starts to show, and CrossByte ships a timing wheel for it:

class MyServer extends ServerApplication {
	public function new() {
		super(WHEEL);
	}
}

The choice belongs to the runtime rather than the build, because a process usually has more than one and they rarely want the same answer. A simulation thread carrying a timer per entity and a network thread carrying a handful can each have what suits them:

var sim = CrossByte.make(DEFAULT, WHEEL);
var net = CrossByte.make(POLL, HEAP);

Measured with one recurring timer per entity, CPU spent per simulated second at sixty ticks:

timers heap wheel
1,000 1ms under 1ms
10,000 10ms 2ms
30,000 44ms 7ms

Arming and cancelling is roughly twice as fast.

Before you switch, the other side of it. The wheel covers a fixed span ahead of now, and anything scheduled past that span waits in a list it rescans periodically — so if your timers are mostly long, you are paying for work the heap never does, and you should stay on the heap. Two smaller differences: timers due in the same tick fire in bucket order rather than by exact time, and a timer can be late by up to a tick. It will never be early; that one is guaranteed.

The short version: reach for the wheel when you have actually seen the scheduler in a profile and your timers are numerous and short. Otherwise the default is already the right answer.

Extensions

CrossByte's extension story is intentional: features that benefit from native backends or external platform libraries can live in sibling haxelibs instead of bloating the core.

Current extension repos:

  • crossbyte-libuv
    • native libuv-backed poll backend
  • crossbyte-brotli
    • native Brotli backend
  • crossbyte-lz4
    • native LZ4 backend

The core remains usable without these extensions. When installed, they can be enabled selectively for native-backed behavior where it matters.

Build defines

All optional, all off unless you pass them.

Define Effect
crossbyte_brotli_native Route Brotli through the native backend from the crossbyte-brotli haxelib instead of the bundled Haxe implementation.
crossbyte_lz4_native Route LZ4 through the native backend from the crossbyte-lz4 haxelib instead of the bundled Haxe implementation.
crossbyte_libuv_native Build the libuv poll backend from the crossbyte-libuv haxelib (cpp only). Needs libuv's headers and library, and LibuvPoll.install() called before the first runtime is created; without the define install() returns false and the built-in backend is used. See that repository's README.
crossbyte_no_http2 Do not auto-register the bundled HTTP/2 backend. A backend registered explicitly through HTTPBackendRegistry still wins either way; this only stops the bundled one from being picked up on its own.
http_debug Log each response line the HTTP client reads, through Logger, so it honours the configured level and sink.
crossbyte_debug Keep crossbyte.io.File out of @:noDebug, so its frames appear in stack traces.

For example:

haxe -lib crossbyte -lib crossbyte-lz4 -D crossbyte_lz4_native -main Main --cpp bin

HashLink and Neko

Both build and run the test suite, in CI on Windows and Linux. What they need, and what they do not have:

HashLink 1.13 or later, and say so. Haxe 4.3 assumes HashLink 1.12 unless told otherwise, and crossbyte.utils.Random uses haxe.atomic, which will not compile for anything older -- the build stops inside the standard library with "Atomic operations require HL 1.13+". Pass the version you run on:

haxe -lib crossbyte -D hl-ver=1.13.0 -main Main --hl main.hl

The .hdll files, next to hl. HashLink resolves every native a program was compiled with when it loads, not when one is called, so a missing library stops the program before main -- and on Windows it says so in a dialog box, which on a service or a build machine nobody will ever click. Which ones a program needs depends on what it compiles in:

library needed by
ssl.hdll anything that uses the network, TLS or not: every socket type reaches sys.ssl
fmt.hdll haxe.crypto.Md5 and Sha1 (the WebSocket handshake, TURN credentials) and haxe.zip
sqlite.hdll SQLiteConnection
mysql.hdll MySQLConnection

The HashLink release for Windows ships all four. A Linux build from source makes them with make libhl hl fmt ssl sqlite mysql, given mbedTLS, zlib, libpng, libturbojpeg, libvorbis and SQLite's headers. A library a program never calls can be skipped with HL_DISABLED_LIBS=sqlite,mysql (HashLink 1.14): its functions then throw when called rather than stopping the load.

What is not there. Neither target has a secure random source, so SecureRandom.isSupported is false and everything that needs one refuses, saying so: BCrypt.hash, PKCE, WebSocket clients, STUN, TURN, ICE and WebRTC. Both are IPv4 only. LocalConnection, SharedChannel and SharedObject and the libsodium, BLAKE3 and mbedTLS crypto are native features only; ALPN (so HTTP/2 over TLS) is native or jvm; and a datagram socket's buffers cannot be sized (DatagramSocket.bufferSizeSupported is false: they read 0 and setting them throws). On Linux, hl polls its sockets through select, which cannot watch a descriptor numbered 1024 or above, and hl has no poll natives to move to: a server there fails its polling once that many descriptors are open. neko polls through its own natives and is not held to it.

Two things about neko's numbers and clock. An Int there is 31 bits, and Array.sort is a native merge sort that takes a comparator's answer too large for one as "less" -- so a comparator written as a subtraction of large values sorts wrongly there; answer -1, 0 or 1. And on Windows haxe.Timer.stamp() is the time of day to the millisecond, moving once a system tick, so two readings a few microseconds apart are usually equal.

Samples

The repository includes small runnable samples for:

  • primordial applications
  • TCP chat
  • RPC
  • LocalConnection, SharedChannel, and SharedObject IPC
  • UDP and reliable datagrams
  • HTTP serving
  • worker/background tasks

See samples/README.md for the current sample index and build commands.

Testing

CrossByte uses utest for its test suite.

The repository root is now described by Aedifex.hx. That file is the source of truth for the library identity, task list, and generated haxelib.json metadata.

To refresh haxelib.json from Aedifex.hx, run:

aedifex haxelib sync <project-root>

Run the fast interpreted suite with Aedifex:

aedifex task interp-tests <project-root>

The raw compiler entrypoint still exists underneath:

haxe ci/interp-tests.hxml

Build the native smoke executable with Aedifex:

aedifex task native-tests <project-root>

The raw compiler entrypoint still exists underneath:

haxe ci/native-tests.hxml

Then run the produced executable:

./export/ci-native-tests/NativeSmokeMain

To inspect the registered CrossByte tasks, run:

aedifex tasks -json <project-root>

Generate docs through Aedifex with:

aedifex task docs-api <project-root>
aedifex task docs-site <project-root>

In the examples above, <project-root> is usually . when you are already in the repository root.

Benchmarks

A performance suite lives in tests/bench and covers the paths that run once per unit of real work -- per datagram, per connectivity check, per event -- so a regression there is a regression multiplied by traffic. Build and run it natively:

haxe ci/bench.hxml
./export/bench/BenchMain.exe

It reports the best of several samples per case; the numbers compare shapes of code on one machine in one sitting and are not comparable across machines. CI runs it so it cannot rot, and ignores the numbers.

CI

The repository CI covers:

  • fast interpreter tests
  • generated API documentation
  • hxcpp API audit builds
  • the native suite on Windows, Linux and macOS
  • native sample builds
  • the whole suite on HashLink and Neko, on Windows and Linux (hl-neko.yml)
  • sibling extension jobs for the optional native modules

CI builds against the production branch of the dimensionscape/hxcpp fork, which is also what a local native build should use: the poll/index fixes CrossByte depends on, and the fork's other corrections since. socket-fixes is the narrow branch the upstream pull request lives on; the note in ci.yml says why CI does not follow it.

Each CI run now also publishes a crossbyte-api-docs artifact containing the generated dox site for that revision.

Design Direction

CrossByte is trying to be a serious runtime layer, not a grab-bag of unrelated helpers.

That means:

  • portable core behavior first
  • native acceleration as opt-in extensions
  • efficient hot paths for network and byte-oriented code
  • typed APIs where they add real leverage
  • enough low-level access to stay useful in unusual projects

If you are building something network-heavy, service-oriented, or systems-adjacent in Haxe, CrossByte is meant to give you a lot of the unglamorous but important foundation work in one place.

About

CrossByte: A versatile cross-platform framework offering essential tools and utilities for streamlined application development across diverse environments.

Resources

Stars

13 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages