
Company News
Free Business Plan Upgrades for Open Source Maintainers
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.
dfhack-remote-node
Advanced tools
A small, strictly-typed Node client for the DFHack
Remote RPC protocol — protobuf over raw TCP (127.0.0.1:5000). It lets a Node
process read from (and, later, act on) a live Dwarf Fortress fort without
compiling against DFHack's C++.
This is a TypeScript port of the original browser client
(alexchandel/dfhack-remote, ISC).
The protocol codec is carried over; the transport was rewritten from
WebSocket + websockify to Node net.Socket, RunCommand console output is now
captured instead of discarded, and methods bind lazily on first use.
Built primarily as the RPC layer for a DFHack MCP server.
npm install dfhack-remote-node
The published package ships prebuilt (dist/ ESM + type declarations, proto
bundle inlined), so no build step is needed to consume it.
To work on the library from a clone instead:
npm install
npm run build # regenerates build/proto.json and bundles dist/
build/proto.json (the compiled protobuf bundle) is committed, so the source
runs immediately; npm run build produces the distributable dist/ (ESM +
type declarations, with the proto bundle inlined).
import { DwarfClient } from 'dfhack-remote-node';
const df = new DwarfClient(); // defaults to 127.0.0.1:5000
await df.connect(); // handshake
console.log(await df.getVersion()); // DFHack version string
const world = await df.getWorldInfo(); // decoded GetWorldInfoOut
console.log(world.worldName?.englishName);
// Arbitrary Lua snippet; returns whatever it prints (captured console output):
const text = await df.runLuaSnippet('print(#df.global.world.units.active)');
// Any method in the METHODS table, by name:
const map = await df.call('GetMapInfo');
df.close();
Every reply object also carries _text — the concatenated console output from
any TEXT frames the call produced.
src/ is split by concern (all TypeScript ESM):
| File | Responsibility |
|---|---|
codec.ts | wire framing: magic bytes, frame ids, encodeMessage, readHandshake, readFrame |
errors.ts | RpcError, ProtocolError |
connection.ts | DfConnection — the net.Socket transport + call queue |
methods.ts | the METHODS table (fully-qualified protobuf type names) |
proto.ts | loads the committed proto bundle as a JSON module import |
client.ts | DwarfClient — the high-level API |
index.ts | public exports |
The source imports the proto bundle as a JSON module
(import protoJson from '../build/proto.json' with { type: 'json' }), so the
bundler inlines it into dist/ — there is no runtime file lookup.
The
.protofiles are pinned to DFHack53.15-r2(pulled from DFHack/dfhack at the commit the running build reports viagetGitDescription). To retarget another version, replace the files from the matching tag and re-runnpm run gen-proto. Method input/output types inMETHODS(src/methods.ts) are fully-qualified names that must match DFHack's registration, orBindMethodreports a "wrong signature" at runtime — which makes drift detectable per method.
npm run build # gen-proto + tsup -> dist/ (ESM + .d.ts, proto inlined)
npm run gen-proto # regenerate build/proto.json from proto/*.proto
npm run typecheck # tsc --noEmit
npm run lint # eslint (flat config)
npm run format # prettier --write
npm test # offline: mock server speaks the real wire format, no game needed
npm run spike # live: connect to a running fort, print version + world + fort name
npm test validates handshake, framing, lazy binding, protobuf round-trip, and
TEXT-frame capture against an in-process mock. npm run spike (M0) requires
Dwarf Fortress running with DFHack and a fort loaded.
DFHack?\n + i32(1), server replies DFHack!\n + i32(1).id:i16, pad:u16, size:i32 (little-endian), then size body bytes.TEXT frames (console output) followed by one
RESULT frame (reply protobuf) or one FAIL frame (errno in the size field).GetBlockList only returns blocks changed since the last call — don't build a
full-map snapshot on it naively (use ResetMapHashes to force a resend).Issues and PRs welcome. Please keep the module boundaries above intact, run
npm run typecheck, npm run lint, and npm test before opening a PR, and add
new RPC methods to src/methods.ts with their fully-qualified protobuf type
names. Changes touching the live protocol should be verified against a running
fort with npm run spike.
ISC. Ported from alexchandel/dfhack-remote (Copyright © 2020 Alexander Chandel); the original copyright and license are preserved in LICENSE.md.
FAQs
Node client for the DFHack Remote RPC protocol (protobuf over TCP)
The npm package dfhack-remote-node receives a total of 33 weekly downloads. As such, dfhack-remote-node popularity was classified as not popular.
We found that dfhack-remote-node demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.