This guide is the deep reference for people who build, package, extend, deploy,
or operate ImHex: system architects, plugin developers, packagers, and power
users. It documents ImHex's architecture, the libimhex SDK and ContentRegistry
extension surface, the event system, the build and compile pipeline,
configuration, security posture, operations, and a code-quality and improvement
analysis.
End-user installation and feature usage live in the companion User Guide; this document assumes that material and focuses on internals.
Note
ImHex is GPLv2-licensed overall, with two components under LGPLv2.1 to permit proprietary plugins:
lib/libimhex(the plugin interface library) andplugins/ui(shared UI elements). This split is deliberate — see §9 Security & Licensing.
Table of Contents
- Executive Summary
- Architecture Overview
- Repository Structure
- Process Model & Startup
- The libimhex SDK
- The ContentRegistry: Extension Surface
- The Event System
- Plugins Inventory
- Security & Licensing
- Building & Compiling
- Plugin Development
- Scripting (C# / .NET and C)
- The MCP Server
- Configuration Reference
- Filesystem & Path Resolution
- Operations
- Code Quality & Conventions
- Testing
- Potential Improvements & Tech Debt
- Quick Reference
1. Executive Summary
ImHex is a cross-platform hex editor and binary-analysis platform written in
modern C++ (C++23). Architecturally it is not a monolith but a small native
core (main) plus a plugin runtime (libimhex) that loads feature plugins
as shared libraries at startup. Almost every user-facing capability — the hex
editor view, the Pattern Language integration, hashing, diffing, disassembly,
YARA, visualizers, the script loader, OS integrations — lives in a plugin that
registers itself through a central ContentRegistry and communicates via a
decoupled EventManager.
Key design properties:
| Property | How it's realized |
|---|---|
| Extensibility | Plugins are shared libs discovered at runtime; everything user-facing is registered through ContentRegistry |
| Decoupling | Components never call each other directly — they post/subscribe to typed events via EventManager |
| Provider abstraction | All features operate on a prv::Provider byte source (file, disk, memory, GDB, remote…) |
| Portability | One codebase targets Windows, macOS, Linux, FreeBSD, and WebAssembly (Emscripten) |
| Resilience | Crash handlers capture stack traces, save a crash-backup project, and run cleanup hooks |
| GUI | Dear ImGui (immediate-mode) rendered via OpenGL 3.0+ |
The codebase analyzed here is commit 8a6be85 (2026-05-30): ~796 files,
~1.05 M characters of source, 474 code files, organized into a core library, a
GUI host, an updater, a Windows forwarder, and ~13 plugins.
2. Architecture Overview
┌─────────────────────────────────────────────┐
│ Executables │
│ │
Explorer / Terminal ─▶│ imhex.exe (forwarder, Windows only) │
│ └─ launches ──▶ imhex-gui (main host) │
│ imhex-updater (standalone updater) │
└───────────────────────┬──────────────────────┘
│
┌──────────────────────────────▼───────────────────────────┐
│ main/gui (host process) │
│ • main(): locale, TaskManager, crash handlers, native │
│ init, messaging, CLI dispatch, then runImHex() │
│ • Window: GLFW + OpenGL + Dear ImGui render loop │
└──────────────────────────────┬───────────────────────────┘
│ links
┌──────────────────────────────▼───────────────────────────┐
│ lib/libimhex (the SDK / runtime) │
│ │
│ PluginManager ContentRegistry EventManager │
│ TaskManager ImHexApi ShortcutManager │
│ ThemeManager LayoutManager WorkspaceManager │
│ LocalizationMgr AchievementMgr TutorialManager │
│ ProjectFile prv::Provider MCP server │
└───┬───────────┬───────────┬───────────┬───────────┬────────┘
│ loads │ loads │ loads │ loads │ loads
┌───▼───┐ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌───▼─────┐
│builtin│ │ hashes │ │disassem.│ │ yara │ │ ...etc │
│plugin │ │ plugin │ │ plugin │ │ plugin │ │ plugins │
└───────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
The flow at runtime:
- The host (
main/gui) starts, sets up theTaskManager, crash handlers, and native window, then asksPluginManagerto load libraries and plugins. - Each plugin, on init, registers content (views, tools, data inspector
rows, hashes, providers, menu items, settings, pattern functions, …) into the
ContentRegistry, and subscribes to events it cares about. - The render loop draws registered views with Dear ImGui every frame, dispatches shortcuts, and posts frame events.
- Features interact only through events (
EventManager) and the shared provider byte source — never by direct cross-plugin calls.
Note
This is a classic registry + event-bus plugin architecture. Adding a feature means writing a plugin that registers entries and subscribes to events — no core changes required. That is the single most important fact for anyone extending ImHex.
3. Repository Structure
| Path | Purpose |
|---|---|
main/gui/ |
The GUI host executable: main(), window/render loop, init tasks, crash handlers, messaging, CLI entry |
main/forwarder/ |
Windows-only launcher that manages the console window and forwards stdio to imhex-gui |
main/updater/ |
Standalone cross-platform updater executable |
lib/libimhex/ |
The SDK: public headers (include/hex/...) + implementation (source/...). The LGPL plugin interface |
lib/trace/ |
Stack-trace / exception-tracing helper library |
plugins/builtin/ |
The default plugin — most core ImHex functionality (views, tools, providers, CLI) |
plugins/ui/ |
Shared UI widgets and the pattern text editor (LGPL, so plugins can reuse it) |
plugins/hashes/ |
Hashing algorithms and the Hashes view |
plugins/disassembler/ |
Capstone-based disassembler and views |
plugins/yara_rules/ |
YARA scanning integration |
plugins/diffing/ |
Data diffing algorithms and view |
plugins/decompress/ |
Decompression support for the data processor |
plugins/visualizers/ |
Pattern Language visualizers (images, audio, 3D, …) |
plugins/remote/ |
Remote (SSH/SFTP) and network providers |
plugins/fonts/ |
Font loading and management |
plugins/windows/ |
Windows-specific integrations |
plugins/script_loader/ |
Loads .NET (C#) and C scripts as content |
cmake/ |
Build modules, the plugin SDK template, toolchain helpers |
dist/ |
Packaging, dependency installer scripts, compiling guides, vcpkg manifest |
resources/ |
Logos, banners, sample projects |
tests/ |
Unit/integration tests (common, helpers, algorithms, plugins) |
Each plugin follows the same internal shape: source/ (implementation),
include/ (its own headers), and romfs/ (embedded resources: localization
lang/, themes, shaders, MCP tool definitions, etc.) baked into the binary via a
ROM-filesystem mechanism.
4. Process Model & Startup
Executables
ImHex ships three executables:
imhex-gui— the real application (the host + plugins).imhex(Windows forwarder) — works around a Windows limitation: a GUI app launched from Explorer would otherwise get a stray console window, while a console app would always allocate one. The forwarder callsFreeConsole()when launched from Explorer (hiding the console) and enables ANSI escape sequences when launched from a terminal, then spawnsimhex-gui.exe, wiring stdio through pipes and a Job Object so the child dies with the parent. It sets__IMHEX_FORWARD_CONSOLE__=1to tell the GUI to forward its console output.imhex-updater— downloads and installs updates (see §16).
Note
The forwarder exists purely because Windows lacks a "console-like GUI app" subsystem. On macOS/Linux a single executable behaves correctly, so there is no forwarder there.
main() sequence (host)
From main/gui/source/main.cpp, startup runs in this order:
setlocale(LC_ALL, "en_US.utf8").TaskManager::setMainThreadId(...)and name the main thread ("ImHex 🔍").crash::setupCrashHandlers()— install signal/exception handlers first, to catch early crashes.trace::enableExceptionCaptureForCurrentThread().Window::initNative()— platform window init.messaging::setupMessaging()— single-instance / inter-instance messaging.- If
argc > 1: disable the crash callback and runinit::runCommandLine(). - Log version, commit, and OS/arch info (so user-submitted logs are useful).
init::runImHex()— enter the render loop.
CLI dispatch
runCommandLine() (in main/gui/source/init/run/cli.cpp) loads plugin
libraries and plugins (without initializing them), then calls
hex::subcommands::processArguments(args). Subcommands are matched against the
--long / -short names registered by each plugin via IMHEX_PLUGIN_SUBCOMMANDS.
On Windows the raw UTF-16 command line is re-parsed with CommandLineToArgvW and
converted to UTF-8. Plugins are intentionally not unloaded after CLI handling
— some CLI commands configure plugin state and then let ImHex start normally.
Crash handling
main/gui/source/crash_handlers.cpp installs handlers for SIGSEGV, SIGILL,
SIGABRT, SIGFPE (and SIGINT, SIGBUS where present); on Windows it uses
SetUnhandledExceptionFilter mapping structured exceptions to the same path. On
a crash it:
- Posts
EventAbnormalTerminationso plugins can clean up. - Exports the current workspace and, if any provider is open, writes a
crash_backup.hexprojto a writable config path. - Writes
crash.json(pointing at the log file and the backup project). - Prints a demangled stack trace and runs exit tasks, then terminates.
The crash callback starts as a native message box (for very early failures) and
is swapped to the file-saving handler once EventImHexStartupFinished fires.
5. The libimhex SDK
lib/libimhex is the LGPL library that both the host and every plugin link
against. Public headers live under include/hex/. The major managers (all static
"manager" classes, deliberately non-instantiable) are:
| Class / namespace | Responsibility |
|---|---|
PluginManager |
Discover, load (load/loadLibraries), initialize, enable/disable, and unload plugins; expose plugin metadata and subcommands |
ContentRegistry |
The registration surface for all extensible content (see §6) |
EventManager |
Typed publish/subscribe event bus (see §7) |
TaskManager |
Async/background/blocking task scheduling, deferred (doLater) calls, thread naming |
ImHexApi |
Runtime facade: System, Provider, HexEditor, Bookmarks, etc. |
ShortcutManager |
Global and view-local keyboard shortcuts, remapping, macOS mode |
ThemeManager |
Load/apply themes, color & style handlers, accent color, export |
LayoutManager |
Save/load/lock window layouts (ImGui docking state) |
WorkspaceManager |
Create/switch/import/export workspaces |
LocalizationManager (Lang/UnlocalizedString) |
Translation lookup; compile-time "..."_lang hashing |
AchievementManager |
Achievement graph (requirements/visibility), progress persistence |
TutorialManager |
Interactive tutorials (steps, highlights, message boxes) |
ProjectFile |
Load/store .hexproj projects; per-provider and global handlers |
prv::Provider |
Abstract byte source interface every feature reads/writes |
Localization & UnlocalizedString
A recurring pattern: APIs take UnlocalizedString keys, not display text.
ImHex resolves them through LocalizationManager at draw time. The _lang
user-defined literal computes a hash at compile time for fast lookup. This is why
plugin code is full of identifiers like hex.builtin.view.hex_editor.name — the
human-readable strings live in each plugin's romfs/lang/*.json.
TaskManager
Tasks come in three flavors: foreground (shown in the footer task list),
background (hidden), and blocking (modal). Task objects support
progress (update/setMaxValue), cooperative interruption
(interrupt/shouldInterrupt), and exception capture. doLater and
doLaterOnce defer work to the start of the next frame on the main thread —
essential because the GUI is immediate-mode and most state must be touched on the
main thread.
Important
When extending ImHex, never block the render thread. Long operations belong in
TaskManager::createTask(...); UI mutations from worker threads must be routed back viaTaskManager::doLater(...).
6. The ContentRegistry: Extension Surface
ContentRegistry (implemented in lib/libimhex/source/api/content_registry.cpp)
is the single namespace through which plugins add functionality. Each
sub-namespace owns a registry of entries plus add(...) functions. The full set:
| Namespace | Registers… |
|---|---|
Settings |
Setting categories, sub-categories, and widgets; onChange/onSave hooks; persistence to settings.json |
CommandPalette |
Command-palette entries and handlers |
PatternLanguage |
Pattern Language functions, dangerous functions, types, pragmas, and (inline) visualizers; owns the per-provider runtime |
Views |
Dockable windows (the UI views), plus the fullscreen view slot |
Tools |
Entries in the Tools panel |
DataInspector |
Data Inspector row types (generator + optional editor) |
DataProcessor |
Node types for the node graph |
UserInterface |
Main-menu items, toolbar items, sidebar items, footer items, welcome-screen entries, title-bar buttons, taskbar menu items |
Provider |
Data-source provider types (creation functions + names) |
DataFormatter |
"Copy as / export as" formatters and find-result exporters |
FileTypeHandler |
Handlers keyed by file extension |
HexEditor |
Data visualizers and minimap visualizers for the hex grid |
Diffing |
Diff algorithms |
Hashes |
Hash algorithm implementations |
BackgroundServices |
Long-running background services |
CommunicationInterface |
External communication endpoints |
DataInformation |
Data Information analyzers (entropy, distribution, …) |
Disassemblers |
Disassembler backends |
Experiments |
Experimental, opt-in features |
Reports |
Report generators |
Registration is uniform: a plugin calls e.g.
ContentRegistry::Hashes::add(std::make_unique<MyHash>()) or
ContentRegistry::Views::add(...) during its init function. Many add calls
log::debug(...) the registration, so running with --verbose prints a complete
inventory of what each plugin contributed — invaluable for debugging missing
content.
Settings widgets
The settings system ships reusable widget types: Checkbox, SliderInteger,
SliderFloat, SliderDataSize, ColorPicker, DropDown, TextBox,
FilePicker, Label, and Spacer. Each implements draw/load/store
against nlohmann::json. Settings persist to settings.json in a config path;
on the Web build they persist to browser localStorage instead.
The Pattern Language runtime
ContentRegistry::PatternLanguage owns a per-provider pl::PatternLanguage
runtime. On creation it wires the runtime's data source to the provider's
read/write, sets include paths from the includes/patterns data folders,
registers all plugin-contributed functions/types/pragmas, and defines __IMHEX__
and __IMHEX_VERSION__. "Dangerous" functions (those with side effects beyond
reading) are registered separately and gated.
Caution
Pattern Language dangerous functions can perform side-effectful operations. They are registered via
addDangerousFunctionand treated distinctly from pure functions. Be deliberate about which capabilities a plugin exposes to user-supplied pattern code.
7. The Event System
EventManager is a typed publish/subscribe bus. Events are declared with the
EVENT_DEF(Name, ParamTypes...) macro (or EVENT_DEF_NO_LOG for high-frequency
events like per-frame ticks), which generates a struct with static
subscribe/unsubscribe/post methods. Event identity is a compile-time FNV
hash of the event name; handlers are stored in a multimap keyed by that id.
Conventions:
- Events live in category headers under
include/hex/api/events/(events_gui.hpp,events_lifecycle.hpp,events_provider.hpp,events_interaction.hpp) — never declared inline elsewhere. - Events (
EventXxx) broadcast that something happened; Requests (RequestXxx) ask something to happen. - Subscriptions can be token-based (pass a
void*owner token) so a plugin can unsubscribe all its handlers at unload. - In release builds, event callbacks are wrapped in try/catch and log the exception (then rethrow); in debug builds they run unguarded for easier debugging.
Representative events (non-exhaustive):
| Category | Examples |
|---|---|
| Lifecycle | EventImHexStartupFinished, EventImHexClosing, EventFirstLaunch, EventImHexUpdated, EventAbnormalTermination, EventCrashRecovered |
| Provider | EventProviderCreated, EventProviderOpened, EventProviderChanged, EventProviderClosed, EventProviderDataModified, EventProviderDirtied |
| Interaction | EventDataChanged, EventRegionSelected, EventBookmarkCreated, EventPatchCreated, EventPatternExecuted, EventFileDropped |
| GUI | EventViewOpened, EventDPIChanged, EventWindowClosing, EventFrameBegin/EventFrameEnd, EventThemeChanged |
| Requests | RequestOpenFile, RequestOpenWindow, RequestCreateProvider, RequestHexEditorSelectionChange, RequestSetPatternLanguageCode, RequestCloseImHex, RequestRestartImHex |
Note
The in-source documentation candidly flags several events as unused (
FIXME: this event is unused and should be scrapped). Treat theevents_*.hppheaders as the source of truth, but don't assume every declared event has a live publisher.
Example: how a feature wires up
The introduction tutorial (plugins/builtin/.../tutorials/introduction.cpp)
illustrates the idioms: it builds a TutorialManager tutorial with steps that
highlight named UI elements (by their localized window names), and advances steps
by subscribing to events like EventProviderOpened and EventViewOpened. UI
elements are addressed by stable string IDs, and pattern code is injected with
RequestSetPatternLanguageCode::post(...) followed by
RequestTriggerPatternEvaluation::post().
8. Plugins Inventory
| Plugin | Role | Notes |
|---|---|---|
builtin |
The bulk of ImHex: hex editor view, data inspector, find/replace, bookmarks, patches, providers, the CLI subcommands, settings, tutorials | Largest plugin; author WerWolv |
ui |
Shared UI widgets + pattern text editor | LGPL so other (incl. proprietary) plugins can reuse it |
hashes |
All hash algorithms + Hashes view | See User Guide for the full algorithm list |
disassembler |
Capstone disassembler + views | Pluggable architectures |
yara_rules |
YARA scanning | Rules from yara/ data folder + Content Store |
diffing |
Data diffing (edit-distance based) | Table + highlight diff views |
decompress |
Decompression nodes | Feeds the data processor |
visualizers |
Pattern visualizers | Images, audio, 3D models, coordinates, shaders |
remote |
Remote/network providers | SSH/SFTP, UDP, etc. |
fonts |
Font loading | Bundled and custom fonts |
windows |
Windows-only integrations | Taskbar progress, etc. |
script_loader |
Load C# (.NET) and C scripts as content | See §12 |
Plugins are distinguished as library plugins (shared infrastructure like
ui, loaded via loadLibraries) versus feature plugins (loaded via load).
A plugin exports a fixed set of C functions (getPluginName, initializePlugin,
getSubCommands, getFeatures, getCompatibleVersion, …) that PluginManager
resolves by symbol — defined for you by the IMHEX_PLUGIN_SETUP macros.
9. Security & Licensing
Licensing
| Component | License | Why |
|---|---|---|
| ImHex (most of it) | GPLv2-only | Copyleft for the application |
lib/libimhex |
LGPLv2.1 | Lets proprietary plugins link the SDK |
plugins/ui |
LGPLv2.1 | Lets proprietary plugins reuse UI elements |
plugins/ui/.../text_editor |
MIT (BalazsJako) | Vendored ImGuiColorTextEdit |
Code signing
Windows builds are signed via SignPath.io (certificate by the SignPath Foundation). ImHex's stated policy: it "will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it." Push access to the repository is limited to a small named set of maintainers.
Network behavior
ImHex only reaches the network on explicit user action. Network touchpoints to be aware of when assessing or sandboxing it:
- Updater — fetches release metadata from the GitHub API and downloads artifacts.
- Content Store — downloads patterns/magic/rules/themes from the ImHex-Patterns repo.
- Remote providers — SSH/SFTP, UDP, GDB, process memory.
- Tools — the HTTP Requests and TCP Client/Server tools, the Wikipedia tool,
and the
--save-editor gistcommand (GitHub Gist API).
Warning
ImHex is a reverse-engineering tool routinely pointed at untrusted binaries and malware. Run it in an isolated VM when analyzing hostile samples. Note these capability surfaces:
- Pattern Language dangerous functions and scripts (C#/.NET, C) can execute side-effectful logic — only run patterns/scripts you trust. YARA-style sources can be embedded in downloadable content.
- Process Memory / GDB / Raw Disk providers touch live system state and may need elevated privileges.
- The updater runs platform installers with elevated privileges (
msiexec,sudo,zenitypassword prompts) and on macOS uses AppleScriptwith administrator privileges. Prefer OS package managers in managed environments and pin the update channel.
MCP exposure
The MCP server binds an internal loopback port (19743) for a local stdio
bridge (imhex --mcp), not a remote service. Still, treat it as a control
channel: anything able to drive that bridge can drive ImHex.
10. Building & Compiling
Toolchain requirements
| Requirement | Detail |
|---|---|
| Compiler | GCC or Clang with C++23 support (latest GCC for Windows/Linux, latest LLVM Clang for macOS) |
| CMake | ≥ 3.20 |
| Generator | Ninja (default in presets) |
| Submodules | Many deps are bundled as git submodules |
Important
MSVC and AppleClang are NOT supported — both lack C++23 features ImHex relies on. Use GCC/LLVM Clang even on Windows/macOS. Clone with
--recurse-submodules; non-bundled dependencies install via the scripts in/dist.
CMake presets
CMakePresets.json defines configure presets (CMake ≥ 3.20, Ninja, Debug by
default):
| Preset | Purpose |
|---|---|
base |
Hidden base: GCC/G++, IMHEX_PATTERNS_PULL_MASTER=ON, USE_SYSTEM_CAPSTONE=ON, IMHEX_USE_DEFAULT_BUILD_SETTINGS=ON, install prefix ./install |
x86_64 |
Standard x86-64 build (inherits base) |
xcode |
macOS Xcode generator with Clang overrides |
vs2022 / vs2022-x86 |
Visual Studio environments using the vcpkg toolchain (VCPKG_MANIFEST_DIR=dist) |
A typical Linux/macOS build:
git clone --recurse-submodules https://github.com/WerWolv/ImHex
cd ImHex
cmake --preset x86_64
cmake --build build/x86_64 --target imhex_all
The imhex_all build target is wired in the x86_64 build preset. Per-platform
instructions live in dist/compiling/{windows,linux,macos}.md.
Notable CMake options
| Option | Effect |
|---|---|
IMHEX_BUNDLE_PLUGIN_SDK=ON |
Install the plugin SDK (for out-of-tree plugin dev) |
USE_SYSTEM_CAPSTONE=ON |
Link the system Capstone instead of bundled |
IMHEX_PATTERNS_PULL_MASTER=ON |
Pull the latest patterns database during build |
IMHEX_USE_DEFAULT_BUILD_SETTINGS=ON |
Apply the project's standard build flags |
11. Plugin Development
ImHex is built to be extended out-of-tree. The workflow:
- Start from the ImHex Plugin Template.
- Get the SDK. Build ImHex with the SDK bundled and install it:
The SDK lands atcmake -G Ninja -DIMHEX_BUNDLE_PLUGIN_SDK=ON -B build cd build DESTDIR=install ninja installinstall/usr/local/share/imhex/sdk. Point theIMHEX_SDK_PATHvariable at that absolute path. - In CI, use the imhex-download-sdk action to fetch a matching SDK automatically.
- In your plugin's init, register content via
ContentRegistry::*::add(...)and subscribe to events viaEventXxx::subscribe(...). - Validate the built plugin before shipping:
This checks the plugin loads, has a valid setup block, and initializes.imhex --validate-plugin /path/to/your_plugin.hexplug
Important
A plugin must be built against the same ImHex version's SDK. The
getCompatibleVersionexport letsPluginManagerreject mismatched plugins — which is exactly what--validate-pluginsurfaces ("make sure the plugin was built using the SDK of this ImHex version"). A plugin missing itsIMHEX_PLUGIN_SETUP/IMHEX_LIBRARY_SETUPblock is reported as invalid.
Plugins live in the plugins data/plugin folder at runtime; --plugins <path>
adds extra load paths, and --plugins with no argument lists what's loaded.
12. Scripting (C# / .NET and C)
The script_loader plugin lets you add content using compiled scripts instead of
full C++ plugins:
- .NET / C# —
plugins/script_loader/support/dotnetbuilds anAssemblyLoaderand a managedImHexLibrary; CMake compiles the C# projects (dotnet build ... -c Release) into DLLs installed alongside the plugins. Project templates live inplugins/script_loader/templates/CSharp(ImHexLibrary,ImHexScript). - C —
plugins/script_loader/support/cexposes a versioned C script API (script_api/v1:bookmarks,logger,mem,ui) compiled into ac_apiobject library linked againstlibimhexandui.
This is the lighter-weight extension path: write a script in C# or C against the provided API rather than maintaining a native plugin and SDK build.
Caution
Scripts execute as native/managed code inside the ImHex process — they are not sandboxed. Only load scripts from sources you trust, exactly as you would a plugin.
13. The MCP Server
ImHex exposes a Model Context Protocol server so AI assistants can drive it.
Topology
- The server lives in
libimhex(lib/libimhex/source/mcp/server.cpp, headerinclude/hex/mcp/server.hpp) and runs inside ImHex on the internal loopback port19743(Server::McpInternalPort). - The client/bridge is launched by
imhex --mcp(handled by the builtin plugin'shandleMCPCommand), which runs anmcp::Clientoverstd::cin/std::cout— the stdio transport AI hosts expect — relaying to the internal server.
Protocol
The server speaks JSON-RPC 2.0 and implements MCP protocol version
2025-06-18. The JsonRpc class parses single and batched requests,
validates the envelope (jsonrpc == "2.0", string method), and maps standard
JSON-RPC error codes (ParseError -32700, InvalidRequest -32600,
MethodNotFound -32601, InvalidParams -32602, InternalError -32603).
initialize returns server info (ImHex + the running version) and advertises a
tools capability. Methods are dispatched by suffix:
notifications/*→ notification handlers (e.g.initializedmarks connected).*/list→ enumerate registered primitives of a type and their capabilities.*/call→ invoke a named primitive (a "tool") witharguments.
Tools (primitives) are registered with Server::addPrimitive(type, capabilities, function), where capabilities is a JSON description carrying the tool's name.
The builtin plugin ships MCP tool definitions under
plugins/builtin/romfs/mcp/tools.
Note
Because tools are registered the same way other content is, plugins can add MCP tools. The response framing appends a
0x00terminator byte per message over the socket transport — relevant if you implement a custom bridge.
14. Configuration Reference
Settings store
Settings are a single JSON document persisted to settings.json in a config path
(or browser localStorage on the Web build). Internally each setting is
addressed by category / name (both unlocalized keys). Plugins register settings
with ContentRegistry::Settings::add(category, subCategory, name, widget) and
react with onChange/onSave callbacks. Defaults are materialized lazily when a
setting is first read.
Setting categories
The builtin plugin registers these top-level categories (display names from localization):
| Key | Display name |
|---|---|
hex.builtin.setting.general |
General |
hex.builtin.setting.interface |
Interface |
hex.builtin.setting.hex_editor |
Hex Editor |
hex.builtin.setting.pattern_editor |
Pattern Editor |
hex.builtin.setting.folders |
Folders |
hex.builtin.setting.proxy |
Proxy |
hex.builtin.setting.toolbar |
Toolbar |
hex.builtin.setting.shortcuts |
Shortcuts |
hex.builtin.setting.plugins |
Plugins |
hex.builtin.setting.loaded_plugins |
Plugins to be loaded |
hex.builtin.setting.experiments |
Experiments |
hex.builtin.setting.imhex |
ImHex (internal/meta) |
Note
Reading a setting in code:
ContentRegistry::Settings::read<T>(category, name, default). For example, the builtin plugin readshex.builtin.setting.general / hex.builtin.setting.general.debug_mode_enabledat startup to decide whether to enable debug mode (always on in DEBUG builds).
Reset
imhex --reset-settings clears and re-stores settings (requires typing
YES I AM ABSOLUTELY SURE). Deleting settings.json has the same effect.
15. Filesystem & Path Resolution
ImHex resolves directories through hex::paths (default_paths.{hpp,cpp}). Three
path classes exist:
ConfigPath— config-home-rooted (config,recent,updates).DataPath— data-home-rooted, can include system folders (patterns, magic, themes, scripts, …).PluginPath— plugin/library directories.
Each exposes all(), read() (existing dirs only), and write() (existing &
writable). The base directories per platform:
| Platform | Config | Data |
|---|---|---|
| Windows (installed) | %LOCALAPPDATA%\imhex |
%LOCALAPPDATA%\imhex |
| Windows (portable) | next to the executable | next to the executable |
| macOS | ~/Library/Application Support/imhex |
same (+ executable dir as system folder) |
| Linux/Web | $XDG_CONFIG_HOME/imhex |
$XDG_DATA_HOME/imhex + $XDG_DATA_DIRS |
Resolution rules worth knowing for packaging:
- Portable Windows deliberately skips
%LOCALAPPDATA%and uses only the executable directory. - Linux AppImage restricts plugin loading to inside the AppImage
(
$APPDIR/usr/lib/imhex). - A compile-time
SYSTEM_PLUGINS_LOCATIONadds a system plugin dir on Linux. - The directory of a loaded project file and any additional folders configured by the user are added to the data search path.
Named subfolders (postfixes) are listed in the User Guide; the canonical list is
the 22-entry hex::paths::All array (Config, Recent, Updates, Libraries,
Plugins, Patterns, PatternsInclude, Magic, Yara,
YaraAdvancedAnalysis, Backups, Resources, Constants, Encodings, Logs,
Scripts, Inspectors, Themes, Nodes, Layouts, Workspaces,
Disassemblers).
Tip
Because most data paths are multi-rooted (user dir + system dirs), packagers can ship read-only content (patterns, magic, themes) in a system data dir while users add their own in the writable home dir.
read()merges them;write()only ever targets writable locations.
16. Operations
Logging
Logs are written to the logs data folder and the console. --verbose / -v
enables debug logging (which, among other things, prints every ContentRegistry
registration and posted event). --debug-mode enables additional debugging
features. Debug builds enable debug mode automatically and show a warning banner
(suppressible with NO_DEBUG_BANNER).
Caution
A known data race exists in
log::debug()when debug logging is disabled (reported upstream). If you build tooling around ImHex logs under heavy multithreaded load, be aware of it.
Updates (the updater executable)
imhex-updater <stable|nightly> is invoked by ImHex to self-update. It:
- Queries the GitHub releases API and selects the release matching the channel
(stable releases have
target_commitishstartingreleases/v; nightly is taggednightly). - Picks the artifact for the current OS+arch by filename suffix — e.g.
Windows-x86_64.msi,macOS-arm64.dmg,Ubuntu-24.04-x86_64.deb,Fedora-42-x86_64.rpm,ArchLinux-x86_64.pkg.tar.zst,x86_64.AppImage,x86_64.flatpak,x86_64.snap. - Downloads to a writable
updatespath and runs the platform installer.
Install commands by type:
| Artifact | Command |
|---|---|
.msi |
msiexec /i "<file>" /qb |
.dmg |
mounts, copies the .app to /Applications via AppleScript admin |
.deb |
sudo apt install --fix-broken (via zenity password) |
.rpm |
sudo rpm -i |
.pkg.tar.zst |
sudo pacman -U |
.AppImage / .flatpak / .snap |
copy / flatpak install / snap install --dangerous |
If the install type can't be auto-updated, the updater opens the latest release
page in a browser for a manual update. Portable Windows is intentionally not
auto-updated via .msi.
Backup & recovery
- Crash backup:
crash_backup.hexproj+crash.jsonwritten to a config path on crash. After a crash, reopen the backup project to recover work. - Backups folder: the
backupsdata path holds additional backups. - Workspaces are exported on crash so the layout survives.
Single-instance messaging
messaging::setupMessaging() enables sending commands (e.g. "open this file") to
an already-running ImHex instance rather than spawning a new one. Cross-instance
payloads arrive as EventNativeMessageReceived.
17. Code Quality & Conventions
Observed patterns across the codebase:
- Modern C++23 throughout — concepts, ranges,
std::optional,<=>,constevalliterals,std::source_location, designated initializers. AutoReset<T>wrappers hold global registry state so it can be cleanly reset (important for the Web build and for plugin reloads).- Static "manager" classes with deleted constructors as the API style;
internal state lives in
implnamespaces exposed through accessor functions. PerProvider<T>binds state to the active provider (e.g. the Pattern Language runtime is per-provider).- Strict separation of display text from logic via
UnlocalizedString. - Defensive event dispatch (try/catch + log in release) and cooperative task interruption rather than thread cancellation.
- ROM-FS embedded resources (
romfs::get(...)) keep assets in the binary. - Honest in-source TODOs/FIXMEs — many event docs flag dead code for removal, which is good signal for contributors.
- Heavy use of
[[nodiscard]]on getters.
Naming: hex:: root namespace; plugins under hex::plugin::<name>; providers
under hex::prv; pattern-language types under pl::; the bundled wolv::
utility library (io/fs/utils/net/types) is used pervasively.
18. Testing
Tests live under tests/ and are wired through CMake:
| Suite | Path | Focus |
|---|---|---|
| Common | tests/common |
Shared test harness / entry point |
| Helpers | tests/helpers |
Helper-function tests |
| Algorithms | tests/algorithms |
Algorithmic correctness |
| Plugins | tests/plugins |
Plugin-level tests |
The harness (tests/common/source/main.cpp) runs a single named test, supports
expected-failure tests (shouldFail inverts the result), and tears down via
TaskManager::exit(), EventImHexClosing, EventManager::clear(), and
PluginManager::unload(). Tests are declared with the TEST_SEQUENCE(name)
macro and assert with TEST_ASSERT / TEST_SUCCESS.
Example — the builtin provider read/write test
(plugins/builtin/tests/source/main.cpp) initializes the Built-in plugin, creates
a mem_file provider, and asserts resize/read/write/zero-fill semantics, plus an
expected-failure test that resizing to -1 returns false. New plugin tests are
added to the AVAILABLE_TESTS list in plugins/builtin/tests/CMakeLists.txt and
registered as CTest cases.
set(AVAILABLE_TESTS
Providers/ReadWrite
Providers/InvalidResize
)
19. Potential Improvements & Tech Debt
Drawn directly from in-source FIXME/TODO markers and community-reported
issues at this commit.
Flagged in source
- Dead events — several events are documented as unused and slated for
removal:
EventFileLoaded,EventPatternEditorChanged,EventStoreContentDownloaded,EventStoreContentRemoved,EventAnySettingChanged,RequestAddExitTask. Cleaning these would reduce API surface confusion. - Naming inconsistencies —
MovePerProviderDatalacks theRequestprefix its siblings use;EventProviderClosingbehaves like a request (it carries a mutablebool*) and is noted as belonging in the requests section. EventImHexUpdatedis flagged as misleadingly named (it fires every startup with previous/current versions, not only on update).
Community-reported (open issues at this commit)
| Area | Issue |
|---|---|
| Pattern editor | Selection desync / skipping after edits (#2748, #2749) |
| Pattern eval | Crash on pattern with pointer-to-struct (#2744) |
| Hashing | CRC16 broken after restart (#2739) |
| CLI | Opening a file from the command line yields an empty layout (#2742, #2715) |
| Visualizers | Bitmap visualizer retains stale section data (#2743) |
| Logging | Data race in log::debug() when debug logging is off (#2751) |
| Endianness | Big-endian DOS date/time broken (#2723) |
| Paths | Default paths not created if missing (#2726) |
| I/O | GVFS SMB shares truncate files to zero bytes (#2660) |
Architectural opportunities
- The
builtinplugin is very large and mixes many concerns; splitting cohesive feature groups (CLI, providers, views) into focused plugins would improve modularity and build times. - The dual GPU/NoGPU and native/Web/WASM matrix means OS-conditional code is
spread across modules; consolidating platform shims behind the
wolv/pathsabstractions would reduce#if defined(OS_*)density. - Settings are untyped JSON addressed by string keys; a typed settings schema would catch key typos at compile time.
20. Quick Reference
Build
git clone --recurse-submodules https://github.com/WerWolv/ImHex
cd ImHex
cmake --preset x86_64 # Ninja, GCC, Debug, USE_SYSTEM_CAPSTONE=ON
cmake --build build/x86_64 --target imhex_all
Plugin SDK
cmake -G Ninja -DIMHEX_BUNDLE_PLUGIN_SDK=ON -B build
cd build && DESTDIR=install ninja install
# SDK at install/usr/local/share/imhex/sdk → set IMHEX_SDK_PATH
imhex --validate-plugin your_plugin.hexplug
Key facts
| Item | Value |
|---|---|
| Language / standard | C++23 |
| Supported compilers | GCC, LLVM Clang (not MSVC/AppleClang) |
| Build system | CMake ≥ 3.20 + Ninja |
| GUI | Dear ImGui over OpenGL 3.0+ |
| Disassembly engine | Capstone |
| MCP internal port | 19743 (loopback), JSON-RPC 2.0, protocol 2025-06-18 |
| Crash backup | crash_backup.hexproj + crash.json in config dir |
| Overall license | GPLv2 (libimhex + ui: LGPLv2.1) |
| Maintainer | WerWolv (+ small core team) |
Core managers (libimhex)
PluginManager · ContentRegistry · EventManager · TaskManager ·
ImHexApi · ShortcutManager · ThemeManager · LayoutManager ·
WorkspaceManager · LocalizationManager · AchievementManager ·
TutorialManager · ProjectFile · prv::Provider
Key dependencies
Dear ImGui (UI) · ImPlot (plots) · ImNodes (data processor) ·
ImGuiColorTextEdit (pattern editor) · nlohmann/json · fmt · Capstone
(disassembly) · YARA · edlib (diffing) · HashLibPlus (hashing) · miniaudio
(audio) · nativefiledialog-extended · microtar · xdgpp · the wolv utility
library · the standalone Pattern Language runtime.
Links
- Repo: https://github.com/WerWolv/ImHex
- Docs: https://docs.werwolv.net
- Pattern Language: https://github.com/WerWolv/PatternLanguage
- Patterns DB: https://github.com/WerWolv/ImHex-Patterns
- Plugin template: https://github.com/WerWolv/ImHex-Plugin-Template
- SDK download action: https://github.com/WerWolv/imhex-download-sdk