From c12b783be8ebad973d44ca10dcec50a415e75d25 Mon Sep 17 00:00:00 2001 From: G <41178744+catomean@users.noreply.github.com> Date: Sat, 29 Aug 2026 07:08:51 +0200 Subject: [PATCH] feat(ops): answer "what datastore does this app actually use?" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asked whether anything still used the self-hosted Supabase stack, I grepped DATABASE_URL across every app env. Every hit pointed at localhost:5432, so the answer looked like "nothing" — and stopping the stack was one command from taking orangecat and botsmann down, with 114 auth users, 114 profiles and 927 messages behind it, written to as recently as the previous evening. A Supabase app never sets DATABASE_URL. It talks HTTPS to PostgREST/Kong via SUPABASE_URL. Absence of a Postgres connection string is not absence of a dependency; it is the signature of a different one. This reads BOTH bindings per app and reports whether a Postgres target actually contains tables, so an empty database cannot masquerade as the live one. What it found on first run: orangecat, botsmann -> self-hosted Supabase (supabase.orangecat.ch) printcraft -> HOSTED supabase.co, never migrated off the cloud 12 apps -> native postgres native db orangecat -> exists with ZERO tables, referenced by nothing Read-only. The question "is X safe to stop?" now has a factual answer instead of a confident wrong one. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P --- scripts/hetzner/data-deps.sh | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 scripts/hetzner/data-deps.sh diff --git a/scripts/hetzner/data-deps.sh b/scripts/hetzner/data-deps.sh new file mode 100755 index 00000000..d325dca1 --- /dev/null +++ b/scripts/hetzner/data-deps.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# data-deps.sh — which datastore does each app on the box ACTUALLY use? +# +# WHY THIS EXISTS: on 2026-08-29 the question "is anything still using the +# Supabase stack?" was answered by grepping DATABASE_URL across every app env. +# Every hit pointed at localhost:5432, so the answer looked like "nothing" — +# and stopping the stack was one command from taking orangecat and botsmann +# down, with 114 auth users and 927 messages behind it. +# +# The reasoning error: a Supabase app never sets DATABASE_URL. It talks HTTPS +# to PostgREST/Kong via SUPABASE_URL. The absence of a Postgres connection +# string is not the absence of a dependency — it is the signature of a +# DIFFERENT one. Grepping for one binding and concluding "unused" reads +# absence as an answer. +# +# It also reports whether a Postgres target actually holds tables, because an +# empty database is worse than a missing one: `orangecat` exists in the native +# cluster with ZERO tables, a leftover pointing nowhere, and a reader who sees +# the name assumes the app lives there. +# +# Usage: bash data-deps.sh (read-only; answers "is X safe to stop?") +set -u +export LC_ALL=C + +# strip: key=, quotes, scheme, then CREDENTIALS (everything up to the last @), +# then any query string. Order matters — stripping the path before the +# credentials mangles a postgres URL into the scheme. +hostof() { sed 's#^[^=]*=##; s#^"##; s#"$##; s#^[a-z+]*://##; s#^.*@##; s#?.*$##'; } +hostonly() { hostof | sed 's#/.*##'; } + +printf '%-22s %-34s %-26s %s\n' APP POSTGRES SUPABASE VERDICT +printf '%-22s %-34s %-26s %s\n' "---" "---" "---" "---" +for d in /opt/*/; do + app=$(basename "$d") + case "$app" in _appcron|backups|monitoring|supabase) continue ;; esac + f="$d/shared/.env"; [ -f "$f" ] || f="$d/app/.env"; [ -f "$f" ] || continue + # app/.env may be a symlink to shared/.env; reading one is enough either way. + + pg=$(grep -m1 '^DATABASE_URL=' "$f" 2>/dev/null | hostof) + sb=$(grep -m1 -E '^(NEXT_PUBLIC_)?SUPABASE_URL=' "$f" 2>/dev/null | hostonly) + + # does the postgres target actually contain anything? + pgnote="" + if [ -n "$pg" ]; then + db=${pg##*/} + n=$(sudo -u postgres psql -d "$db" -tAc \ + "select count(*) from information_schema.tables where table_schema='public';" 2>/dev/null | tr -d ' ') + [ -n "$n" ] && [ "$n" = "0" ] && pgnote=" [EMPTY — decoy]" + fi + + if [ -n "$sb" ] && [ -n "$pg" ]; then v="supabase (postgres var is a fossil)" + elif [ -n "$sb" ]; then v="supabase" + elif [ -n "$pg" ]; then v="native postgres" + else v="no datastore configured" + fi + printf '%-22s %-34s %-26s %s\n' "$app" "${pg:-—}$pgnote" "${sb:-—}" "$v" +done