This file is meant ONLY for LLM agents.
Bladedance is an IRC server forked from InspIRCd v4.10.1, in the middle of a full, permanent migration from C++ to Rust. This file tells any AI agent working in this repo how to build/run it and how to make migration progress correctly. Read this in full before touching any file.
- Build:
./build.sh - Run:
./start.sh
Never invoke meson or ninja directly (meson setup, meson compile,
meson install, ninja, ninja -C build, etc. are all off-limits), and never
call cargo build/cargo test by hand as a substitute either. Always go
through ./build.sh. Reasons this matters, not just style:
build.shdoes several things a baremeson compilewill not: it builds thesrc/rustcore static lib with Cargo, copies it tobuild/librust_core.awhere meson's linker step expects it, dynamically scaffolds a throwaway Cargo project per file insrc/modules/*.rsand builds each as acdylib, and copies the resulting.sofiles intorun/modules/. Skipping this means stale or missing Rust artifacts and a server that silently fails to load modules.- It re-copies
librust_core.aa second time aftermeson compile, which matters because meson's own build graph does not know about Cargo's output and won't rebuild/copy it for you. - If
build/doesn't exist yet it also runsmeson setup buildfor you — there is no separate "first-time setup" step to remember.
If ./build.sh fails, fix the underlying problem (missing dependency, broken
Rust code, meson.build error) — do not "unblock yourself" by dropping to
meson/ninja/cargo directly. If you believe the scripts themselves need a
new step (e.g. a newly added Rust module needs building, a new dependency
needs adding to the generated per-module Cargo.toml in build.sh), edit
build.sh itself so the fix is permanent and the next agent/run benefits
too. The same applies to start.sh if the run invocation needs to change.
This is not a permanent hybrid architecture — it is a migration in progress. The finished state has:
- Zero
.cpp/.hfiles undersrc/(or wherever remains at that point). - No
meson.build,meson_options.txt, or Meson/Ninja dependency anywhere in the repo or inbuild.sh. - A single (or small number of) Cargo workspace(s) as the only build system.
build.shshould eventually be a thin wrapper aroundcargo build --release(or be retired in favor of calling Cargo directly, once agents/docs are updated to match) andstart.shshould run the compiled Rust binary directly. - No
_glue.cppshim files — these are temporary FFI adapters, not permanent architecture. Every one that currently exists should eventually be deleted once the C++ code it bridges to is gone.
Treat every session as an opportunity to shrink the C++ surface, not just add
Rust alongside it forever. When in doubt about priority, prefer work that
lets a _glue.cpp file or a meson.build block be deleted over work that
only adds new standalone Rust.
Follow the strangler-fig approach already in use in this repo (see
skills/RUST_MIGRATION.md for the general methodology). Concretely, here:
- Pick the next target using the dependency order below (leaves first).
- Look for the existing
*_glue.cppfile for that subsystem if one exists (e.g.xline_glue.cpppairs withxline.rs) — it defines the current FFI contract you need to either extend or fully replace. - Port behavior, not syntax, into a
.rsfile following the idioms already established in the existingsrc/*.rsfiles (they set the house style — match it rather than inventing a new pattern per file). - Wire it in so
./build.shpicks it up:- Core library code goes in
src/rust/(part of therust_corecrate built once and linked into the C++ binary vialibrust_core.a). - Loadable IRC modules go in
src/modules/*.rs—build.shautomatically scaffolds and builds each one as its owncdyliband drops it inrun/modules/. Follow the existing per-moduleCargo.tomltemplate insidebuild.sh(dependencies:mongodb,tokio,chrono,async-trait,rust_core,tracingare already available; add new deps to the heredoc inbuild.shif a module needs one, not by hand per-build).
- Core library code goes in
- Update or delete the corresponding
_glue.cppand anymeson.buildentry that referenced the old.cppfile once nothing else depends on it. - Run
./build.shand confirm a clean build, then./start.shand smoke test the affected behavior before moving on. - Delete the superseded
.cpp/.hfiles. Don't leave dead C++ behind "just in case" — that is the opposite of progress on this migration.
Rough guide, adjust as coupling is discovered:
- Remaining standalone
m_*.cppmodules with few cross-module dependents (most IRC feature modules — checksrc/modules/). core_*command modules undersrc/coremods/(whowas, whois, stats, lusers, etc.) — most are already fairly isolated.- Supporting subsystems that still have a paired
*_glue.cppbut no dependents left after step 1–2 clears (check withgrep -rlon the header name acrosssrc/). - Central/hub files last:
base.cpp,channels.cpp,server.cpp,modulemanager_glue.cpp,socketengine*,streamsocket.cpp— these have the most dependents and the least room for error. - Only once step 4 is done: collapse the build itself — remove
meson.build/meson_options.txt, rewritebuild.sh/start.sh(or retire them) to be pure Cargo, deleteinclude/C++ headers that are no longer referenced.
- Never break
./build.shor./start.sh. If a change can't keep both working, split it into smaller increments. - Keep all FFI surface concentrated in the
*_glue.cppfiles and the corresponding.rsmodules — don't scatter new ad hocextern "C"glue elsewhere. - Don't reintroduce SpanningTree/server-linking — it was deliberately removed (see README) and is out of scope to restore.
- New features and bugfixes should be written in Rust, not C++, even if the
surrounding subsystem hasn't been migrated yet — don't add new
.cppfiles. - Config format migration to TOML (see README Todo) is a separate, valid unit of work and can be picked up independently of the C++→Rust migration.