Two pieces of infrastructure existed only on servers, as single copies with no
history and no review:
/usr/local/bin/supabase-deploy-functions the gate both projects deploy through
/opt/supabase-self-hosted/functions-registry.json who owns which function name
The gate exists to stop silent drift between what a repo declares and what a
server actually runs. Leaving it unversioned meant the mechanism was drifting the
same way it was built to prevent — and losing that host would have lost it
entirely, along with any record of why it works the way it does.
Both are now in ops/ and synced to the host by CI on every deploy, installed
before the functions they validate so a change to the gate ships together with
them. Verified identical to the server copies by checksum before committing, so
this captures the running state rather than replacing it.
Branch protection gets the same treatment: apply-repo-settings.sh is idempotent
and has a --check mode that reports drift without changing anything. It was
applied by hand through the API today, so nothing here reflected it.
Documented alongside: why the registry lives in this repo even though pwap-web
depends on it (a pwap-web change needs a PR here — deliberate friction, since
the registry is the record of who owns what and should not be edited on a server
where nobody sees the change), and why listing each CI job individually in the
protection rules is a weakness here that pwap-web avoids with an aggregate job.
The functions in this repo do not all live in the same place, and the deploy
step I added yesterday sent all of them to the self-hosted host on vps3.
telegram-bot and ask are served from the cloud project vbhftvdayqfmcgmzdxfv.
Both Telegram bots reach it by webhook — ?bot=krd is @pezkuwichainBot and
?bot=dks is @DKSKurdistanBot — and news.pex.mom's assistant calls ask there.
Copies of both still sit in the vps3 volume from before that split, holding a
bot token revoked on 2026-07-19, and take no traffic. Deploying to vps3 was
therefore updating a dead copy while the live one kept running old code.
So the self-hosted job now excludes those two, and a second job deploys them to
the cloud project. That closes a real gap: they had been deployed by hand for
months, and git drifted far enough behind that on 2026-07-21 the running
function body had to be extracted from the deployed bundle to recover it.
The host registry records the split too, so the gate cannot be pointed at the
wrong target by accident.
The Supabase edge-function volume on the host is shared with pwap-web, which
deploys into it by rsyncing its whole tree. Both projects defined a function
called telegram-auth, so on 2026-06-28 pwap-web's login-widget handler replaced
this project's initData handler. Sign-in and every wallet screen behind it have
returned 401 ever since; nothing failed loudly because the name still resolved.
Rename this project's two colliding functions to a tgm- namespace so the two
deploys can no longer reach the same directory, and point the client at the new
name. process-withdraw is renamed too: it is currently unreferenced here, but it
moves platform funds, so leaving it shadowed by another project's version is not
something to keep.
Also deploy functions from CI. They were last pushed by hand in April, so seven
of them had drifted behind the repo and one had never shipped at all. The new
step writes through the host's ownership gate, which refuses any name this
project does not own — the drift and the collision both stop here.
Ownership is recorded in /opt/supabase-self-hosted/functions-registry.json.
- Split monolithic ci.yml into focused workflow files
- Add code-quality.yml with complexity analysis and duplicate detection
- Replace template codeql.yml with comprehensive security.yml (CodeQL, dependency audit, dependency review, secret scan)
- Separate deploy into its own workflow triggered by CI success