carlos / platform Public

cli: carlos add -backing unit — whole-app tenant defaults

Unit tenants default their socket to the carlos-app@<host>.socket path
(/run/carlos/apps/<host>.sock) and take no /data db default — their
data lives in the unit's StateDirectory under the box replicator.
Instances listing shows the backing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 8cc77fab685f31c1588593f7c5e4eaa7d9dd6e7a parent 6fab1ba
1 file changed, +20 −7
  • cli.go +20 −7
diff --git a/cli.go b/cli.go
index 089135fcf..dd602f9c0 100644
--- a/cli.go
+++ b/cli.go
@@ -55,6 +55,7 @@ func runAdd(args []string) {
addr := fs.String("addr", "", "TCP upstream host:port (whole-app tenants)")
channel := fs.String("channel", "stable", "release channel to follow")
hibernate := fs.Bool("hibernate", false, "hibernate when idle")
+ backing := fs.String("backing", "", `process backing: "" (agent exec child) | "unit" (carlos-app@<host> systemd unit)`)
dbPath := fs.String("db", "", "instance database path")
fs.Parse(args)
if fs.NArg() != 1 {
@@ -64,10 +65,22 @@ func runAdd(args []string) {
if reservedSlugs()[*slug] {
log.Fatalf("slug %q is reserved", *slug)
}
+ if *backing != "" && *backing != registry.BackingUnit {
+ log.Fatalf("backing %q: want \"\" or %q", *backing, registry.BackingUnit)
+ }
if *socket == "" && *addr == "" && *kind == "instance" {
- *socket = filepath.Join("/run/carlos", host+".sock")
+ if *backing == registry.BackingUnit {
+ // The carlos-app@<host>.socket unit's path — enable that unit
+ // alongside this route (or use -addr for a TCP-only app).
+ *socket = filepath.Join("/run/carlos/apps", host+".sock")
+ } else {
+ *socket = filepath.Join("/run/carlos", host+".sock")
+ }
}
- if *dbPath == "" && *kind == "instance" {
+ // No DB default for unit tenants: their data lives in the unit's
+ // StateDirectory (/var/lib/carlos-app/<host>), replicated by the box
+ // litestream unit — -db stays an explicit operator choice there.
+ if *dbPath == "" && *kind == "instance" && *backing != registry.BackingUnit {
*dbPath = filepath.Join(dataDir(), host+".db")
}
r, err := registry.Open(*reg)
@@ -76,8 +89,8 @@ func runAdd(args []string) {
}
if err := r.Upsert(registry.Route{
Host: host, Slug: *slug, Kind: *kind, Socket: *socket, Addr: *addr,
- App: *app, Channel: *channel, Hibernate: *hibernate, DBPath: *dbPath,
- Status: "ready",
+ App: *app, Channel: *channel, Hibernate: *hibernate, Backing: *backing,
+ DBPath: *dbPath, Status: "ready",
}); err != nil {
log.Fatal(err)
}
@@ -115,14 +128,14 @@ func runInstances(args []string) {
log.Fatal(err)
}
w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
- fmt.Fprintln(w, "HOST\tAPP\tKIND\tCHANNEL\tSTATUS\tVERSION\tHIB")
+ fmt.Fprintln(w, "HOST\tAPP\tKIND\tCHANNEL\tSTATUS\tVERSION\tHIB\tBACKING")
for _, t := range routes {
hib := ""
if t.Hibernate {
hib = "yes"
}
- fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
- t.Host, t.App, t.Kind, t.Channel, t.Status, t.Version, hib)
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
+ t.Host, t.App, t.Kind, t.Channel, t.Status, t.Version, hib, t.Backing)
}
w.Flush()
}