kampodine 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 — 2026-10-07
4
+
5
+ **Breaking: project-generic naming.** Every origin-coupled default name is
6
+ gone; the package now speaks only for itself.
7
+
8
+ - `KAMPODINE_HOST` replaces `ESPELLAR_HOST` everywhere (deploy, env); the
9
+ key-resolution ladder is `--ssh-key` | `KAMPODINE_SSH_KEY` | ssh-agent /
10
+ ssh-config — the old `ESPELLAR_SSH_KEY` / `ESSELLAR_SSH_KEY` aliases are
11
+ no longer mentioned anywhere
12
+ - Guest paths renamed: `/etc/esellar/*` → `/etc/kampodine/*` (env file,
13
+ deployed-sha, anchor.conf); the anchor watcher is `kampodine-anchor`
14
+ - Service/container/image names: `esellar-api` → `kampodine-api`,
15
+ `esellar-blue`/`esellar-green` → `kampodine-blue`/`kampodine-green`,
16
+ golden image `esellar-alpine*` → `kampodine-alpine*`
17
+ - `OCI_COMPARTMENT` is now REQUIRED (no default — compartments are
18
+ account-specific); `OCI_PROFILE` defaults to `default` (the OCI CLI's
19
+ own default profile) instead of a named profile
20
+ - Existing guests keep booting, but flips against guests provisioned by
21
+ older releases need their watcher/paths updated (or re-run
22
+ `vm-prepare` on a fresh host) since the anchor protocol path changed
23
+
3
24
  ## 0.3.0 — 2026-10-07
4
25
 
5
26
  Full-lifecycle orchestration with first-class help — the "mini vercel CLI"
package/README.md CHANGED
@@ -48,12 +48,12 @@ kampodine deploy:
48
48
  - **`kampodine deploy`** — stream deploy; `--version <sha>` restreams an
49
49
  existing build; `--rollback [sha]` is an instant image-tag rollback (the
50
50
  previous image stays on the VM for exactly this)
51
- - **`kampodine env`** — manage the remote app env file (`/etc/esellar/env`,
51
+ - **`kampodine env`** — manage the remote app env file (`/etc/kampodine/env`,
52
52
  0600 root) without ever printing a secret: `list` shows KEY + fingerprints
53
53
  only (value length + first 2 chars), `push --file <env>` uploads over ssh
54
54
  stdin into a 0600 temp + atomic `mv`, `pull` streams the raw payload to
55
55
  stdout or `--out` (written 0600). Host/key resolution is identical to
56
- deploy: `--host root@<ip>` | `ESPELLAR_HOST`, `--ssh-key <path>` |
56
+ deploy: `--host root@<ip>` | `KAMPODINE_HOST`, `--ssh-key <path>` |
57
57
  `KAMPODINE_SSH_KEY` | ssh-agent. `env fingerprint` previews the masking
58
58
  for any local file — values never leave stdin
59
59
  - **`kampodine dns`** — OCI DNS records for the post-sslip.io era:
package/cli.js CHANGED
@@ -39,7 +39,7 @@ DNS
39
39
  dns OCI DNS records (oci config-file / instance-principal auth ONLY): records | add | rm
40
40
 
41
41
  ENV
42
- env remote app env file (/etc/esellar/env, 0600): list | push | pull — values NEVER printed, fingerprints only
42
+ env remote app env file (/etc/kampodine/env, 0600): list | push | pull — values NEVER printed, fingerprints only
43
43
 
44
44
  Every command supports --help with usage + examples. All further args pass through to the underlying script.
45
45
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kampodine",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Kamal-style deploys for Alpine + Podman hosts, built on kamal-proxy",
5
5
  "license": "AGPL-3.0",
6
6
  "type": "module",
@@ -8,7 +8,7 @@
8
8
  # API call, rollback is the same call reversed.
9
9
  #
10
10
  # Colors are instance display names: <app>-blue / <app>-green (defaults:
11
- # esellar-blue / esellar-green).
11
+ # kampodine-blue / kampodine-green).
12
12
  # The reserved IP is derived from live OCI state — no local state file.
13
13
  #
14
14
  # Usage:
@@ -20,7 +20,7 @@
20
20
  #
21
21
  # FLIP = ACME-FIRST:
22
22
  # 1. health gate on the target's own IP
23
- # 2. anchor.conf written on the TARGET guest over ssh — the esellar-anchor
23
+ # 2. anchor.conf written on the TARGET guest over ssh — the kampodine-anchor
24
24
  # watcher service (shipped by vm-prepare) configures the anchor private
25
25
  # address within one interval; flip waits for `ip addr` to show it
26
26
  # 3. OCI assigns the reserved IP to the target's anchor (secondary private
@@ -49,7 +49,7 @@
49
49
  # instead — it runs the same ACME-first sequence there.
50
50
  #
51
51
  # provision has TWO routes:
52
- # 1. NATIVE — a UEFI_64 esellar-alpine* custom image exists in the
52
+ # 1. NATIVE — a UEFI_64 kampodine-alpine* custom image exists in the
53
53
  # compartment: launch it directly (the golden image boots as-is).
54
54
  # 2. INJECT (provision-via-migrate) — OCI pins imported custom images to
55
55
  # firmware=BIOS and A1/Ampere is UEFI-only, but the template instance
@@ -63,7 +63,7 @@
63
63
  # 'gunzip | sudo dd', reboot, verify /etc/alpine-release). The instance
64
64
  # record keeps the platform image metadata — exactly like the template.
65
65
  #
66
- # Env: OCI_PROFILE (default esellar-api), OCI_COMPARTMENT (default esellar).
66
+ # Env: OCI_PROFILE (default: "default"), OCI_COMPARTMENT (required).
67
67
  # Injection extras: ALPINE_QCOW2 (golden disk path), OPS_SSH_PUBKEY (ops
68
68
  # public key for the platform-image launch), PLATFORM_SSH_USER (default
69
69
  # ubuntu), INJECT_PROBE_SLEEP / INJECT_PROBE_TRIES (ssh wait tuning).
@@ -71,8 +71,8 @@
71
71
  # loopback; the reserved IP is checked over :80 with the prod Host header).
72
72
  set -euo pipefail
73
73
 
74
- PROFILE="${OCI_PROFILE:-esellar-api}"
75
- COMPARTMENT_NAME="${OCI_COMPARTMENT:-esellar}"
74
+ PROFILE="${OCI_PROFILE:-default}"
75
+ COMPARTMENT_NAME="${OCI_COMPARTMENT:-kampodine}"
76
76
  APP_HOST_HEADER="${APP_HOST_HEADER:-app.example.com}"
77
77
  REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)}"
78
78
  SSH_OPTS=(-o ConnectTimeout=6 -o BatchMode=yes -o StrictHostKeyChecking=accept-new)
@@ -86,7 +86,7 @@ usage() {
86
86
  cat <<'EOF'
87
87
 
88
88
  Each sub-step has its own --help: status | init | provision | flip | rollback.
89
- Env: OCI_PROFILE (default esellar-api), OCI_COMPARTMENT (default esellar).
89
+ Env: OCI_PROFILE (default: "default"), OCI_COMPARTMENT (required).
90
90
 
91
91
  Examples:
92
92
  kampodine bluegreen status
@@ -181,7 +181,7 @@ compartment_ocid() {
181
181
  instance_by_color() {
182
182
  local comp="$1" color="$2" row
183
183
  row="$(oci compute instance list -c "$comp" --profile "$PROFILE" \
184
- --display-name "esellar-$color" --lifecycle-state RUNNING \
184
+ --display-name "kampodine-$color" --lifecycle-state RUNNING \
185
185
  --query 'sort_by(data, &"time-created")[-1] | ["id", "availability-domain"] | join('\'' '\'', @)' \
186
186
  --raw-output 2>/dev/null || true)"
187
187
  [[ -n "$row" ]] || return 0
@@ -240,7 +240,7 @@ reserved_anchor_ip() {
240
240
  return 0
241
241
  fi
242
242
  oci network private-ip create --profile "$PROFILE" --vnic-id "$vnic" \
243
- --display-name "esellar-reserved-anchor" \
243
+ --display-name "kampodine-reserved-anchor" \
244
244
  --query 'data.id' --raw-output 2>/dev/null
245
245
  }
246
246
 
@@ -249,9 +249,9 @@ FLIP_POLL_SLEEP="${FLIP_POLL_SLEEP:-5}"
249
249
  FLIP_ADDR_TRIES="${FLIP_ADDR_TRIES:-12}" # guest watcher pickup: 12 x 5s = 60s
250
250
  FLIP_ACME_TRIES="${FLIP_ACME_TRIES:-24}" # LE HTTP-01: 24 x 5s = 120s
251
251
 
252
- # Guest anchor protocol (the esellar-anchor watcher half lives in
253
- # infra/alpine-host/ansible/roles/container-service/files/esellar-anchor.sh —
254
- # both halves of the conf path must stay in sync: /etc/esellar/anchor.conf).
252
+ # Guest anchor protocol (the kampodine-anchor watcher half lives in
253
+ # infra/alpine-host/ansible/roles/container-service/files/kampodine-anchor.sh —
254
+ # both halves of the conf path must stay in sync: /etc/kampodine/anchor.conf).
255
255
  # Remote commands are composed client-side BY DESIGN (vm-prepare convention);
256
256
  # the interpolated values are OCI-API derived and regex-gated at the call
257
257
  # sites, never user input.
@@ -260,7 +260,7 @@ FLIP_ACME_TRIES="${FLIP_ACME_TRIES:-24}" # LE HTTP-01: 24 x 5s = 120s
260
260
  write_anchor_conf() {
261
261
  local ip="$1" addr="$2"
262
262
  ssh "${SSH_OPTS[@]}" "root@$ip" \
263
- "umask 077; mkdir -p /etc/esellar; printf 'ANCHOR_ADDR=%s\nANCHOR_IFACE=\n' '$addr' > /etc/esellar/anchor.conf && echo ANCHOR_CONF_WRITTEN"
263
+ "umask 077; mkdir -p /etc/kampodine; printf 'ANCHOR_ADDR=%s\nANCHOR_IFACE=\n' '$addr' > /etc/kampodine/anchor.conf && echo ANCHOR_CONF_WRITTEN"
264
264
  }
265
265
 
266
266
  # shellcheck disable=SC2029
@@ -274,7 +274,7 @@ anchor_addr_ready() {
274
274
  read_anchor_conf_addr() {
275
275
  local ip="$1" line addr
276
276
  line="$(ssh "${SSH_OPTS[@]}" "root@$ip" \
277
- 'grep -h "^ANCHOR_ADDR=" /etc/esellar/anchor.conf 2>/dev/null | head -n 1' 2>/dev/null || true)"
277
+ 'grep -h "^ANCHOR_ADDR=" /etc/kampodine/anchor.conf 2>/dev/null | head -n 1' 2>/dev/null || true)"
278
278
  addr="${line#ANCHOR_ADDR=}"
279
279
  addr="${addr//\"/}"
280
280
  addr="${addr//\'/}"
@@ -297,7 +297,7 @@ delete_guest_addr() {
297
297
  # re-added the address once; retry the delete across one watcher interval.
298
298
  cleanup_target_anchor() {
299
299
  local ip="$1" addr="$2" i
300
- ssh "${SSH_OPTS[@]}" "root@$ip" "rm -f /etc/esellar/anchor.conf" >/dev/null 2>&1 || true
300
+ ssh "${SSH_OPTS[@]}" "root@$ip" "rm -f /etc/kampodine/anchor.conf" >/dev/null 2>&1 || true
301
301
  for ((i = 1; i <= 3; i++)); do
302
302
  delete_guest_addr "$ip" "$addr"
303
303
  anchor_addr_ready "$ip" "$addr" >/dev/null 2>&1 || return 0
@@ -323,7 +323,7 @@ flip_failure_rollback() {
323
323
  if [[ "$bpip" == ocid1.privateip* ]]; then
324
324
  if oci network public-ip update --public-ip-id "$rocid" --profile "$PROFILE" \
325
325
  --private-ip-id "$bpip" --force >/dev/null 2>&1; then
326
- say "rolled back to esellar-$ob (existing anchor $bpip)" >&2
326
+ say "rolled back to kampodine-$ob (existing anchor $bpip)" >&2
327
327
  recovered=1
328
328
  fi
329
329
  fi
@@ -331,14 +331,14 @@ flip_failure_rollback() {
331
331
  if (( ! recovered )); then
332
332
  if oci network public-ip update --public-ip-id "$rocid" --profile "$PROFILE" \
333
333
  --private-ip-id "" --force --wait-for-state AVAILABLE >/dev/null 2>&1; then
334
- say "reserved ip UNASSIGNED (dormant) — no existing anchor on esellar-$ob" >&2
334
+ say "reserved ip UNASSIGNED (dormant) — no existing anchor on kampodine-$ob" >&2
335
335
  else
336
336
  cleanup_target_anchor "$tpub" "$taddr"
337
337
  die "AUTO-ROLLBACK FAILED — reserved ip state unknown; flip manually via console: $rocid"
338
338
  fi
339
339
  fi
340
340
  cleanup_target_anchor "$tpub" "$taddr"
341
- say "esellar-$to cleaned (anchor.conf removed, anchor address deleted) — investigate before retrying" >&2
341
+ say "kampodine-$to cleaned (anchor.conf removed, anchor address deleted) — investigate before retrying" >&2
342
342
  }
343
343
 
344
344
  # instance_healthy <ephemeral_ip> -> ssh + loopback app check.
@@ -349,7 +349,7 @@ instance_healthy() {
349
349
  local ip="$1"
350
350
  [[ -n "$ip" ]] || return 1
351
351
  ssh "${SSH_OPTS[@]}" "root@$ip" \
352
- "rc-service esellar-api status >/dev/null 2>&1 && busybox wget -q -O /dev/null http://127.0.0.1:8080/up" 2>/dev/null
352
+ "rc-service kampodine-api status >/dev/null 2>&1 && busybox wget -q -O /dev/null http://127.0.0.1:8080/up" 2>/dev/null
353
353
  }
354
354
 
355
355
  # instance_wait_running <iid> — poll lifecycle-state to RUNNING. The OCI CLI's
@@ -365,7 +365,7 @@ instance_wait_running() {
365
365
  --query 'data."lifecycle-state"' --raw-output 2>/dev/null || true)"
366
366
  case "$state" in
367
367
  RUNNING) return 0 ;;
368
- FAILED | TERMINATED | TERMINATING) die "esellar instance reached $state — nothing to inject, check the console" ;;
368
+ FAILED | TERMINATED | TERMINATING) die "kampodine instance reached $state — nothing to inject, check the console" ;;
369
369
  esac
370
370
  sleep "$sleep_s"
371
371
  done
@@ -390,7 +390,7 @@ inject_alpine() {
390
390
  # the Alpine verify forever. The scrub below
391
391
  # clears the phase file between the two boots; the user's file is never
392
392
  # touched.
393
- kh="$(mktemp "${TMPDIR:-/tmp}/esellar-inject-kh.XXXXXX")"
393
+ kh="$(mktemp "${TMPDIR:-/tmp}/kampodine-inject-kh.XXXXXX")"
394
394
  iss() { ssh -o UserKnownHostsFile="$kh" "${SSH_OPTS[@]}" "$@"; }
395
395
  say "inject: waiting for ssh (${ruser}@${ip}, platform-image first boot)…"
396
396
  ssh_wait_probe() { iss "${ruser}@${ip}" true; }
@@ -400,7 +400,7 @@ inject_alpine() {
400
400
  [[ "$i" == "$probe_tries" ]] && die "ssh never came up on ${ip} (${ruser}) — check the instance console connection"
401
401
  sleep "$probe_sleep"
402
402
  done
403
- raw="$(mktemp "${TMPDIR:-/tmp}/esellar-inject-raw.XXXXXX")"
403
+ raw="$(mktemp "${TMPDIR:-/tmp}/kampodine-inject-raw.XXXXXX")"
404
404
  say "inject: converting ${qcow2} -> raw…"
405
405
  qemu-img convert -O raw "$qcow2" "$raw"
406
406
  say "inject: streaming golden disk -> ${ip} boot volume (gunzip | dd, conv=fsync)…"
@@ -440,7 +440,7 @@ inject_alpine() {
440
440
  done
441
441
  rm -f "$kh"
442
442
  [[ -n "$rel" ]] || die "injection streamed but no ALPINE 3.x ssh on ${ip} after reboot (got: '${rel:-nothing}') — check the serial console; terminate, do NOT flip to ${color}"
443
- say "INJECTED esellar-$color: Alpine ${rel} boots on ${ip} (instance image metadata stays the platform image — like green)"
443
+ say "INJECTED kampodine-$color: Alpine ${rel} boots on ${ip} (instance image metadata stays the platform image — like green)"
444
444
  say "next: kampodine vm-prepare --host root@${ip} -> kampodine deploy --host root@${ip}"
445
445
  say "then 'bluegreen.sh flip --to ${color}' (health-gated) once its app checks green."
446
446
  }
@@ -458,11 +458,12 @@ for help_arg in "$@"; do
458
458
  ;;
459
459
  esac
460
460
  done
461
+ [[ -n "${OCI_COMPARTMENT:-}" ]] || die "set OCI_COMPARTMENT=<compartment name or ocid> — no default: compartments are account-specific"
461
462
  case "$cmd" in
462
463
  status)
463
464
  comp="$(compartment_ocid)"
464
465
  rp="$(reserved_ip "$comp")"
465
- say "== esellar blue/green pair (compartment $COMPARTMENT_NAME) =="
466
+ say "== kampodine blue/green pair (compartment $COMPARTMENT_NAME) =="
466
467
  if [[ -n "$rp" ]]; then
467
468
  read -r rocid raddr rholder <<<"$rp"
468
469
  say "reserved IP : $raddr ($rocid)"
@@ -475,9 +476,9 @@ case "$cmd" in
475
476
  if [[ -n "$row" ]]; then
476
477
  read -r iid pub ad <<<"$row"
477
478
  if instance_healthy "$pub"; then verdict=HEALTHY; else verdict=UNHEALTHY/unreachable; fi
478
- say "esellar-$color : $iid ip=${pub:-none} ad=${ad:-?} app=$verdict"
479
+ say "kampodine-$color : $iid ip=${pub:-none} ad=${ad:-?} app=$verdict"
479
480
  else
480
- say "esellar-$color : not provisioned"
481
+ say "kampodine-$color : not provisioned"
481
482
  fi
482
483
  done
483
484
  ;;
@@ -491,7 +492,7 @@ case "$cmd" in
491
492
  exit 0
492
493
  fi
493
494
  addr="$(oci network public-ip create -c "$comp" --profile "$PROFILE" \
494
- --lifetime RESERVED --display-name esellar-active \
495
+ --lifetime RESERVED --display-name kampodine-active \
495
496
  --query 'data."ip-address"' --raw-output)"
496
497
  say "created DORMANT reserved IP: $addr (unassigned — no instance attached)"
497
498
  say "Point DNS/SSLIP at this address when the pair goes active."
@@ -502,25 +503,25 @@ case "$cmd" in
502
503
  color="${1:-}"
503
504
  [[ "$color" == blue || "$color" == green ]] || die "usage: kampodine bluegreen provision <blue|green>"
504
505
  comp="$(compartment_ocid)"
505
- instance_by_color "$comp" "$color" | grep -q . && die "esellar-$color already RUNNING"
506
+ instance_by_color "$comp" "$color" | grep -q . && die "kampodine-$color already RUNNING"
506
507
  # base: the OTHER color's AD + subnet (same fault domain layout), golden image
507
508
  other=green; [[ "$color" == green ]] && other=blue
508
509
  orow="$(instance_by_color "$comp" "$other")"
509
- [[ -n "$orow" ]] || die "esellar-$other not RUNNING — need its AD/subnet as the pair template"
510
+ [[ -n "$orow" ]] || die "kampodine-$other not RUNNING — need its AD/subnet as the pair template"
510
511
  # instance_by_color rows are "<ocid> <ephemeral_public_ip> <ad>" — field 2
511
512
  # is the IP, the AD is field 3 (launching with the IP as AD fails).
512
513
  read -r oiid _ oad <<<"$orow"
513
514
  subnet="$(oci compute vnic-attachment list -c "$comp" --profile "$PROFILE" \
514
515
  --instance-id "$oiid" --query 'data[0]."subnet-id"' --raw-output)"
515
516
 
516
- # Route selection. NATIVE only with a UEFI_64 esellar-alpine* custom image:
517
+ # Route selection. NATIVE only with a UEFI_64 kampodine-alpine* custom image:
517
518
  # OCI pins IMPORTED images to firmware=BIOS and A1 is UEFI-only, so a BIOS
518
519
  # verdict means the import would die at launch (Shape ... is not valid for
519
520
  # image) — skip it. Otherwise take the template's LIVE image-id from its
520
521
  # instance record and inject the golden disk (provision-via-migrate).
521
522
  mode="" image=""
522
523
  custom="$(oci compute image list -c "$comp" --profile "$PROFILE" --all --sort-by TIMECREATED \
523
- --query "data[?\"display-name\" != null && starts_with(\"display-name\", 'esellar-alpine')] | [0].id" \
524
+ --query "data[?\"display-name\" != null && starts_with(\"display-name\", 'kampodine-alpine')] | [0].id" \
524
525
  --raw-output 2>/dev/null || true)"
525
526
  if [[ "$custom" == ocid1.image* ]]; then
526
527
  fw="$(oci compute image get --image-id "$custom" --profile "$PROFILE" \
@@ -528,7 +529,7 @@ case "$cmd" in
528
529
  if [[ "$fw" == "UEFI_64" ]]; then
529
530
  image="$custom" mode="native"
530
531
  else
531
- say "note: newest esellar-alpine* custom image is firmware=${fw:-unknown} — A1 rejects BIOS-pinned imports, skipping to the platform-image + injection route"
532
+ say "note: newest kampodine-alpine* custom image is firmware=${fw:-unknown} — A1 rejects BIOS-pinned imports, skipping to the platform-image + injection route"
532
533
  fi
533
534
  fi
534
535
  if [[ -z "$image" ]]; then
@@ -536,7 +537,7 @@ case "$cmd" in
536
537
  --query 'data."image-id"' --raw-output 2>/dev/null || true)"
537
538
  [[ "$image" == ocid1.image* ]] || die "template instance has no resolvable image-id — cannot launch or inject"
538
539
  mode="inject"
539
- qcow2="${ALPINE_QCOW2:-${REPO_ROOT}/infra/alpine-host/build/esellar-alpine-3.22.6-aarch64.qcow2}"
540
+ qcow2="${ALPINE_QCOW2:-${REPO_ROOT}/infra/alpine-host/build/kampodine-alpine-3.22.6-aarch64.qcow2}"
540
541
  [[ -f "$qcow2" ]] || die "golden qcow2 not found: $qcow2 (build via packer, or set ALPINE_QCOW2)"
541
542
  command -v qemu-img >/dev/null 2>&1 || die "qemu-img not found in PATH (brew install qemu) — required for the qcow2 -> raw conversion"
542
543
  # ops ssh public key for the platform-image first boot. OPS_SSH_PUBKEY
@@ -549,22 +550,22 @@ case "$cmd" in
549
550
  keyfile="$OPS_SSH_PUBKEY"
550
551
  else
551
552
  agent_keys="$(ssh-add -L 2>/dev/null | grep -v '\.pub$' || true)"
552
- [[ -n "$agent_keys" ]] || die "no ssh key available: OPS_SSH_PUBKEY unset and ssh-add lists no keys (ssh-add ~/.ssh/id_ed25519-esellar, or set OPS_SSH_PUBKEY)"
553
- keyfile="$(mktemp "${TMPDIR:-/tmp}/esellar-ops-pubkey.XXXXXX")"
553
+ [[ -n "$agent_keys" ]] || die "no ssh key available: OPS_SSH_PUBKEY unset and ssh-add lists no keys (ssh-add ~/.ssh/id_ed25519-kampodine, or set OPS_SSH_PUBKEY)"
554
+ keyfile="$(mktemp "${TMPDIR:-/tmp}/kampodine-ops-pubkey.XXXXXX")"
554
555
  printf '%s\n' "$agent_keys" > "$keyfile"
555
556
  chmod 600 "$keyfile"
556
557
  fi
557
558
  fi
558
559
 
559
- say "launching esellar-$color: image=${image:0:60}… ad=$oad subnet=${subnet:0:60}… mode=$mode"
560
+ say "launching kampodine-$color: image=${image:0:60}… ad=$oad subnet=${subnet:0:60}… mode=$mode"
560
561
  # NOTE: --profile stays ON the launch line (script-gates static scan reads
561
562
  # the invocation line, not array contents).
562
563
  launch_args=(-c "$comp" --availability-domain "$oad" --subnet-id "$subnet"
563
564
  --image-id "$image" --shape VM.Standard.A1.Flex --shape-config '{"ocpus":2,"memoryInGBs":12}'
564
- --assign-public-ip true --display-name "esellar-$color")
565
+ --assign-public-ip true --display-name "kampodine-$color")
565
566
  [[ "$mode" == "inject" ]] && launch_args+=(--ssh-authorized-keys-file "$keyfile")
566
567
  iid="$(oci compute instance launch "${launch_args[@]}" --profile "$PROFILE" --query 'data.id' --raw-output)"
567
- say "LAUNCHED esellar-$color: $iid"
568
+ say "LAUNCHED kampodine-$color: $iid"
568
569
  if [[ "$mode" == "native" ]]; then
569
570
  say "next: wait RUNNING -> ssh in -> kampodine vm-prepare -> kampodine deploy --host root@<ephemeral-ip>"
570
571
  say "then 'bluegreen.sh flip --to $color' (health-gated) once its app checks green."
@@ -594,16 +595,16 @@ case "$cmd" in
594
595
  read -r rocid raddr _ <<<"$rp"
595
596
  rhost="${raddr//./-}.sslip.io"
596
597
  trow="$(instance_by_color "$comp" "$to")"
597
- [[ -n "$trow" ]] || die "esellar-$to is not RUNNING — nothing to flip to"
598
+ [[ -n "$trow" ]] || die "kampodine-$to is not RUNNING — nothing to flip to"
598
599
  read -r tiid tpub _ <<<"$trow"
599
600
  tvnic="$(primary_vnic_of "$tiid")"
600
- [[ -n "$tvnic" ]] || die "no primary VNIC on esellar-$to"
601
+ [[ -n "$tvnic" ]] || die "no primary VNIC on kampodine-$to"
601
602
  tpip="$(reserved_anchor_ip "$tvnic")"
602
- [[ "$tpip" == ocid1.privateip* ]] || die "could not resolve/create the reserved-anchor secondary private ip on esellar-$to"
603
+ [[ "$tpip" == ocid1.privateip* ]] || die "could not resolve/create the reserved-anchor secondary private ip on kampodine-$to"
603
604
  if instance_healthy "$tpub"; then
604
- say "target health: esellar-$to app HEALTHY on its own IP"
605
+ say "target health: kampodine-$to app HEALTHY on its own IP"
605
606
  else
606
- (( force )) || die "esellar-$to app UNHEALTHY — refusing flip (override: --force)"
607
+ (( force )) || die "kampodine-$to app UNHEALTHY — refusing flip (override: --force)"
607
608
  say "target health: UNHEALTHY — flipping anyway (--force)"
608
609
  fi
609
610
 
@@ -611,19 +612,19 @@ case "$cmd" in
611
612
  # once the hostname resolves to the reserved ip AND routes to the target,
612
613
  # so the guest anchor address goes FIRST, then the assign, then the cert.
613
614
  ob=green; [[ "$to" == green ]] && ob=blue
614
- say "flip[1/4]: anchor conf -> esellar-$to guest ($tpub), watcher configures the address"
615
+ say "flip[1/4]: anchor conf -> kampodine-$to guest ($tpub), watcher configures the address"
615
616
  tsubnet="$(oci compute vnic-attachment list -c "$comp" --profile "$PROFILE" \
616
617
  --instance-id "$tiid" --query 'data[0]."subnet-id"' --raw-output 2>/dev/null || true)"
617
- [[ "$tsubnet" == ocid1.subnet* ]] || die "could not resolve esellar-$to's subnet id"
618
+ [[ "$tsubnet" == ocid1.subnet* ]] || die "could not resolve kampodine-$to's subnet id"
618
619
  tcidr="$(oci network subnet get --subnet-id "$tsubnet" --profile "$PROFILE" \
619
620
  --query 'data."cidr-block"' --raw-output 2>/dev/null || true)"
620
- [[ "$tcidr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$ ]] || die "could not resolve esellar-$to's subnet cidr (got: ${tcidr:-none})"
621
+ [[ "$tcidr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$ ]] || die "could not resolve kampodine-$to's subnet cidr (got: ${tcidr:-none})"
621
622
  tanchor_addr="$(oci network private-ip get --private-ip-id "$tpip" --profile "$PROFILE" \
622
623
  --query 'data."ip-address"' --raw-output 2>/dev/null || true)"
623
- [[ "$tanchor_addr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "could not resolve the anchor private address on esellar-$to"
624
+ [[ "$tanchor_addr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "could not resolve the anchor private address on kampodine-$to"
624
625
  taddr_cidr="${tanchor_addr}/${tcidr##*/}"
625
626
  if ! write_anchor_conf "$tpub" "$taddr_cidr" >/dev/null 2>&1; then
626
- die "anchor.conf write failed on esellar-$to ($tpub) — nothing mutated, flip aborted"
627
+ die "anchor.conf write failed on kampodine-$to ($tpub) — nothing mutated, flip aborted"
627
628
  fi
628
629
  addr_ok=0
629
630
  for ((i = 1; i <= FLIP_ADDR_TRIES; i++)); do
@@ -632,15 +633,15 @@ case "$cmd" in
632
633
  done
633
634
  if (( ! addr_ok )); then
634
635
  cleanup_target_anchor "$tpub" "$taddr_cidr"
635
- die "guest watcher never configured $taddr_cidr on esellar-$to — is rc-service esellar-anchor running? (conf removed, NOTHING mutated)"
636
+ die "guest watcher never configured $taddr_cidr on kampodine-$to — is rc-service kampodine-anchor running? (conf removed, NOTHING mutated)"
636
637
  fi
637
- say "flip[2/4]: guest answers on $taddr_cidr — reserved $raddr -> esellar-$to (anchor $tpip)"
638
+ say "flip[2/4]: guest answers on $taddr_cidr — reserved $raddr -> kampodine-$to (anchor $tpip)"
638
639
  oci network public-ip update --public-ip-id "$rocid" --profile "$PROFILE" \
639
640
  --private-ip-id "$tpip" --force --wait-for-state ASSIGNED >/dev/null \
640
641
  || { cleanup_target_anchor "$tpub" "$taddr_cidr"; die "OCI flip call failed — guest conf removed; run 'kampodine bluegreen status'"; }
641
- say "flip[3/4]: reserved IP ASSIGNED — registering $rhost on esellar-$to's kamal-proxy (ACME HTTP-01 through the reserved ip)…"
642
+ say "flip[3/4]: reserved IP ASSIGNED — registering $rhost on kampodine-$to's kamal-proxy (ACME HTTP-01 through the reserved ip)…"
642
643
  if ! ssh "${SSH_OPTS[@]}" "root@$tpub" \
643
- "podman exec kamal-proxy kamal-proxy deploy esellar-api --host=$rhost --target=esellar-api:8080 --tls --health-check-path=/api/auth/ok"; then
644
+ "podman exec kamal-proxy kamal-proxy deploy kampodine-api --host=$rhost --target=kampodine-api:8080 --tls --health-check-path=/api/auth/ok"; then
644
645
  flip_failure_rollback "$to" "$ob" "$comp" "$rocid" "$tpub" "$taddr_cidr"
645
646
  exit 1
646
647
  fi
@@ -656,14 +657,14 @@ case "$cmd" in
656
657
  fi
657
658
  served="$(curl -s -m 8 --resolve "$rhost:443:$raddr" "https://$rhost/api/auth/ok" || true)"
658
659
  say "flip[4/4]: cert for $rhost VALID + serving through $raddr"
659
- say "FLIPPED: https://$raddr/ (https://$rhost/) now serves from esellar-$to"
660
+ say "FLIPPED: https://$raddr/ (https://$rhost/) now serves from kampodine-$to"
660
661
  say "served /api/auth/ok: ${served:-<no body>}"
661
662
  ;;
662
663
 
663
664
  rollback)
664
665
  # DORMANT rollback: holder guest cleanup
665
666
  # (anchor.conf removal + anchor address delete — the flip tool's explicit
666
- # job; the esellar-anchor watcher is add-only) then the OCI unassign
667
+ # job; the kampodine-anchor watcher is add-only) then the OCI unassign
667
668
  # (documented CLI semantics: an empty --private-ip-id unassigns). For a
668
669
  # real pair where traffic must land on the other color, use
669
670
  # 'flip --to <other>' — it runs the full ACME-first sequence there.
@@ -692,10 +693,10 @@ case "$cmd" in
692
693
  if [[ -z "$holder_color" ]]; then
693
694
  say "warning: reserved $raddr held by an anchor of neither RUNNING color (terminated instance?) — unassigning without guest cleanup"
694
695
  else
695
- say "current holder: esellar-$holder_color ($holder_ip) — cleaning the guest anchor, then unassigning"
696
+ say "current holder: kampodine-$holder_color ($holder_ip) — cleaning the guest anchor, then unassigning"
696
697
  conf_addr="$(read_anchor_conf_addr "$holder_ip")"
697
698
  cleanup_target_anchor "$holder_ip" "$conf_addr"
698
- say "esellar-$holder_color guest cleaned (anchor.conf removed${conf_addr:+, address $conf_addr deleted})"
699
+ say "kampodine-$holder_color guest cleaned (anchor.conf removed${conf_addr:+, address $conf_addr deleted})"
699
700
  fi
700
701
  say "unassigning reserved $raddr (-> dormant)…"
701
702
  oci network public-ip update --public-ip-id "$rocid" --profile "$PROFILE" \
package/scripts/deploy.sh CHANGED
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # Mac: podman build (VITE_BUILD_ID=<sha>) -> podman save (docker-archive)
5
5
  # streamed over SSH -> podman load on the VM
6
- # VM: retag to 127.0.0.1:5000/esellar-api:latest -> rc-service esellar-api
6
+ # VM: retag to 127.0.0.1:5000/kampodine-api:latest -> rc-service kampodine-api
7
7
  # restart (the OpenRC supervise-daemon service re-runs the whole
8
8
  # `podman run` against :latest — no pull in the run, so restarts are
9
9
  # offline-safe) -> exec-fetch health probe with served-sha verify ->
@@ -26,12 +26,12 @@
26
26
  set -euo pipefail
27
27
 
28
28
  REPO_ROOT="$(git rev-parse --show-toplevel)"
29
- ESPELLAR_HOST="${ESPELLAR_HOST:-}"
29
+ KAMPODINE_HOST="${KAMPODINE_HOST:-}"
30
30
  PROXY_HOST="${PROXY_HOST:-app.example.com}"
31
- ENV_FILE_REMOTE="/etc/esellar/env"
32
- DEPLOYED_SHA_FILE="/etc/esellar/deployed-sha"
31
+ ENV_FILE_REMOTE="/etc/kampodine/env"
32
+ DEPLOYED_SHA_FILE="/etc/kampodine/deployed-sha"
33
33
  ENV_CLEAR_KEYS='^(NODE_ENV|PORT|LIBSQL_TENANT_DIR|LIBSQL_API_MOUNT|STATIC_SPA_MOUNT)='
34
- IMAGE="127.0.0.1:5000/esellar-api"
34
+ IMAGE="127.0.0.1:5000/kampodine-api"
35
35
 
36
36
  MODE="deploy"
37
37
  VERSION=""
@@ -40,8 +40,7 @@ REFRESH_CONFIG=0
40
40
  # SSH key resolution — GENERIC, no hardcoded personal paths:
41
41
  # 1. --ssh-key flag (explicit, per-invocation)
42
42
  # 2. KAMPODINE_SSH_KEY env (project-level: direnv / .envrc / export)
43
- # 3. ESSELLAR_SSH_KEY env (legacy alias, kept for existing setups)
44
- # 4. empty → ssh-agent and/or the operator's ~/.ssh/config Host block
43
+ # 3. empty → ssh-agent and/or the operator's ~/.ssh/config Host block
45
44
  # (the POSIX way: per-host IdentityFile belongs in ssh config, not here)
46
45
  if [ -n "${KAMPODINE_SSH_KEY:-}" ]; then
47
46
  SSH_KEY="$KAMPODINE_SSH_KEY"
@@ -66,15 +65,15 @@ EOF
66
65
  grep '^# kampodine deploy' "$0" | sed 's/^# //'
67
66
  cat <<'EOF'
68
67
 
69
- Host/key resolution: --host | ESPELLAR_HOST; --ssh-key | KAMPODINE_SSH_KEY |
70
- ESPELLAR_SSH_KEY | ssh-agent / ~/.ssh/config.
68
+ Host/key resolution: --host | KAMPODINE_HOST; --ssh-key | KAMPODINE_SSH_KEY |
69
+ KAMPODINE_SSH_KEY | ssh-agent / ~/.ssh/config.
71
70
  EOF
72
71
  exit 0
73
72
  }
74
73
 
75
74
  while [[ $# -gt 0 ]]; do
76
75
  case "$1" in
77
- --host) ESPELLAR_HOST="$2"; shift 2 ;;
76
+ --host) KAMPODINE_HOST="$2"; shift 2 ;;
78
77
  --ssh-key) SSH_KEY="$2"; shift 2 ;;
79
78
  --version) VERSION="$2"; MODE="version"; shift 2 ;;
80
79
  --rollback)
@@ -89,13 +88,13 @@ while [[ $# -gt 0 ]]; do
89
88
  esac
90
89
  done
91
90
  [[ "$VERSION" != *..* && "$VERSION" =~ ^[0-9a-f]{4,40}$|^$ ]] || die "--version must be a git sha fragment"
92
- [ -n "$ESPELLAR_HOST" ] || die "set ESPELLAR_HOST=root@<vm-ip> (or a ~/.ssh/config Host alias via --host)"
91
+ [ -n "$KAMPODINE_HOST" ] || die "set KAMPODINE_HOST=root@<vm-ip> (or a ~/.ssh/config Host alias via --host)"
93
92
 
94
93
  SSH_ARGS=(-o ConnectTimeout=10 -o BatchMode=yes)
95
94
  [[ -n "$SSH_KEY" ]] && SSH_ARGS+=(-i "$SSH_KEY")
96
95
  # $1 is a composed remote command — client-side expansion is the design.
97
96
  # shellcheck disable=SC2029
98
- vm() { ssh "${SSH_ARGS[@]}" "$ESPELLAR_HOST" "$1"; }
97
+ vm() { ssh "${SSH_ARGS[@]}" "$KAMPODINE_HOST" "$1"; }
99
98
 
100
99
  # --- macOS ssh-agent quirk (first deploy from a fresh machine) ---------------
101
100
  if [[ "$(uname -s)" == "Darwin" ]]; then
@@ -141,7 +140,7 @@ vm "podman tag $IMAGE:$VER $IMAGE:latest" || die "stream retag failed"
141
140
 
142
141
  # --- env file from varlock (schema-only from HEAD; secrets from pass) ----------
143
142
  say "generating $ENV_FILE_REMOTE from varlock (schema-only from HEAD; secrets resolve from pass — no plaintext secret files)…"
144
- ENV_TMP="$(mktemp /tmp/esellar-env.XXXXXX)"
143
+ ENV_TMP="$(mktemp /tmp/kampodine-env.XXXXXX)"
145
144
  trap 'rm -f "$ENV_TMP"' EXIT
146
145
  # Schema-only resolution: extract the COMMITTED schema into a scratch dir
147
146
  # inside the repo (plugin resolution needs node_modules; local
@@ -158,20 +157,20 @@ git -C "$REPO_ROOT" archive HEAD apps/api/.env.schema | tar -x -C "$ENV_SCHEMA_D
158
157
  | sed -E 's/^([A-Za-z_][A-Za-z0-9_]*)="(.*)"$/\1=\2/' > "$ENV_TMP" \
159
158
  || die "varlock env generation failed (pass store unlocked? schema committed?)"
160
159
  printf 'API_GIT_SHA=%s\n' "$VER" >> "$ENV_TMP"
161
- scp -q "${SSH_ARGS[@]}" "$ENV_TMP" "$ESPELLAR_HOST:/etc/esellar/env.tmp"
162
- vm "mv /etc/esellar/env.tmp $ENV_FILE_REMOTE && chmod 600 $ENV_FILE_REMOTE"
160
+ scp -q "${SSH_ARGS[@]}" "$ENV_TMP" "$KAMPODINE_HOST:/etc/kampodine/env.tmp"
161
+ vm "mv /etc/kampodine/env.tmp $ENV_FILE_REMOTE && chmod 600 $ENV_FILE_REMOTE"
163
162
  rm -f "$ENV_TMP"
164
163
  rm -rf "$ENV_SCHEMA_DIR"
165
164
 
166
165
  if [[ $REFRESH_CONFIG -eq 1 ]]; then
167
166
  say "ansible container-service refresh (--tags container-service — rc script/config drift repair)…"
168
167
  (cd "$REPO_ROOT/infra/alpine-host/ansible" \
169
- && ansible-playbook -i "esellar-vm ansible_host=${ESPELLAR_HOST#*@},ansible_user=${ESPELLAR_HOST%%@*}," playbook.yml --tags container-service --private-key "${SSH_KEY:-~/.ssh/id_ed25519}") \
168
+ && ansible-playbook -i "kampodine-vm ansible_host=${KAMPODINE_HOST#*@},ansible_user=${KAMPODINE_HOST%%@*}," playbook.yml --tags container-service --private-key "${SSH_KEY:-~/.ssh/id_ed25519}") \
170
169
  || die "ansible playbook failed"
171
170
  fi
172
171
 
173
- say "rc-service esellar-api restart (supervise-daemon: stop-old/start-new podman run on the retagged :latest)…"
174
- vm "rc-service esellar-api restart" || die "esellar-api restart failed (rc-service esellar-api status)"
172
+ say "rc-service kampodine-api restart (supervise-daemon: stop-old/start-new podman run on the retagged :latest)…"
173
+ vm "rc-service kampodine-api restart" || die "kampodine-api restart failed (rc-service kampodine-api status)"
175
174
 
176
175
  say "health probe (host-side wget /api/auth/ok + served-sha — works for node AND scratch images)…"
177
176
  HEALTH_OK=0
@@ -188,17 +187,17 @@ for _ in $(seq 1 30); do
188
187
  sleep 3
189
188
  done
190
189
  if [[ $HEALTH_OK -ne 1 ]]; then
191
- vm "podman logs --tail 30 esellar-api 2>&1" || true
192
- vm "rc-service esellar-api status 2>&1" || true
190
+ vm "podman logs --tail 30 kampodine-api 2>&1" || true
191
+ vm "rc-service kampodine-api status 2>&1" || true
193
192
  PREV_HINT="$(vm "test -f $DEPLOYED_SHA_FILE && cat $DEPLOYED_SHA_FILE" 2>/dev/null || true)"
194
193
  die "container never became healthy — rollback: kampodine deploy --rollback${PREV_HINT:+ $PREV_HINT}"
195
194
  fi
196
- CANARY="$(vm "podman logs esellar-api 2>&1 | grep -c ENV_IMPORT_SUSPECT || true")"
195
+ CANARY="$(vm "podman logs kampodine-api 2>&1 | grep -c ENV_IMPORT_SUSPECT || true")"
197
196
  say "container healthy, env-canary hits: $CANARY"
198
197
 
199
198
  say "kamal-proxy re-point (podman exec — same invocation kamal used)…"
200
199
  if vm "podman ps --format '{{.Names}}' | grep -qx kamal-proxy"; then
201
- vm "podman exec kamal-proxy kamal-proxy deploy esellar-api --host=$PROXY_HOST --target=esellar-api:8080 --tls --health-check-path=/api/auth/ok" \
200
+ vm "podman exec kamal-proxy kamal-proxy deploy kampodine-api --host=$PROXY_HOST --target=kampodine-api:8080 --tls --health-check-path=/api/auth/ok" \
202
201
  || die "proxy re-point failed (podman logs kamal-proxy)"
203
202
  else
204
203
  say " kamal-proxy not running — skipping re-point"
package/scripts/dns.sh CHANGED
@@ -12,8 +12,8 @@
12
12
  # Types are pinned to A | AAAA | CNAME. `add` merges into the existing RRSet
13
13
  # (round-robin records survive); `rm` filters it; both are idempotence-aware.
14
14
  #
15
- # Zone/compartment: OCI_PROFILE (default esellar-api), OCI_COMPARTMENT
16
- # (default esellar). Zone defaults to the compartment's ONLY zone; pass
15
+ # Zone/compartment: OCI_PROFILE (default: "default"), OCI_COMPARTMENT (required)
16
+ # (required). Zone defaults to the compartment's ONLY zone; pass
17
17
  # --zone <id-or-name> when several exist.
18
18
  #
19
19
  # Usage:
@@ -23,8 +23,8 @@
23
23
  # (all: optional --zone <id-or-name>; --instance-principal for instance auth)
24
24
  set -euo pipefail
25
25
 
26
- PROFILE="${OCI_PROFILE:-esellar-api}"
27
- COMPARTMENT_NAME="${OCI_COMPARTMENT:-esellar}"
26
+ PROFILE="${OCI_PROFILE:-default}"
27
+ COMPARTMENT_NAME="${OCI_COMPARTMENT:-kampodine}"
28
28
  AUTH_ARGS=()
29
29
 
30
30
  say() { printf '\033[1;34m[dns]\033[0m %s\n' "$*"; }
@@ -37,13 +37,13 @@ usage() {
37
37
 
38
38
  Auth is the oci CLI's own — its config file (--profile) or instance
39
39
  principal. kampodine never accepts, stores, or logs credential material.
40
- Zone/compartment: OCI_PROFILE (default esellar-api), OCI_COMPARTMENT (default
41
- esellar). Types are pinned to A | AAAA | CNAME; ttl range 60..172800.
40
+ Zone/compartment: OCI_PROFILE (default: "default"), OCI_COMPARTMENT (default
41
+ kampodine). Types are pinned to A | AAAA | CNAME; ttl range 60..172800.
42
42
 
43
43
  Examples:
44
44
  kampodine dns records
45
- kampodine dns records --zone esellar.example.com
46
- kampodine dns add --name app --type A --value 203.0.113.10 --zone esellar.example.com
45
+ kampodine dns records --zone kampodine.example.com
46
+ kampodine dns add --name app --type A --value 203.0.113.10 --zone kampodine.example.com
47
47
  kampodine dns add --name www --type CNAME --value app.example.com --ttl 3600
48
48
  kampodine dns rm --name app --type A --value 203.0.113.10
49
49
  EOF
@@ -111,6 +111,8 @@ need_provider() {
111
111
  || die "oci CLI not found — brew install oci-cli (https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm), then: oci setup config"
112
112
  command -v jq >/dev/null 2>&1 \
113
113
  || die "jq not found — brew install jq (dns.sh uses it for RRSet surgery)"
114
+ [[ -n "${OCI_COMPARTMENT:-}" ]] \
115
+ || die "set OCI_COMPARTMENT=<compartment name or ocid> — no default: compartments are account-specific"
114
116
  }
115
117
 
116
118
  compartment_ocid() {
package/scripts/env.sh CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # env.sh — manage the remote app env file (/etc/esellar/env, 0600 root).
2
+ # env.sh — manage the remote app env file (/etc/kampodine/env, 0600 root).
3
3
  #
4
4
  # SECRETS NEVER PRINT. `env list`, `env push`, and the `env pull` summaries
5
5
  # surface FINGERPRINTS ONLY: KEY + value length + first 2 characters. Raw
@@ -8,8 +8,8 @@
8
8
  # secret ever lands in terminal output, logs, or process args.
9
9
  #
10
10
  # Host/key resolution is IDENTICAL to deploy.sh:
11
- # --host root@<ip> | ESPELLAR_HOST | ~/.ssh/config Host alias
12
- # --ssh-key <path> | KAMPODINE_SSH_KEY | ESPELLAR_SSH_KEY (legacy) |
11
+ # --host root@<ip> | KAMPODINE_HOST | ~/.ssh/config Host alias
12
+ # --ssh-key <path> | KAMPODINE_SSH_KEY |
13
13
  # ssh-agent (the POSIX default; per-host IdentityFile belongs in ssh config)
14
14
  #
15
15
  # Usage:
@@ -23,14 +23,14 @@
23
23
  # file you push unless you mean to own them here.
24
24
  set -euo pipefail
25
25
 
26
- ENV_FILE_REMOTE="${KAMPODINE_ENV_REMOTE:-/etc/esellar/env}"
26
+ ENV_FILE_REMOTE="${KAMPODINE_ENV_REMOTE:-/etc/kampodine/env}"
27
27
  ENV_TMP_BASE="$(dirname "$ENV_FILE_REMOTE")"
28
28
 
29
- ESPELLAR_HOST="${ESPELLAR_HOST:-}"
29
+ KAMPODINE_HOST="${KAMPODINE_HOST:-}"
30
30
  # SSH key resolution — same ladder as deploy.sh:
31
31
  # 1. --ssh-key flag (explicit, per-invocation)
32
32
  # 2. KAMPODINE_SSH_KEY env (project-level: direnv / .envrc / export)
33
- # 3. ESPELLAR_SSH_KEY env (legacy alias, kept for existing setups)
33
+ # 3. KAMPODINE_SSH_KEY env (legacy alias, kept for existing setups)
34
34
  # 4. empty → ssh-agent and/or the operator's ~/.ssh/config Host block
35
35
  if [ -n "${KAMPODINE_SSH_KEY:-}" ]; then
36
36
  SSH_KEY="$KAMPODINE_SSH_KEY"
@@ -47,8 +47,8 @@ usage() {
47
47
 
48
48
  Fingerprints NEVER leak values: every line is KEY + value length + first 2 chars.
49
49
  Raw values move only in push's upload stream and pull's stdout/--out payload.
50
- Host/key resolution matches deploy.sh: --host | ESPELLAR_HOST; --ssh-key |
51
- KAMPODINE_SSH_KEY | ESPELLAR_SSH_KEY | ssh-agent / ~/.ssh/config.
50
+ Host/key resolution matches deploy.sh: --host | KAMPODINE_HOST; --ssh-key |
51
+ KAMPODINE_SSH_KEY | ssh-agent / ~/.ssh/config.
52
52
 
53
53
  Examples:
54
54
  kampodine env list --host root@203.0.113.10
@@ -72,7 +72,7 @@ FILE=""
72
72
  OUT=""
73
73
  while [[ $# -gt 0 ]]; do
74
74
  case "$1" in
75
- --host) ESPELLAR_HOST="$2"; shift 2 ;;
75
+ --host) KAMPODINE_HOST="$2"; shift 2 ;;
76
76
  --ssh-key) SSH_KEY="$2"; shift 2 ;;
77
77
  --file) FILE="$2"; shift 2 ;;
78
78
  --out) OUT="$2"; shift 2 ;;
@@ -85,7 +85,7 @@ SSH_ARGS=(-o ConnectTimeout=10 -o BatchMode=yes)
85
85
  [[ -n "$SSH_KEY" ]] && SSH_ARGS+=(-i "$SSH_KEY")
86
86
  # $1 is a composed remote command — client-side expansion is the design.
87
87
  # shellcheck disable=SC2029
88
- vm() { ssh "${SSH_ARGS[@]}" "$ESPELLAR_HOST" "$1"; }
88
+ vm() { ssh "${SSH_ARGS[@]}" "$KAMPODINE_HOST" "$1"; }
89
89
 
90
90
  # --- macOS ssh-agent quirk (first run from a fresh machine) -------------------
91
91
  if [[ "$(uname -s)" == "Darwin" ]]; then
@@ -94,7 +94,7 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
94
94
  fi
95
95
 
96
96
  require_host() {
97
- [[ -n "$ESPELLAR_HOST" ]] || die "target required: --host root@<ip> or ESPELLAR_HOST=root@<ip> (resolution matches deploy.sh)"
97
+ [[ -n "$KAMPODINE_HOST" ]] || die "target required: --host root@<ip> or KAMPODINE_HOST=root@<ip> (resolution matches deploy.sh)"
98
98
  }
99
99
 
100
100
  # env_fp_lines — env content on stdin -> fingerprint table on stdout.
@@ -126,7 +126,7 @@ env_fp_lines() {
126
126
  }
127
127
 
128
128
  fetch_remote_env() {
129
- vm "cat $ENV_FILE_REMOTE" || die "cannot read $ENV_FILE_REMOTE on $ESPELLAR_HOST (no env file yet? run: kampodine env push --file <env>, or kampodine deploy)"
129
+ vm "cat $ENV_FILE_REMOTE" || die "cannot read $ENV_FILE_REMOTE on $KAMPODINE_HOST (no env file yet? run: kampodine env push --file <env>, or kampodine deploy)"
130
130
  }
131
131
 
132
132
  case "$SUB" in
@@ -142,7 +142,7 @@ case "$SUB" in
142
142
  require_host
143
143
  count="$(grep -cE '^[A-Za-z_][A-Za-z0-9_]*=' "$FILE" || true)"
144
144
  [[ "${count:-0}" -gt 0 ]] || die "no KEY=VALUE lines in $FILE — nothing to push"
145
- say "pushing $FILE -> $ESPELLAR_HOST:$ENV_FILE_REMOTE (fingerprint summary below; values are NEVER printed)"
145
+ say "pushing $FILE -> $KAMPODINE_HOST:$ENV_FILE_REMOTE (fingerprint summary below; values are NEVER printed)"
146
146
  env_fp_lines < "$FILE"
147
147
  tmp_remote="$ENV_TMP_BASE/env.tmp.$$"
148
148
  # 0600 FROM CREATION: umask 077 + ssh stdin pipe — the temp is never
@@ -151,8 +151,8 @@ case "$SUB" in
151
151
  vm "umask 077; cat > $tmp_remote" < "$FILE" || die "upload failed"
152
152
  vm "chmod 600 $tmp_remote && mv -f $tmp_remote $ENV_FILE_REMOTE" \
153
153
  || die "atomic install failed (remote temp left at: $tmp_remote)"
154
- say "installed $ENV_FILE_REMOTE (0600) on $ESPELLAR_HOST"
155
- say "restart to apply: ssh $ESPELLAR_HOST 'rc-service esellar-api restart' # or: kampodine deploy"
154
+ say "installed $ENV_FILE_REMOTE (0600) on $KAMPODINE_HOST"
155
+ say "restart to apply: ssh $KAMPODINE_HOST 'rc-service kampodine-api restart' # or: kampodine deploy"
156
156
  ;;
157
157
 
158
158
  pull)
@@ -167,7 +167,7 @@ case "$SUB" in
167
167
  # stdout IS the payload (pipe into whatever needs the values);
168
168
  # the human-readable summary goes to stderr, masked.
169
169
  printf '%s\n' "$raw"
170
- say "fingerprint summary for $ENV_FILE_REMOTE on $ESPELLAR_HOST (payload above on stdout):" >&2
170
+ say "fingerprint summary for $ENV_FILE_REMOTE on $KAMPODINE_HOST (payload above on stdout):" >&2
171
171
  env_fp_lines <<<"$raw" >&2
172
172
  fi
173
173
  ;;
@@ -14,26 +14,26 @@
14
14
  # Auth: ~/.oci/config (brew install oci-cli; oci setup config) by default, or
15
15
  # --from-pass to source API credentials from the local pass store — the key
16
16
  # NEVER enters this repo, the image, or any log (values only go into env vars).
17
- # Every oci call pins --profile $PROFILE (OCI_PROFILE, default esellar-api) —
18
- # the CLI's DEFAULT profile is NOT the esellar tenancy.
17
+ # Every oci call pins --profile $PROFILE (OCI_PROFILE, default "default") —
18
+ # the CLI's DEFAULT profile is NOT the kampodine tenancy.
19
19
  # Placeholder pass paths (create yours to match):
20
- # esellar/oci/user user OCID
21
- # esellar/oci/tenancy tenancy OCID
22
- # esellar/oci/fingerprint API key fingerprint
23
- # esellar/oci/api-key PEM private key
20
+ # kampodine/oci/user user OCID
21
+ # kampodine/oci/tenancy tenancy OCID
22
+ # kampodine/oci/fingerprint API key fingerprint
23
+ # kampodine/oci/api-key PEM private key
24
24
  #
25
25
  # Usage:
26
26
  # kampodine image-import # defaults below
27
27
  # kampodine image-import --from-pass
28
- # kampodine image-import --image build/esellar-alpine-3.22.6-aarch64.qcow2
28
+ # kampodine image-import --image build/kampodine-alpine-3.22.6-aarch64.qcow2
29
29
  set -euo pipefail
30
30
 
31
31
  REPO_ROOT="$(git rev-parse --show-toplevel)"
32
32
  IMAGE=""
33
- BUCKET="${OCI_IMPORT_BUCKET:-esellar-image-import}"
34
- NAME_PREFIX="${OCI_IMPORT_NAME:-esellar-alpine}"
35
- COMPARTMENT_NAME="${OCI_COMPARTMENT:-esellar}"
36
- PROFILE="${OCI_PROFILE:-esellar-api}"
33
+ BUCKET="${OCI_IMPORT_BUCKET:-kampodine-image-import}"
34
+ NAME_PREFIX="${OCI_IMPORT_NAME:-kampodine-alpine}"
35
+ COMPARTMENT_NAME="${OCI_COMPARTMENT:-kampodine}"
36
+ PROFILE="${OCI_PROFILE:-default}"
37
37
  FROM_PASS=0
38
38
  KEEP_OBJECT=0
39
39
 
@@ -65,18 +65,19 @@ while [[ $# -gt 0 ]]; do
65
65
  done
66
66
 
67
67
  command -v oci >/dev/null 2>&1 || die "oci CLI not found — brew install oci-cli, then: oci setup config (infra/oci/README.md § CLI setup)"
68
+ [[ -n "${OCI_COMPARTMENT:-}" ]] || die "set OCI_COMPARTMENT=<compartment name or ocid> — no default: compartments are account-specific"
68
69
  command -v pass >/dev/null 2>&1 || true
69
70
 
70
71
  if [[ $FROM_PASS -eq 1 ]]; then
71
72
  command -v pass >/dev/null 2>&1 || die "pass not installed (brew install pass)"
72
73
  say "sourcing OCI API credentials from pass (values never printed/logged)…"
73
74
  for entry in user tenancy fingerprint api-key; do
74
- pass show "esellar/oci/$entry" >/dev/null 2>&1 || die "missing pass entry esellar/oci/$entry"
75
+ pass show "kampodine/oci/$entry" >/dev/null 2>&1 || die "missing pass entry kampodine/oci/$entry"
75
76
  done
76
- OCI_CLI_USER="$(pass show esellar/oci/user)"
77
- OCI_CLI_TENANCY="$(pass show esellar/oci/tenancy)"
78
- OCI_CLI_FINGERPRINT="$(pass show esellar/oci/fingerprint)"
79
- OCI_CLI_KEY_CONTENT="$(pass show esellar/oci/api-key)"
77
+ OCI_CLI_USER="$(pass show kampodine/oci/user)"
78
+ OCI_CLI_TENANCY="$(pass show kampodine/oci/tenancy)"
79
+ OCI_CLI_FINGERPRINT="$(pass show kampodine/oci/fingerprint)"
80
+ OCI_CLI_KEY_CONTENT="$(pass show kampodine/oci/api-key)"
80
81
  export OCI_CLI_USER OCI_CLI_TENANCY OCI_CLI_FINGERPRINT OCI_CLI_KEY_CONTENT
81
82
  fi
82
83
 
@@ -84,7 +85,7 @@ fi
84
85
  if [[ -z "$IMAGE" ]]; then
85
86
  # Constrained glob (our own build output names) — ls is fine here.
86
87
  # shellcheck disable=SC2012
87
- IMAGE="$(ls -t "$REPO_ROOT/infra/alpine-host/build"/esellar-alpine-*.qcow2 2>/dev/null | head -1 || true)"
88
+ IMAGE="$(ls -t "$REPO_ROOT/infra/alpine-host/build"/kampodine-alpine-*.qcow2 2>/dev/null | head -1 || true)"
88
89
  [[ -n "$IMAGE" ]] || die "no qcow2 under infra/alpine-host/build/ — run: (cd infra/alpine-host && packer build .)"
89
90
  fi
90
91
  [[ -f "$IMAGE" ]] || die "image not found: $IMAGE"
@@ -96,7 +97,7 @@ NAMESPACE="$(oci os ns get --profile "$PROFILE" --query data --raw-output)"
96
97
 
97
98
  STAMP="$(date +%Y%m%d-%H%M%S)"
98
99
  OBJECT_NAME="${NAME_PREFIX}-${STAMP}.qcow2"
99
- IMAGE_NAME="${NAME_PREFIX}-$(basename "$IMAGE" .qcow2 | sed 's/^esellar-alpine-//')-${STAMP}"
100
+ IMAGE_NAME="${NAME_PREFIX}-$(basename "$IMAGE" .qcow2 | sed 's/^kampodine-alpine-//')-${STAMP}"
100
101
 
101
102
  say "uploading $(basename "$IMAGE") -> os://$BUCKET/$OBJECT_NAME"
102
103
  oci os object put -bn "$BUCKET" --profile "$PROFILE" --file "$IMAGE" --name "$OBJECT_NAME" --force \
@@ -162,7 +163,7 @@ say " display : $IMAGE_NAME"
162
163
  say " firmware : $IMPORT_FIRMWARE"
163
164
  say "next:"
164
165
  if [[ "$IMPORT_FIRMWARE" == "UEFI_64" ]]; then
165
- say " A1-ready. kampodine bluegreen provision <blue|green> picks this image (newest esellar-alpine*)."
166
+ say " A1-ready. kampodine bluegreen provision <blue|green> picks this image (newest kampodine-alpine*)."
166
167
  else
167
168
  say " A1 launch will REJECT this image (firmware $IMPORT_FIRMWARE) — see the WARN above."
168
169
  fi
@@ -15,7 +15,7 @@ set -euo pipefail
15
15
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
16
  REPO_ROOT="${REPO_ROOT:-$(cd "${SCRIPT_DIR}/../../.." && pwd)}"
17
17
  TENANT_DIR="${TENANT_DIR:-/data/tenants}"
18
- SERVICE="${SERVICE:-esellar-api}"
18
+ SERVICE="${SERVICE:-kampodine-api}"
19
19
 
20
20
  log() { printf '[migrate-all] %s\n' "$*"; }
21
21
  fail() { printf '[migrate-all][FAIL] %s\n' "$*" >&2; exit 1; }
@@ -25,7 +25,7 @@ Usage:
25
25
  kampodine migrate [--allow-running]
26
26
 
27
27
  Examples:
28
- kampodine migrate # stop the API first (rc-service esellar-api stop)
28
+ kampodine migrate # stop the API first (rc-service kampodine-api stop)
29
29
  kampodine migrate --allow-running # deliberate: migrate while the API serves (SQLITE_BUSY risk)
30
30
 
31
31
  Runs drizzle migrations over the libSQL dbs under TENANT_DIR (default
@@ -51,11 +51,11 @@ command -v pnpm >/dev/null 2>&1 || fail "pnpm not found in PATH"
51
51
  [[ -d "$TENANT_DIR" ]] || fail "tenant dir ${TENANT_DIR} does not exist"
52
52
 
53
53
  # Writing schema while the API serves traffic risks SQLITE_BUSY on a single-writer
54
- # engine — the service must be stopped first (rc-service esellar-api stop). Override exists
54
+ # engine — the service must be stopped first (rc-service kampodine-api stop). Override exists
55
55
  # for deliberate local use.
56
56
  if systemctl is-active --quiet "$SERVICE" 2>/dev/null; then
57
57
  if [[ $ALLOW_RUNNING -ne 1 ]]; then
58
- fail "${SERVICE} is running — stop it first (rc-service esellar-api stop) or pass --allow-running"
58
+ fail "${SERVICE} is running — stop it first (rc-service kampodine-api stop) or pass --allow-running"
59
59
  fi
60
60
  log "WARNING: migrating while ${SERVICE} is running (--allow-running)"
61
61
  fi
@@ -12,21 +12,21 @@
12
12
  # 2. apk repositories: ensure the v3.22 community repo (podman lives there)
13
13
  # 3. podman stack: podman podman-docker crun catatonit netavark
14
14
  # aardvark-dns fuse-overlayfs
15
- # 4. /etc/esellar/ (kampodine deploy writes /etc/esellar/env here, 0600 root)
15
+ # 4. /etc/kampodine/ (kampodine deploy writes /etc/kampodine/env here, 0600 root)
16
16
  # 5. /etc/containers/registries.conf (insecure 127.0.0.1:5000; search docker.io)
17
17
  # 6. sysctl net.ipv4.ip_unprivileged_port_start=80 (persisted + applied live)
18
- # 7. OpenRC services /etc/init.d/esellar-api + /etc/init.d/kamal-proxy
18
+ # 7. OpenRC services /etc/init.d/kampodine-api + /etc/init.d/kamal-proxy
19
19
  # (supervise-daemon around plain `podman run`), rc-update'd into the
20
20
  # default runlevel — that IS boot survival (no quadlets, no systemctl)
21
- # 8. esellar-anchor: the blue-green reserved-ip flip's GUEST half — a
22
- # busybox watcher (no container) that polls /etc/esellar/anchor.conf and
21
+ # 8. kampodine-anchor: the blue-green reserved-ip flip's GUEST half — a
22
+ # busybox watcher (no container) that polls /etc/kampodine/anchor.conf and
23
23
  # `ip addr add`s the anchor address at flip time. Started+enabled on every
24
24
  # VM, inert without the conf (flip tooling writes it over ssh).
25
25
  # 9. kamal-proxy image pulled + service UP — the first deploy execs into it
26
26
  # to issue the fresh ACME certificate
27
27
  #
28
- # The esellar-api container is NOT started here: neither its image (pulled by
29
- # the first deploy over the registry tunnel) nor /etc/esellar/env (written by
28
+ # The kampodine-api container is NOT started here: neither its image (pulled by
29
+ # the first deploy over the registry tunnel) nor /etc/kampodine/env (written by
30
30
  # kampodine deploy) exists yet on a fresh VM.
31
31
  #
32
32
  # Idempotent: safe to re-run. apk add is a no-op when satisfied; managed files
@@ -36,12 +36,12 @@
36
36
  #
37
37
  # kampodine vm-prepare --host root@<new-ip>
38
38
  # ... --pull-images # + pre-pull the app image over the registry tunnel
39
- # ... --ssh-key <path> # ssh identity (default: agent / ESPELLAR_SSH_KEY)
39
+ # ... --ssh-key <path> # ssh identity (default: agent / KAMPODINE_SSH_KEY)
40
40
  set -euo pipefail
41
41
 
42
42
  HOST=""
43
43
  DO_PULL=0
44
- SSH_KEY="${ESPELLAR_SSH_KEY:-}"
44
+ SSH_KEY="${KAMPODINE_SSH_KEY:-}"
45
45
 
46
46
  say() { printf '\033[1;32m[vm-prepare]\033[0m %s\n' "$*"; }
47
47
  die() { printf '\033[1;31m[vm-prepare] FAIL:\033[0m %s\n' "$*" >&2; exit 1; }
@@ -56,7 +56,7 @@ EOF
56
56
  cat <<'EOF'
57
57
 
58
58
  Host/key resolution: --host | (no env default — explicit flag);
59
- --ssh-key | ESPELLAR_SSH_KEY | ssh-agent / ~/.ssh/config.
59
+ --ssh-key | KAMPODINE_SSH_KEY | ssh-agent / ~/.ssh/config.
60
60
  EOF
61
61
  exit 0
62
62
  }
@@ -87,7 +87,7 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
87
87
  fi
88
88
 
89
89
  # --- managed file payloads (built Mac-side, scp'd) ----------------------------
90
- TMPDIR_LOCAL="$(mktemp -d /tmp/esellar-vm-prepare.XXXXXX)"
90
+ TMPDIR_LOCAL="$(mktemp -d /tmp/kampodine-vm-prepare.XXXXXX)"
91
91
  trap 'rm -rf "$TMPDIR_LOCAL"' EXIT
92
92
 
93
93
  cat > "$TMPDIR_LOCAL/registries.conf" <<'REGISTRIES'
@@ -102,17 +102,17 @@ location = "127.0.0.1:5000"
102
102
  insecure = true
103
103
  REGISTRIES
104
104
 
105
- cat > "$TMPDIR_LOCAL/60-esellar.conf" <<'SYSCTL'
105
+ cat > "$TMPDIR_LOCAL/60-kampodine.conf" <<'SYSCTL'
106
106
  # Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
107
107
  # OpenRC contract: let unprivileged/container paths bind port 80
108
108
  # (kamal-proxy publishes 80+443; belt-and-braces for a future rootless move).
109
109
  net.ipv4.ip_unprivileged_port_start=80
110
110
  SYSCTL
111
111
 
112
- cat > "$TMPDIR_LOCAL/esellar-api" <<'INITD_API'
112
+ cat > "$TMPDIR_LOCAL/kampodine-api" <<'INITD_API'
113
113
  #!/sbin/openrc-run
114
114
  # Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
115
- # KEEP IN SYNC with ansible/roles/container-service/templates/esellar-api.initd.j2
115
+ # KEEP IN SYNC with the golden image's container-service init template
116
116
  # (rendered with role defaults): vm-prepare bootstraps a fresh VM BEFORE the
117
117
  # first ansible converge; ansible owns the file afterwards (drift repair).
118
118
  #
@@ -122,7 +122,7 @@ cat > "$TMPDIR_LOCAL/esellar-api" <<'INITD_API'
122
122
  # dies, the WHOLE run restarts against the local :latest (no pull inside the
123
123
  # run — restarts are offline-safe; the registry tunnel is down between deploys).
124
124
  #
125
- # Env: /etc/esellar/env (0600 root, written by kampodine deploy per deploy) is
125
+ # Env: /etc/kampodine/env (0600 root, written by kampodine deploy per deploy) is
126
126
  # the single SECRETS source (podman --env-file reads KEY=VALUE lines directly).
127
127
  # The five CLEAR vars (NODE_ENV, PORT, LIBSQL_TENANT_DIR, LIBSQL_API_MOUNT,
128
128
  # STATIC_SPA_MOUNT) are owned HERE via -e — deploy filters them out of the env
@@ -130,16 +130,16 @@ cat > "$TMPDIR_LOCAL/esellar-api" <<'INITD_API'
130
130
  # (tenant sqlite files) — without the bind mount, tenant data would be
131
131
  # container-ephemeral and lost on every restart.
132
132
 
133
- name="esellar-api"
134
- description="esellar API + SPA container (127.0.0.1:5000/esellar-api:latest on 8080)"
133
+ name="kampodine-api"
134
+ description="kampodine API + SPA container (127.0.0.1:5000/kampodine-api:latest on 8080)"
135
135
 
136
136
  supervisor=supervise-daemon
137
137
  command="/usr/bin/podman"
138
- command_args="run --rm --name esellar-api --network kamal -p 8080:8080"
138
+ command_args="run --rm --name kampodine-api --network kamal -p 8080:8080"
139
139
  command_args="$command_args -v /data/tenants:/data/tenants"
140
140
  command_args="$command_args -e NODE_ENV=production -e PORT=8080"
141
141
  command_args="$command_args -e LIBSQL_TENANT_DIR=/data/tenants -e LIBSQL_API_MOUNT=1 -e STATIC_SPA_MOUNT=1"
142
- command_args="$command_args --env-file /etc/esellar/env 127.0.0.1:5000/esellar-api:latest"
142
+ command_args="$command_args --env-file /etc/kampodine/env 127.0.0.1:5000/kampodine-api:latest"
143
143
 
144
144
  # Respawn forever (blue's restart=always precedent): a crash loop self-heals at
145
145
  # the next deploy's restart; the 10s delay bounds log noise.
@@ -147,26 +147,26 @@ respawn_delay=10
147
147
  respawn_max=0
148
148
 
149
149
  # podman run's OWN stderr (missing image, name conflicts, netavark failures);
150
- # container stdout/stderr go to `podman logs esellar-api`.
151
- supervise_daemon_args="--stderr /var/log/esellar-api-service.log"
150
+ # container stdout/stderr go to `podman logs kampodine-api`.
151
+ supervise_daemon_args="--stderr /var/log/kampodine-service.log"
152
152
 
153
153
  depend() {
154
154
  need net
155
- after esellar-datamount
155
+ after kampodine-datamount
156
156
  }
157
157
 
158
158
  start_pre() {
159
159
  # SIGKILLed runs leave the container name holding port 8080 (--rm only
160
160
  # cleans CLEAN exits) — sweep any leftover before the supervised run.
161
- podman container exists esellar-api 2>/dev/null && podman rm -f esellar-api >/dev/null 2>&1
161
+ podman container exists kampodine-api 2>/dev/null && podman rm -f kampodine-api >/dev/null 2>&1
162
162
  return 0
163
163
  }
164
164
 
165
165
  stop_post() {
166
166
  # supervise-daemon killed the podman client; make sure the container goes
167
167
  # down too (TERM -> graceful shutdown, then a tolerant sweep).
168
- podman stop --time 10 esellar-api >/dev/null 2>&1 || true
169
- podman rm -f --time 0 esellar-api >/dev/null 2>&1 || true
168
+ podman stop --time 10 kampodine-api >/dev/null 2>&1 || true
169
+ podman rm -f --time 0 kampodine-api >/dev/null 2>&1 || true
170
170
  }
171
171
  INITD_API
172
172
 
@@ -174,7 +174,7 @@ cat > "$TMPDIR_LOCAL/kamal-proxy" <<'INITD_PROXY'
174
174
  #!/sbin/openrc-run
175
175
  # Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
176
176
  # KEEP IN SYNC with ansible/roles/container-service/templates/kamal-proxy.initd.j2
177
- # (see the esellar-api header for the vm-prepare/ansible split).
177
+ # (see the kampodine-api header for the vm-prepare/ansible split).
178
178
  #
179
179
  # TLS edge (kamal-proxy, Let's Encrypt HTTP-01 on :80). Publishes 80+443; the
180
180
  # LE certs + host->target registrations persist in the kamal-proxy-config named
@@ -182,7 +182,7 @@ cat > "$TMPDIR_LOCAL/kamal-proxy" <<'INITD_PROXY'
182
182
  # the first deploy survives container recreation AND reboots. Start-fresh:
183
183
  # nothing is carried from the retired blue VM. Target registration happens at
184
184
  # deploy time (kampodine deploy: podman exec kamal-proxy kamal-proxy deploy
185
- # esellar-api --host=$PROXY_HOST --target=esellar-api:8080 --tls
185
+ # kampodine-api --host=$PROXY_HOST --target=kampodine-api:8080 --tls
186
186
  # --health-check-path=/api/auth/ok).
187
187
 
188
188
  name="kamal-proxy"
@@ -212,19 +212,19 @@ stop_post() {
212
212
  }
213
213
  INITD_PROXY
214
214
 
215
- cat > "$TMPDIR_LOCAL/esellar-anchor.sh" <<'ANCHOR_WATCHER'
215
+ cat > "$TMPDIR_LOCAL/kampodine-anchor.sh" <<'ANCHOR_WATCHER'
216
216
  #!/bin/sh
217
- # KEEP IN SYNC with ansible/roles/container-service/files/esellar-anchor.sh
217
+ # KEEP IN SYNC with ansible/roles/container-service/files/kampodine-anchor.sh
218
218
  # (vm-prepare rendered-content convention: vm-prepare bootstraps a fresh VM
219
219
  # BEFORE the first ansible converge; ansible owns the file afterwards).
220
220
  #
221
- # esellar-anchor.sh — guest half of the blue-green reserved-ip flip.
221
+ # kampodine-anchor.sh — guest half of the blue-green reserved-ip flip.
222
222
  #
223
223
  # OCI assigns the reserved PUBLIC ip to a SECONDARY private ip ("the anchor")
224
224
  # on the instance VNIC, but this image has NO oracle-cloud-agent: nothing
225
225
  # configures that private ip inside the guest, so packets to the reserved ip
226
226
  # die until the address exists on the interface. `kampodine bluegreen flip` writes
227
- # /etc/esellar/anchor.conf over ssh at flip time; this watcher polls it and
227
+ # /etc/kampodine/anchor.conf over ssh at flip time; this watcher polls it and
228
228
  # runs `ip addr add` within one interval. The unit is enabled+started on every
229
229
  # VM by default and is INERT without the conf — a VM that never flips never
230
230
  # touches its addresses.
@@ -236,13 +236,13 @@ cat > "$TMPDIR_LOCAL/esellar-anchor.sh" <<'ANCHOR_WATCHER'
236
236
  # ADD-ONLY BY DESIGN: this script NEVER removes or flushes addresses (rollback
237
237
  # cleanup is the flip tool's explicit ssh job). Idempotent: a present address
238
238
  # is a no-op. Only state TRANSITIONS are logged (bounded log noise), to
239
- # stdout — supervise-daemon tees it to /var/log/esellar-anchor.log.
239
+ # stdout — supervise-daemon tees it to /var/log/kampodine-anchor.log.
240
240
 
241
- CONF="/etc/esellar/anchor.conf"
242
- INTERVAL="${ESPELLAR_ANCHOR_INTERVAL:-5}"
241
+ CONF="/etc/kampodine/anchor.conf"
242
+ INTERVAL="${KAMPODINE_ANCHOR_INTERVAL:-5}"
243
243
  STATE="boot" # last logged transition (boot | idle | added | error)
244
244
 
245
- log() { printf '%s esellar-anchor: %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$*"; }
245
+ log() { printf '%s kampodine-anchor: %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$*"; }
246
246
 
247
247
  # supervise-daemon SIGTERMs on stop — die cleanly with the current tick.
248
248
  trap 'exit 0' TERM INT
@@ -283,33 +283,33 @@ while :; do
283
283
  done
284
284
  ANCHOR_WATCHER
285
285
 
286
- cat > "$TMPDIR_LOCAL/esellar-anchor" <<'INITD_ANCHOR'
286
+ cat > "$TMPDIR_LOCAL/kampodine-anchor" <<'INITD_ANCHOR'
287
287
  #!/sbin/openrc-run
288
288
  # Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
289
- # KEEP IN SYNC with ansible/roles/container-service/templates/esellar-anchor.initd.j2
290
- # (rendered with role defaults — see the esellar-api header for the
289
+ # KEEP IN SYNC with ansible/roles/container-service/templates/kampodine-anchor.initd.j2
290
+ # (rendered with role defaults — see the kampodine-api header for the
291
291
  # vm-prepare/ansible split).
292
292
  #
293
293
  # Guest half of the blue-green reserved-ip flip: supervise-daemon runs the
294
- # esellar-anchor.sh watcher, which polls /etc/esellar/anchor.conf (written by
294
+ # kampodine-anchor.sh watcher, which polls /etc/kampodine/anchor.conf (written by
295
295
  # `kampodine bluegreen flip` over ssh at flip time) and `ip addr add`s the
296
296
  # anchor address when it appears. Started+enabled on EVERY vm by default and
297
297
  # INERT without the conf: a VM that never flips never touches its addresses —
298
- # unlike the container units, there is no esellar_start_containers gate.
299
- # Transitions land in /var/log/esellar-anchor.log.
298
+ # unlike the container units, there is no kampodine_start_containers gate.
299
+ # Transitions land in /var/log/kampodine-anchor.log.
300
300
 
301
- name="esellar-anchor"
301
+ name="kampodine-anchor"
302
302
  description="Blue-green reserved-ip anchor address watcher (guest half of the flip)"
303
303
 
304
304
  supervisor=supervise-daemon
305
- command="/usr/local/sbin/esellar-anchor.sh"
305
+ command="/usr/local/sbin/kampodine-anchor.sh"
306
306
 
307
307
  # Respawn forever: the watcher itself never exits (trap TERM/INT -> exit 0 on
308
308
  # stop), so a respawn means the script died abnormally — retry gently.
309
309
  respawn_delay=5
310
310
  respawn_max=0
311
311
 
312
- supervise_daemon_args="--stdout /var/log/esellar-anchor.log --stderr /var/log/esellar-anchor.log"
312
+ supervise_daemon_args="--stdout /var/log/kampodine-anchor.log --stderr /var/log/kampodine-anchor.log"
313
313
 
314
314
  depend() {
315
315
  need net
@@ -366,7 +366,7 @@ say "sshd hardening: ensure drop-in + Include + restart (golden image predates t
366
366
  vm 'grep -q "^Include /etc/ssh/sshd_config.d/\*.conf" /etc/ssh/sshd_config || sed -i "1i Include /etc/ssh/sshd_config.d/*.conf" /etc/ssh/sshd_config' \
367
367
  || die "could not ensure the sshd_config Include line"
368
368
  vm 'mkdir -p /etc/ssh/sshd_config.d && chmod 700 /etc/ssh/sshd_config.d'
369
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/sshd-hardening.conf" "$HOST:/etc/ssh/sshd_config.d/99-esellar-hardening.conf"
369
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/sshd-hardening.conf" "$HOST:/etc/ssh/sshd_config.d/99-kampodine-hardening.conf"
370
370
  vm 'rc-service sshd restart' || die "sshd restart failed after hardening ensure"
371
371
 
372
372
  say "gate: sshd hardening effective"
@@ -404,50 +404,50 @@ PODSTACK_GATE
404
404
  vm 'rc-update show boot | grep -q cgroups || rc-update add cgroups boot' || die "rc-update cgroups failed"
405
405
  vm 'rc-service cgroups status >/dev/null 2>&1 || rc-service cgroups start' || die "cgroups start failed"
406
406
 
407
- # --- 3. /etc/esellar + managed config files ------------------------------------
408
- say "creating /etc/esellar (env file lands here via kampodine deploy, 0600 root)…"
409
- vm 'mkdir -p /etc/esellar && chmod 700 /etc/esellar'
407
+ # --- 3. /etc/kampodine + managed config files ------------------------------------
408
+ say "creating /etc/kampodine (env file lands here via kampodine deploy, 0600 root)…"
409
+ vm 'mkdir -p /etc/kampodine && chmod 700 /etc/kampodine'
410
410
 
411
411
  say "writing /etc/containers/registries.conf (insecure 127.0.0.1:5000; search docker.io)…"
412
412
  vm 'mkdir -p /etc/containers'
413
413
  scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/registries.conf" "$HOST:/etc/containers/registries.conf"
414
414
 
415
- say "writing /etc/sysctl.d/60-esellar.conf + applying live…"
416
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/60-esellar.conf" "$HOST:/etc/sysctl.d/60-esellar.conf"
415
+ say "writing /etc/sysctl.d/60-kampodine.conf + applying live…"
416
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/60-kampodine.conf" "$HOST:/etc/sysctl.d/60-kampodine.conf"
417
417
  vm_sh <<'SYSCTL_APPLY' || die "sysctl ensure/apply failed"
418
418
  rc-update show boot | grep -Eq '^[[:space:]]*sysctl[[:space:]]*\|' || rc-update add sysctl boot
419
419
  sysctl -w net.ipv4.ip_unprivileged_port_start=80 > /dev/null
420
420
  SYSCTL_APPLY
421
421
 
422
422
  # --- 4. OpenRC services (canonical: ansible container-service templates) --------
423
- say "ensuring the 'kamal' podman network (deploy's proxy re-point resolves esellar-api:8080 by network DNS)…"
423
+ say "ensuring the 'kamal' podman network (deploy's proxy re-point resolves kampodine-api:8080 by network DNS)…"
424
424
  # shellcheck disable=SC2016
425
425
  vm 'podman network exists kamal 2>/dev/null || podman network create kamal' \
426
426
  || die "podman network create kamal failed"
427
- say "installing OpenRC services esellar-api + kamal-proxy (supervise-daemon around podman run)…"
428
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/esellar-api" "$HOST:/etc/init.d/esellar-api"
427
+ say "installing OpenRC services kampodine-api + kamal-proxy (supervise-daemon around podman run)…"
428
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-api" "$HOST:/etc/init.d/kampodine-api"
429
429
  scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kamal-proxy" "$HOST:/etc/init.d/kamal-proxy"
430
- vm 'chmod 755 /etc/init.d/esellar-api /etc/init.d/kamal-proxy'
430
+ vm 'chmod 755 /etc/init.d/kampodine-api /etc/init.d/kamal-proxy'
431
431
 
432
- # --- 4b. esellar-anchor (blue-green flip guest half — watcher, no container) ----
432
+ # --- 4b. kampodine-anchor (blue-green flip guest half — watcher, no container) ----
433
433
  # Ships on EVERY vm: started + enabled now, INERT until a flip writes
434
- # /etc/esellar/anchor.conf over ssh (ansible container-service owns both files
434
+ # /etc/kampodine/anchor.conf over ssh (ansible container-service owns both files
435
435
  # afterwards — drift repair keeps them in sync).
436
- say "installing the esellar-anchor watcher service (blue-green flip guest half)…"
436
+ say "installing the kampodine-anchor watcher service (blue-green flip guest half)…"
437
437
  # /usr/local/sbin does NOT exist on the golden image (fresh Alpine ships no
438
438
  # /usr/local hierarchy; the alpine-base role creates it — vm-prepare runs
439
439
  # BEFORE any ansible)
440
440
  vm 'mkdir -p /usr/local/sbin' || die "mkdir /usr/local/sbin failed"
441
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/esellar-anchor.sh" "$HOST:/usr/local/sbin/esellar-anchor.sh"
442
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/esellar-anchor" "$HOST:/etc/init.d/esellar-anchor"
443
- vm 'chmod 755 /usr/local/sbin/esellar-anchor.sh /etc/init.d/esellar-anchor' || die "chmod esellar-anchor files failed"
444
- vm 'sh -n /usr/local/sbin/esellar-anchor.sh' || die "esellar-anchor.sh does not parse (busybox sh)"
445
- vm 'sh -n /etc/init.d/esellar-anchor' || die "/etc/init.d/esellar-anchor does not parse"
446
- vm 'rc-update show default | grep -qE "^[[:space:]]*esellar-anchor[[:space:]]*\\|" || rc-update add esellar-anchor default' \
447
- || die "rc-update add esellar-anchor default failed"
448
- vm 'rc-service esellar-anchor start' || die "rc-service esellar-anchor start failed"
449
-
450
- for SVC in esellar-api kamal-proxy; do
441
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-anchor.sh" "$HOST:/usr/local/sbin/kampodine-anchor.sh"
442
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-anchor" "$HOST:/etc/init.d/kampodine-anchor"
443
+ vm 'chmod 755 /usr/local/sbin/kampodine-anchor.sh /etc/init.d/kampodine-anchor' || die "chmod kampodine-anchor files failed"
444
+ vm 'sh -n /usr/local/sbin/kampodine-anchor.sh' || die "kampodine-anchor.sh does not parse (busybox sh)"
445
+ vm 'sh -n /etc/init.d/kampodine-anchor' || die "/etc/init.d/kampodine-anchor does not parse"
446
+ vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-anchor[[:space:]]*\\|" || rc-update add kampodine-anchor default' \
447
+ || die "rc-update add kampodine-anchor default failed"
448
+ vm 'rc-service kampodine-anchor start' || die "rc-service kampodine-anchor start failed"
449
+
450
+ for SVC in kampodine-api kamal-proxy; do
451
451
  vm "rc-update show default | grep -qE '^[[:space:]]*${SVC}[[:space:]]*\\|' || rc-update add ${SVC} default" \
452
452
  || die "rc-update add $SVC default failed"
453
453
  done
@@ -456,7 +456,7 @@ done
456
456
  # re-runs after a deploy must not bounce production.
457
457
  # shellcheck disable=SC2016
458
458
  vm_sh <<'UNIT_DRIFT' || true
459
- for SVC in esellar-api kamal-proxy esellar-anchor; do
459
+ for SVC in kampodine-api kamal-proxy kampodine-anchor; do
460
460
  if rc-service "$SVC" status > /dev/null 2>&1; then
461
461
  echo "RUNNING: $SVC (unit file was overwritten — rc-service $SVC restart to apply, on your call)"
462
462
  fi
@@ -483,26 +483,26 @@ if [[ $DO_PULL -eq 1 ]]; then
483
483
  # installs it later; vm-prepare runs BEFORE any ansible)
484
484
  vm 'busybox wget -q -O /dev/null http://127.0.0.1:5000/v2/' || {
485
485
  pkill -f "ssh.*-R 5000" 2>/dev/null || true; sleep 1
486
- nohup ssh -R 5000:127.0.0.1:5000 -N -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes "$HOST" >/tmp/esellar-tunnel.log 2>&1 &
486
+ nohup ssh -R 5000:127.0.0.1:5000 -N -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes "$HOST" >/tmp/kampodine-tunnel.log 2>&1 &
487
487
  sleep 3
488
488
  }
489
- vm 'busybox wget -q -O /dev/null http://127.0.0.1:5000/v2/' || die "registry tunnel did not come up (/tmp/esellar-tunnel.log)"
490
- vm 'podman pull --tls-verify=false 127.0.0.1:5000/esellar-api:latest' || die "app image pull failed"
489
+ vm 'busybox wget -q -O /dev/null http://127.0.0.1:5000/v2/' || die "registry tunnel did not come up (/tmp/kampodine-tunnel.log)"
490
+ vm 'podman pull --tls-verify=false 127.0.0.1:5000/kampodine-api:latest' || die "app image pull failed"
491
491
  else
492
492
  say "skipping app-image pre-pull (pass --pull-images, or let the first deploy pull)"
493
493
  fi
494
494
 
495
- # --- 7. converge esellar-api ONLY if a previous deploy left image + env ---------
495
+ # --- 7. converge kampodine-api ONLY if a previous deploy left image + env ---------
496
496
  # On a truly fresh VM neither exists — the FIRST DEPLOY provides both and
497
497
  # starts the service. Re-runs of this script after a deploy heal drift.
498
- say "esellar-api start check (needs image + /etc/esellar/env — first deploy provides both)…"
498
+ say "kampodine-api start check (needs image + /etc/kampodine/env — first deploy provides both)…"
499
499
  # shellcheck disable=SC2016
500
- vm_sh <<'API_CONVERGE' || die "esellar-api start failed (image + env present — investigate: podman logs esellar-api)"
501
- if [ -f /etc/esellar/env ] && podman image exists 127.0.0.1:5000/esellar-api:latest; then
502
- rc-service esellar-api start
503
- echo "esellar-api started (image + env present)"
500
+ vm_sh <<'API_CONVERGE' || die "kampodine-api start failed (image + env present — investigate: podman logs kampodine-api)"
501
+ if [ -f /etc/kampodine/env ] && podman image exists 127.0.0.1:5000/kampodine-api:latest; then
502
+ rc-service kampodine-api start
503
+ echo "kampodine-api started (image + env present)"
504
504
  else
505
- echo "esellar-api deferred: no image and/or /etc/esellar/env yet (normal on a fresh VM — first deploy handles it)"
505
+ echo "kampodine-api deferred: no image and/or /etc/kampodine/env yet (normal on a fresh VM — first deploy handles it)"
506
506
  fi
507
507
  API_CONVERGE
508
508
 
@@ -522,16 +522,16 @@ vm 'grep -q "127.0.0.1:5000" /etc/containers/registries.conf && grep -q "insecur
522
522
  || die "registries.conf incomplete (127.0.0.1:5000 insecure + docker.io search)"
523
523
 
524
524
  say "gate: services enabled in the default runlevel"
525
- vm 'rc-update show default | grep -qE "^[[:space:]]*esellar-api[[:space:]]*\\|"' || die "esellar-api not in the default runlevel"
525
+ vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-api[[:space:]]*\\|"' || die "kampodine-api not in the default runlevel"
526
526
  vm 'rc-update show default | grep -qE "^[[:space:]]*kamal-proxy[[:space:]]*\\|"' || die "kamal-proxy not in the default runlevel"
527
527
  vm 'rc-service kamal-proxy status >/dev/null' || die "kamal-proxy service not started"
528
528
 
529
- say "gate: esellar-anchor watcher running + INERT (no anchor conf on a fresh VM)"
530
- vm 'rc-update show default | grep -qE "^[[:space:]]*esellar-anchor[[:space:]]*\\|"' || die "esellar-anchor not in the default runlevel"
531
- vm 'rc-service esellar-anchor status >/dev/null' || die "esellar-anchor service not started"
532
- vm '! test -e /etc/esellar/anchor.conf' || die "/etc/esellar/anchor.conf already exists on a fresh VM (wrong machine?)"
533
- vm 'grep -q "no anchor conf" /var/log/esellar-anchor.log' \
534
- || die "esellar-anchor is started but never logged its idle transition (watcher loop not running?)"
529
+ say "gate: kampodine-anchor watcher running + INERT (no anchor conf on a fresh VM)"
530
+ vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-anchor[[:space:]]*\\|"' || die "kampodine-anchor not in the default runlevel"
531
+ vm 'rc-service kampodine-anchor status >/dev/null' || die "kampodine-anchor service not started"
532
+ vm '! test -e /etc/kampodine/anchor.conf' || die "/etc/kampodine/anchor.conf already exists on a fresh VM (wrong machine?)"
533
+ vm 'grep -q "no anchor conf" /var/log/kampodine-anchor.log' \
534
+ || die "kampodine-anchor is started but never logged its idle transition (watcher loop not running?)"
535
535
 
536
536
  # --- summary ---------------------------------------------------------------------
537
537
  IP="${HOST#*@}"