Public: anyone can read this; nobody can change it.

germinate: ship Grafana observability to daughters (#6) #8

opened this view on origin

Check · Review · Land · Live — It conflicts with its base. Next: builder, it conflicts with its base.

Merges on the origin

feat/observability-grafana → main 1 commit

  • Merge on the origin. This repository mirrors open-platform/mitosis, which is truth for it. Merge there and the next sync brings back the merge commit it actually made. Open on the origin

A guest

This pull comes from a guest: its preview runs without the app's variables or a forge token until a person who can write here approves it.

This pull waits for a person who can write here to review it; it never lands on its own.

Review

Sign in to review this change.
4 files changed +78 −6
bin/germinate +19 −3
@@ -28,6 +28,9 @@ source "${MITOSIS_LIB:-$(dirname "$0")/lib.sh}"
2828 : "${FORGEJO_USER:=plat}"; : "${FORGEJO_PASSWORD:=platplatplat}"; : "${FORGEJO_EMAIL:[email protected]}"
2929 : "${FLUX_NS:=flux-system}"; : "${FLUX_PATH:=clusters/local}"; : "${FLUX_BRANCH:=main}"
3030 : "${CIBTAG:=0.1.0}" # ci-builder image tag the workflows container: onto
31 : "${MONITORING:=}" # non-empty (e.g. MONITORING=1) ships the observability
32 # stack (Prometheus+Grafana) in a LEAN daughter; the
33 # full profile always includes it.
3134 # BYO claude credentials (default empty) — sealed into plat-agents-secrets so a
3235 # daughter launched with one can build apps the instant it reconciles. Captured
3336 # into named locals so we can report (NOT echo) whether agents are credentialed,
@@ -75,7 +78,7 @@ for b in "$WORK/seed/repos/"*.bundle; do
7578 base=$(basename "$b" .bundle); git clone -q --branch "$FLUX_BRANCH" "$b" "$WORK/r-${base#*__}"; REPONAMES+=("${base%%__*}/${base#*__}")
7679 done
7780 GITOPS_DIR="$WORK/r-gitops"
78 [ "$PROFILE" = lean ] && trim_gitops_lean "$GITOPS_DIR"
81 [ "$PROFILE" = lean ] && trim_gitops_lean "$GITOPS_DIR" "$MONITORING"
7982
8083 # Pin dind's bridge MTU to the cluster overlay so image builds' large downloads
8184 # aren't silently dropped (k3d/flannel overlay = 1450; dind otherwise defaults
@@ -94,8 +97,10 @@ fork_rekey_sops "$GITOPS_DIR" "$FORK_PUB"
9497 # admin password and thread it through helm/flux-git-auth so they agree with the
9598 # sealed forgejo-admin + plat-mcp-secrets PLAT_AUTH/GITOPS_REMOTE. The BYO claude
9699 # credential (BYO_ANTHROPIC/BYO_OAUTH) is sealed into plat-agents-secrets here.
97 FORK_ADMIN_PASSWORD=$(ANTHROPIC_API_KEY="$BYO_ANTHROPIC" CLAUDE_CODE_OAUTH_TOKEN="$BYO_OAUTH" \
98 fork_regen_secrets "$GITOPS_DIR" "$DOMAIN" | sed -n 's/^FORK_ADMIN_PASSWORD=//p')
100 _REGEN_OUT=$(ANTHROPIC_API_KEY="$BYO_ANTHROPIC" CLAUDE_CODE_OAUTH_TOKEN="$BYO_OAUTH" \
101 fork_regen_secrets "$GITOPS_DIR" "$DOMAIN")
102 FORK_ADMIN_PASSWORD=$(printf '%s\n' "$_REGEN_OUT" | sed -n 's/^FORK_ADMIN_PASSWORD=//p')
103 FORK_GRAFANA_ADMIN_PASSWORD=$(printf '%s\n' "$_REGEN_OUT" | sed -n 's/^FORK_GRAFANA_ADMIN_PASSWORD=//p')
99104 [ -n "$FORK_ADMIN_PASSWORD" ] || { echo "FATAL: failed to generate admin password"; exit 1; }
100105 FORGEJO_PASSWORD="$FORK_ADMIN_PASSWORD"
101106 fork_set_identity_secrets "$GITOPS_DIR" "$DOMAIN" # encrypted identity fields
@@ -307,6 +312,11 @@ if fork_register_oauth "$GITOPS_DIR" "$FJ" "$FJA" "$DOMAIN"; then
307312 else
308313 echo " WARN: OAuth registration failed — mcp login will be broken until re-registered"
309314 fi
315 # Grafana SSO: when the observability stack is shipped (full profile, or lean with
316 # MONITORING set), register its own sovereign Forgejo OAuth app and seal the client
317 # id/secret into grafana-oauth. fork_register_grafana_oauth no-ops when absent.
318 fork_register_grafana_oauth "$GITOPS_DIR" "$FJ" "$FJA" "$DOMAIN" \
319 || echo " WARN: grafana OAuth registration failed — Grafana SSO broken until re-registered"
310320 MCPTAG=$(grep -oE 'tag: *"[0-9][0-9.]*"' "$GITOPS_DIR/clusters/local/platform/mcp/ocirepository.yaml" 2>/dev/null | grep -oE '[0-9][0-9.]*' | head -1)
311321 AGENTSTAG=$(grep -oE 'tag: *"[0-9][0-9.]*"' "$GITOPS_DIR/clusters/local/platform/agents/ocirepository.yaml" 2>/dev/null | grep -oE '[0-9][0-9.]*' | head -1)
312322
@@ -513,6 +523,12 @@ echo " open https://git.$DOMAIN , sign in as '$FORGEJO_USER', open $FORGEJO
513523 echo " → 'Use this template' (name + description = the spec) — an agent builds it,"
514524 echo " opens PR #1, a validator browser-tests it, and it ships to <app>-$FORGEJO_USER.$DOMAIN."
515525 echo ""
526 if [ -f "$GITOPS_DIR/clusters/local/platform/monitoring/kustomization.yaml" ]; then
527 echo " Observability:"
528 echo " Grafana https://grafana.$DOMAIN — core dashboards preloaded; sign in"
529 echo " with Forgejo, or break-glass admin / ${FORK_GRAFANA_ADMIN_PASSWORD:-<sealed>}"
530 echo ""
531 fi
516532 if [ -z "$BYO_ANTHROPIC" ] && [ -z "$BYO_OAUTH" ]; then
517533 cat <<EOF
518534 ┌──────────────────────────────────────────────────────────────────────────┐
bin/lib.sh +56 −3
@@ -68,13 +68,16 @@ fork_rewrite_identity() { # $1 = gitops clone, $2 = parent domain (hint), $3 =
6868 rewrite_identity_domain "$g" "$old" "$new"
6969 }
7070
71 trim_gitops_lean() { # $1 = path to a working gitops clone
72 local g="$1"
71 trim_gitops_lean() { # $1 = gitops clone, $2 = include monitoring (non-empty keeps the observability stack)
72 local g="$1" mon="${2:-}"
7373 # 1) platform kustomization → core only, and WITHOUT keda (keda becomes its own
7474 # Flux Kustomization below so its CRDs install before forgejo's ScaledJob).
7575 printf 'apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\nresources:\n - coredns\n - plat-system\n - forgejo\n - plat-storage\n - mcp\n - agents\n - registry-node-config\n - registry-pull-secret.yaml\n' \
7676 > "$g/clusters/local/platform/kustomization.yaml"
77 rm -rf "$g/clusters/local/platform/monitoring" "$g/clusters/local/platform/grafana" "$g/clusters/local/platform/pgadmin"
77 # grafana/pgadmin are legacy dirs always dropped from lean; monitoring is kept
78 # only when the caller opted in (MONITORING), else dropped to stay minimal.
79 rm -rf "$g/clusters/local/platform/grafana" "$g/clusters/local/platform/pgadmin"
80 [ -n "$mon" ] || rm -rf "$g/clusters/local/platform/monitoring"
7881 # 2) flux-kustomizations → cert-manager/pki/cnpg/traefik + keda (separate, wait) +
7982 # platform (dependsOn keda too) + apps. Splitting keda out fixes the fresh-cluster
8083 # deadlock: the forgejo ScaledJob needs keda.sh CRDs that the keda HelmRelease
@@ -104,6 +107,18 @@ apiVersion: kustomize.toolkit.fluxcd.io/v1
104107 kind: Kustomization
105108 metadata: { name: keda, namespace: flux-system }
106109 spec: { interval: 5m, path: ./clusters/local/platform/keda, prune: true, wait: true, timeout: 6m, sourceRef: { kind: GitRepository, name: flux-system } }
110 FK
111 # Observability rides a lean fork only when opted in (MONITORING). Its own
112 # Kustomization — it ships CRDs + a sops-sealed Grafana secret (hence the
113 # decryption block) — and is deliberately never a dependency of `apps`.
114 [ -n "$mon" ] && cat >> "$g/clusters/local/flux-kustomizations.yaml" <<'MFK'
115 ---
116 apiVersion: kustomize.toolkit.fluxcd.io/v1
117 kind: Kustomization
118 metadata: { name: monitoring, namespace: flux-system }
119 spec: { interval: 5m, path: ./clusters/local/platform/monitoring, prune: true, wait: true, timeout: 12m, decryption: { provider: sops, secretRef: { name: sops-age } }, dependsOn: [{ name: cert-manager }], sourceRef: { kind: GitRepository, name: flux-system } }
120 MFK
121 cat >> "$g/clusters/local/flux-kustomizations.yaml" <<'FK'
107122 ---
108123 apiVersion: kustomize.toolkit.fluxcd.io/v1
109124 kind: Kustomization
@@ -308,9 +323,25 @@ stringData:
308323 EOF
309324 ( cd "$g" && sops --encrypt --in-place "$P/registry-pull-secret.yaml" )
310325
326 # Observability (full profile, or lean+MONITORING): the committed grafana-oauth
327 # secret is sealed to the GENESIS recipient, which fork_verify_all_sealed rejects.
328 # Reseal it to the fork key with a fresh break-glass admin password + a placeholder
329 # OAuth client (filled post-handoff by fork_register_grafana_oauth).
330 local grafana_pw=""
331 if [ -f "$g/$P/monitoring/grafana-oauth-secret.yaml" ]; then
332 grafana_pw=$(openssl rand -hex 16)
333 _fork_seal "$g" "$P/monitoring/grafana-oauth-secret.yaml" grafana-oauth monitoring "$_FORK_LBL_MANAGED" <<EOF
334 GF_SECURITY_ADMIN_USER: admin
335 GF_SECURITY_ADMIN_PASSWORD: $grafana_pw
336 GF_AUTH_GENERIC_OAUTH_CLIENT_ID: pending-registration
337 GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET: pending-registration
338 EOF
339 fi
340
311341 git -C "$g" add -A
312342 git -C "$g" -c user.email=mitosis@plat -c user.name=mitosis commit -q -m "mitosis fork: regenerate platform secrets (sealed to sovereign key)" || true
313343
344 [ -n "$grafana_pw" ] && printf 'FORK_GRAFANA_ADMIN_PASSWORD=%s\n' "$grafana_pw"
314345 printf 'FORK_ADMIN_PASSWORD=%s\n' "$admin_pw"
315346 }
316347
@@ -413,6 +444,28 @@ fork_register_oauth() { # $1=gitops $2=forgejo_url $3=basicauth $4=DOMAIN
413444 git -C "$g" -c user.email=mitosis@plat -c user.name=mitosis commit -aqm "mitosis fork: plat-mcp OAuth client ($dom)" || true
414445 }
415446
447 # fork_register_grafana_oauth — when the observability stack is shipped, register a
448 # sovereign Forgejo OAuth app for Grafana and seal client_id/secret into the
449 # grafana-oauth secret. Returns 0 (no-op) when monitoring is absent.
450 # $1=gitops $2=forgejo_url $3=basicauth $4=DOMAIN
451 fork_register_grafana_oauth() {
452 local g="$1" fj="$2" auth="$3" dom="$4"
453 local gs="$1/clusters/local/platform/monitoring/grafana-oauth-secret.yaml" resp cid csec
454 [ -f "$gs" ] || return 0 # observability not shipped — nothing to register
455 resp=$(curl -fsS -u "$auth" -H 'Content-Type: application/json' \
456 -d "{\"name\":\"grafana\",\"redirect_uris\":[\"https://grafana.$dom/login/generic_oauth\"],\"confidential_client\":true}" \
457 "$fj/api/v1/user/applications/oauth2" 2>/dev/null) || true
458 cid=$(printf '%s' "$resp" | jq -r '.client_id // empty' 2>/dev/null)
459 csec=$(printf '%s' "$resp" | jq -r '.client_secret // empty' 2>/dev/null)
460 if [ -z "$cid" ] || [ -z "$csec" ]; then
461 echo "WARN: fork: grafana OAuth registration returned no client_id/secret (resp: ${resp:0:200})"; return 1
462 fi
463 sops set "$gs" '["stringData"]["GF_AUTH_GENERIC_OAUTH_CLIENT_ID"]' "\"$cid\""
464 sops set "$gs" '["stringData"]["GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET"]' "\"$csec\""
465 git -C "$g" -c user.email=mitosis@plat -c user.name=mitosis commit -aqm "mitosis fork: grafana OAuth client ($dom)" || true
466 echo " grafana OAuth app registered on the daughter"
467 }
468
416469 # fork_seal_ca — after cert-manager mints plat-local-ca-tls, seal its ca.crt
417470 # (base64) into plat-mcp-secrets.PLAT_CA_TLS_CRT_B64. Reads the secret from the
418471 # live cluster; caller ensures it exists.
bin/mitosis +2
@@ -18,6 +18,7 @@ mitosis — Open Platform self-replication
1818 CLUSTER=<name> K3S_IMAGE=<pinned k3s>
1919 HTTP_PORT/HTTPS_PORT (default 80/443; PORTS=none to skip)
2020 CLAUDE_CODE_OAUTH_TOKEN | ANTHROPIC_API_KEY (one, optional)
21 MONITORING=1 (ship Prometheus+Grafana observability)
2122
2223 mitosis seed Export a portable seed of the current platform.
2324 env: PLAT_PAT (required, Forgejo PAT w/ repo read)
@@ -29,6 +30,7 @@ mitosis — Open Platform self-replication
2930 env: SEED=<seed.tar.gz> (required)
3031 DOMAIN=<your-domain> (default plat.local)
3132 FORK_KEY=<path> (where to write the new age key)
33 MONITORING=1 (ship Prometheus+Grafana observability)
3234
3335 Examples:
3436 nix run .#up # a sovereign platform locally
bin/up +1
@@ -118,6 +118,7 @@ fi
118118 echo "==> germinating the platform onto k3d-${CLUSTER}…"
119119 env SEED="$SEED" DOMAIN="$DOMAIN" \
120120 ${FORK_KEY:+FORK_KEY="$FORK_KEY"} \
121 ${MONITORING:+MONITORING="$MONITORING"} \
121122 "$HERE/germinate"
122123
123124 # germinate prints the YOUR PLATFORM card (domain, admin creds, fork key, /etc/hosts,

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs