Initial project snapshot

This commit is contained in:
2026-04-28 22:29:50 +03:00
commit 8ba0561f4f
365 changed files with 91832 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
# Installation Authority Tooling
The Product Root private key must stay outside the repository and outside the
cluster database. The backend stores only the public key and signed activation
records.
Generate a Product Root key pair:
```powershell
go run scripts/installation/product-root-tool.go generate-key
```
Configure production backend nodes with the generated `public_key_b64`:
```powershell
$env:INSTALLATION_AUTHORITY_MODE = "strict"
$env:INSTALLATION_PRODUCT_ROOT_PUBLIC_KEY_B64 = "<public_key_b64>"
```
Create a signed first-owner activation manifest:
```powershell
go run scripts/installation/product-root-tool.go activate `
-private-key-file C:\secure\rap-product-root.json `
-install-id install-prod-001 `
-owner-email owner@example.com `
-expires-at 2026-05-01T00:00:00Z `
-environment production
```
Use the output `activation_payload` and `activation_signature` in the admin
panel first-owner screen or in `POST /api/v1/installation/bootstrap-owner`.
+160
View File
@@ -0,0 +1,160 @@
package main
import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
"time"
)
const activationSchemaVersion = "rap.installation.activation.v1"
func main() {
if len(os.Args) < 2 {
fail("usage: go run scripts/installation/product-root-tool.go <generate-key|activate> [flags]")
}
switch os.Args[1] {
case "generate-key":
generateKey()
case "activate":
activate(os.Args[2:])
default:
fail("unknown command %q", os.Args[1])
}
}
func generateKey() {
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
fail("generate key: %v", err)
}
writeJSON(map[string]string{
"key_type": "ed25519",
"private_key_b64": base64.StdEncoding.EncodeToString(privateKey),
"public_key_b64": base64.StdEncoding.EncodeToString(publicKey),
})
}
func activate(args []string) {
fs := flag.NewFlagSet("activate", flag.ExitOnError)
privateKeyB64 := fs.String("private-key-b64", "", "base64 Ed25519 private key")
privateKeyFile := fs.String("private-key-file", "", "file with base64 key or generate-key JSON")
installID := fs.String("install-id", "", "installation id; generated when empty")
ownerEmail := fs.String("owner-email", "", "first owner email")
role := fs.String("role", "platform_admin", "platform_admin or platform_recovery_admin")
expiresAt := fs.String("expires-at", "", "RFC3339 expiry time")
environment := fs.String("environment", "", "optional environment label")
nonce := fs.String("nonce", "", "optional nonce")
if err := fs.Parse(args); err != nil {
fail("parse flags: %v", err)
}
keyText := strings.TrimSpace(*privateKeyB64)
if keyText == "" && strings.TrimSpace(*privateKeyFile) != "" {
content, err := os.ReadFile(*privateKeyFile)
if err != nil {
fail("read private key file: %v", err)
}
keyText = extractPrivateKeyText(content)
}
privateKey := decodePrivateKey(keyText)
email := strings.ToLower(strings.TrimSpace(*ownerEmail))
if email == "" || !strings.Contains(email, "@") {
fail("owner-email is required")
}
normalizedRole := strings.TrimSpace(*role)
if normalizedRole != "platform_admin" && normalizedRole != "platform_recovery_admin" {
fail("role must be platform_admin or platform_recovery_admin")
}
id := strings.TrimSpace(*installID)
if id == "" {
id = randomID("install")
}
payload := map[string]any{
"schema_version": activationSchemaVersion,
"install_id": id,
"owner_email": email,
"platform_role": normalizedRole,
"issued_at": time.Now().UTC().Format(time.RFC3339),
}
if strings.TrimSpace(*expiresAt) != "" {
if _, err := time.Parse(time.RFC3339, strings.TrimSpace(*expiresAt)); err != nil {
fail("expires-at must be RFC3339: %v", err)
}
payload["expires_at"] = strings.TrimSpace(*expiresAt)
}
if strings.TrimSpace(*environment) != "" {
payload["environment"] = strings.TrimSpace(*environment)
}
if strings.TrimSpace(*nonce) != "" {
payload["nonce"] = strings.TrimSpace(*nonce)
}
canonical, err := json.Marshal(payload)
if err != nil {
fail("canonicalize payload: %v", err)
}
signature := ed25519.Sign(privateKey, canonical)
writeJSON(map[string]any{
"activation_payload": payload,
"activation_signature": base64.StdEncoding.EncodeToString(signature),
})
}
func extractPrivateKeyText(content []byte) string {
text := strings.TrimSpace(string(content))
var generated struct {
PrivateKey string `json:"private_key_b64"`
}
if err := json.Unmarshal(content, &generated); err == nil && strings.TrimSpace(generated.PrivateKey) != "" {
return strings.TrimSpace(generated.PrivateKey)
}
return text
}
func decodePrivateKey(value string) ed25519.PrivateKey {
if strings.TrimSpace(value) == "" {
fail("private key is required")
}
decoded, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value))
if err != nil {
if raw, rawErr := base64.RawStdEncoding.DecodeString(strings.TrimSpace(value)); rawErr == nil {
decoded = raw
} else {
fail("private key must be base64 encoded: %v", err)
}
}
if len(decoded) != ed25519.PrivateKeySize {
fail("private key must decode to %d bytes", ed25519.PrivateKeySize)
}
return ed25519.PrivateKey(decoded)
}
func randomID(prefix string) string {
var bytes [16]byte
if _, err := rand.Read(bytes[:]); err != nil {
fail("generate id: %v", err)
}
return fmt.Sprintf("%s-%s", prefix, hex.EncodeToString(bytes[:]))
}
func writeJSON(value any) {
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
if err := encoder.Encode(value); err != nil {
fail("write json: %v", err)
}
}
func fail(format string, args ...any) {
fmt.Fprintf(os.Stderr, format+"\n", args...)
os.Exit(1)
}