Add the Hammerhead control plane
Dashboard and API, accounts (organizations, roles, invitations, 2FA, OIDC single sign-on, API keys, revocable sessions), an encrypted SQLite database, resource logins, alerts and /metrics, mail egress management with DKIM keys, and the .env loader. Split out of the original single repository; depends on Reef for the shared types. The end-to-end tests that run Hammerhead, Blacktip and Bamboo together live in e2e/. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3daa368233
commit
f521fa8668
35 files changed
+10392
-1
No files matched your search
@@ -0,0 +1,61 @@
|
||||
// Hammerhead is the control plane: web UI + API that tells blacktip and bamboo what to do.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.veillax.com/Veillax/hammerhead"
|
||||
"git.veillax.com/Veillax/hammerhead/internal/secretbox"
|
||||
"git.veillax.com/Veillax/reef/dotenv"
|
||||
)
|
||||
|
||||
func env(k, def string) string {
|
||||
if v := os.Getenv(k); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func main() {
|
||||
// secrets come from a .env file (or the real environment), read before flags so flags can default to them
|
||||
envFile := dotenv.ArgPath(os.Args[1:], ".env")
|
||||
if used, err := dotenv.Load(envFile); err != nil {
|
||||
log.Fatalf("read %s: %v", envFile, err)
|
||||
} else if used != "" {
|
||||
log.Printf("loaded settings from %s", used)
|
||||
}
|
||||
|
||||
listen := flag.String("listen", env("HAMMERHEAD_LISTEN", ":8080"), "address for the web UI and API")
|
||||
data := flag.String("data", env("HAMMERHEAD_DATA", "./hammerhead-data"), "directory for the database")
|
||||
url := flag.String("url", env("HAMMERHEAD_URL", ""), "public URL people reach hammerhead at (used for login redirects and SSO); can also be set in Settings")
|
||||
flag.String("env", ".env", "file to read secrets and settings from")
|
||||
genkey := flag.Bool("genkey", false, "print a new random HAMMERHEAD_MASTER_KEY and exit")
|
||||
flag.Parse()
|
||||
if *genkey {
|
||||
fmt.Println(secretbox.GenerateKey())
|
||||
return
|
||||
}
|
||||
|
||||
days, _ := strconv.Atoi(os.Getenv("HAMMERHEAD_LOG_DAYS"))
|
||||
srv, err := hammerhead.New(*data, hammerhead.Options{
|
||||
AdminEmail: os.Getenv("HAMMERHEAD_ADMIN_EMAIL"),
|
||||
AdminPassword: os.Getenv("HAMMERHEAD_ADMIN_PASSWORD"),
|
||||
DashboardURL: *url,
|
||||
MasterKey: os.Getenv("HAMMERHEAD_MASTER_KEY"),
|
||||
MetricsToken: os.Getenv("HAMMERHEAD_METRICS_TOKEN"),
|
||||
LogRetentionDays: days,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer srv.Close()
|
||||
log.Printf("hammerhead listening on %s (open it in a browser to finish setup)", *listen)
|
||||
hs := &http.Server{Addr: *listen, Handler: srv.Handler(), ReadHeaderTimeout: 10 * time.Second}
|
||||
log.Fatal(hs.ListenAndServe())
|
||||
}
|
||||
Reference in new issue
Block a user