HexRift
Config generator for the Conglomerate distributed proxy network. Takes a topology definition and produces Xray JSON configs and HAProxy configs for every node. Hub nodes additionally support WireGuard and XDNS inbounds.
Installation
uv sync
Usage
All commands require a topology YAML file:
hexrift --yaml conglomerate.yaml <command>
Commands
| Command | Description |
|---|---|
validate |
Validate the topology YAML against the schema |
show |
Visualize the network topology (regions, nodes, users, guests, portals) |
derive [users|groups|portals|nodes|all] |
Show derived identifiers (UUIDs, shortIds, emails) |
nodes list [--names|--domains|--json] [--type exit|hub] |
List nodes with hostnames; machine-friendly output for automation |
nodes add NODE_ID [--type exit|hub] [--region ID] [--hostname HOST] [--no-ipv6] [--hysteria] [--reality-dest HOST:PORT] [--reality-server-names LIST] [--reality-xhttp-path PATH] |
Add a node to its region in the topology YAML, creating the region when missing |
nodes remove NODE_ID |
Remove a node from the topology YAML, keeping its region |
gen-keys [NODE_ID|--all] [--force] [--keys-dir PATH] |
Generate x25519 + ML-KEM 768 keypairs for nodes |
build [NODE_ID|--all] --xray|--haproxy [--keys-dir PATH] [--out-dir PATH] |
Build Xray config.json and/or HAProxy .cfg |
gen-portal [PORTAL_ID|--all] [--group ID] [--fp FINGERPRINT] [--out-dir PATH] [--keys-dir PATH] |
Build Xray bridge config.json for portal(s) from the top-level portals: section |
diff NODE_ID [--current-dir PATH] [--keys-dir PATH] |
Diff generated config against deployed config |
share USERNAME [--hub NODE_ID] [--fp FINGERPRINT] [--cdn] [--wg] [--server] [--guest LABEL] [--all-guests] [--bare] [--keys-dir PATH] |
Generate VLESS share URLs or WireGuard client configs (--wg) |
Examples
# Validate topology
hexrift validate
# Visualize topology
hexrift show
# Show all derived identifiers
hexrift derive all
# List all exit node IDs (for scripts)
hexrift nodes list --names --type exit
# Structured node list (id, hostname, region, type) for other tools
hexrift nodes list --json
# Add a node to the topology file (creates the region if needed)
hexrift nodes add nlA20 --reality-dest www.samsung.com:443 --reality-xhttp-path /login/
# Generate keys for all nodes
hexrift gen-keys --all
# Build Xray config for a specific node
hexrift build nlA00 --xray --out-dir ./out
# Build all configs (Xray + HAProxy)
hexrift build --all --xray --haproxy --out-dir ./out
# Diff against deployed config
hexrift diff nlA00 --current-dir /etc/xray
# Generate a share link (CDN URL)
hexrift share alice --cdn
# Generate share links for all guests of a user
hexrift share alice --all-guests --bare | clip
# Generate a WireGuard client config
hexrift share alice --wg
Topology options
Beyond the basic hub/exit split:
- HAProxy-less nodes - by default every node runs HAProxy on
:443in front of Xray. Sethaproxy: falseto drop HAProxy and have Xray's Reality inbound bind0.0.0.0:443(or[::]:443when ipv6 is supported) directly.build --haproxythen emits a no-op stubhaproxy.cfgso managed HAProxy service stays up without touching:443. CDN (cdn_xhttp_path) needs HAProxy TLS termination and cannot be combined withhaproxy: false. - All-in-one node - set
routing.hub_default: directto make a hub egress everything itself (directoutbound) instead of routing to exit region. This allows topology with hub node(s) and no exit regions - single node clients connect to that proxies straight to the internet.hub_routesstill apply for per-domain/user exceptions. - Hysteria 2 -
defaults.hub.hysteria:(or a node-levelhysteria:) adds a QUIC/UDP inbound for users withaccess: [hysteria];hexrift share USER --hy2prints thehysteria2://URL. An exit region withprotocol: hysteriais dialed by hubs over Hysteria instead of VLESS+Reality (tuning viadefaults.exit.hysteria→regions[].hysteria→nodes[].hysteria); an exit that defineshysteriaon the region/node serves the listener regardless ofprotocol, so switching a region'sprotocolonly redeploys hub configs. Hysteria needs a real TLS cert: by default HexRift derives a self-signed leaf from the node's Reality key (key_type: ed25519, orecdsa-p256) and pins it (hub outbounds viapinnedPeerCertSha256, share URLs viapinSHA256); setcertificate: {cert_file, key_file}+snito serve an operator-issued cert instead. Xray's Hysteria dialer parrots Chrome's QUIC ClientHello, which cannot negotiate Ed25519 certs, so hubs dialing aned25519exit setdisableChromeParrot; ahysteria2://URL has no such switch, so hub listeners that Xray-based client apps dial should useecdsa-p256. UDP ports must not collide withwireguard/xdnson the same hub. See Topology Schema. - Site-to-site portals -
portals:declares a machine (e.g. a home server) that dials the hubs and opens a reverse tunnel; hub traffic from the portal's member users that matchesroutesegresses there.publish:forwards a hub-bound port into the tunnel for the ingress direction - that port is unauthenticated internet ingress and ignoresportals[].users, so setallow.strict: true(the default) confines portal-side egress to the declaredroutes/publishmatchers and blackholes the rest. See Topology Schema.
Architecture
hexrift/
components/
schema/ # Pydantic models for yaml
derive/ # Identity derivation (UUIDs, shortIds, emails), defaults resolution,
# topology->Xray-fragment construction, WireGuard and Hysteria derivation
keys/ # x25519 + ML-KEM 768 keypair generation and storage
render/ # Xray config builder + HAProxy Jinja2 templates
core/ # BaseApplication / Component / Controller framework
inbounds/ # Pluggable inbound specs (xhttp, cdn, proxy, xdns, wireguard, hysteria) + node contexts
links/ # Pluggable hub→exit link protocols (vless+reality, hysteria): dial context + outbound
shared/ # Cross-component helpers (crypto encoding, Xray/xhttp/hysteria fragments)
templates/ # Jinja2 stubs
haproxy/
wireguard/
Derivation
All identifiers are deterministically derived from the topology:
NAMESPACE UUID= UUID5(UUID(0), namespace)User UUID= UUID5(NAMESPACE_UUID, username)Server UUID= UUID5(USER_UUID,{username}-server)Portal UUID= UUID5(NAMESPACE_UUID,portal/{id})Guest UUID= UUID5(USER_UUID,{label})Hub-exit UUID= UUID5(NAMESPACE_UUID,{hubId}-{exitId})Warp UUID= Hub-exit UUID with 3rd segment replaced byffffGroup shortId= SHA256{groupId}.{namespace}Hub shortId= SHA256{nodeId}.hub.{namespace}Exit shortId= SHA256{nodeId}.exit.{namespace}WireGuard keypair= x25519(HMAC-SHA256(reality_private_key,{identity_uuid}.wireguard.{namespace}))Hysteria certificate= self-signed leaf for the SNI; key seed = HMAC-SHA256(reality_private_key,hysteria-tls.{namespace}) fored25519,hysteria-tls-ecdsa-p256.{namespace}forecdsa-p256; the pin is SHA-256 of its DERHysteria obfs password= base64url(HMAC-SHA256(reality_private_key,hysteria-obfs.{namespace}))
Keys
Keypairs are stored in keys/{nodeId}.yaml. Hub nodes in the same region share the same keypair. Key strings follow the format:
- Decryption (server inbound):
{method}.{mode}.{session_time}[.{padding}].{PRIVATE_KEY_b64} - Encryption (client outbound):
{method}.{mode}.0rtt.{PUBLIC_KEY_b64}
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 hexrift-0.12.3.tar.gz.
File metadata
- Download URL: hexrift-0.12.3.tar.gz
- Upload date:
- Size: 249.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ced6c880daf199a64b7d25d41239a9aa8999ce53eb38f458a02e817d151c0334
|
|
| MD5 |
91e4be715293ca5e2d8818603729a906
|
|
| BLAKE2b-256 |
f24f2295558431a275097711a705b733963d3516f4a1b5b573415a682e8c394a
|
File details
Details for the file hexrift-0.12.3-py3-none-any.whl.
File metadata
- Download URL: hexrift-0.12.3-py3-none-any.whl
- Upload date:
- Size: 107.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
273c7bb18e061d66b2f36e4f63a3ca5fae5be51e464e5df968c02af56c9ae746
|
|
| MD5 |
583056b277c5ef57eb2e901f929b0e28
|
|
| BLAKE2b-256 |
9a8a4c2f80138177300fb5fc6959f48afb514f0b8582adba502344975894fa5a
|