main.go raw
1 // orly-launcher is a process supervisor that manages the database and relay
2 // processes in split mode. It starts the database server first, waits for it
3 // to be ready, then starts the relay with the gRPC database backend.
4 package main
5
6 import (
7 "context"
8 "fmt"
9 "os"
10 "os/signal"
11 "syscall"
12
13 "next.orly.dev/pkg/lol/chk"
14 "next.orly.dev/pkg/lol/log"
15 "next.orly.dev/pkg/version"
16 )
17
18 func main() {
19 cfg, err := loadConfig()
20 if err != nil {
21 fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err)
22 os.Exit(1)
23 }
24
25 // Handle version request
26 if len(os.Args) > 1 && (os.Args[1] == "version" || os.Args[1] == "-v" || os.Args[1] == "--version") {
27 fmt.Println(version.V)
28 os.Exit(0)
29 }
30
31 // Handle help request
32 if len(os.Args) > 1 && (os.Args[1] == "help" || os.Args[1] == "-h" || os.Args[1] == "--help") {
33 printHelp()
34 os.Exit(0)
35 }
36
37 ctx, cancel := context.WithCancel(context.Background())
38 defer cancel()
39
40 supervisor := NewSupervisor(ctx, cancel, cfg)
41
42 // Handle shutdown signals
43 sigChan := make(chan os.Signal, 1)
44 signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
45
46 go func() {
47 sig := <-sigChan
48 log.I.F("received signal %v, shutting down...", sig)
49 cancel()
50 }()
51
52 log.I.F("starting orly-launcher %s", version.V)
53 if cfg.ServicesEnabled {
54 log.I.F("database binary: %s", cfg.DBBinary)
55 log.I.F("relay binary: %s", cfg.RelayBinary)
56 log.I.F("database listen: %s", cfg.DBListen)
57 } else {
58 log.I.F("services disabled - running admin UI only")
59 }
60
61 // Start admin server if enabled
62 var adminServer *AdminServer
63 if cfg.AdminEnabled {
64 adminServer = NewAdminServer(cfg, supervisor)
65
66 // Ensure binary directory structure exists
67 if err := adminServer.updater.EnsureDirectories(); chk.E(err) {
68 log.W.F("failed to create binary directories: %v", err)
69 }
70
71 go func() {
72 if err := adminServer.Start(ctx); err != nil {
73 // Don't exit on admin server error, just log it
74 log.W.F("admin server stopped: %v", err)
75 }
76 }()
77 log.I.F("admin UI available at http://localhost:%d/admin", cfg.AdminPort)
78 if len(cfg.AdminOwners) > 0 {
79 log.I.F("admin owners: %v", cfg.AdminOwners)
80 } else {
81 log.W.F("no admin owners configured - admin API access disabled")
82 }
83 }
84
85 // Only start services if enabled
86 if cfg.ServicesEnabled {
87 if err := supervisor.Start(); chk.E(err) {
88 fmt.Fprintf(os.Stderr, "failed to start: %v\n", err)
89 os.Exit(1)
90 }
91 }
92
93 // Wait for context cancellation (signal received)
94 <-ctx.Done()
95
96 if cfg.ServicesEnabled {
97 log.I.F("stopping supervisor...")
98 if err := supervisor.Stop(); chk.E(err) {
99 log.E.F("error during shutdown: %v", err)
100 }
101 }
102
103 log.I.F("orly-launcher stopped")
104 }
105
106 func printHelp() {
107 fmt.Printf(`orly-launcher %s
108
109 Process supervisor for split-mode deployment of ORLY relay with admin web UI.
110
111 Usage: orly-launcher [command]
112
113 Commands:
114 help, -h, --help Show this help
115 version, -v, --version Show version
116
117 Environment Variables:
118 Process Management:
119 ORLY_LAUNCHER_SERVICES_ENABLED Start DB/relay on launch (default: true)
120 ORLY_LAUNCHER_DB_BINARY Path to orly-db binary (default: orly-db-{backend})
121 ORLY_LAUNCHER_RELAY_BINARY Path to orly binary (default: orly)
122 ORLY_LAUNCHER_ACL_BINARY Path to orly-acl binary (default: orly-acl-{mode})
123 ORLY_LAUNCHER_DB_BACKEND Database backend: badger, neo4j (default: badger)
124 ORLY_LAUNCHER_DB_LISTEN Address for database server (default: 127.0.0.1:50051)
125 ORLY_LAUNCHER_ACL_LISTEN Address for ACL server (default: 127.0.0.1:50052)
126 ORLY_LAUNCHER_ACL_ENABLED Enable ACL server (default: false)
127 ORLY_ACL_MODE ACL mode: follows, managed, curation (default: follows)
128 ORLY_LAUNCHER_DB_READY_TIMEOUT Timeout waiting for DB ready (default: 30s)
129 ORLY_LAUNCHER_STOP_TIMEOUT Timeout for graceful stop (default: 30s)
130 ORLY_DATA_DIR Data directory (passed to orly-db)
131 ORLY_LOG_LEVEL Log level for all processes (default: info)
132
133 Admin UI:
134 ORLY_LAUNCHER_ADMIN_ENABLED Enable admin HTTP server (default: true)
135 ORLY_LAUNCHER_ADMIN_PORT Admin server port (default: 8080)
136 ORLY_LAUNCHER_OWNERS Comma-separated hex pubkeys for admin access
137 ORLY_LAUNCHER_BIN_DIR Directory for versioned binaries
138
139 The launcher will:
140 1. Start the admin HTTP server (optional)
141 2. Start the database server (orly-db)
142 3. Wait for the database to be ready
143 4. Start the ACL server if enabled (orly-acl)
144 5. Start sync services if enabled
145 6. Start the relay (orly) with ORLY_DB_TYPE=grpc
146 7. Monitor all processes and restart if they crash
147 8. On shutdown, stop in reverse dependency order
148
149 Admin UI Features:
150 - View process status and versions
151 - Update binaries from release URLs
152 - Edit configuration
153 - Restart/rollback binaries
154
155 Example:
156 # Start with default binaries in PATH
157 orly-launcher
158
159 # Start with admin access for a specific pubkey
160 ORLY_LAUNCHER_OWNERS=abc123... orly-launcher
161
162 # Start with custom binary paths
163 ORLY_LAUNCHER_DB_BINARY=/opt/orly/orly-db-badger \
164 ORLY_LAUNCHER_RELAY_BINARY=/opt/orly/orly \
165 orly-launcher
166 `, version.V)
167 }
168