Terminal client for selected Hack The Box Labs API workflows.
Project description
htb-terminal
Python terminal client for selected HTB Labs API workflows (v4 and v5): machines, VPN, OVPN files, and raw API calls. Zero external dependencies.
See the command reference for every command, option, default, and the API endpoint each command calls.
Sources
- Official HTB Enterprise Public API documentation: https://enterprise-help.hackthebox.com/en/articles/13375637-introduction-to-enterprise-public-api
- Official HTB article on Lab/OpenVPN access: https://help.hackthebox.com/en/articles/5185687-gs-introduction-to-lab-access
- v4 Postman collection: https://documenter.getpostman.com/view/13129365/TVeqbmeq
- Readable community reference for Labs v4 endpoints: https://github.com/D3vil0p3r/HackTheBox-API
Note: HTB officially documents the Enterprise API. The Labs v4 endpoints used here come from the Postman collection and community references; they may change without notice.
Screenshots
Compact active-machine output keeps the session details and profile-only status text readable without dumping the full HTB profile.
machine active --oneline collapses that into a single status line.
Tables use compact columns, terminal colors, and truncation by default. Use --wide when you need full values.
user info shows your rank, points, and owns at a glance.
speedrun shows a live, per-step status while it connects the VPN, sets the MTU, and spawns the machine.
These images are generated with scripts/screenshots.py (see Regenerating screenshots).
Installation
Requires Python 3.10+. This project has no external dependencies.
Install as a global command with pipx (recommended) so
htb is available from any directory:
pipx install htbx
htb --help
Or with pip:
pip install htbx
htb --help
The package is published on PyPI as htbx; it installs the htb command
(an htbx alias is installed too). To run the latest from source without
installing, use a clone:
chmod +x ./htb
./htb --help
Authentication
Generate an App Token from your HTB profile settings, then save it once with
init:
htb init
# Paste your HTB App Token (input hidden): ...
htb init --check # also verify the token and print who you are
init stores the token in your user config directory
(~/.config/htb-terminal/token, or $XDG_CONFIG_HOME/htb-terminal/token) with
owner-only permissions, so a pipx-installed htb finds it from anywhere. You
can also pass it non-interactively:
htb init --token "$MY_TOKEN"
echo "$MY_TOKEN" | htb init
The token is resolved in this order, first match wins:
HTB_API_TOKENenvironment variable.--token-file PATH, when given../api.tokenin the current directory (handy as a per-project override).- The user config file written by
htb init.
api.token is gitignored; never commit your token.
Examples
Full details for each command are in the command reference.
./htb machine active
./htb machine profile "BoardLight"
./htb machine list
./htb machine list --retired --page 1
./htb machine list --sp-tier 1
./htb machine search board
./htb machine search kerberos --all --limit 10
./htb machine search "breach creds" --all --profiles
./htb machine start "BoardLight" --mode auto
./htb machine start 444 --mode play
./htb machine start 478 --mode spawn
# Seasonal release: keep retrying a full spawn server, then wait for the IP.
./htb machine start Nimbus --mode auto --wait --retry-for 360 --interval 5
./htb machine stop
./htb machine reset
./htb machine extend
./htb machine submit 444 HTB{flag} --difficulty 50
./htb machine active --oneline
./htb user info
./htb vpn servers
./htb vpn servers starting_point
./htb vpn switch us-free-1
./htb vpn switch 'US Machines VIP+ 1' # friendly names from live listing work
./htb vpn download us-free-1 -o lab-vpn.ovpn
./htb vpn connect us-free-1 -o lab-vpn.ovpn
# Speedrun a seasonal release: connect VPN, set MTU 1300, spawn, wait for IP.
sudo htb speedrun Seasonal us-free-1
sudo htb speedrun Pov 'US Machines VIP+ 1' # live server name also works
./htb raw GET /machine/active
./htb raw POST /vm/spawn --data '{"machine_id":478}'
Output
By default, commands print human-readable output with terminal-width wrapping and automatic color when stdout is an interactive terminal.
Use raw JSON for scripts:
./htb --json machine active
Color can be controlled globally:
./htb --color never machine active
./htb --color always machine list
Tables are compact by default and truncate long cells to fit common terminal widths. Use --wide to keep full table values:
./htb --wide machine list
./htb --wide machine search active-directory --all
machine active enriches the active session response with the matching machine profile when a machine is active, but prints a compact summary by default. The summary includes useful profile-only text such as info_status and description when HTB returns it. Use --details for synopsis and Academy module names, or --json before the command for the full enriched response:
./htb machine active --details
./htb --json machine active
Machine search
machine search intentionally uses the documented/listed Labs v4 machine list endpoints and filters the results locally. It does not depend on an undocumented search endpoint.
By default it scans playable machines:
./htb machine search linux
Useful options:
--retired: search retired machines only.--all: search playable and retired machines.--profiles: also fetch each scanned machine profile and search profile-only fields such as descriptions.--limit N: stop after printing up toNmatches.--max-pages N: cap the number of API pages scanned per list.
Without --profiles, the query matches machine id, name, OS, difficulty, tags, maker names, and common list fields. Use --profiles for terms that may only exist in the detailed machine profile, for example description text mentioning breached credentials.
Spawn a seasonal box with --wait
When a seasonal machine drops (Saturdays 19:00 UTC) the spawn servers are full,
so a plain machine start fails immediately with a capacity error. If you are
already connected to the VPN, add --wait to keep retrying until a slot frees
up and then block until the machine reports its IP:
./htb machine start Connected --mode auto --wait --retry-for 360 --interval 5
--retry-for SECONDS: total time to keep retrying the spawn (default 600).--interval SECONDS: base delay between spawn attempts and ready-state polls; each spawn delay is jittered ±20% so simultaneous clients do not retry in lockstep at the release moment.
Only transient rejections are retried -- capacity messages (full, busy,
try again, ...) and 429/5xx responses, including the Release Arena's
"Failed to spawn ... Please try again." Real failures (bad token, unknown
machine) fail fast instead of spinning for minutes.
If HTB rejects the start with "You already have an active instance", the command inspects what is active and reacts automatically:
- Same machine, already has an IP -- returns it without churning the instance.
- Same machine, no IP (stuck slot) -- terminates the stuck instance and retries.
- Different machine is active -- fails with a clear error naming the blocker.
For the full hands-off flow that also connects the VPN and tunes the MTU,
use speedrun below.
Speedrun a season release
When a seasonal machine drops (Saturdays 19:00 UTC), the slow part is the manual
dance of connecting the VPN, fixing the tunnel MTU, and spamming spawn until a
slot frees up. speedrun does all of it in one command and shows a live status
for each step:
sudo htb speedrun Seasonal us-free-1
sudo htb speedrun Pov 'EU Machines VIP+ 1'
speedrun: Seasonal via us-free-1
resolve machine 'Seasonal' ... ok
switch vpn server us-free-1 ... ok
download ovpn config ... ok
start openvpn ... ok
wait for tun0 ... ok (3s)
set tun0 mtu 1300 ... ok
spawn machine ... ok (42s)
wait for machine ip ... ok (18s)
ready: Seasonal 10.10.11.50
It needs root (for OpenVPN and the MTU change), so run it with sudo. The VPN
runs in the foreground; press Ctrl-C to disconnect. Tunables: --mtu,
--interface, --retry-for, --interval, --mode, and --variant.
Running with Sudo & Privilege Elevation
Since commands like speedrun and vpn connect manage OpenVPN processes and network interfaces, they require sudo to run. Here are some key behaviors and tips when running under sudo:
- Token Resolution: By default,
sudoswitches the environment home directory to/root. If a token exists at/root/.config/htb-terminal/token, it will be used. If not, the client automatically falls back to looking for your token under the invoking user's home config directory (retrieved using theSUDO_USERenvironment variable, e.g./home/username/.config/htb-terminal/token). - Command Not Found (
htbx): If you installedhtbxviapipx, it resides in your user binary directory (e.g.~/.local/bin), which is not inroot'sPATH. Ifsudo htbxfails withcommand not found, usesudo htbinstead, or create a symlink to make it globally available under/usr/bin:sudo ln -s $(which htb) /usr/bin/htb
Shell completion
Enable tab-completion for commands, subcommands, and options. The same script
works for both the htb and htbx commands.
# bash — add to ~/.bashrc:
eval "$(htb completion bash)"
# zsh — add to ~/.zshrc (before compinit, or save to a file on your $fpath):
eval "$(htb completion zsh)"
Architecture
htb_terminal/cli.py: thin entry point — dispatch and error rendering.htb_terminal/parser.py: argparse definitions, one builder per command group.htb_terminal/handlers.py: command handlers (args + client -> result).htb_terminal/config.py: loads/saves the token and API URL.htb_terminal/http.py: authenticated HTTP client.htb_terminal/output.py: human-readable and JSON rendering.htb_terminal/ui.py:StepRunnerlive step status for workflows.htb_terminal/netcfg.py: host network/process ops (OpenVPN, interface, MTU).htb_terminal/timefmt.py: relative time formatting for display.htb_terminal/completion.py: bash/zsh completion scripts from one command map.htb_terminal/services/machines.py:MachineService— machine API operations.htb_terminal/services/user.py:UserService— current-user profile.htb_terminal/services/speedrun.py:SpeedrunService— season-release flow.htb_terminal/services/payloads.py: pure helpers that reshape machine-list JSON.htb_terminal/services/search.py: local machine search and result ranking.htb_terminal/services/spawn.py: transient spawn-failure detection.htb_terminal/services/vpn.py: VPN and OVPN operations.
Each module keeps a single responsibility to make future changes easier if HTB
changes an endpoint. MachineService only talks to the API; payload reshaping,
search ranking, and spawn-error detection are stateless modules it composes, so
they are testable in isolation and stay small.
Regenerating screenshots
The images in docs/screenshots/ are produced by scripts/screenshots.py,
which renders each command's output to SVG with rich and screenshots it with a
headless Chromium — no PTY needed, so it works on CI and remote boxes:
python3 scripts/screenshots.py # capture all shots
python3 scripts/screenshots.py vpn-servers speedrun # capture a subset
Commands that hit the API need a saved token (htb init); the machine active
shots need an active machine. The speedrun and machine-start-wait shots use
the fixed demos scripts/demo_speedrun.py and scripts/demo_seasonal_start.py,
which render a realistic status with no token, root, or network.
Development
Install the dev tools and run the test suite:
pip install -e ".[dev]"
pytest
ruff check .
The tests are also runnable with the standard library alone:
python3 -m unittest discover -s tests -v
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file htbx-0.5.1.tar.gz.
File metadata
- Download URL: htbx-0.5.1.tar.gz
- Upload date:
- Size: 52.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a2de00f4506142e5cecbb653f83092724299f1778e803b098c4c34cca84da44
|
|
| MD5 |
d17f89bcdd4f0b63a42eea9d55ffae40
|
|
| BLAKE2b-256 |
4c488ad0e39291e4d50f85d140ad6962bd99e54280eb3dde9fb872e492e0e630
|
Provenance
The following attestation bundles were made for htbx-0.5.1.tar.gz:
Publisher:
release.yml on Okymi-X/htb-terminal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
htbx-0.5.1.tar.gz -
Subject digest:
3a2de00f4506142e5cecbb653f83092724299f1778e803b098c4c34cca84da44 - Sigstore transparency entry: 1981404045
- Sigstore integration time:
-
Permalink:
Okymi-X/htb-terminal@597999cf99d2ff794713d6ba34b4e13fc71132c1 -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/Okymi-X
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@597999cf99d2ff794713d6ba34b4e13fc71132c1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file htbx-0.5.1-py3-none-any.whl.
File metadata
- Download URL: htbx-0.5.1-py3-none-any.whl
- Upload date:
- Size: 42.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2aaeb256baa45a121f024721e832477f99e2fc0f1557a5213072e7f6ba78ba3
|
|
| MD5 |
5472a688f5ef0b2b0645953f84acab03
|
|
| BLAKE2b-256 |
82e956df491119bd241195ee0036061f02c49390b6f44d20d6da0ee57a1c43ad
|
Provenance
The following attestation bundles were made for htbx-0.5.1-py3-none-any.whl:
Publisher:
release.yml on Okymi-X/htb-terminal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
htbx-0.5.1-py3-none-any.whl -
Subject digest:
c2aaeb256baa45a121f024721e832477f99e2fc0f1557a5213072e7f6ba78ba3 - Sigstore transparency entry: 1981404133
- Sigstore integration time:
-
Permalink:
Okymi-X/htb-terminal@597999cf99d2ff794713d6ba34b4e13fc71132c1 -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/Okymi-X
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@597999cf99d2ff794713d6ba34b4e13fc71132c1 -
Trigger Event:
push
-
Statement type: