Initial commit: Disaster recovery CLI tool

A Go-based CLI tool for recovering servers from backups to new cloud VMs.

Features:
- Multi-cloud support: Exoscale, Cloudscale, Hetzner Cloud
- Backup sources: Local filesystem, Hetzner Storage Box
- 6-stage restore pipeline with /etc whitelist protection
- DNS migration with safety checks and auto-rollback
- Dry-run by default, requires --yes to execute
- Cloud-init for SSH key injection

Packages:
- cmd/recover-server: CLI commands (recover, migrate-dns, list, cleanup)
- internal/providers: Cloud provider implementations
- internal/backup: Backup source implementations
- internal/restore: 6-stage restore pipeline
- internal/dns: Exoscale DNS management
- internal/ui: Prompts, progress, dry-run display
- internal/config: Environment and host configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Olaf Berberich
2025-12-08 00:31:27 +00:00
commit 29a2886402
26 changed files with 3826 additions and 0 deletions

60
internal/config/hosts.go Normal file
View File

@@ -0,0 +1,60 @@
package config
// HostConfig defines a recoverable host
type HostConfig struct {
Name string // Short name (proton, elektron, etc.)
FQDN string // Full domain name
DNSZone string // DNS zone for A/AAAA records
Services []string // Docker services to restore
BackupDir string // Subdirectory in backup source
}
// KnownHosts contains all configured hosts
var KnownHosts = map[string]HostConfig{
"proton": {
Name: "proton",
FQDN: "proton.obr.sh",
DNSZone: "obr.sh",
Services: []string{"gitea", "traefik", "portainer"},
BackupDir: "proton",
},
"photon": {
Name: "photon",
FQDN: "photon.obnh.io",
DNSZone: "obnh.io",
Services: []string{"gitea", "nginx"},
BackupDir: "photon",
},
"elektron": {
Name: "elektron",
FQDN: "elektron.obr.sh",
DNSZone: "obr.sh",
Services: []string{"gitea", "dns", "monitoring"},
BackupDir: "elektron",
},
"fry": {
Name: "fry",
FQDN: "fry.obr.sh",
DNSZone: "obr.sh",
Services: []string{"mastodon", "gitea", "traefik"},
BackupDir: "fry",
},
}
// GetHost returns host config by name
func GetHost(name string) (*HostConfig, bool) {
h, ok := KnownHosts[name]
if !ok {
return nil, false
}
return &h, true
}
// ListHosts returns all known host names
func ListHosts() []string {
hosts := make([]string, 0, len(KnownHosts))
for name := range KnownHosts {
hosts = append(hosts, name)
}
return hosts
}