kctl-hz 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kctl_hz-0.3.0/.env.example +70 -0
- kctl_hz-0.3.0/.gitignore +33 -0
- kctl_hz-0.3.0/PKG-INFO +18 -0
- kctl_hz-0.3.0/README.md +355 -0
- kctl_hz-0.3.0/docs/api-reference.md +165 -0
- kctl_hz-0.3.0/docs/deployment-guide.md +343 -0
- kctl_hz-0.3.0/docs/infrastructure.md +182 -0
- kctl_hz-0.3.0/infra/hetzner/cloud_init.tf +27 -0
- kctl_hz-0.3.0/infra/hetzner/dns.tf +17 -0
- kctl_hz-0.3.0/infra/hetzner/firewalls.tf +29 -0
- kctl_hz-0.3.0/infra/hetzner/load_balancers.tf +86 -0
- kctl_hz-0.3.0/infra/hetzner/main.tf +32 -0
- kctl_hz-0.3.0/infra/hetzner/networks.tf +36 -0
- kctl_hz-0.3.0/infra/hetzner/outputs.tf +113 -0
- kctl_hz-0.3.0/infra/hetzner/placement_groups.tf +18 -0
- kctl_hz-0.3.0/infra/hetzner/servers.tf +85 -0
- kctl_hz-0.3.0/infra/hetzner/ssh_keys.tf +9 -0
- kctl_hz-0.3.0/infra/hetzner/templates/cloud-init-base.yml +94 -0
- kctl_hz-0.3.0/infra/hetzner/templates/cloud-init-database.yml +112 -0
- kctl_hz-0.3.0/infra/hetzner/templates/cloud-init-dokploy.yml +109 -0
- kctl_hz-0.3.0/infra/hetzner/terraform.tfvars.example +274 -0
- kctl_hz-0.3.0/infra/hetzner/variables.tf +179 -0
- kctl_hz-0.3.0/pyproject.toml +46 -0
- kctl_hz-0.3.0/skills/hetzner-admin/SKILL.md +290 -0
- kctl_hz-0.3.0/src/kctl_hz/__init__.py +3 -0
- kctl_hz-0.3.0/src/kctl_hz/__main__.py +5 -0
- kctl_hz-0.3.0/src/kctl_hz/cli.py +165 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/__init__.py +0 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/aliases.py +85 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/config_cmd.py +377 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/costs.py +111 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/dns.py +145 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/doctor_cmd.py +82 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/firewalls.py +293 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/health.py +34 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/images.py +151 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/ips.py +330 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/labels.py +137 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/load_balancers.py +303 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/locations.py +105 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/networks.py +271 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/placement_groups.py +149 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/rdns.py +127 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/s3.py +411 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/self_test.py +279 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/server_types.py +101 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/servers.py +598 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/skill_cmd.py +76 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/snapshots.py +161 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/ssh_keys.py +86 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/status.py +57 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/storage_boxes.py +287 -0
- kctl_hz-0.3.0/src/kctl_hz/commands/volumes.py +208 -0
- kctl_hz-0.3.0/src/kctl_hz/core/__init__.py +0 -0
- kctl_hz-0.3.0/src/kctl_hz/core/callbacks.py +38 -0
- kctl_hz-0.3.0/src/kctl_hz/core/client.py +55 -0
- kctl_hz-0.3.0/src/kctl_hz/core/config.py +153 -0
- kctl_hz-0.3.0/src/kctl_hz/core/exceptions.py +26 -0
- kctl_hz-0.3.0/src/kctl_hz/core/output.py +10 -0
- kctl_hz-0.3.0/src/kctl_hz/core/plugins.py +57 -0
- kctl_hz-0.3.0/src/kctl_hz/core/resolve.py +163 -0
- kctl_hz-0.3.0/src/kctl_hz/core/utils.py +80 -0
- kctl_hz-0.3.0/tests/__init__.py +0 -0
- kctl_hz-0.3.0/tests/conftest.py +103 -0
- kctl_hz-0.3.0/tests/test_client.py +186 -0
- kctl_hz-0.3.0/tests/test_commands.py +445 -0
- kctl_hz-0.3.0/tests/test_config.py +192 -0
- kctl_hz-0.3.0/tests/test_output.py +292 -0
- kctl_hz-0.3.0/tests/test_resolve.py +103 -0
- kctl_hz-0.3.0/tests/test_smoke.py +157 -0
- kctl_hz-0.3.0/tests/test_utils.py +108 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# kodemeio-hetzner-cloud - Environment Configuration Template
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Copy this file to .env.prod and fill in your values:
|
|
5
|
+
# cp .env.example .env.prod
|
|
6
|
+
# =============================================================================
|
|
7
|
+
|
|
8
|
+
# === Hetzner Cloud API ===
|
|
9
|
+
# Get your token from: https://console.hetzner.cloud/ > Project > Security > API Tokens
|
|
10
|
+
HCLOUD_TOKEN=your-hetzner-cloud-api-token
|
|
11
|
+
|
|
12
|
+
# === Hetzner DNS API (separate token from Cloud API) ===
|
|
13
|
+
# Get your token from: https://dns.hetzner.com/settings/api-token
|
|
14
|
+
HETZNER_DNS_TOKEN=your-hetzner-dns-api-token
|
|
15
|
+
|
|
16
|
+
# === S3 / Hetzner Object Storage ===
|
|
17
|
+
S3_ENDPOINT=https://fsn1.your-objectstorage.com
|
|
18
|
+
S3_ACCESS_KEY_ID=your-s3-access-key
|
|
19
|
+
S3_SECRET_ACCESS_KEY=your-s3-secret-key
|
|
20
|
+
S3_BUCKET=kodemeio-terraform-state
|
|
21
|
+
S3_REGION=fsn1
|
|
22
|
+
|
|
23
|
+
# === Mattermost Notifications ===
|
|
24
|
+
MM_WEBHOOK_URL=https://mm.kodeme.io/hooks/your-webhook-id
|
|
25
|
+
MM_API_URL=https://mm.kodeme.io/api/v4
|
|
26
|
+
MM_BOT_TOKEN=your-mattermost-bot-token
|
|
27
|
+
NOTIFY_MATTERMOST=false
|
|
28
|
+
|
|
29
|
+
# === Telegram Notifications ===
|
|
30
|
+
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
|
|
31
|
+
TELEGRAM_CHAT_ID=your-telegram-chat-id
|
|
32
|
+
NOTIFY_TELEGRAM=false
|
|
33
|
+
|
|
34
|
+
# === Email / SMTP ===
|
|
35
|
+
SMTP_HOST=smtp.kodeme.io
|
|
36
|
+
SMTP_PORT=587
|
|
37
|
+
SMTP_USER=system@kodeme.io
|
|
38
|
+
SMTP_PASSWORD=your-smtp-password
|
|
39
|
+
SMTP_SSL=true
|
|
40
|
+
NOTIFY_EMAIL_FROM=noreply@kodeme.io
|
|
41
|
+
NOTIFY_EMAIL_TO=admin@kodeme.io
|
|
42
|
+
NOTIFY_EMAIL=false
|
|
43
|
+
|
|
44
|
+
# === Service Identity ===
|
|
45
|
+
SERVICE_NAME=kodemeio-hetzner-cloud
|
|
46
|
+
DEPLOY_ENV=prod
|
|
47
|
+
COMPOSE_PROJECT_NAME=kodemeio-hcloud
|
|
48
|
+
|
|
49
|
+
# === Hetzner Cloud Defaults ===
|
|
50
|
+
HETZNER_DEFAULT_LOCATION=fsn1
|
|
51
|
+
HETZNER_DEFAULT_IMAGE=ubuntu-24.04
|
|
52
|
+
HETZNER_DEFAULT_SERVER_TYPE=cx22
|
|
53
|
+
HETZNER_DEFAULT_NETWORK_ZONE=eu-central
|
|
54
|
+
|
|
55
|
+
# === Resource Naming ===
|
|
56
|
+
HETZNER_RESOURCE_PREFIX=kodemeio
|
|
57
|
+
HETZNER_ENVIRONMENT=prod
|
|
58
|
+
|
|
59
|
+
# === Cost Budget (EUR/month) ===
|
|
60
|
+
HETZNER_MONTHLY_BUDGET=100
|
|
61
|
+
|
|
62
|
+
# === Docker Resource Limits ===
|
|
63
|
+
CPU_LIMIT=0.5
|
|
64
|
+
MEMORY_LIMIT=256M
|
|
65
|
+
|
|
66
|
+
# === Timezone ===
|
|
67
|
+
TZ=Asia/Jakarta
|
|
68
|
+
|
|
69
|
+
# === Debug ===
|
|
70
|
+
DEBUG=false
|
kctl_hz-0.3.0/.gitignore
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.eggs/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
|
|
14
|
+
# IDE
|
|
15
|
+
.idea/
|
|
16
|
+
.vscode/
|
|
17
|
+
*.swp
|
|
18
|
+
*.swo
|
|
19
|
+
|
|
20
|
+
# Testing
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.coverage
|
|
23
|
+
htmlcov/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.ruff_cache/
|
|
26
|
+
|
|
27
|
+
# OS
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
|
|
31
|
+
# Environment
|
|
32
|
+
.env
|
|
33
|
+
.env.local
|
kctl_hz-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kctl-hz
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Kodemeio Hetzner CLI — manage Hetzner Cloud infrastructure
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: httpx>=0.28.0
|
|
7
|
+
Requires-Dist: kctl-lib>=0.4.0
|
|
8
|
+
Requires-Dist: pydantic>=2.10.0
|
|
9
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
10
|
+
Requires-Dist: rich>=13.9.0
|
|
11
|
+
Requires-Dist: typer>=0.15.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: mypy>=1.14.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest-httpx>=0.35.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
18
|
+
Requires-Dist: types-pyyaml>=6.0.0; extra == 'dev'
|
kctl_hz-0.3.0/README.md
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# kctl-hz
|
|
2
|
+
|
|
3
|
+
Kodemeio Hetzner Cloud CLI — manage Hetzner Cloud infrastructure.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **139 commands** across 24 groups (servers, volumes, firewalls, networks, load-balancers, IPs, DNS, S3, storage-boxes, and more)
|
|
8
|
+
- **Terraform IaC** — production-ready infrastructure definitions with 3 architecture options
|
|
9
|
+
- **Multi-API support** — Hetzner Cloud API + DNS API + Robot API + S3
|
|
10
|
+
- **Profile-based config** — multiple named profiles with token/credential storage
|
|
11
|
+
- **Multiple output formats** — pretty (Rich tables), JSON, CSV, YAML
|
|
12
|
+
- **Cost estimation** — monthly cost calculator for running resources
|
|
13
|
+
- **10 quick aliases** — `sl`, `sg`, `sc`, `hc`, `ss`, `vl`, `fl`, `nl`, `ce`, `dz`
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv tool install kctl-hz
|
|
19
|
+
kctl-hz config init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# List all servers
|
|
26
|
+
kctl-hz servers list
|
|
27
|
+
|
|
28
|
+
# Get server details
|
|
29
|
+
kctl-hz servers get my-server
|
|
30
|
+
|
|
31
|
+
# Create a new server
|
|
32
|
+
kctl-hz servers create my-server --type cx22 --image ubuntu-24.04
|
|
33
|
+
|
|
34
|
+
# Check account health
|
|
35
|
+
kctl-hz health check
|
|
36
|
+
|
|
37
|
+
# Show resource status summary
|
|
38
|
+
kctl-hz status show
|
|
39
|
+
|
|
40
|
+
# Estimate monthly costs
|
|
41
|
+
kctl-hz costs estimate
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Global Options
|
|
45
|
+
|
|
46
|
+
All commands support these global options:
|
|
47
|
+
|
|
48
|
+
| Option | Short | Description |
|
|
49
|
+
|--------|-------|-------------|
|
|
50
|
+
| `--json` | | Output as JSON |
|
|
51
|
+
| `--quiet` | `-q` | Suppress info messages |
|
|
52
|
+
| `--format` | `-f` | Output format: `pretty`, `json`, `csv`, `yaml` (default: `pretty`) |
|
|
53
|
+
| `--no-header` | | Omit column headers in CSV output |
|
|
54
|
+
| `--profile` | `-p` | Config profile name |
|
|
55
|
+
| `--token` | | Hetzner Cloud API token override |
|
|
56
|
+
| `--dns-token` | | Hetzner DNS API token override |
|
|
57
|
+
| `--version` | `-V` | Show version and exit |
|
|
58
|
+
|
|
59
|
+
## Command Groups
|
|
60
|
+
|
|
61
|
+
### servers — Cloud server lifecycle management
|
|
62
|
+
|
|
63
|
+
| Command | Description |
|
|
64
|
+
|---------|-------------|
|
|
65
|
+
| `list` | List all servers |
|
|
66
|
+
| `get` | Get server details |
|
|
67
|
+
| `create` | Create a new server |
|
|
68
|
+
| `delete` | Delete a server |
|
|
69
|
+
| `reboot` | Reboot a server |
|
|
70
|
+
| `shutdown` | Gracefully shut down a server |
|
|
71
|
+
| `power-off` | Force power off a server |
|
|
72
|
+
| `reset` | Hard reset a server |
|
|
73
|
+
| `rebuild` | Rebuild a server from an image |
|
|
74
|
+
| `resize` | Resize a server to a different type |
|
|
75
|
+
| `update` | Update server metadata |
|
|
76
|
+
| `enable-rescue` | Enable rescue mode |
|
|
77
|
+
| `disable-rescue` | Disable rescue mode |
|
|
78
|
+
| `console` | Open server console |
|
|
79
|
+
| `metrics` | Show server CPU/network metrics |
|
|
80
|
+
|
|
81
|
+
### volumes — Block storage management
|
|
82
|
+
|
|
83
|
+
| Command | Description |
|
|
84
|
+
|---------|-------------|
|
|
85
|
+
| `list` | List all volumes |
|
|
86
|
+
| `get` | Get volume details |
|
|
87
|
+
| `create` | Create a new volume |
|
|
88
|
+
| `attach` | Attach volume to a server |
|
|
89
|
+
| `detach` | Detach volume from a server |
|
|
90
|
+
| `delete` | Delete a volume |
|
|
91
|
+
| `update` | Update volume metadata |
|
|
92
|
+
| `extend` | Extend volume size |
|
|
93
|
+
| `protect` | Toggle deletion protection |
|
|
94
|
+
|
|
95
|
+
### firewalls — Firewall rules management
|
|
96
|
+
|
|
97
|
+
| Command | Description |
|
|
98
|
+
|---------|-------------|
|
|
99
|
+
| `list` | List all firewalls |
|
|
100
|
+
| `get` | Get firewall details |
|
|
101
|
+
| `create` | Create a new firewall |
|
|
102
|
+
| `delete` | Delete a firewall |
|
|
103
|
+
| `update` | Update firewall metadata |
|
|
104
|
+
| `add-rule` | Add an inbound or outbound rule |
|
|
105
|
+
| `remove-rule` | Remove a firewall rule |
|
|
106
|
+
| `apply` | Apply firewall to a server |
|
|
107
|
+
| `remove-from` | Remove firewall from a server |
|
|
108
|
+
|
|
109
|
+
### networks — Private network management
|
|
110
|
+
|
|
111
|
+
| Command | Description |
|
|
112
|
+
|---------|-------------|
|
|
113
|
+
| `list` | List all networks |
|
|
114
|
+
| `get` | Get network details |
|
|
115
|
+
| `create` | Create a new private network |
|
|
116
|
+
| `delete` | Delete a network |
|
|
117
|
+
| `update` | Update network metadata |
|
|
118
|
+
| `add-subnet` | Add a subnet to the network |
|
|
119
|
+
| `remove-subnet` | Remove a subnet |
|
|
120
|
+
| `attach-server` | Attach a server to the network |
|
|
121
|
+
| `detach-server` | Detach a server from the network |
|
|
122
|
+
| `change-ip-range` | Change the network IP range |
|
|
123
|
+
| `add-route` | Add a static route |
|
|
124
|
+
| `remove-route` | Remove a static route |
|
|
125
|
+
|
|
126
|
+
### dns — Hetzner DNS zone and record management
|
|
127
|
+
|
|
128
|
+
| Command | Description |
|
|
129
|
+
|---------|-------------|
|
|
130
|
+
| `zones` | List all DNS zones |
|
|
131
|
+
| `records` | List records in a zone |
|
|
132
|
+
| `create-record` | Create a DNS record |
|
|
133
|
+
| `update-record` | Update an existing DNS record |
|
|
134
|
+
| `delete-record` | Delete a DNS record |
|
|
135
|
+
|
|
136
|
+
### ips — Floating and primary IP management
|
|
137
|
+
|
|
138
|
+
| Command | Description |
|
|
139
|
+
|---------|-------------|
|
|
140
|
+
| `list` | List all IPs |
|
|
141
|
+
| `get` | Get floating IP details |
|
|
142
|
+
| `create-floating` | Create a floating IP |
|
|
143
|
+
| `update-floating` | Update floating IP metadata |
|
|
144
|
+
| `delete-floating` | Delete a floating IP |
|
|
145
|
+
| `assign` | Assign floating IP to a server |
|
|
146
|
+
| `unassign` | Unassign floating IP |
|
|
147
|
+
| `get-primary` | Get primary IP details |
|
|
148
|
+
| `create-primary` | Create a primary IP |
|
|
149
|
+
| `update-primary` | Update primary IP metadata |
|
|
150
|
+
| `delete-primary` | Delete a primary IP |
|
|
151
|
+
|
|
152
|
+
### ssh-keys — SSH key management
|
|
153
|
+
|
|
154
|
+
| Command | Description |
|
|
155
|
+
|---------|-------------|
|
|
156
|
+
| `list` | List all SSH keys |
|
|
157
|
+
| `create` | Upload a new SSH key |
|
|
158
|
+
| `update` | Update SSH key name |
|
|
159
|
+
| `delete` | Delete an SSH key |
|
|
160
|
+
|
|
161
|
+
### snapshots — Server snapshot management
|
|
162
|
+
|
|
163
|
+
| Command | Description |
|
|
164
|
+
|---------|-------------|
|
|
165
|
+
| `list` | List all snapshots |
|
|
166
|
+
| `get` | Get snapshot details |
|
|
167
|
+
| `create` | Create a snapshot from a server |
|
|
168
|
+
| `delete` | Delete a snapshot |
|
|
169
|
+
| `restore` | Restore a server from a snapshot |
|
|
170
|
+
| `update` | Update snapshot metadata |
|
|
171
|
+
|
|
172
|
+
### load-balancers — Load balancer management
|
|
173
|
+
|
|
174
|
+
| Command | Description |
|
|
175
|
+
|---------|-------------|
|
|
176
|
+
| `list` | List all load balancers |
|
|
177
|
+
| `get` | Get load balancer details |
|
|
178
|
+
| `create` | Create a new load balancer |
|
|
179
|
+
| `delete` | Delete a load balancer |
|
|
180
|
+
| `update` | Update load balancer metadata |
|
|
181
|
+
| `add-target` | Add a server target |
|
|
182
|
+
| `remove-target` | Remove a server target |
|
|
183
|
+
| `add-service` | Add a service (port mapping) |
|
|
184
|
+
| `remove-service` | Remove a service |
|
|
185
|
+
|
|
186
|
+
### s3 — Hetzner Object Storage (S3-compatible)
|
|
187
|
+
|
|
188
|
+
| Command | Description |
|
|
189
|
+
|---------|-------------|
|
|
190
|
+
| `buckets` | List all S3 buckets |
|
|
191
|
+
| `ls` | List objects in a bucket |
|
|
192
|
+
| `size` | Show total size of a bucket |
|
|
193
|
+
| `cp` | Copy objects to/from a bucket |
|
|
194
|
+
| `sync` | Sync a local directory to/from a bucket |
|
|
195
|
+
| `mb` | Create a new bucket |
|
|
196
|
+
| `rb` | Delete a bucket |
|
|
197
|
+
|
|
198
|
+
### storage-boxes — Hetzner Robot storage box management
|
|
199
|
+
|
|
200
|
+
| Command | Description |
|
|
201
|
+
|---------|-------------|
|
|
202
|
+
| `list` | List all storage boxes |
|
|
203
|
+
| `get` | Get storage box details |
|
|
204
|
+
| `update` | Update storage box metadata |
|
|
205
|
+
| `snapshots` | List storage box snapshots |
|
|
206
|
+
| `create-snapshot` | Create a storage box snapshot |
|
|
207
|
+
| `delete-snapshot` | Delete a storage box snapshot |
|
|
208
|
+
| `reset-password` | Reset storage box password |
|
|
209
|
+
| `subaccounts` | List storage box subaccounts |
|
|
210
|
+
|
|
211
|
+
### placement-groups — Placement group management
|
|
212
|
+
|
|
213
|
+
| Command | Description |
|
|
214
|
+
|---------|-------------|
|
|
215
|
+
| `list` | List all placement groups |
|
|
216
|
+
| `get` | Get placement group details |
|
|
217
|
+
| `create` | Create a placement group |
|
|
218
|
+
| `delete` | Delete a placement group |
|
|
219
|
+
| `update` | Update placement group metadata |
|
|
220
|
+
|
|
221
|
+
### images — Server image management
|
|
222
|
+
|
|
223
|
+
| Command | Description |
|
|
224
|
+
|---------|-------------|
|
|
225
|
+
| `list` | List available images |
|
|
226
|
+
| `get` | Get image details |
|
|
227
|
+
| `delete` | Delete a custom image |
|
|
228
|
+
| `update` | Update image metadata |
|
|
229
|
+
|
|
230
|
+
### labels — Resource label management
|
|
231
|
+
|
|
232
|
+
| Command | Description |
|
|
233
|
+
|---------|-------------|
|
|
234
|
+
| `list` | List labels on a resource |
|
|
235
|
+
| `set` | Set a label on a resource |
|
|
236
|
+
| `remove` | Remove a label from a resource |
|
|
237
|
+
|
|
238
|
+
### rdns — Reverse DNS management
|
|
239
|
+
|
|
240
|
+
| Command | Description |
|
|
241
|
+
|---------|-------------|
|
|
242
|
+
| `get` | Get reverse DNS entry |
|
|
243
|
+
| `set` | Set a reverse DNS entry |
|
|
244
|
+
| `delete` | Delete a reverse DNS entry |
|
|
245
|
+
|
|
246
|
+
### server-types, locations, costs, health, status
|
|
247
|
+
|
|
248
|
+
| Group | Commands | Description |
|
|
249
|
+
|-------|----------|-------------|
|
|
250
|
+
| `server-types` | `list`, `get` | List and inspect available server types |
|
|
251
|
+
| `locations` | `list`, `get`, `datacenters` | Regions, cities, and datacenter details |
|
|
252
|
+
| `costs` | `estimate` | Estimate monthly cost for running resources |
|
|
253
|
+
| `health` | `check` | API connectivity and token health check |
|
|
254
|
+
| `status` | `show` | Summary of all active resources across the account |
|
|
255
|
+
|
|
256
|
+
### config — Profile management
|
|
257
|
+
|
|
258
|
+
| Command | Description |
|
|
259
|
+
|---------|-------------|
|
|
260
|
+
| `init` | Initialize a new profile interactively |
|
|
261
|
+
| `add` | Add a named profile |
|
|
262
|
+
| `use` | Switch active profile |
|
|
263
|
+
| `show` | Display current profile (secrets masked) |
|
|
264
|
+
| `validate` | Validate config file structure |
|
|
265
|
+
| `remove` | Remove a profile |
|
|
266
|
+
| `set` | Set a config value in the active profile |
|
|
267
|
+
| `profiles` | List all profiles |
|
|
268
|
+
| `current` | Show active profile name |
|
|
269
|
+
|
|
270
|
+
## Quick Aliases
|
|
271
|
+
|
|
272
|
+
| Alias | Expands to |
|
|
273
|
+
|-------|-----------|
|
|
274
|
+
| `sl` | `servers list` |
|
|
275
|
+
| `sg <name>` | `servers get <name>` |
|
|
276
|
+
| `sc <name>` | `servers create <name>` |
|
|
277
|
+
| `hc` | `health check` |
|
|
278
|
+
| `ss` | `status show` |
|
|
279
|
+
| `vl` | `volumes list` |
|
|
280
|
+
| `fl` | `firewalls list` |
|
|
281
|
+
| `nl` | `networks list` |
|
|
282
|
+
| `ce` | `costs estimate` |
|
|
283
|
+
| `dz` | `dns zones` |
|
|
284
|
+
|
|
285
|
+
## Shell Completions
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
# Zsh
|
|
289
|
+
kctl-hz --install-completion zsh
|
|
290
|
+
|
|
291
|
+
# Bash
|
|
292
|
+
kctl-hz --install-completion bash
|
|
293
|
+
|
|
294
|
+
# Fish
|
|
295
|
+
kctl-hz --install-completion fish
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Structure
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
kctl-hz/
|
|
302
|
+
├── src/kctl_hz/ # Python CLI (139 commands, 24 groups)
|
|
303
|
+
├── tests/ # 108+ pytest tests
|
|
304
|
+
├── infra/hetzner/ # Terraform IaC (13 .tf files + cloud-init templates)
|
|
305
|
+
├── docs/ # Deployment guide, infrastructure reference, API reference
|
|
306
|
+
├── skills/ # Claude Code skill definitions
|
|
307
|
+
└── .env.example # Required environment variables
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Terraform
|
|
311
|
+
|
|
312
|
+
Three production architecture options in `infra/hetzner/`:
|
|
313
|
+
|
|
314
|
+
| Option | Servers | Cost | Use Case |
|
|
315
|
+
|--------|---------|------|----------|
|
|
316
|
+
| A — Single | 1x cx52 | ~EUR75/mo | Dev/staging |
|
|
317
|
+
| B — 2-Server | cx42 + cx32 | ~EUR75/mo | Production <500 users |
|
|
318
|
+
| C — 3-Server | cx32 + cx42 + cx42 | ~EUR100/mo | Scale-ready 500+ users |
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
cd infra/hetzner
|
|
322
|
+
terraform init
|
|
323
|
+
terraform plan
|
|
324
|
+
terraform apply
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Infrastructure Architecture
|
|
328
|
+
|
|
329
|
+
kctl-hz manages the Kodemeio Hetzner Cloud infrastructure, which follows a multi-server layout:
|
|
330
|
+
|
|
331
|
+
- **Servers** run Docker Compose services via Dokploy, joined to `dokploy-network` (external bridge)
|
|
332
|
+
- **Private networks** (10.0.0.0/8) connect servers within a project for internal DB/Redis traffic
|
|
333
|
+
- **Firewalls** restrict inbound to ports 80, 443, and 22 (SSH) only — all other ports are blocked
|
|
334
|
+
- **Floating IPs** are used for failover and point to Traefik reverse proxy on the target server
|
|
335
|
+
- **S3 / storage-boxes** provide object storage for backups and static assets
|
|
336
|
+
- **DNS** (Hetzner DNS API) manages A/AAAA/CNAME records pointing to floating or primary IPs
|
|
337
|
+
- All secrets (API tokens, robot credentials, S3 keys) are stored in `~/.config/kodemeio/config.yaml` under the `hz` service key
|
|
338
|
+
|
|
339
|
+
## Config File
|
|
340
|
+
|
|
341
|
+
Config is stored at `~/.config/kodemeio/config.yaml` under the `hz` service key:
|
|
342
|
+
|
|
343
|
+
```yaml
|
|
344
|
+
profiles:
|
|
345
|
+
default:
|
|
346
|
+
hz:
|
|
347
|
+
token: hcloud_...
|
|
348
|
+
dns_token: dns_...
|
|
349
|
+
robot_user: u1234
|
|
350
|
+
robot_password: "****"
|
|
351
|
+
s3_endpoint: https://fsn1.your-objectstorage.com
|
|
352
|
+
s3_access_key: "****"
|
|
353
|
+
s3_secret_key: "****"
|
|
354
|
+
active_profile: default
|
|
355
|
+
```
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Hetzner API Reference
|
|
2
|
+
|
|
3
|
+
Quick reference for the Hetzner API endpoints used by `hcloud-mgr`.
|
|
4
|
+
|
|
5
|
+
## Cloud API (api.hetzner.cloud/v1)
|
|
6
|
+
|
|
7
|
+
### Servers
|
|
8
|
+
|
|
9
|
+
| Method | Endpoint | Description |
|
|
10
|
+
|--------|----------|-------------|
|
|
11
|
+
| GET | `/servers` | List all servers |
|
|
12
|
+
| GET | `/servers?name=NAME` | Get server by name |
|
|
13
|
+
| POST | `/servers` | Create server |
|
|
14
|
+
| PUT | `/servers/{id}` | Update server (rename, labels) |
|
|
15
|
+
| DELETE | `/servers/{id}` | Delete server |
|
|
16
|
+
| POST | `/servers/{id}/actions/poweron` | Power on |
|
|
17
|
+
| POST | `/servers/{id}/actions/poweroff` | Power off (hard) |
|
|
18
|
+
| POST | `/servers/{id}/actions/shutdown` | Shutdown (graceful) |
|
|
19
|
+
| POST | `/servers/{id}/actions/reboot` | Reboot (soft) |
|
|
20
|
+
| POST | `/servers/{id}/actions/reset` | Reset (hard) |
|
|
21
|
+
| POST | `/servers/{id}/actions/rebuild` | Rebuild from image |
|
|
22
|
+
| POST | `/servers/{id}/actions/change_type` | Resize server |
|
|
23
|
+
| POST | `/servers/{id}/actions/enable_rescue` | Enable rescue mode |
|
|
24
|
+
| POST | `/servers/{id}/actions/request_console` | Request VNC console |
|
|
25
|
+
| POST | `/servers/{id}/actions/create_image` | Create snapshot |
|
|
26
|
+
|
|
27
|
+
### Volumes
|
|
28
|
+
|
|
29
|
+
| Method | Endpoint | Description |
|
|
30
|
+
|--------|----------|-------------|
|
|
31
|
+
| GET | `/volumes` | List all volumes |
|
|
32
|
+
| POST | `/volumes` | Create volume |
|
|
33
|
+
| DELETE | `/volumes/{id}` | Delete volume |
|
|
34
|
+
| POST | `/volumes/{id}/actions/attach` | Attach to server |
|
|
35
|
+
| POST | `/volumes/{id}/actions/detach` | Detach from server |
|
|
36
|
+
| POST | `/volumes/{id}/actions/resize` | Resize volume |
|
|
37
|
+
|
|
38
|
+
### Firewalls
|
|
39
|
+
|
|
40
|
+
| Method | Endpoint | Description |
|
|
41
|
+
|--------|----------|-------------|
|
|
42
|
+
| GET | `/firewalls` | List all firewalls |
|
|
43
|
+
| POST | `/firewalls` | Create firewall |
|
|
44
|
+
| DELETE | `/firewalls/{id}` | Delete firewall |
|
|
45
|
+
| POST | `/firewalls/{id}/actions/set_rules` | Set all rules (replace) |
|
|
46
|
+
| POST | `/firewalls/{id}/actions/apply_to_resources` | Apply to server |
|
|
47
|
+
| POST | `/firewalls/{id}/actions/remove_from_resources` | Remove from server |
|
|
48
|
+
|
|
49
|
+
### Networks
|
|
50
|
+
|
|
51
|
+
| Method | Endpoint | Description |
|
|
52
|
+
|--------|----------|-------------|
|
|
53
|
+
| GET | `/networks` | List all networks |
|
|
54
|
+
| POST | `/networks` | Create network |
|
|
55
|
+
| DELETE | `/networks/{id}` | Delete network |
|
|
56
|
+
| POST | `/networks/{id}/actions/add_subnet` | Add subnet |
|
|
57
|
+
| POST | `/networks/{id}/actions/delete_subnet` | Remove subnet |
|
|
58
|
+
| POST | `/networks/{id}/actions/add_route` | Add route |
|
|
59
|
+
| POST | `/networks/{id}/actions/delete_route` | Remove route |
|
|
60
|
+
|
|
61
|
+
### SSH Keys
|
|
62
|
+
|
|
63
|
+
| Method | Endpoint | Description |
|
|
64
|
+
|--------|----------|-------------|
|
|
65
|
+
| GET | `/ssh_keys` | List all SSH keys |
|
|
66
|
+
| POST | `/ssh_keys` | Create SSH key |
|
|
67
|
+
| PUT | `/ssh_keys/{id}` | Update SSH key |
|
|
68
|
+
| DELETE | `/ssh_keys/{id}` | Delete SSH key |
|
|
69
|
+
|
|
70
|
+
### Floating IPs
|
|
71
|
+
|
|
72
|
+
| Method | Endpoint | Description |
|
|
73
|
+
|--------|----------|-------------|
|
|
74
|
+
| GET | `/floating_ips` | List all floating IPs |
|
|
75
|
+
| POST | `/floating_ips` | Create floating IP |
|
|
76
|
+
| DELETE | `/floating_ips/{id}` | Delete floating IP |
|
|
77
|
+
| POST | `/floating_ips/{id}/actions/assign` | Assign to server |
|
|
78
|
+
| POST | `/floating_ips/{id}/actions/unassign` | Unassign from server |
|
|
79
|
+
|
|
80
|
+
### Primary IPs
|
|
81
|
+
|
|
82
|
+
| Method | Endpoint | Description |
|
|
83
|
+
|--------|----------|-------------|
|
|
84
|
+
| GET | `/primary_ips` | List all primary IPs |
|
|
85
|
+
|
|
86
|
+
### Load Balancers
|
|
87
|
+
|
|
88
|
+
| Method | Endpoint | Description |
|
|
89
|
+
|--------|----------|-------------|
|
|
90
|
+
| GET | `/load_balancers` | List all load balancers |
|
|
91
|
+
| POST | `/load_balancers` | Create load balancer |
|
|
92
|
+
| DELETE | `/load_balancers/{id}` | Delete load balancer |
|
|
93
|
+
| POST | `/load_balancers/{id}/actions/add_target` | Add target server |
|
|
94
|
+
| POST | `/load_balancers/{id}/actions/remove_target` | Remove target |
|
|
95
|
+
| POST | `/load_balancers/{id}/actions/add_service` | Add service |
|
|
96
|
+
| POST | `/load_balancers/{id}/actions/delete_service` | Remove service |
|
|
97
|
+
|
|
98
|
+
### Images / Snapshots
|
|
99
|
+
|
|
100
|
+
| Method | Endpoint | Description |
|
|
101
|
+
|--------|----------|-------------|
|
|
102
|
+
| GET | `/images?type=snapshot` | List all snapshots |
|
|
103
|
+
| GET | `/images/{id}` | Get image details |
|
|
104
|
+
| DELETE | `/images/{id}` | Delete snapshot |
|
|
105
|
+
|
|
106
|
+
### Placement Groups
|
|
107
|
+
|
|
108
|
+
| Method | Endpoint | Description |
|
|
109
|
+
|--------|----------|-------------|
|
|
110
|
+
| GET | `/placement_groups` | List all placement groups |
|
|
111
|
+
| POST | `/placement_groups` | Create placement group |
|
|
112
|
+
| DELETE | `/placement_groups/{id}` | Delete placement group |
|
|
113
|
+
|
|
114
|
+
### Actions
|
|
115
|
+
|
|
116
|
+
| Method | Endpoint | Description |
|
|
117
|
+
|--------|----------|-------------|
|
|
118
|
+
| GET | `/actions/{id}` | Get action status |
|
|
119
|
+
|
|
120
|
+
### Reference Data
|
|
121
|
+
|
|
122
|
+
| Method | Endpoint | Description |
|
|
123
|
+
|--------|----------|-------------|
|
|
124
|
+
| GET | `/locations` | List all locations |
|
|
125
|
+
| GET | `/server_types` | List all server types |
|
|
126
|
+
|
|
127
|
+
## DNS API (dns.hetzner.com/api/v1)
|
|
128
|
+
|
|
129
|
+
| Method | Endpoint | Description |
|
|
130
|
+
|--------|----------|-------------|
|
|
131
|
+
| GET | `/zones` | List all DNS zones |
|
|
132
|
+
| GET | `/zones?name=NAME` | Get zone by name |
|
|
133
|
+
| GET | `/records?zone_id=ID` | List records for zone |
|
|
134
|
+
| GET | `/records/{id}` | Get record details |
|
|
135
|
+
| POST | `/records` | Create DNS record |
|
|
136
|
+
| PUT | `/records/{id}` | Update DNS record |
|
|
137
|
+
| DELETE | `/records/{id}` | Delete DNS record |
|
|
138
|
+
|
|
139
|
+
### Authentication
|
|
140
|
+
|
|
141
|
+
- Cloud API: `Authorization: Bearer {HCLOUD_TOKEN}`
|
|
142
|
+
- DNS API: `Auth-API-Token: {HETZNER_DNS_TOKEN}`
|
|
143
|
+
|
|
144
|
+
### Pagination
|
|
145
|
+
|
|
146
|
+
Cloud API uses `page` and `per_page` query parameters:
|
|
147
|
+
```
|
|
148
|
+
GET /servers?page=1&per_page=50
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Response includes pagination metadata:
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"meta": {
|
|
155
|
+
"pagination": {
|
|
156
|
+
"page": 1,
|
|
157
|
+
"per_page": 50,
|
|
158
|
+
"previous_page": null,
|
|
159
|
+
"next_page": 2,
|
|
160
|
+
"last_page": 3,
|
|
161
|
+
"total_entries": 150
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|