Hook

Hook

A graphical HTTP/HTTPS API client built on the Nilorea C Library. Local-first. No cloud. No account. Git-native.

Features

Dependencies

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

Building on Linux

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)
make appimage     # build dist/hook-<version>-x86_64.AppImage
make deb          # build dist/hook_<version>_amd64.deb (self-contained)
make docker-build # portable .deb + AppImage in a Debian 13 container
make bump-version # grow version.txt by one (packaging targets do this)

The .deb installs the application, its default data, the bundled libraries and the HTML documentation under /opt/hook, with a launcher in /usr/bin and desktop menu entries for both the application and its documentation. User data goes to ~/.local/share/hook and ~/.config/hook (XDG), seeded from the packaged defaults on first run.

The binary is built at ./hook. Launch with ./hook.sh for ASan/LSan suppression of third-party library leaks.

Linux packaging (.deb and AppImage)

make appimage     # dist/hook-<VERSION>-x86_64.AppImage (bundles allegro/...)
make deb          # dist/hook_<VERSION>_amd64.deb (fat package, /opt/hook)
make deb VERSION=1.2.0

Versioning

version.txt (tracked) holds the application version as MAJOR.MEDIUM.MINOR and is the single source of truth: the Makefile generates src/hook_version.h from it (HOOK_VERSION, reported in the startup log) and stamps it into the .deb control file and the .deb / AppImage / Windows setup file names.

Every packaging build grows it by one: deb, appimage and installer-windows run make bump-version before building, so no two artifacts share a version. Plain make and make test never touch it. The minor part wraps to 0 and carries into medium at 11, and medium carries into major at 11 (0.1.10 -> 0.2.0, 0.10.10 -> 1.0.0). Passing VERSION=x.y.z on the command line writes that value to version.txt instead of incrementing.

Both artifacts bundle Hook’s non-system libraries (Allegro, libgit2, PCRE2, libyaml, …) and rely on the host only for the system stack (glibc, X11, GL, GTK). The .deb installs a read-only tree under /opt/hook with a launcher at /usr/bin/hook; user data lives under ~/.local/share/hook and config under ~/.config/hook (same XDG model as the AppImage). Its Debian dependencies are:

libc6 (>= 2.41), zlib1g, libglib2.0-0t64, libgtk-3-0t64, libx11-6,
libxcursor1, libxi6, libxinerama1, libxrandr2, libglx0, libopengl0,
libfreetype6, libfontconfig1, libcom-err2, libgpg-error0

That list (DEB_DEPENDS in the Makefile) names Debian 13 “trixie” packages, the suite the release build targets – see below.

glibc is backward-compatible only: a binary linked against a new glibc will not start on an older system. Building on the host therefore ties the artifacts to the host’s glibc. To build portable artifacts, build inside a Debian 13 “trixie” (glibc 2.41) container – the result runs on Debian 13+, Ubuntu 25.04+ and other distributions shipping glibc 2.41 or newer. Set SUITE= to build against an older base (SUITE=bullseye targets glibc 2.31 and thus most 2020-or-later distributions), but note the .deb Depends list in the Makefile carries trixie package names:

make docker-build                    # bumps version.txt, then builds
make docker-build VERSION=1.2.0      # explicit version; DOCKER=podman ok
packaging/docker-build.sh            # same, called directly
packaging/docker-build.sh 1.2.0

The script builds packaging/Dockerfile, bind-mounts this repository and the sibling nilorea-library source tree, runs a clean container build, and writes dist/hook_-_amd64.deb and dist/hook---x86_64.AppImage. Without an explicit version it first runs make bump-version on the host, so the tracked counter grows with every release build.

Overrides. VERSION is the only one passed as an argument (or on the make command line); the rest are environment variables read by the script, and make docker-build forwards the environment, so both spellings work:

Override Default What it does
VERSION grown from version.txt Upstream version to build. Given explicitly it skips the bump; the container codename is appended either way.
SUITE trixie Debian base suite of the build image (--build-arg DEBIAN_SUITE). It sets the glibc floor, the Allegro API floor and the codename tag.
IMAGE hook-build:$SUITE Tag of the build image the script builds and runs.
NILOREA_DIR ../../nilorea-library Path to the nilorea-library source tree.
DOCKER docker Container CLI. May be a multi-word prefix, e.g. DOCKER="sudo docker" or DOCKER=podman.
make docker-build SUITE=bullseye              # older glibc target
make docker-build DOCKER=podman
NILOREA_DIR=~/src/nilorea-library make docker-build
SUITE=bookworm IMAGE=hook-build:bookworm packaging/docker-build.sh 1.2.0

SUITE and IMAGE are also read by packaging/gen-third-party-licenses.sh (make third-party-licenses), which resolves the bundled libraries of a release .deb against that same image – use the image that produced the package being read.

The .deb Depends list (DEB_DEPENDS in the Makefile) is hand-maintained and tracks the default suite: it names the trixie t64 packages and requires glibc 2.41. Building another suite is fine for testing, but re-check that list before shipping such a package.

Building on Windows (MSYS2 / MinGW-w64)

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.

Running

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]

GUI layout

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.

CLI usage

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

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

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.

User-Agent presets

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.

Git integration

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.

Configuration

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.

Running tests

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.

Generating documentation

make doc

Output is in docs/html/index.html. Requires doxygen and graphviz. Call graphs are generated for all public functions.

Documentation

License

Proprietary.

Copyright (c) 2026 Castagnier Mickael (gullradriel). All rights reserved.

This software is proprietary. No permission is granted to use, copy,
modify, or distribute it, in source or binary form, except under a
separate written agreement.

See LICENSE for the full text.

Third-party components (compiled-in sources, redistributed shared libraries and fonts) are listed with their licenses in THIRD_PARTY_LICENSES. Nothing compiled into the Hook binary is copyleft: nilorea-library is Apache-2.0, and its NOTICE ships with every artifact as that license requires. The copyleft notice at the end of that file covers the libraries Hook redistributes as separate shared objects (libgit2, libgcrypt, libkeyutils) and the LGPL system libraries it links against without redistributing.