Local Test

Works on my machine. On purpose.

A desktop app that serves any folder on your computer at a real http://localhost address, in one click, with no terminal and no account. When the folder is software rather than a site, it runs that instead, and kills the whole process tree when you stop it.

free · no account · macOS · Windows · no telemetry

Where to start

If you have never run a server

You do not need to learn anything first

Local Test is a window with a list of folders and a Deploy button. You pick a folder, you press it, and a browser opens on your work. There is no terminal, no config file, no signup, and nothing to break. If you do not have a folder yet, the app can make you one that works.

If python -m http.server is muscle memory

Here is what it actually does

A Node http server bound to 127.0.0.1, with a Host allowlist, a symlink-resolved path jail, a dotfile and key-material deny list, Range support, and SSE live reload. Command projects run in their own process group. Nothing is reported anywhere.

Interactive, in your browser

Rehearse the deploy before you download

A hand-built replica of the Local Test window: not a video, not a screenshot, and not the real app. Press Deploy, change the port, open the Activity tab. It responds the way the app does on your machine, including the request log.

Local Test local pages

Projects (1)

  • portfolio :8001

v1.9.1

portfolio

LIVE

~/Sites/portfolio

Local deployment

Your site is live at

Live at http://localhost:8001/, the Activity tab has the request log.

A replica. Nothing is served, and no button here reaches your machine.

What it does

Serves a folder

Deploy starts a static server that follows the rules a static host follows: index.html at directory roots, your own 404.html, a redirect when a directory URL is missing its slash, a clean listing when a folder has no index page, correct content types, and Range requests so video and audio seek properly.

Live reload is on by default, save a file and the open page refreshes itself. Per project you can turn listings off, switch on SPA fallback for client-side routers, or name the folder to serve; left on auto, Deploy serves the project root, or finds the built site in _site, dist, build, public, or out when the root has no index page. A port that is already taken gives a plain error and a one-click move to the next free one.

Runs software, too

When the folder is a program rather than a site, Deploy becomes Run. Adding a folder with a package.json start or dev script, a Cargo.toml, a go.mod, or a Python entry point prefills the command; you can write any command you like instead.

Its output streams into the Activity feed line by line, a KEY=value block sets environment variables for the run, and Stop kills the whole process tree rather than leaving children behind.

Files, and an editor

Every project opens on four tabs, Overview, Files, Activity, Settings. Files browses the project and renders its README the way a code host does, and you can create, rename, and delete files there (deletes go to the Trash).

Any text file opens in an editor with edit, split, and preview modes: a Markdown toolbar, Cmd/Ctrl+B, I and K, find and replace, lists that continue themselves, word and reading-time counts, and an auto-save switch that pairs with live reload so the browser keeps up as you type. Inserting an image copies it into the project for you. Cmd/Ctrl+F searches every text file in the project and drops you into the editor at the line you pick.

Git and GitHub, one button deep

The project’s Settings tab shows the branch, the changed files, ahead and behind counts, and the last commit. One button commits everything with your message; another pushes, using the git credentials you already have.

Link a project to an owner/name repository and Sync now clones it or fast-forward-pulls it, optionally before every deploy. App settings can add a repository straight from GitHub into a clone folder you choose.

A window to work in

Cmd/Ctrl+K opens a command palette over everything, and a cheat sheet lists the rest of the shortcuts. Drag the sidebar to reorder projects, and search it when the list gets long. Seven starter templates scaffold something into an empty folder, six of them deployable and one a Minecraft plugin for IntelliJ.

The menu-bar icon starts and stops projects without opening the window; on macOS the Dock badge counts what is live. Preview opens the running site at phone, tablet, or desktop size. Activity logs every request with the time it took beside a running uptime, and exports to a file. Your project list and settings export and import, for moving to another machine.

Thirteen themes and a font size. Open a project in any of sixteen editors, the whole JetBrains family included, or point it at one of your own. The button carries a menu for switching, and a project can have its own.

Local by default

Servers bind to 127.0.0.1, so a deployed site is visible to your machine and nothing else, until you turn on LAN sharing for the day you want your phone to see it. Every request is jailed to the project folder with symlinks resolved, so a cloned repository can’t serve something from elsewhere on your disk.

There is no account, no analytics, and no telemetry, nothing about you or your projects is collected. What does use the network is short and deliberate: the update check on launch, which asks whether a newer version exists (switchable off), the download when you choose to install an update, and git clone, pull, and push when you press those buttons.

Arrived in v1.34.0

It sets up Minecraft servers, too

The same window and the same Stop button. Choose a flavour, a Minecraft version, a folder, and how much memory the server may have. Local Test fetches or builds the server software, writes the two files that have to sit beside it, and starts it on the Java that version actually needs. Java Edition, on your own machine, hosted nowhere, with no panel to rent and nobody to pay monthly.

Five flavours. Two of them are a download, and two of them are compiled on your own machine.
Flavour How it arrives Checked against Plugins
PaperDownload from PaperMCSHA-256Yes
PurpurDownload from PurpurMCMD-5Yes
VanillaDownload from MojangSHA-1No
SpigotCompiled on your machineNone publishedYes
CraftBukkitCompiled on your machineNone publishedYes

Paper is the sensible default: it runs Spigot and Bukkit plugins unchanged, it is faster once there are players in the world, and it is a download, so it is ready in the time it takes to fetch a file.

Why two of them take twenty minutes

Spigot and CraftBukkit may not lawfully be handed out as files, by this app or by anybody: they contain decompiled Mojang code. What SpigotMC publish instead is BuildTools, a jar that fetches the pieces and compiles the server where it will be used, which is the only lawful route to one. So picking either of those downloads a compiler rather than a server, and on an ordinary machine that is five to twenty minutes and needs git installed.

Local Test says that before it starts, and puts the compiler’s own output in the Activity tab while it runs. Twenty silent minutes looks exactly like an app that has hung, and the reasonable thing to do to an app that has hung throws the build away.

What is checked before it runs

Every address is checked against a list of hosts compiled into the app before a request is made, so a manifest fetched from the internet cannot point the download somewhere else. The hosts are mojang.com for the vanilla jar and the version list, papermc.io for Paper, and hub.spigotmc.org for BuildTools. Where the publisher publishes a checksum, the bytes are checked against it: a SHA‑1 from Mojang, a SHA‑256 per build from PaperMC.

BuildTools is the one download with nothing to check it against, and the app says so rather than implying a verification that is not happening: SpigotMC publish no checksum for BuildTools, so this download is protected by HTTPS and the pinned host and nothing further.

The EULA is yours to accept

Mojang require their agreement to be accepted before any server will start, and that agreement is between you and Mojang. It is not this app’s to sign. Local Test writes the eula.txt that has to sit beside the server with the line set to eula=false, and it will not write true on your behalf however much more convenient that would be.

It asks once, with Mojang’s own address in the question, aka.ms/MinecraftEULA, and then writes what you chose. Saying no is a correct outcome, not a fault.

The address goes into Minecraft, not a browser

This is the one part of Local Test that is not HTTP. A Minecraft server speaks its own protocol over TCP, so there is no page to open and no link to click, and the app does not pretend otherwise. What it shows you is what you paste into Minecraft’s own Add Server box: localhost from this machine, or the machine’s address on your network for everybody else in the house.

Port 25565 is the default, so the port can be left off when adding the server in Minecraft. Any other port has to be typed on the end, and the app says which one it is.

Three more things it does, because each one is a bad afternoon otherwise. It reads which Java the version needs from Mojang’s own manifest and checks the machine before the download, rather than letting you meet a stack trace about class file versions. It refuses to install on top of a world that is already there. And it suggests half the machine’s memory, never less than 1 GB and never more than 8 GB, because most of the machine is the wrong answer when the operating system still needs some.

For the sceptical

What is actually running

Deploy starts a server in Local Test’s main process. It is Node’s own http module, about 600 lines, with no framework underneath it and no runtime dependencies in the app at all. This is the behaviour you get, stated precisely enough to argue with.

The static server

Resolution order
index.html then index.htm at any directory root. A directory URL without a trailing slash gets a 301 to the slashed form, each segment re-encoded. With no index page, either a generated listing or your own 404.html, depending on the per-project listings switch.
Serve root
The project root, unless you name a subfolder. Left on auto, when the root has no index page it looks for one in _site, dist, build, public, out, in that order. It does not run your build.
Methods
GET and HEAD. Anything else is 405 with an Allow header. There is no upload path, because there is no other verb.
Range requests
Full bytes= support including suffix ranges, answering 206 with Content-Range, or 416 when the start is past the end. Video and audio scrub correctly.
Response headers
Every file and error response carries Cache-Control: no-cache and X-Content-Type-Options: nosniff; redirects and the reload stream carry the first of those. Files add Content-Length, Last-Modified, and Accept-Ranges. Content types come from a fixed table; an unknown extension is application/octet-stream, never guessed from the bytes.
Live reload
A recursive fs.watch on the project, debounced 150 ms, broadcasting over Server-Sent Events at /__localtest/reload. The client is one EventSource injected before </body> of HTML responses only, so your files on disk are never touched. Changes under .git, node_modules, and .DS_Store are ignored. Where recursive watching is unavailable, serving still works and only the reload is missing.
SPA fallback
Off by default. On, a miss with no file extension serves index.html at 200, so client-side routers get their deep links. Requests that do have an extension still 404, so a missing asset stays a missing asset.
Resource ceilings
256 concurrent connections, 24 live-reload subscribers, a 20 s headers timeout and a 120 s request timeout. Stop severs open sockets so the port is free immediately rather than after keep-alive drains.

Security posture

Binding
127.0.0.1. Another machine on your network cannot reach a deployed site at all, because nothing is listening on an address it can route to. LAN sharing is a separate, off-by-default setting that binds 0.0.0.0 instead, and turns directory listings off unless you explicitly ask for them.
Host header allowlist
A request whose Host is not a name this server is genuinely reachable under gets 403. Without that, any website you visit could point a domain it controls at 127.0.0.1 and read your projects out of your own browser. This is DNS rebinding, and it is the class of bug most local dev servers still have.
Path jail
Every read passes one function. It rejects paths that leave the root lexically, then realpaths the target and confirms containment again, so a symlink cannot walk out. It opens the file once and re-asserts containment on what the handle points at, which closes the window between the check and the read.
Jailed fallbacks
The directory index, the custom 404.html, the SPA shell, and a named serve folder all go through the same function. A symlinked 404.html was a real hole; it is now a test.
Deny list
Dotfiles at any depth, which covers .env and .git, plus node_modules and obvious key material (id_rsa, *.pem, *.key, *.p12, *.pfx). .well-known is the single exception. A denied path answers 404, not 403, so the refusal does not confirm the file exists.
Update channel
HTTPS only, except loopback for the test harness. A redirect may not drop TLS or change host. The published SHA-256 is required and verified before anything is installed: no checksum means no install, rather than a silent fall-through.
Provenance
Each release publishes the installers, a source archive, and SHA256SUMS.txt. Because the builds are unsigned, that checksum is the thing worth checking. shasum -a 256 -c does it in one command on macOS and Linux; Windows has no shasum, so the check there is Get-FileHash, and the download section prints the exact line for each.
On the wire
No account, no analytics, no telemetry, no crash reporting, no first-run ping. Three things reach the network and all three are yours: the update check (switchable off), an update you chose to install, and git clone, pull, and push when you press those buttons.

Running software

Launch
Your command runs through the platform shell with the project folder as its working directory, inheriting the environment plus the project’s own KEY=value block. Adding a folder with a package.json start or dev script, a Cargo.toml, a go.mod, or a Python entry point prefills a command you are free to replace.
Process tree
On macOS and Linux the child gets its own process group, and Stop signals the group, not the shell: SIGTERM first, then SIGKILL three seconds later if it is still there. On Windows it is taskkill /T /F. This is why a stopped project does not leave a dev server holding the port.
Output
stdout and stderr are read line by line into the Activity feed with ANSI escapes stripped, lines capped at 500 characters and an unterminated buffer flushed past 4 KB, so a program that never emits a newline cannot grow the window without limit.
Exit
An unexpected exit is reported with its code or signal and the project returns to stopped. A crash on your side is never a crash on the app’s side.

How it is verified

Test suites
More than 10,650 checks across sixty-one plain-Node suites, with no test framework underneath them: the static server, the process runner, the update engine, the Markdown engine, theme contrast, the organizer, the templates, GitHub sync, the Minecraft setup, the editor and its highlighting, the secrets vault, and the assistant. They run without Electron, so the server engine can be exercised on its own. One more boots the real app windowless and drives it end to end.
Security regressions
The traversal, symlink-escape, symlinked-index, symlinked-404, dotfile and DNS-rebinding cases are each a named assertion. They exist because an external audit found them live in 1.8.0, which is what 1.9.0 fixed.
End to end
A second suite boots the real app windowless and runs a deploy, a software run, a GitHub clone, a Markdown edit and save, a project search, and auto-save.
Contrast
Every one of the ten in-app themes is asserted against WCAG AA contrast by a test, so a theme cannot ship unreadable.
Dependencies
None at runtime. The whole shipped app is Electron plus first-party code, so the supply chain you inherit is Electron’s and nothing else.

Longer form, setting by setting, in the server reference and privacy and network sections of the docs. Where this page and the app disagree, the app is right and this page is a bug.

How it works

  1. 1

    Add a folder

    Any folder, a class project, a portfolio, a prototype. Deploying it only ever reads; nothing is written unless you edit a file yourself.

  2. 2

    Deploy

    Or Run, if the folder is software. A port that is already taken gives a plain error and a one-click move to the next free one.

  3. 3

    Open it

    Your browser opens at http://localhost:8000. Edit the files; live reload refreshes the page as you save.

What it is actually like

Seven of the screens you will spend time in. Click any one to look closer.

The Files tabBrowse the folder without leaving the app. Open a file, rename it, make a new one, copy its path. Dotfiles and node_modules are hidden, because they are noise here and refused over the network anyway.
The code editorEighteen languages highlighted by a tokenizer written for this app, so a hash in a Python string is not a comment. Line numbers, indenting that knows the language, brackets that bring their partner, and JSON, YAML, JavaScript, CSS and HTML checked for errors as you type.
The Markdown editorMarkdown side by side with its preview, and a toolbar for the marks nobody remembers. Plain text files open the same way. Save writes straight to disk, and the running site reloads on its own.
The Git panelHistory, the diff of a file before you commit it, tick boxes so one stray edit does not ride along, discard one file, put a file back as it was at an earlier commit, branches. It also checks every time for things that should never be committed, and says plainly when one is already in the history, because a .gitignore cannot help then.
When a run failsNot "exited with code 1". What happened, why, and a button that fixes it. Dependencies that were never installed are noticed before you press Run rather than after, with the package manager taken from the lockfile that is actually in the folder.
The activity logEvery request the server answered, with the status it answered with. Filter to the 404s when a page is not loading, or to Problems when you just want what went wrong.
The repository browserEvery git repository already on your machine, found in one scan, with its branch and how it would run. Tick the ones you want. Nothing is copied: a project is a bookmark to a folder.

Thirteen themes

The same screen, ten times, because the only thing that changes between these is the colour and the typeface. All ten ship with the app, alongside a font size, and every one has been checked against WCAG AA contrast by a test rather than by eye. Two of them are pocket consoles. Click any one to look closer.

Defaultfollows your system
Pocket Greenhandheld LCD
Berry Pockettranslucent purple
Midnight Harbordeep navy
Neon Driftcyan on violet
Amber TerminalCRT amber
Rose Quartzpastel pink
Matcha Creamsoft green
Glaciericy blue
Paper & Inkwarm serif
Block Worldearth and grass, square corners
Reef Brightwarm yellow and turquoise
High Contrastbuilt to clear AAA

Default follows whatever your system is set to, which is why its capture is dark here.

Why not just…

Against python -m http.server, npx serve and friends, and against pushing to a hosted static site. The bottom group is where Local Test is the wrong tool, which is the half of a comparison table that is usually missing.

Local Test compared with the two usual ways of previewing a folder.
TaskLocal TestTerminal serversA hosted static site
Where it is the better option
Getting a site upone clickflags and portspush to a repo first
Several projects at oncea list of portsa terminal tab eacha deploy each
Who can reach itthis machine onlyoften 0.0.0.0the whole internet
DNS rebinding defenceHost allowlistusually nonenot applicable
Symlink escapejailed, with testsvaries by toolnot applicable
Dotfiles and .envrefused as 404often servedusually excluded
Live reloadbuilt inextra toolingno
Runs your softwarebuilt inseparate toolsno
Stopping cleanlygroup, then SIGKILLorphans possiblenot applicable
Show it on your phoneone switchIP jugglingpublic URL
Where it is not
Running your buildserves output onlynoyes, on push
HTTPS and certificatesnowith mkcertyes, automatic
A URL to send a clientnowith a tunnelyes
Scripting and CIno, it is a GUIyesyes
Proxying an APInosome toolsredirects or functions
Signed binaryno, checksums insteadvia your package managernot applicable
Linuxuntestedyesnot applicable

All three are good tools, and two of them are free in a way this one only partly is. Local Test is for the moments you want the first column, and a terminal is still the right answer for the last seven rows.

What it deliberately isn’t

The first three are what people are usually asking when they ask what this is, and the answer to all three is no. Most of the rest is scope that was decided against. The last two are simply not done yet.

Questions people actually ask

What is “localhost”, actually?

It’s the address your computer uses to talk to itself. http://localhost:8000 means “this machine, channel 8000.” It looks and behaves like a real website in your browser, because it is one, it just lives entirely on your computer.

Does anything leave my computer?

Your files don’t. A deployed project is served by a server bound to 127.0.0.1, which only your own machine can reach, and there is no account, no analytics, and no telemetry, nothing about you or your projects is collected.

Three things do use the network, and all three are things you asked for: on launch the app asks whether a newer version exists (a switch in Settings turns that off); choosing Install downloads that release; and git clone, pull, and push run when you press those buttons. The LAN switch is the only setting that widens who can see a deployed site, and only to devices on your own Wi‑Fi.

Do I need to know how to code?

No. If you have a folder with an index.html in it (from a class, a template, an export) you can use Local Test. If you don’t have one, Cmd/Ctrl+N builds a starter project (blank page, landing page, docs site, single-page app, Node server, portfolio, Spigot plugin). The first six deploy immediately. The last one is different: it is a Minecraft plugin you build in IntelliJ, so there is nothing in it to deploy. There’s a tutorial built into the app that assumes nothing.

Does it work on Windows?

The code is written for it: Windows paths throughout, an NSIS installer and uninstaller, process-tree kill through taskkill /T /F, and the Windows shell behind the editor and terminal buttons. The installer is built and published, and it is on the download button above.

What has not happened is a validation pass on real Windows hardware. Development and testing so far have been on macOS, so treat the Windows build as supported and unproven, and a bug report from a Windows machine is the single most useful thing anyone could send. One known quirk: turning on LAN sharing may raise the Windows firewall prompt on the first deploy, which is expected.

Will it change the files in my folder?

Serving only reads. Everything that writes is something you pressed: saving in the editor (or leaving auto-save on), inserting an image, which copies the file into the project, renaming a file, deleting one, which moves it to the Trash, scaffolding a template into an empty folder, and git commit, push, or a sync that pulls, including the pull-before-deploy setting, if you turned that on for a linked repository.

Does it update itself?

It checks. On launch it reads the public releases feed and stays quiet unless something newer exists; you can turn that check off in Settings, or run it by hand from the version badge. If you choose to install, it downloads the release and (on macOS) swaps the app in place and reopens it; on Windows it hands off to the installer. Because the app isn’t code-signed, that swap happens visibly on relaunch rather than silently in the background.

Why won’t macOS open the download?

Because it isn’t signed with an Apple Developer ID. That’s a paid yearly membership, and this is a free project, so the first launch of a downloaded copy is right-click (or Control-click) → Open, then Open again in the dialog. Once, and it opens normally after that. A copy you build from source never shows it at all.

My site has a build step, can it still serve it?

Local Test serves files exactly as they are; it doesn’t run your build. Run it the way you normally do, and Deploy will find the result: when the project root has no index page, it serves _site, dist, build, public, or out, whichever has one. You can also name the folder yourself in the project’s Settings tab.

Can I look at it on my phone?

Yes, with a deliberate switch. LAN sharing is off by default; turning it on in Settings binds new deploys to your network interface as well, and the project shows a second address like http://192.168.1.20:8000 that devices on the same Wi‑Fi can open. If you only want to check how it looks at that size, the Preview buttons open the live site in a phone-, tablet-, or desktop-sized window without any of that.

What does it cost?

Nothing. There is no trial, no account, no card, and no telemetry, and every feature described on this page is in the download. There is no limit on how many projects you keep, either: there was one, and it is off. What it costs says the whole of it in six lines.

How is this different from python -m http.server?

For one folder, once, it mostly is not, and if that command is already in your shell history it is a fine answer. The differences show up in three places.

Defaults. Python’s server has no Host check, so any web page you have open can point a domain it controls at your port and read the directory. It serves dotfiles, so a .env or a .git/config in the folder is a URL away. It follows symlinks out of the tree. Local Test refuses all three, and has a named test for each.

Several at once. Six projects is six terminal tabs you have to remember the ports of, or one list with a state dot beside each name.

Stopping. Ctrl+C in the wrong tab leaves a dev server holding a port. Stop signals the process group and follows up with SIGKILL.

What you give up is scriptability. Local Test is a GUI, so it does not belong in a Makefile or a CI job, and a terminal server still does.

Can I read the source before I run it?

Yes. Every release publishes a source archive next to the installers, with its SHA-256 in SHA256SUMS.txt like everything else. The static server is one file of plain Node with no framework under it, and the test suites are in the archive too, so you can run them yourself.

Why should I trust an unsigned binary?

You should not trust it because it asks you to. Signing proves an identity paid for a certificate; it does not prove the code is good, and its absence here means one thing only, that a yearly fee has not been paid for a free app.

What is available instead is checkable. Every artifact has a published SHA-256, so you can confirm the file you downloaded is the file that was built, and the source archive lets you read what it does first. The app applies the same rule to itself: an update with no published checksum is refused rather than installed.

Does it work with Vite, Next, Rails, or my framework’s dev server?

Yes, as a command project rather than a static one. Set the command to whatever you already run (npm run dev, bin/rails s, cargo run) and Deploy becomes Run: your dev server does the serving on its own port, and Local Test is the launcher, the log, and a Stop button that takes the whole process tree with it.

The static server is for output, not for sources. Once your build has produced dist or _site, deploying the project statically serves that, which is a closer rehearsal of production than a dev server is.

What happens to my projects if I stop using it?

Nothing, because there is nothing to get out. Your projects are folders on your disk that the app remembers the paths of; it does not import, copy, or wrap them, and uninstalling never touches them. The project list itself is a config.json in your app-data folder, which the app can export and import, and which you can read in any text editor.

What it costs

Nothing, in the plainest sense of the word. There is no account to make, no card to enter, no trial clock counting down, and no upgrade prompt anywhere in the app. You download it and it works.

That is a description of today rather than a promise about next year. The licence permits paid work on as many of your own machines as you use; what it does not permit is redistributing or selling it, which is what leaves the door open to charging for it one day. If that ever happens it will happen in a release, and every release is written down in the changelog.

Get Local Test

Free, and it needs no account, no card, and no signup. Nothing about you is collected, and there is no limit on how many projects you keep. What it costs, in full.

Your computer will warn you the first time

Neither build is code-signed: an Apple Developer ID and a Windows certificate are both paid yearly memberships, and this app is free. So macOS refuses a normal double-click on a downloaded copy, and Windows SmartScreen stops the installer. Both warnings mean “nobody paid to put their name on this”, which is true, and neither means anything was found wrong with the file.

macOS. Right-click (or Control-click) the app and choose Open, then Open in the dialog. Once, then it opens normally forever after. Windows. Click More info on the SmartScreen box, then Run anyway.

What replaces a signature here is something you can check yourself: one command tells you whether the file you received is byte-for-byte the file that was published.

Downloads are prepared per release. If a button is greyed out that file is not on the server right now. Each button carries its own file size once the manifest has loaded.

The macOS and Windows builds are released separately, so one can be a version behind the other. Each button says which version it is whenever they differ, and the changelog says what is in each.

Check the download is the file we published

The app is not signed by Apple or Microsoft, so your computer cannot vouch for it. You can still confirm the file you got is byte-for-byte the one published here. Copy the line for your system, paste it into a terminal, and it answers in one word. If the answer is not OK, delete the file and do not open it.

macOS shasum -a 256 ~/Downloads/<file>
Windows Get-FileHash "$HOME\Downloads\<file>" -Algorithm SHA256

The macOS line is for Terminal, and works the same on Linux. The Windows line is for PowerShell, which is what the Start menu means by “Terminal” on Windows 11 and by “Windows PowerShell” on Windows 10. Both assume the file is in your Downloads folder; if you saved it somewhere else, change the path.

macOS installer
not published yet

All checksums are also listed in SHA256SUMS.txt. On macOS and Linux, shasum -a 256 -c SHA256SUMS.txt checks the lot in one step, and reports the files in it you did not download as missing rather than as wrong. Windows has no shasum at all, so there the line above is the check, one file at a time. Either way the app verifies this same checksum itself before installing an update, and refuses a build whose checksum the manifest does not publish.

Your download has started

macOS will block it the first time

This is expected, and it is not a virus warning. Local Test is not signed with an Apple Developer ID, because that is a paid yearly membership and this app is free. macOS treats any unsigned app the same way, so it stops the first launch and shows something like:

“Local Test” can’t be opened because Apple cannot check it for malicious software.

macOS Sequoia (15) and newer

Apple removed the old right‑click shortcut in these versions, so use this one.

  1. Drag Local Test into your Applications folder, then double‑click it once. It will be blocked. Click Done.
  2. Open System Settings and go to Privacy & Security.
  3. Scroll down to the Security section. You will see a line saying Local Test was blocked.
  4. Click Open Anyway, then confirm with your password or Touch ID.

macOS Sonoma (14) and older

  1. Drag Local Test into your Applications folder.
  2. Right‑click (or Control‑click) the app icon and choose Open.
  3. In the dialog that appears, click Open again.

You only do this once. After the first launch the app opens normally, and updates install without asking again.

Make sure the file is really ours first

Since macOS cannot vouch for the app, you can check the download yourself. In Terminal, from your Downloads folder:

shasum -a 256 <file>

Compare the result with the checksum shown under the download button, or in SHA256SUMS.txt. If they differ, delete the file and do not open it.

If you would rather use the Terminal

This removes the quarantine flag macOS puts on downloaded files:

xattr -d com.apple.quarantine "/Applications/Local Test.app"

Only run that on a file you have checked. It is the same permission you grant by clicking Open Anyway, just typed out.