vibeapp.win

The apps

Portal

One home page for a park of small apps on a single machine: status, Start and Stop, access control, and Claude Code in a browser tab.

  • Since 10 August 2026
  • Page updated 29 October 2026
  • Traced to 27 sources
Portal's front page: a grid of app cards with icons and Claude and Edit buttons, with the names and details blurred.

Portal's front page on 3 October 2026. App names, addresses, descriptions and status details are blurred.

What it is

Most of the apps Henning and Claude have built run in containers on one Windows machine. Portal is the front door to all of them. It has one card per app that shows whether the app is running, with buttons to start, restart and stop it.

It also does the setup that used to take six or seven steps across three dashboards. It gives an app a public address, routes it through a shared tunnel and puts a login in front of it. And it has a panel where Claude Code runs in a real terminal in the browser.

How it works

In August 2026, six apps shared one Cloudflare tunnel and three kept their own. All nine had Cloudflare Access in front. Portal shows a plan before it changes anything there, and it refuses to publish an address with nothing in front of it.

Portal is deliberately not allowed to control Docker directly. Status comes from a small separate service that can only read. A separate watcher on the host carries out Start and Stop.

Replacing the Portal container closes the terminal tabs.

The documents Claude reads

When Portal registers an app, it writes four documents about how the app is built, how it is published and how it calls Claude. It also adds a pointer in CLAUDE.md, the file Claude Code actually reads. The four documents now have their own folder. Portal overwrites anything in that folder and leaves everything else in the app alone. Uploading a Markdown file no longer replaces an existing file without asking. Before, uploading a file named claude.md replaced the app’s whole CLAUDE.md.

Rules that apply to every app now live in one shared place. Every Claude session loads them, including the terminal in Portal. A new app’s CLAUDE.md now says only what is specific to that app.

When the watcher looked dead

In early October, Stop sometimes hung at “waiting for the watcher” even though the watcher was running. The fix on 7 October closed three gaps in Claude’s code:

  • A docker compose command that hung blocked the queue. Every command now has a time limit.
  • Reused process IDs fooled the lock. The lock now depends on a heartbeat.
  • An unexpected error crashed the watcher. Now it is logged instead.

Apps that lost their port

On the night of 10 October, one app stopped answering, but its container still showed as healthy. The port forwarding on the host was gone, and the status showed green. This can happen after Docker Desktop restarts. Until then, someone had to notice this and recreate the container by hand.

Now the watcher checks the apps’ ports every five minutes. If a port fails twice in a row while its container is healthy, the watcher recreates only the container that publishes that port, from the same image. It does this at most once per quarter hour per app. After three tries it gives up and says so on the card. It never touches stopped apps. For Portal itself it only warns by default, because recreating Portal would close the terminal tabs.

Pending updates

Since 13 October, each card in the Status tab shows how many component updates Dependabot has proposed for the app and whether GitHub has security alerts for it, with links. Critical and high alerts are highlighted. Portal updates nothing itself. It only reads, using its own read-only token, and keeps GitHub’s answer for half an hour. Apps without a GitHub repository get no line.

Keeping the tunnel software current

On 26 October it turned out that both tunnels were running a cloudflared build from June, version 2026.6.1. The compose file asked for the latest image, but that tag only moves when the image is pulled again, and that had not happened since June. Versions before 2026.10.2 trust eight compromised eMudhra intermediate certificates that Cloudflare had warned about on 20 October. Claude first pinned cloudflared to 2026.10.4.

The next day a nightly job replaced the pin. Between 03 and 05, the host watcher pulls the latest cloudflared image and recreates the tunnels that follow it, one at a time. It waits until each tunnel has registered its connection with Cloudflare before it moves on, and goes back to the previous image if the tunnel does not connect. Each tunnel is down for about four seconds during the switch, so the job runs at night. Tunnels pinned to a specific version are left alone, but if the pinned version is old, the app’s card in Status says so. That line appears only after Portal itself has been rebuilt.

What we expected, and what happened

Read it like a code review. Each removed line is something we expected, from a plan, a spec, a README or a code comment. The added line under it is what the history shows actually happened.

Expected: Portal writes documents into each app's repository so that Claude Code there would not have to guess how the app is published and built.

Happened: The documents existed but were not being read: Claude Code reads CLAUDE.md in the repository root and does not look in docs/ by itself. When Portal started maintaining a pointer block in each CLAUDE.md, ten of eleven apps were missing it.

Expected: Copying the host's Claude login into the containers would keep every container logged in.

Happened: It made things worse. Refresh tokens rotate, so several copies of one identity kept invalidating each other, and Henning lost a working day to it. A long-lived token that does not rotate fixed it on 14 August.

Expected: The setup button sets up DNS, the tunnel route and access control together, and reports what it did.

Happened: Under one condition it silently skipped the access step and still reported success. Since 24 August Portal refuses to publish an address that has no access control in front of it.

Expected: The plan for setting up an app: always show what will change before writing anything, and make every write safe to repeat.

Happened: Held up, and was tested by counting the actual write calls. The same plan-first pattern was later reused for the button that removes an app, which tears things down in a fixed order.

Expected: Changelog entries were written at the end of each working session, describing what had been fixed.

Happened: Portal's changelog records that in one week, two fixes in Portal were first written up as wins, and both were wrong. Changelogs are now drafted by Claude from the actual diff, after the commit, and nothing is committed until a human has read it.

Expected: A lock added in 1.17.0, after two watchers on the host had torn jobs apart, would make sure only one watcher ever ran.

Happened: The lock went by the process ID in a file. A watcher that was killed left that file behind, and once Windows had given the ID to another process, every new watcher believed the old one was still alive. Stop then sat at 'waiting for the watcher'. Since 7 October the watcher writes a heartbeat every five seconds, and the lock counts only while the heartbeat is fresh.

Expected: The pointer block that Portal had maintained in each app's CLAUDE.md since August would keep Claude Code pointed at Portal's documents.

Happened: The block disappeared whenever Claude wrote a new CLAUDE.md as an app was finished. On 23 September it was missing in seven of 21 apps, exactly the newest ones. The documents existed but were not read. Since 8 October Portal puts the block back after an overwrite, and new apps get it from the start.

Page history

  1. First version of this page.
  2. 3 October 2026: we checked the page again and found no new commits in Portal since the last update, so its content is unchanged.
  3. On 7 October 2026 Portal's host watcher got time limits, a heartbeat and crash logging, and Start was fixed for an app with a non-ASCII folder name; the page now describes both and the commit count is updated.
  4. 2026-10-12: Portal's host watcher now recreates a container that has lost its port after a Docker restart, Portal's generated documents moved into their own folder, uploads no longer overwrite files without asking, and the commit count and facts on this page were updated.
  5. On 13 October 2026, Portal's Status tab started showing pending Dependabot updates and GitHub security alerts for each app. The page now describes this and updates the commit count to 38.
  6. Between 15 and 19 October 2026 Portal had no new commits, so the only change on this page is a commit count re-checked on 19 October.
  7. 2026-10-26: Portal had no new commits since 19 October, so only the commit count date on this page changed.
  8. 29 October 2026: added the cloudflared pin of 26 October and the nightly tunnel update job of 27 October, and updated the commit count to 40.