BRAMPP → Changelog

Changelog

What changed, release by release

Written for people deciding whether an upgrade is worth it. Fixes say what was actually broken, not "various improvements" — if a bug cost you an afternoon, you deserve to know it is gone.

August 14, 2026

2.05

The database tools can genuinely write now — but updating a row and dropping a table are no longer the same permission. Alongside that, four ways domain operations could leave the machine inconsistent.

Added

  • Schema discovery: db_tables and db_describe. Tables with row counts and size, columns with types, keys and defaults. The two things an AI client needs most often no longer require writing raw SQL — and checking a column name beats guessing it.
  • SQL writing is split into three levels. Reading is open; INSERT/UPDATE/REPLACE need allow_write; DELETE/DROP/TRUNCATE/ALTER/CREATE additionally need allow_destructive. Updating a row and dropping a table are not the same request and cannot share a switch.

Fixed

  • Turning on allow_write removed every check. Even the multiple-statement rejection lived only in the read-only branch, so allow_write also accepted "DROP DATABASE a; DROP DATABASE b". Multiple statements are now refused at every level, and SQL cannot reach the filesystem under any flag — write permission on a database is not permission to read the disk.
  • A deleted domain's vhost could be written back. The record check sat around the write rather than inside it, and deletion can complete during the configtest wait in between. The result is an orphan vhost pointing at a certificate that no longer exists: configtest fails permanently and Apache will not start at all, taking every site on the machine with it — silently, because the error log was suppressed.
  • Creating a domain saved the record last. The site folder, certificate, vhost and /etc/hosts entry all existed before the record did, and the hosts step can sit on an admin password prompt indefinitely. Quit in that window and the files stayed, invisible to the UI and collected by nothing.
  • Restoring a backup removed a vhost out from under a live share. The tunnel kept carrying the Host header to a block that no longer existed, so the request fell through to the default site — localhost root, phpMyAdmin and Adminer included. A running background app also became unstoppable once its record was gone. Liveness is now checked before removal, and localhost.conf could previously be mistaken for an orphan.
  • nginx.conf was rewritten without protection. A failed backup did not stop the write, there was no nginx -t and no rollback. Worse, pruning dropped the oldest first, so the user's pre-BRAMPP nginx.conf disappeared after six routine operations. The oldest backup is now kept permanently.

Changed

  • Diagnostics reports vhost files with no record — and deliberately does not delete them. Hand-written configuration lives in the same directory; "no record" and "garbage" are not the same thing.
  • The MCP permission boundary is under test. The scope and write requirement of all 25 tools were written by hand with nothing asserting them; flipping one flag would silently expose a destructive tool at a read-only permission level.
August 14, 2026

2.04

BRAMPP could not be installed on the machine it was designed for. This release goes through the setup wizard end to end.

Fixed

  • Setup could never be completed on a clean Mac. brew install httpd ships extra/httpd-ssl.conf but not the server.crt it points at. BRAMPP enabled the SSL include because the file existed, configtest failed with SSLCertificateFile: file does not exist, the write was rolled back, the Apache group never completed — and because that group is required, the Finish button never enabled. The check is no longer whether the file exists but whether every certificate it references is on disk; the include stays off until BRAMPP writes its own SSL config with mkcert certificates.
  • Installs froze in the wizard with an empty console. Packages ran without a PTY, so a question brew asked never reached the console at all — brew does not end a prompt line with a newline, and the buffer holds the trailing segment. The user saw a frozen screen, a progress bar that stopped moving and no way to cancel. Installs now go through the same flow as the rest of the app: live output, the download percentage, an answerable prompt bar and its auto-confirm countdown.
  • mkcert was described as optional and could not be skipped. The package was still marked required, so the packages step could not be passed, and localhost setup refused to proceed without it. A user stuck on the CA — a lost rootCA-key.pem, a declined keychain prompt — could neither finish the wizard nor reach a working HTTP setup. Without mkcert, localhost is now configured over HTTP and SSL can be added later.
  • The wizard's other writer to httpd.conf had no protection. No backup, no configtest, no rollback — and what it writes rearranges every PHP handler block in the file, including ones belonging to vhosts. On a hand-rolled configuration that was unrecoverable. Now: back up, write, validate, roll back on failure.
  • Installing a package without Homebrew reported an opaque command not found; it now says Homebrew is what is missing.
  • A status refresh rewrote PHP-FPM configuration. On launch, under the running process, www.conf was changed from 9000 to 908X with nothing restarted: the file said one port, the process was bound to another, and vhosts proxied to the file's — every PHP site returning 502. For someone who set up brew php by hand this read as "I installed BRAMPP and it broke my working PHP". Reads no longer write, and the correction now happens together with the restart.
  • A vhost was removed even when its open share could not be closed, on disable and on rename. The public address kept serving, now showing the default site — phpMyAdmin and Adminer included. All three call sites are guarded.
August 14, 2026

2.03

pgAdmin4 support is gone, and domain deletion no longer leaves things behind.

Removed

  • pgAdmin4 is no longer installed or managed. Homebrew moved pgAdmin4 from a formula to a cask; BRAMPP still probed the old formula path, so it reported "not installed" and the Remove button — gated on that same check — never appeared, leaving configuration BRAMPP itself had written with no way to clean it up from the UI. Installing a desktop application is not BRAMPP's job. Adminer remains for PostgreSQL (a single file covering both MySQL and PostgreSQL) and always worked. PostgreSQL itself is untouched. Updating does not uninstall pgAdmin4 for you — run brew uninstall --cask pgadmin4 if you want it gone.

Fixed

  • Deleting a domain now refuses to proceed if its open share cannot be closed. The attempt's result used to be discarded and success logged unconditionally. If the tunnel did not actually die, the record and the vhost went anyway — the public address kept serving, now showing the default site, and since the tunnel is keyed by domain name it could no longer be stopped from the UI.
  • Deletion left the domain's log files behind — two to four per domain, every time. All four paths are cleaned now: an nginx domain also gets an Apache companion vhost, and a domain that switched web servers leaves files under the old one.
  • Tunnel log and pid files survived deletion of the domain they belonged to.
  • A failed /etc/hosts removal now warns. With the record already gone, nothing would ever look for that line again and it became permanent in silence.
  • The delete confirmation says what happens. It only said "cannot be undone" — never that your site folder and everything in it stays.
August 14, 2026

2.02

The update check itself was broken, from the menu and from Settings alike. You clicked, nothing happened, and no error appeared anywhere. This release fixes it and writes the boundary into tests so it cannot break the same way again.

Fixed

  • The "Check for Updates" menu item did nothing at all. The item sat in the menu, took your click, and produced not a single line in response. Two menu groups were targeting the same place: one rebuilt the app menu outright, the other anchored itself after the group that no longer existed. That is not defined behaviour — the item is drawn, but its action is never wired. Neither the compiler nor the running app said a word.
  • Underneath that sat a second fault: the Settings button was silent too. Both entry points sent the same internal notification, and nothing was listening for it — so fixing the menu alone would still have produced nothing. The check is now called directly, with nothing in between.
  • Update windows fell behind the main window. You started a check and then stared at a screen where nothing appeared to happen; the window had opened, it just was not visible. It now stays on top.
  • The "Checking…" label in Settings was not waiting for the result, it was counting a fixed delay: on a slow connection it cleared before the check finished, on a fast one it lingered after. It now waits for the real answer.

Changed

  • There is now one place to check for updates: Settings → Updates. The app-menu item is gone. Two doors to the same job ends with a behaviour fixed behind one of them and left stale behind the other.
  • The check answers you now. A "checking" window opens the moment you click, and turns into a green confirmation telling you that you are up to date. Previously nothing appeared at all when there was no new version — which is exactly what made the menu item look broken.
13 August 2026

2.01

The last ten items from the audit backlog, and one trace the 2.0 update left behind. The weight is where it has been all along: the app saying it had done something it had not.

Fixed

  • Updating left its disk image mounted. After moving to 2.0 every launch logged volume is read only: the detach never ran as the app quit, and the next launch tried to delete the mount point. The detach now happens before quitting, and the swap script sweeps any leftover as a fallback.
  • Uninstall claimed to have deleted files it had not — with MariaDB that means believing your data directory is gone while it sits on disk. It also deleted that directory without confirming the server had stopped.
  • The install screen showed a green check for an operation that had been cancelled; the outcome was inferred from any tick in the log body.
  • Two installs could start at once, the second leaving the first with a prompt nobody could answer.
  • PHP-FPM would not start at all with a multi-pool www.conf — every pool's listen was rewritten to the same port.
  • The wizard wrote shared Apache configuration with no way back. It now backs the file up, runs configtest, and restores from the backup if that fails. httpd-ssl.conf is backed up before it is overwritten too.
  • The MariaDB root probe measured the wrong thing: it succeeded whenever a password was found somewhere on the machine, such as in ~/.my.cnf.
  • Changing one field in a database settings panel reset the four nobody had touched. BRAMPP's file is read after the user's, so every line it wrote overrode theirs.
  • The certificate trust check passed unconditionally, counting every certificate on the system without looking for the one it was checking. Expiry was never checked either.
  • Anyone whose mkcert install would not complete could not finish the wizard — HTTPS is optional and the group is now marked as such.
  • Apache could take Nginx's port, so the two could not run together. BRAMPP's scheme is Apache on 80/443 and Nginx on 8080/8443, and running both at once depends on that split. Homebrew's stock httpd-ssl.conf ships Listen 8443, though, and the wizard preserved it as if it were the user's choice. Both services then bind 8443 and whichever starts second fails with Address already in use — all the user sees is "Nginx will not start", while the cause is in Apache. The port written now checks for the clash, while a port that really is a deliberate choice (9443, say) is left alone. Diagnostics reports the clash as its own finding.
  • phpMyAdmin and pgAdmin links did not work on a non-standard HTTPS port.
12 August 2026

2.0

Nineteen more fixes, weighted towards the install and uninstall flows: the app could report having removed something it had not, or installed something it had not.

Added

  • Homebrew packages have their own tab and can be selected and upgraded individually. brew outdated counts every formula on the machine — fifty here, with httpd and php lost between cocoapods and jadx. They are split three ways: what BRAMPP manages, what those depend on, and everything else it does not touch. A running service that gets upgraded is restarted, because an upgrade replaces the binary while the running process keeps the old one.
  • A manual update check answers immediately. The window opens with the click and turns into the result in place. The check at launch stays silent.

Fixed

  • Uninstall claimed to have deleted files it had not. A failed removal passed silently and the script still finished with success — with MariaDB that means believing your data directory is gone while it sits on disk.
  • The data directory was deleted without confirming the server had stopped. Pulling files out from under a running server leaves it inconsistent. The port is now polled for ten seconds and nothing is deleted while it is still listening.
  • The install screen reported success it did not have. The outcome was inferred from any tick in the log body, so a PostgreSQL configuration that aborted still showed a green check.
  • Two installs could start at once, the second overwriting the first and leaving it with a prompt nobody could answer.
  • Turning the profiler off did nothing — PHP-FPM was not restarted, so the running pool stayed in profile mode and kept writing cachegrind files.
  • One Save fired as many concurrent service restarts as there were changed settings, and one that lost the lock could leave PHP-FPM stopped.
  • Certificate expiry was never checked. An expired certificate sat on disk unrenewed, so the browser refused the site while the app considered the certificate present.
  • The diagnostics trust check passed unconditionally — it counted every certificate on the system and never looked for the one it was checking.
  • An undefined directive in the php.ini panel showed its default as though it came from the file, and switching PHP version carried the previous version's values across.
  • An extension registered in php.ini read as disabled; enabling it added a second registration and PHP loaded it twice.
12 August 2026

1.9

Eighteen fixes, most of one kind: the app reported that something had happened when it had not — or never mentioned something that did.

Fixed

  • Closing the window with the menu bar icon turned off made the app unreachable. No window, no Dock icon, no menu bar item, nothing in Cmd-Tab — the only way back was to relaunch.
  • Stop services and quit, from the Dock menu, left Node, Python and .NET apps running. They are started with nohup, so they outlive BRAMPP and return as a port already in use.
  • A deleted domain's vhost could be written back. The delete path has four waiting points and one of them can ask for an administrator password; an in-flight edit would restore the vhost during that window.
  • Stopping a share said the address was dead even when it was not. When the process survives the signals the site stays on the internet, while the assistant reported it closed.
  • Application logs vanished past 5 MB. Rotation gave the file a new identity while the app kept writing to the old one, so the window showed nothing more for that run.
  • A single malformed byte hid the whole log — the window stopped updating and the log looked like it had gone quiet.
  • The same log lines appeared twice.
  • A disabled domain showed a green running dot whenever the web server was up, though its vhost had been removed and the site was not being served.
  • Restoring a database could double rows on MySQL. When the first attempt failed the dump was applied again from the start, replaying inserts that the failed attempt had already committed.
  • Only two days of the on-disk log history were searched though seven are kept: "what happened four days ago" came back empty.
  • A new domain welded itself onto the last line of /etc/hosts when the file did not end in a newline — losing the new name and corrupting the line it landed on.
  • When the Apache configuration was already broken, freshly written companion files were deleted too — which fixed nothing and lost them as well.
  • A console line claiming the MCP server had stopped appeared on every refresh while the port was held by another process.
  • Two copies of BRAMPP writing the same log file overwrote each other's lines.
  • A space in the executable path shifted the columns in process information, showing part of the command name as the CPU figure.
  • Cmd-N with the window hidden queued a window nobody could see.
  • Stopping a share did not lowercase the domain, so a share opened with capitals could not be closed with lowercase.
  • The help text listed two tools under the wrong permission area.
12 August 2026

1.8

Updating now finishes inside the app: one button, the app quits and reopens on the new version. No opening the image and dragging — and no administrator password, ever.

Added

  • Update and restart. The new version is copied next to the target while the app is still running, so after quitting only two same-volume renames remain — the window in which no application exists lasts milliseconds rather than seconds. The old bundle is not deleted but moved aside: it is removed once the new version has launched and stayed alive, and put back if it has not. Your Homebrew services keep running — you are updating the app, not shutting down your environment. If the target folder is not writable the button is not offered at all and dragging stays the primary action; teaching people that BRAMPP asks for their password to update itself is teaching them exactly what an attacker wants.
  • Repair configuration, in Diagnostics — shown only when there is something to repair. It fixes the old Alias order in the phpMyAdmin/Adminer configuration: the file is backed up as .brampp.bak, configtest is run, and the backup is restored if it fails.

Changed

  • The shared address arrives sooner. The wait polled the system resolver for a full 45 seconds. Measured on a live tunnel: dig @1.1.1.1 resolved the name in 0.02s while the system resolver could not resolve it at all. Going by dig alone would be wrong — browsers use getaddrinfo, and calling it ready on the edge's answer hands over a link that will not open. Both run together: dig only establishes that the name exists at the edge, and the moment it does, the system resolver gets six seconds instead of forty-five before the address is handed over with the warning it already carried.

Fixed

  • The update window opened behind the main window. Activating the app brings its own main window forward, leaving the notice that had just been ordered front underneath it — so the window opened and the user never saw it.
12 August 2026

1.7

Four fixes, all of one kind: the app told you something had happened when it had not. The worst was in sharing — deleting a site did not kill its public address, it repointed it at a different site.

Fixed

  • Deleting, renaming or disabling a shared domain left the tunnel running — and the address began serving a different site. The tunnel connects to 127.0.0.1 and carries the domain in the Host header, so once the vhost was gone the request matched no server block and fell through to the default one. A random trycloudflare.com address then published ~/Sites/localhost/phpmyadmin and /adminer included — while you believed you had shut it down. Apache's Require local did not help either, because the request arrives from loopback. Renaming was worse still: the tunnel record kept the old name, so the badge read "off" and there was no way left to stop a running tunnel. All three paths that touch a vhost now close the share first and say in the console why.
  • Restoring a database through the assistant reported a completely failed job as imported. The PostgreSQL path passed no ON_ERROR_STOP, so psql exited zero even when every statement in the dump failed, and the error text was swallowed on the way. The same work has been done correctly in the interface for a long time — the MCP path had fallen behind. It now runs with --single-transaction as well: a restore that breaks midway rolls back entirely, and the retry no longer writes over a half-applied database.
  • Clearing the application log lost that run's log entirely. The file was deleted, but the wrapper's redirection is set up once when the process starts: no new file took its place and the app kept writing into one nothing could open. The log window stayed empty until you restarted the app. The file is now emptied rather than removed.
  • A disabled PHP extension looked "not installed" and could not be switched back on. Installed state was read only from php -m, and disabling drops the extension from that list — so the row fell back to an Install button. Pressing it failed with "already installed", because the PEAR registration was still there. Short of renaming the file by hand in Finder, there was no way back.
12 August 2026

1.6

The state BRAMPP keeps belongs to the machine, not to a process — and until this release a second copy of the app, or a test run, would walk all over it. Also: BRAMPP now tells you when there is a new version, and verifies the download five ways before it opens.

Fixed

  • Running the unit tests — or simply having a second copy of BRAMPP open — tore down the live environment. Cloudflare tunnels were killed, their pid and log files deleted, the MCP port was bound on top of the copy already holding it, Homebrew services were started, and the "last running services" list was overwritten. The machine-wide state BRAMPP keeps belongs to the machine, not to a process; only the primary instance touches it now (Core/ProcessRole.swift). Console lines also carry which process wrote them, because the installed app, an Xcode build, the test host and previews all append to one file — that is what made this so hard to see.
  • A share kept being reported as live after its cloudflared had died. The Services row stayed green, the MCP list_shares tool still handed out the address, and Cloudflare answered Error 1033. Tunnels are now checked against reality on the refresh loop that was already running.
  • Quitting a second copy of BRAMPP killed the first copy's live tunnels. A quitting process now only stops the tunnels it started itself.
  • "Stop sharing" reported success while the site stayed public. There was no SIGKILL escalation and the wait was 0.4 s against cloudflared's 30-second grace period.
  • Restoring a database reported success when it had failed. The script ran if psql …; then exit 0; fi and read rc=$? afterwards — in POSIX an if whose condition fails and which has no else exits 0, so the failure code was never seen. Both PostgreSQL and MariaDB.
  • Uninstalling MariaDB, PostgreSQL or Python said only that "the package and its config files will be removed" — while the script deleted the data directory: every database, or the whole site-packages tree. The confirmation now names the concrete paths, separates configuration from data, says what dies, and asks you to type the service name.
  • The generated phpMyAdmin and Adminer Apache configs made apachectl configtest warn AH00671. The two Alias directives were in the wrong order, so the second could never match.
  • Three console lines printed their raw translation key instead of text.

Added

  • An update notice on launch — the installed version, the new version, the release notes as a scrollable list, and the choice to skip that version or be reminded later.
  • A dedicated Updates tab in Settings: channel, check now, automatic checking, automatic download, and what to do when one is found.
  • Downloads are verified before anything opens — the checksum published in the update manifest, that the file really came from the expected release asset (re-checked after redirects), the code signature, that the Team Identifier matches the running app's own, and Gatekeeper's notarization verdict. Any failure deletes the download. BRAMPP never replaces itself: the verified disk image is handed to you.
  • The manifest's blockedVersions and minimumOS are honoured, so a release that turns out to be harmful can warn the people already running it.
  • A button on the cloudflared row in Services that stops every open share at once, next to the menu bar command that already did it.

Changed

  • Update checking reads the published manifest for the version and the checksum, and GitHub only for the release notes — so the host that serves the file is not also the host that vouches for it. When the manifest lags behind, the check falls back to GitHub and offers the release page instead of an unverifiable download.
  • The release script publishes that manifest itself, with the real checksum, and only for a build that was actually notarized.
  • The unused project framework detector was removed.
12 August 2026

1.5

Tagged but never packaged: no DMG was published for this number and its contents shipped with 1.6. The tag earns its place anyway — the v1.4 tag does not build a 1.4.

Changed

  • Release builds use -Osize instead of -O. An end-to-end Release build went from 35 minutes to 46 seconds. Type checking was not the cause — the whole module type-checks in 3.3 seconds — and compiler statistics put 97.8% of the work in SIL optimization. Measured on the same module: -O takes 1615 seconds, -Osize takes 54, and the object is smaller, 12.5 MB against 13.3.
  • Settings now says that a permission change needs the client to reconnect. The MCP section counts enabled tools live, so raising the sharing permission to write immediately reads "23 tools enabled" — while the connected client is still working from the 20 it fetched when it connected. The server is stateless HTTP with no event stream, so there is no channel to push notifications/tools/list_changed over, and initialize correctly does not claim a capability it cannot honour.
  • The brampp.yml project manifest was removed. It was built so a domain's settings could travel with the repository, and it turned out not to be wanted. Removing it rather than leaving it unused follows the same rule: a menu item that writes a file nobody reads is worse than no menu item.

Fixed

  • Building from the v1.4 tag produced an app calling itself 1.3. MARKETING_VERSION stayed at 1.3 there and the released binary got its number from an uncommitted command-line override. The number now lives in the project file, where a build can find it.
10 August 2026

1.4

Sharing gets the two fixes it needed to be dependable, and the checks the app already ran internally — port ownership, config syntax, the mkcert trust store — become something you can ask for.

Added

  • Services can be installed from the assistant tooinstall_service, limited to BRAMPP's own catalogue so an arbitrary formula name is refused.
  • Project actions on the domain row — open the folder in an installed editor, open a terminal there, run composer or npm. Nothing appears unless it applies: no composer.json means no composer entry, and npm scripts are read from package.json. A menu item that errors on click is worse than no menu item. Editors are found by their .app bundle rather than their command, because a perfectly normal install can leave the CLI off PATH.
  • Mailpit, as a real brew service — unlike cloudflared it is a proper daemon. Mail sent to 127.0.0.1:1025 stays on the machine, and there is a button to open the mailbox once it is running.
  • An environment diagnostics panel. The port-ownership, config-syntax and mkcert trust-store checks now run together, on demand, from the Services tab, rather than surfacing as log lines after something has already failed. A port finding names the process and PID holding it rather than saying "in use", because which one it is decides what you do next.
  • A brampp.yml project manifest, carrying a domain's settings in the project folder so cloning the repo on another Mac sets the environment up the same way, alongside framework detection that names what it finds in a project. (Both removed in 1.5 and 1.6.)
  • Static update manifests for all three platforms were published. The ones without a release carry a null version, so a client can tell "nothing released" apart from "could not check".

Changed

  • A shared site no longer pays for name resolution on every request. The tunnel used to point at the domain, which on a .local name goes to Multicast DNS and times out after five seconds before falling back to the hosts file — measured on a real site: 5.01s with the name, 0.012s straight to the address. It now connects to 127.0.0.1 and carries the domain in both the Host header and the TLS server name, so the vhost and the certificate still match. Sending only the header returns 421.
  • The address is not handed to you until it resolves. It was reported the moment it appeared in cloudflared's log, while the DNS record was not yet visible to the machine — open it then and you get ERR_NAME_NOT_RESOLVED. The wait now goes through getaddrinfo, the same path the browser takes. dig was the wrong check: it queries the server in resolv.conf directly and can succeed while the system resolver still fails. Measured against a fresh tunnel name: Google DNS answered 1 of 6 attempts, Cloudflare's own resolver 6 of 6. If the wait runs out the address is still returned, with a console line naming the cause.

Fixed

  • cloudflared reported as not installed while sitting in /opt/homebrew/bin. Install detection for runtime services switches on category, and the sharing category fell through to the default branch that returns nothing — so it could never report as present, whatever was on disk.
  • A real Apache warning was reported as all-clear. Apache tags warnings by module — [alias:warn] — with the word "warning" never appearing, so a check matching only on that word missed a genuine warning on this machine.
10 August 2026

1.3

One click puts a running site on a temporary public address, and the console finally keeps a history you can read after a restart.

Added

  • Share a running site over a temporary public address. One click opens a Cloudflare Quick Tunnel and hands back an https://…trycloudflare.com link — for showing a client, or opening the site on a phone. No Cloudflare account. Nothing is persisted, no tunnel starts on its own, and every one closes when BRAMPP quits.
  • Share a bare port too — a dev server on :5173 that has no BRAMPP domain yet. Database and cache ports are refused: a Quick Tunnel carries HTTP only, and those services usually sit here without a password.
  • A console log that survives a restart. Lines are written to a daily file under ~/Library/Application Support/BRAMPP/logs and pruned after a week. The in-memory buffer holds 300 lines and a single brew install can sweep it, so "what happened ten minutes ago" was previously unanswerable.
  • Live status for Redis — uptime, connected clients, memory against peak, total commands, the keyspace hit rate and the key count per database, read straight from INFO.
  • Three more MCP tools for sharing, behind a fifth permission scope that defaults to no access — the only scope that does, because these tools put a site on the open internet.
  • read_log filters by level, text and time, and can read the file on disk instead of the live buffer.
  • A PHP profiler panel driving Xdebug's profile mode: settings written for you, cachegrind output collected in one folder and listed. Only requests carrying XDEBUG_TRIGGER are measured unless you ask for all of them, because profiling everything fills the disk within minutes.
  • cloudflared appears under Services — as a runtime rather than a brew service, since it has no daemon and lives only while a share is open. Its row lists which sites are public right now.

Changed

  • Examples now suggest .test instead of .local. .local is reserved for Multicast DNS by RFC 6762 and can stall resolution on macOS. The local-HTTPS guide had said so for a while; the app was still handing out .local. Existing domains keep working — validation never depended on the suffix.
  • Log windows stream instead of polling. They used to re-run tail every five seconds, spawning a shell and re-reading the whole file for a few new lines. A file watcher now reads only what was appended, and survives the log being recreated when the supervisor restarts an app.
  • The domain row says for itself whether the site is on the internet — a green antenna when it is public, a red struck-through one when it is not. The share sheet also gained a Close button: leaving the window used to mean killing the tunnel, while the common case is wanting the address to stay up.

Fixed

  • Eight alerts appeared in Turkish regardless of the interface language — invalid domain name, reserved name, duplicate name, missing .NET SDK, and all four outcomes of the health check. They were Swift string literals that never reached the translation catalog.
  • Sharing a site whose backend was down produced a live address with nothing behind it. The visitor got a 502 and the person who sent the link only found out afterwards. Sharing is now refused unless the domain is enabled, its web server is up, and — for Node.js, Python and .NET — the app is listening.
8 August 2026

1.2

Signed with a Developer ID certificate and notarized by Apple, with the ticket stapled to both the app and the DMG so it verifies offline.

Added

  • Database backup and restore over MCPdb_export and db_import. MariaDB/MySQL uses mysqldump --single-transaction --routines --triggers, a consistent snapshot that does not lock InnoDB tables. A failed export deletes its partial file, because a corrupt backup is worse than none.
  • Update check — Settings asks GitHub for the latest release on request. It never self-updates and the download stays in your browser.
  • A second skill file, brampp_mysql, covering the database tools and their safety rules.

Fixed

  • Nginx was never detected as running. Its master process rewrites its own title to nginx: master process …, so a pgrep -x nginx check could never match. This was also why a newly added domain seemed to need a stop and start before it appeared.
  • The installer could hang forever. Homebrew's confirmation prompt ends in a carriage return, and the code that flushed the buffer discarded the prompt along with it — so the countdown waited for something it had already thrown away.
  • "Open Main Window" did nothing from the menu bar, because the panel holds key-window status at click time and only gives it up after the action returns.
  • The web server joined the dependency chain. Starting an ASP.NET Core, Node or Python app behind Apache or Nginx now starts that server too, and a dependency that is not installed is reported rather than silently skipped.
31 July 2026

1.1 — first public release

Service control, local domains with real HTTPS, database tools and an MCP server, in one native app on top of the Homebrew services already on the machine.

  • Apache, Nginx, PHP-FPM 8.1–8.5, MariaDB, PostgreSQL and Redis, started and stopped with port-ownership checks rather than a naive process match.
  • Local domains for PHP, Node.js, Python, ASP.NET Core and static sites — vhost, hosts entry, site folder, starter project and certificate in one step.
  • Automatic HTTPS through mkcert, with the local certificate authority installed into the system trust store.
  • A built-in MCP server, bound to loopback and off by default, with per-area permissions.
GitHubAll releases and DMG downloads → NextInstall BRAMPP →