A graphical HTTP/HTTPS API client built on the Nilorea C Library. Local-first. No cloud. No account. Git-native.
Linux (Debian/Ubuntu):
sudo apt-get install build-essential
sudo apt-get install liballegro5-dev
sudo apt-get install libssl-dev libpcre2-dev libgit2-dev zlib1g-dev libyaml-dev
sudo apt-get install doxygen graphviz cppcheck
The Nilorea library is required at ../../nilorea-library/ (compiled from source).
Windows (MSYS2 MinGW-w64):
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-make
pacman -S mingw-w64-x86_64-allegro
pacman -S mingw-w64-x86_64-openssl mingw-w64-x86_64-pcre2
pacman -S mingw-w64-x86_64-libgit2 mingw-w64-x86_64-zlib mingw-w64-x86_64-libyaml
pacman -S mingw-w64-x86_64-doxygen mingw-w64-x86_64-graphviz
make # build the hook binary
make test # run all tests (requires network for some)
make doc # generate Doxygen documentation
make check # run cppcheck static analysis
make clean # remove build artifacts
make fixtures # regenerate test CA certificate
make asan # rebuild with AddressSanitizer + UBSan (Linux only)
make tsan # rebuild with ThreadSanitizer (Linux only)
The binary is built at ./hook. Launch with ./hook.sh for ASan/LSan suppression of third-party library leaks.
Open an MSYS2 MinGW64 shell:
make # Makefile auto-detects OS=Windows_NT
make test
make doc
The Windows build includes Makefile.windows which adds -lws2_32, -lwinmm, -lgdi32, -lcrypt32 to the link flags.
GUI mode:
./hook # or ./hook.sh for sanitizer suppressions
CLI mode:
./hook run -f collection.json [-e env.json] [--curl]
./hook export -f collection.json [-e env.json]
The toolbar provides:
[Collections] [col list] [Environments] [env list] [Mock Server] [SSE] [WS] [Git] [User Agent] [Save in Collection] [Help]
Request bar:
[UA preset] [Method] [URL] [SEND] (Enter in URL field also sends)
Request tabs: Params | Headers | Body | Auth | Scripts | Settings
Sidebar: recursive collection tree + editing buttons
Collection
> Users
v Admin
GET /admin/me
POST /admin/invite
> Orders
[ + Folder ] [ + Subfolder ]
[ Rename ] [ Delete ]
History
200 GET https://httpbin.org/get
201 POST ...
Click a request leaf in the tree to load it into the editor. Folders nest to arbitrary depth.
Response panel:
[200 OK] [183 ms | 14.2 KB | 12 headers]
[Body] [Headers] [Log] [Clear] [AutoScroll] [Save]
The entire GUI scales proportionally when the window is resized. Layout windows (toolbar, sidebar, main, statusbar) scale with the display. Popup windows reposition but keep their size.
hook run -f <collection.json> [-e <env.json>] [--curl]
Execute the first request in the collection.
--curl forces the curl subprocess backend.
hook export -f <collection.json> [-e <env.json>]
Print the equivalent curl command to stdout.
hook import postman <file.json> -o <output.json>
Import a Postman Collection v2.1 file.
hook import bruno <file_or_dir> -o <output.json>
Import a Bruno .bru file or directory of .bru files.
hook mock <routes.json> [--port N]
Start a mock HTTP server. Default port 18080.
hook git status
Print working directory status.
hook git stage <path>
hook git stage --all
Stage files for commit.
hook git unstage <path>
Remove a file from the staging area.
hook git commit -m "message"
Commit staged files. Uses config author or defaults.
hook git log [--max N]
Print commit history. Default: last 20 entries.
hook git diff [path]
Print unified diff for a file or all changed files.
hook git restore <path>
Restore a file to its last committed state.
hook git branch
List local branches. Current branch marked with *.
hook git branch <name>
Create a new branch at HEAD.
hook git switch <name>
Switch to an existing branch.
Collections are JSON files containing folders and requests with full settings:
{
"name": "My API",
"base_url": "https://api.example.com",
"folders": [
{
"name": "Users",
"folders": [
{
"name": "Admin",
"requests": [
{ "method": "GET", "url": "/admin/me" }
]
}
],
"requests": [
{
"method": "GET",
"url": "{{base_url}}/users",
"headers": [{"name": "Accept", "value": "application/json", "enabled": 1}],
"params": [{"key": "page", "value": "1", "enabled": 1}],
"body_type": 0,
"auth_type": 1,
"auth_bearer_token": "{{api_token}}",
"follow_redirects": 1,
"max_redirects": 10,
"timeout_ms": 30000,
"compressed": 1,
"user_agent": "Hook/1.0"
}
]
}
]
}
Folders can nest to arbitrary depth via the folders
array on each folder object. Hook’s tree editors build and maintain this
structure for you; the format is round-trip safe if you prefer to edit
collections by hand.
Environments are JSON files with variable key-value pairs:
{
"name": "dev",
"variables": {
"base_url": "https://api.dev.example.com",
"api_token": "sk-dev-123..."
}
}
Variables are expanded in URLs, headers, body, and auth fields using the {{varname}} syntax. Environment variables take precedence over collection base_url.
JSON files in user-agents/ with name and user_agent fields:
{
"name": "Chrome Windows",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; ...) Chrome/131.0.0.0"
}
Select from the UA preset combobox in the request bar or the User Agent popup. The last-used preset is saved in hook.conf and restored on startup.
Enable Git in the Git popup settings. Hook can init, stage, commit, diff, log, restore, and manage branches. Only files in these directories are tracked: collections/, environments/, mock-routes/, scripts/, user-agents/.
Remote settings (repo URL, branch, auth) are saved in hook.conf for future push/pull support.
All settings are persisted in hook.conf (JSON format). Loaded at startup, saved on exit. Includes: TLS defaults, proxy, git settings, user-agent, redirect/retry/compression defaults, cookie jar, client certificates.
make test
All tests print one line per check. Exit code 0 = PASS, 1 = FAIL. Tests that require network access skip gracefully if unreachable. Tests use ASan/LSan via -fsanitize=address,undefined.
make doc
Output is in docs/html/index.html. Requires doxygen and graphviz. Call graphs are generated for all public functions.
See LICENSE file for details.