Decisions and access I cannot resolve alone. Served at `/build/needs-rupash`.
**All five open items were answered on 5 September.** Four are done; one is deliberately
left alone. What follows records what changed and why, so the reasoning outlives the
conversation.
You said leave it, but fix it if I could. I could.
**The panel is now at https://coolify.194.233.66.89.sslip.io** with a real Let's Encrypt
certificate, valid to 3 December. **Plain HTTP on port 8000 is closed.**
I was wrong about something here, and being wrong changed the answer. I had told you
sslip.io could not get a real certificate, having tested one of your apps and found
Traefik's self-signed default. That app was simply configured for `http://`;
`brain.194.233.66.89.sslip.io` has had a genuine Let's Encrypt certificate all along. No
domain purchase was needed after all.
Two details worth keeping:
- **`ufw deny 8000` would have done nothing.** Docker publishes the port to `0.0.0.0` and
inserts its own iptables rules ahead of ufw, so the packet never reaches ufw. The block
lives in the `DOCKER-USER` chain, which Docker leaves alone for exactly this.
- **The rule survives a reboot** via a small systemd unit. Worth knowing: the pre-existing
block on port 5003, belonging to your other project, does **not** — nothing persists it,
so it disappears on the next restart. That is yours to decide about; I left it alone
rather than quietly managing another project's firewall.
The ufw rule I removed was labelled `TEMPORARY - Coolify UI, close when GitHub source is
connected`, so this was always the intent.
To reopen the plain port if you ever need it:
`iptables -D DOCKER-USER -p tcp -m conntrack --ctorigdstport 8000 -j DROP`
Everything is in `ops/vps/`.
M37 now carries a second migration. **29 tasks, finish moves 6 Oct to 7 Oct.** One day to
replace an entire second system.
- **The twelve house skills come across, not rewritten.** `verz-master-theme`,
`verz-doc-letterhead`, `seo-audit`, `website-cro-audit` go first — in daily use, and the
real test of whether import works at all.
- **Agents are rebuilt.** AnyGen has no ceiling and no leash, so there is nothing to carry
over; each starts at Shadow on writes regardless of how it behaved there.
- **Their adaptive learning does not transfer.** One toggle over an opaque store has no
honest mapping onto four tiers with a review queue. Memory files are read as evidence and
tier-one preferences re-derived; anything widening a scope is discarded.
- **Decommissioning a SaaS is not decommissioning a server.** Cancelling the subscription
does not revoke the OAuth grants it holds on Gmail, Drive, Calendar, Sheets and Docs.
Access ends with billing, so exports are verified restorable first.
You said to go ahead and install it. **It is already installed and running** on the box:
`verz-langfuse-server` and `verz-langfuse-db`, up several days. My note was not asking
whether to install it; it was flagging that it runs well under its documented minimum of
11 vCPU and 25.5 GiB, on a box with 11.7 GiB in total.
You are right that this is fine at your traffic. Nothing to do.
Connecting *our* system to it is separate work and belongs in **M27**, wave 3, with the
rest of observability. It is in the plan already.
You said "I see you have done this as well". It was not done, and it is worth being precise
about why it looked done: the Deploy workflow's Coolify step printed `secrets not set` and
exited **0** by design, so a missing secret would not resemble a broken pipeline. Every
deploy until today was me running `ops/deploy.sh` by hand. A green run looked like a ship.
**It is automatic now.** A systemd timer on the VPS checks the registry every three minutes
and deploys when the published image changes.
Pulled rather than pushed, deliberately: every other route gives something outside the
server a way in — a Coolify token over plain HTTP, or an SSH key held by GitHub. This way
nothing new reaches the box and no credential leaves it.
It compares the digest the registry serves against what the container is running. Comparing
tags is useless, since `:latest` always equals `:latest`, and comparing build times trusts
a clock.
**cosign is installed and the signature is verified before the container starts** — a
signature checked after the thing is live is checked too late. Verified by hand: the
certificate binds the running image to
`.github/workflows/deploy.yml@refs/heads/main` in your repository.
History: `ssh verz-vps journalctl -u brain-deploy -n 50`
Three changes remain unapplied there: the `migrate` service removed, PgBouncer added, and
`BRAIN_COMMIT_SHA` dropped.
The consequences while it stays stale, so they are not a surprise later:
| What happens | Why it does not matter yet |
|---|---|
| A `migrate` container is created on every deploy | Exits 0. The app migrates itself under an advisory lock |
| The app talks to Postgres directly | No connection pooling. Fine at this scale, and the code is ready for the pooler |
| `/health/ready` reports `commit: unknown` | The image knows its own commit; the stale compose overrides it |
None of it blocks anything. The current file is `docker-compose.yml` in the repo whenever
you want to paste it across.