__ _ __ / /_ ___ (_)___/ /______ ______ / __ \/ _ \/ / __ / ___/ / / / __ \ / / / / __/ / /_/ / / / /_/ / / / / \/ /_/\___/_/\__,_/_/ \__,_/_/ /_/
Hotline-protocol wire format, codecs, and clients for the Heidrun Mac client and HeidrunServer. Pure Swift 6; the value layer has no Apple-only dependencies, so it builds on both macOS and Linux.
HeidrunCore— wire-level value types (PacketHeader,PacketObject,TransactionType,RemotePath,ConnectionSettings), codecs (PacketCodec,FileListEntryCodec,NewsBundleEntryCodec, …), the Network.framework-basedHotlineNetworkClient, andHotlineTrackerClient. Apple-only files are#if canImport(Network)-gated so Linux builds skip them cleanly.HeidrunNIOClient— cross-platform Hotline transport on SwiftNIO. ReusesHeidrunCore's codecs andEventBroadcaster.heidrun— text-only Hotline CLI ("modern HX") built onHeidrunNIOClient.
// Package.swift
dependencies: [
.package(url: "https://github.com/franckjej/heidrun-protocol.git", exact: "1.0.0-rc23")
]
// in a target's dependencies:
.product(name: "HeidrunCore", package: "heidrun-protocol")Pin pre-release tags with exact:, never from: — SemVer pre-release identifiers compare lexically, so from: "1.0.0-rc10" quietly resolves back to rc9 ('1' < '9').
swift build
swift testswift run heidrun <host[:port]> -l <login> -p <pw> -n <nick>Opens an interactive Hotline session. The REPL works like classic HX:
- Chat — bare text → public;
/msg <socket> <text>,/me <action> - Users —
/who,/info <socket>,/nick <name> - Files —
/ls [path],/finfo <path>,/get <path>,/put <local> [<remote-dir>](HTXF, 64 KiB chunks, progress) - News (plain) —
/news,/post <text> - News (threaded) —
/tnews [path],/tthreads <path>,/tread <path> <id>,/tpost <path> | <title> | <body>,/treply <path> <id> | <body> - Admin (needs the matching server privileges) —
/newuser <login> <pass> <nick> [priv,…],/getuser <login>,/moduser <login> <nick> [priv,…] [pass],/deluser <login>,/kick <socket> [ban],/broadcast <message>. Privileges are comma-separated names (e.g.readChat,sendChat,postNews);/getuserlists them back. - Server-forwarded — any unrecognised
/cmdis forwarded as chat (e.g./topic <subject>).//foosends the literal text/foo. - Housekeeping —
/help,/version(CLI + negotiated protocol versions;//versionasks the server for its own),/privs(list the privilege names admin commands accept),/quit
Arrow keys browse command history (persists at ~/.heidrun_history); TAB completes builtin command names; the connection auto-reconnects on disconnect with capped exponential backoff.
For CI and shell scripts, the admin operations are also available as flags that connect, perform one operation, and exit with a status code (non-zero on a usage or server error) — no REPL:
heidrun <host> -l admin -p <pw> --create-user bob secret Bob readChat,sendChat
heidrun <host> -l admin -p <pw> --show-user bob
heidrun <host> -l admin -p <pw> --modify-user bob Bobby readChat --user-password newpass
heidrun <host> -l admin -p <pw> --kick 5 --ban
heidrun <host> -l admin -p <pw> --broadcast "back in 5"
heidrun <host> -l admin -p <pw> --delete-user bobFlags: --create-user, --show-user, --modify-user (+ --user-password to set one), --delete-user, --kick (+ --ban), --broadcast. Pass one per invocation.
heidrun --version prints the package version and heidrun --list-privileges prints the valid privilege names — both work offline (no <server> needed).
Heidrun layers additive, non-standard extensions on top of base Hotline (e.g. emoji user avatars). They degrade gracefully to standard behaviour on vanilla servers. Wire layouts are specified in docs/PROTOCOL-EXTENSIONS.md.
- All multi-byte ints are big-endian.
- String encoding defaults to
.macOSRoman; overridable per connection. - Login + password obfuscation: XOR every byte with
0xFFon classic login (TX 107) and most account-admin transactions — exceptopenLogin(352), where login goes plain. - Path encoding (objIDs 202, 212, 325):
UInt16 componentCount+ per-component(UInt16 0, UInt8 length, name bytes). - HTXF handshake variants:
- File download —
"HTXF"+UInt32 transferID+UInt32 transferSize+UInt32 reserved (0). - Folder upload — the trailing 4 bytes become
UInt16 1, 0. - Folder download — 18 bytes with a
UInt16 3sentinel.
- File download —
- Hotline timestamps: seconds since
1904-01-01 00:00:00 UTC(classic Mac epoch). SeeHotlineDate. - File upload framing:
FILP40-byte header (forkCount=3) →INFOblock (74 + nameLen) with HFS type/creator + 1904-epoch dates + name →DATAfork header (16 B) + data fork →MACRfork header (16 B) + resource fork. Resource forks round-trip on single-file uploads, folder uploads, and folder downloads; pass an emptyDatafor data-fork-only files.
GPL-2.0. Full text in LICENSE.
The macOS client this package serves is a Swift port of the 2002 Heidrun Hotline client by Göran Granström, whose original plug-in modules were GPL-2.0; this package shares that lineage.
Copyright © Daubit & Francke GmbH. The copyright holder reserves all rights to license this code under other terms — commercial, proprietary, BSD/MIT-style, or any other arrangement — for its own products and for third parties on request. The GPL-2.0 grant above governs public/community use; it does not bind the copyright holder's re-use of the same code under different terms.
For a non-GPL licence: jens.francke@daubit-francke.de.
Links the following Apache 2.0 packages: swift-nio and swift-argument-parser, plus transitively swift-atomics, swift-collections, and swift-system.