hhctl
A command-line tool and a Terraform provider for Hammerhead. Both talk only to Hammerhead's public HTTP API with an API key, so they work against any server you can reach.
client/ Go API client (the only thing that speaks HTTP)
cmd/hhctl/ the CLI
cmd/terraform-provider-hammerhead/
internal/provider/ Terraform provider (terraform-plugin-framework)
internal/fakehh/ in-memory fake of the API, for tests
examples/ Terraform configuration and a shell script
Needs a Hammerhead with API-key support for server scope (branch feature/api-tokens-hhctl; see
hammerhead/docs/api-automation.md for the audit of what the API could and could not do before).
Credentials
| Key | Made with | Can |
|---|---|---|
| Organization key | Hammerhead dashboard, or POST /api/orgs/{org}/apikeys |
sites, ports, domains and alert channels of its organization; list edges it may use |
| Server key | POST /api/admin/apikeys as a signed-in server admin |
everything above in any organization, plus edges, server alert channels and creating organizations |
The secret is shown once when the key is created. Keep it out of version control; pass it in the environment.
export HAMMERHEAD_URL=https://hammerhead.example.com
export HAMMERHEAD_TOKEN=hhk_...
export HAMMERHEAD_ORG=... # optional with an organization key
CLI
go install git.veillax.com/Veillax/hhctl/cmd/hhctl@latest
hhctl orgs list
hhctl edges create --name fra --tunnel 203.0.113.5:51820 # server key; prints the edge's token
hhctl sites create --name home --tag lab # prints the agent's token
hhctl domains add --name example.com --edge fra
hhctl ports create --name web --proto https --edge fra --port 443 \
--hostname app.example.com --target home:192.168.1.30:8080
hhctl ports create -f port.json # any option, as JSON
hhctl ports list
hhctl alerts channel-create --name ops --kind email --target ops@example.com
hhctl alerts channels
hhctl ports delete <id>
Edges and sites can be given by name or ID. -o json prints the API's JSON (and the secret created, once) instead of a
table. Commands: orgs, edges, sites, ports, domains (list, create/add, delete) and alerts
(list, channels, channel-create, channel-delete, channel-test; --server for server-level ones).
See hhctl -h, and examples/bootstrap.sh.
Terraform provider
Resources: hammerhead_site, hammerhead_port, hammerhead_domain, hammerhead_edge, hammerhead_alert_channel.
Data source: hammerhead_edge (by name).
terraform {
required_providers {
hammerhead = { source = "veillax/hammerhead" }
}
}
provider "hammerhead" {} # url, token and org come from HAMMERHEAD_URL / _TOKEN / _ORG
resource "hammerhead_site" "home" {
name = "home"
tags = ["lab"]
}
resource "hammerhead_port" "web" {
name = "web"
proto = "https"
edge_id = data.hammerhead_edge.fra.id
port = 443
hostname = "app.example.com"
target {
site_id = hammerhead_site.home.id
host = "192.168.1.30"
port = 8080
}
}
A full example is in examples/main.tf. Notes:
hammerhead_site.tokenandhammerhead_edge.tokenare sensitive outputs: hand them to the Bamboo / Blacktip program.- Ports are replaced wholesale by the API.
passwordandpinare sent only when they change, because changing them signs every visitor out. Hammerhead never returns them, so out-of-band changes show up only as a removed password. - The server does not return an alert channel's webhook URL or Sandbar key, so those are kept as configured and drift in them is not detected.
- Domains cannot be edited in place; any change replaces them.
- Deleting an edge or a site in Hammerhead also deletes the ports that depend on it; let Terraform order the destroy via references as in the example.
- Import:
terraform import hammerhead_site.home <id>(same for port, domain, edge); alert channels take<id>orserver/<id>, and needtargetset in configuration after import.
For local use, build the provider and point Terraform at it with a dev_overrides block in ~/.terraformrc:
go build -o ~/go/bin/terraform-provider-hammerhead ./cmd/terraform-provider-hammerhead
provider_installation {
dev_overrides { "veillax/hammerhead" = "/home/you/go/bin" }
direct {}
}
Tests
go test ./...
The tests run against internal/fakehh, a small in-memory imitation of the API, and drive the provider's
CRUD methods directly (no terraform binary needed). The fake was checked by hand against a real Hammerhead
for the CLI flows; there are no automated acceptance tests (terraform-plugin-testing) yet.