# 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: ```sh cabal-cli cabal:// ``` 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 `. 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](https://github.com/cabal-club/cabal-cli#commands). The [cabal-core API](https://github.com/cabal-club/cabal-core#cabalgetlocalkeycb) 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=` 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 ` 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//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`: ```sh 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: ```powershell $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.