Files
dat-thang/docs/cable-interop.md
T
divorce b735900f2e 0.0.002
ddat interop with dat js
cable interop with cabal js
ddat's frontend = dat-thang
cable fe = cable-thang
dillo support based on cel's dillo-dat
bootstrap node
xplatform support

Signed-off-by: divo <418@cryspace.space>
2026-10-19 00:17:02 -04:00

8.0 KiB

Cable and Cabal interoperability notes

This document describes the test-only Cabal/Hypercore interoperability harness under tests/interop/cabal/. It is kept to compare historical behavior and is excluded from production builds and runtime paths.

The production cable and cable-thang clients are C/S7 applications: Cesspool handles discovery and transport, Sleep handles persistent local feeds, and S7 represents room events as S-expressions. Cabal CLI and Hypercore are test targets, not production dependencies.

Which client to run

The working historical Cabal terminal client is cabal-cli. To join a room, run:

cabal-cli cabal://<room-key>

The cable executable also supports local Sleep feed and Cesspool bootstrap commands. The native Cable chat client is cable-thang, using a terminal-style SDL2 layout inspired by Cabal's TUI. Build with make ENABLE_SDL2=1; create an address in the terminal with build/linux/cable --new, then launch the GUI with build/linux/cable-thang <cable://address>. Use /join NAME, /nick NAME, Ctrl+N, and Ctrl+P in the client.

Cabal behaviors being ported

The Cabal CLI's /whoami (also /key) displays the local user key, while /nick changes that user's display name. Those are different things: the user's public key is the stable identity within that room; the nickname is a readable label published as an about record in that user's room feed. The CLI's usage and commands are listed in the cabal-cli README. The cabal-core API documents getLocalKey() as the local user's key and documents messages as records published to user feeds. Cabal creates its local writer feed inside each room's storage, so a user's key is room-scoped, stable for that room, and can differ in another room. A room key and its optional ?admin=<key> moderation key are separate from each user's feed key. Cabal text records use chat/text with channel and text fields.

Dat-era Cabal reference

Cabal Core 5.0.1 is a concrete older reference candidate: it was published about seven years ago, and a 2019 Cabal walkthrough describes the chat as running over Dat (the walkthrough was later updated for Cabal Core 5.0). The bundled Dillo Dat tree supplies a nearby protocol reference with Hypercore 7.7.1, hypercore-protocol 6.12.0, Hyperdrive 9.16.0, and hyperdiscovery 7.1.1. Use those versions to guide backwards-adaptation; do not treat current Cabal Core 16 or its Hyper:// networking as the target.

The repeatable JS behavior tests remain useful for room identity, nicknames, channels, and record shapes. To run them against the historical package, point CABAL_JS_MODULE and CABAL_CLIENT_MODULE at a Cabal Core 5.0.x install, then run make test-cabal-interop.

Current C groundwork

  • cable address accepts room keys with cabal://, cable://, or hyper://, including a ?admin=<64 hex> parameter, and prints canonical cable:// room addresses while retaining the admin key separately.
  • cable whoami <room-address> and dat-thang expose a persistent local user key as a cable://<64 hex> user address. Each room gets its own 64-byte Ed25519 secret at ~/.cable/rooms/<room-key>/user-feed.key, with owner-only permissions. The key is stable in that room across restarts and distinct from the room and admin keys.
  • hyper:// parsing only aliases the key representation; production Cable does not implement the newer Hypercore stack.
  • The library defines a bounded Cable text-message envelope and libsodium sealed-box encrypt/decrypt helpers.
  • dat-thang's left panel is a peer list again. Double-clicking a discovered peer opens a direct-message pane; messages use the C-only sealed Cesspool record and can be exchanged between Cable-enabled dat-thang clients.
  • cable-thang is a native C/S7 room client. Cesspool handles discovery and authenticated transport, Sleep persists local feeds, and S7 builds signed room-event S-expressions. The room UI supports room creation, named channels, nickname changes, and channel cycling. This path is Cable's own protocol and does not claim Cabal wire interoperability.

Hypercore feed storage check

The test-only Cable compatibility writer occupies the room/0 slot used by Cabal Core 5's multifeed, exposes the expected localname, and shares the Hypercore tree, signature, bitfield, key, and data files. Run tests/interop/dat/cable_hypercore_compat.ps1 on Windows with WSL and Node installed. It writes Cabal-shaped nick and chat JSON blocks from C, opens and verifies them with the bundled Hypercore 7.7.1, appends a JavaScript block, then confirms C reads that appended block. This verifies the standalone C feed format against the legacy Hypercore fixture. It is separate from the native Sleep feeds used by cable-thang.

Side-by-side regression harness

tests/interop/cabal/cabal_cable_side_by_side.js creates a Cabal JS room, publishes a nick and chat/text entry, and verifies that a second Cabal JS instance receives both through Cabal's live replication stream. It then gives Cable that exact room address, including ?admin=, checks address normalization, and checks that Cable's local user address persists in the room and changes for a second room. This covers address normalization and the CLI's local identity utility; it does not exercise cable-thang's network session.

tests/interop/cabal/cabal_cli_features.js uses cabal-cli's own cabal-client API to create a room, execute /nick and /join, traverse joined channels using the same channel ordering as the CLI's Ctrl+N handler, and exchange chat/text entries between two independently opened Cabal clients over live Hypercore replication. The C SDL live harness is tests/interop/cabal/cable_js_c_live.js; it launches one Cabal Core JavaScript peer and one headless SDL/C client, then checks peer discovery and chat in both directions through the Cabal Hypercore feeds. Run it with make test-cable-live ENABLE_SDL2=1 after building SDL2 support and installing Node.js and Cabal Core. The test uses a local Hyperswarm DHT bootstrap node and separate client UDP ports.

Run it once the historical JS cabal-core package and Node are available. The test invokes Cabal JS directly; the UI command for people is cabal-cli:

make test-cabal-interop CABAL_JS_MODULE=/path/to/cabal-cli/node_modules/cabal-core
make test-cabal-interop CABAL_CLIENT_MODULE=/path/to/cabal-cli/node_modules/cabal-client

On Windows with Cable built in WSL, run the same harness with Windows Node and the installed Cabal package, routing each Cable CLI call through WSL:

$env:CABAL_JS_MODULE = "$env:APPDATA\npm\node_modules\cabal-cli\node_modules\cabal-core"
$env:CABLE_BIN = "$PWD\build\cable"
$env:CABLE_RUNNER = 'wsl'
node tests/interop/cabal/cabal_cable_side_by_side.js

The harness maps its temporary Cable home directory into WSL for this mode. This is useful when the WSL distribution has no Node installation; no second Node runtime is needed.

The normal make test runs this harness when it can resolve cabal-core; it prints SKIP when the configured Node runtime cannot resolve the optional JS package. A standalone make test-cabal-interop reports the resolution error directly.

Compatibility boundary

The upstream Cabal components documented here use Hypercore feed replication and their own Cabal database/index conventions. Cable's current GUI messages use a Cesspool transport record and do not enter Cabal feeds, so they are not yet readable by Cabal JS clients. /nick in Cabal appends a { type: "about", content: { name: nick } } record to the user's room feed; the public user-feed key remains its unique identity. Cable has not yet implemented that nickname record in the Cabal message schema. The next compatibility layer needs the Hypercore transport/session selected for the deployed Cabal version, feed discovery, Cabal's multiwriter user-feed model, and its JSON message/index schema. Dat archive wire identifiers and dat:// handling remain independent.