# AppPorts and the stub portal that keeps moved Mac apps openable

> AppPorts moves macOS applications, app data and tool directories onto external storage and leaves a small local stub so Finder and the Dock still open them. It is a SwiftUI utility for Mac users whose internal disk is full, and it leans on APFS mounts, symbolic links and code signature repair to keep the moved apps runnable.

**wzh4869/AppPorts** — A macOS utility to seamlessly migrate applications to external storage and reclaim local disk space.

- Repository: https://github.com/wzh4869/AppPorts
- Website: https://appports.shimoko.com
- Stars: 2,145 · Forks: 97
- Language: Swift
- License: Apache-2.0
- Published: 2026-10-14 · Updated: 2026-10-14 · Language: en
- Canonical page: https://hysenlabs.com/projects/wzh4869-appports

## Full internal storage is the problem this utility takes on

Internal storage fills with things nobody asked to keep there: multi-gigabyte application bundles, chat history, downloaded video, tool caches. AppPorts treats that as a relocation job rather than a cleanup job. Large applications, application data and tool directories move to external storage, and then a local entry point is reconstructed for each one so that daily work does not change shape. The project's own summary of the intent fits in one short line: move apps and data to an external drive, open them as usual.

The audience is narrow. It is a Mac user with an external drive attached, an internal disk close to full, and a preference for keeping Finder and Dock behaviour intact instead of relearning where things live. The code is Swift and SwiftUI, the project badges itself for the macOS platform, and its topics run `app-manager`, `disk-cleaner`, `external-storage`, `full-disk-access`, `macos`, `swift`, `swiftui` and `symbolic-links`. That full-disk-access tag is the honest one to read first, because moving other people's data directories is not something a narrowly scoped helper can do.

## A stub portal keeps the icon and hides the shortcut arrow

macOS will not open an application from a folder the system does not treat as an app location, which is why dragging a bundle onto an external drive and hoping is not a migration. AppPorts leaves a `Stub Portal` on the Mac after the real application moves to the external drive. The stub keeps the familiar icon and does not carry the shortcut arrow that an ordinary alias would add, so Finder, the Dock and the system app launcher all resolve the same entry point. With the drive connected, apps open the way they did before.

Two supporting details come with that layout. Local and external applications appear side by side instead of in separate lists, and Dock shortcuts are synchronized both when migrating and when restoring, so existing Dock entries follow their applications. The method itself is chosen per app: it depends on the application, the macOS version and the storage conditions, so the same menu action can end in a different mechanism on two different Macs.

The hand-rolled alternative is a symbolic link made in a shell, which produces the same end state for a simple bundle and costs nothing. What it skips is everything around the move: the readiness check, the drive connection check, the signature inspection, the orphan marking, the Dock synchronization. For three apps a link is a fair substitute. For a drive you plan to fill with everything, the distance between a link and a tool that checks what it is about to break is the whole distance.

## APFS mounts and symbolic links handle data two different ways

Data does not move by a single method, and the split matters more here than it does for applications. `APFS mount migration` applies to suitable container data, and the named cases are WeChat chat history, photos, videos and received files. Under this path the application keeps reading from its original path and its original signature stays intact, so nothing inside the bundle has to be rewritten.

Ordinary data folders, developer tool directories and custom folders take the other path, symbolic links, with the real folder on the external drive and a link standing in at the original location. Both look identical from the Dock, but they fail differently, and the project's own troubleshooting points at both shapes of failure: when an application stops opening after migration it tells you to check the drive connection, the data mount status and the signature. A mount that is not active and a link whose target moved both leave a missing path behind, and orphaned link detection exists to mark that case rather than leave a launcher that fails quietly.

## Locked migration cannot protect copies held on a network volume

Moving an application breaks its updater, because an updater that rewrites its own bundle expects to write back into the folder it was installed from and now finds it on another volume. AppPorts detects Sparkle, Electron and other update mechanisms, and for suitable applications it offers locked migration, which protects the external copy from a half-finished update. The lock has to be removed inside the app before an update runs, so updating becomes a step you take rather than something that happens at midnight.

The exception arrives without much ceremony: network volumes do not use these local file locks. That one clause removes the main reason to pick locked migration when your external storage is mounted over a network share instead of attached as a local disk, because the protection disappears while the migration itself still proceeds. The documentation does not describe how the two cases are told apart before a move begins, and the topic list carries no separate flag for it either.

## Signature repair stops at sandboxed apps in default mode

Moving or relinking a bundle is exactly the kind of edit that invalidates a code signature, so signature status gets its own handling. The app checks it, offers repair steps for applications it marks as signature replaced, saves a complete backup of the original application before touching it, and restores the original signature where that is supported. The default mode refuses to re-sign sandboxed apps. A sandboxed application that trips the signature check is therefore not handled out of the box, and you keep the backup plus the manual repair path instead of an automatic fix.

This is where the two halves of the design pull against each other. `APFS mount migration` is chosen precisely because it leaves the original signature intact, while the signature management path exists for the migrations that do not. One project, two answers to the same problem, and the deciding factor is the application plus the macOS version rather than a preference you set before starting.

## Gatekeeper quarantine hits the tool on its own first launch

The roughest edge belongs to AppPorts itself rather than to the applications it moves. If an official download produces a damaged warning the first time it opens, Gatekeeper may be blocking an unsigned or unnotarized build. The documented procedure is to confirm the source, move the application into Applications, then strip the quarantine attribute:

```bash
xattr -rd com.apple.quarantine /Applications/AppPorts.app
```

A utility whose stated purpose is signature management, shipped in a form the system flags on first open, is an odd pairing, and the repository sets out no signing route behind the published build. Recovery after a migration is a separate matter: when another application stops opening, the documented checks are the drive connection, the data mount status and the signature, and problems left over from older re-signing methods point to a macOS 27 upgrade and repair guide hosted on the project's documentation site.

## Store apps take the native path while everything else takes a stub

App Store applications get a different branch because a symbolic link is the wrong instrument for them. On macOS 15.1 and later, AppPorts works with the system's native external installation and in-place updates for eligible App Store apps, inside whatever location and format requirements the system imposes. Those applications avoid the stub portal entirely and inherit the system's update behaviour rather than having it intercepted.

That native route is the real alternative to the stub approach for one class of app, and the difference is concrete: no stub to maintain, no Dock arrow to hide, updates handled by macOS itself. The cost is eligibility plus the system's format rules, and it covers nothing outside the App Store. Developer tools and custom directories fall back to links and stubs regardless.

Restoring is a first-class operation rather than an undo button. You reconnect the original drive, make sure the Mac has enough free space, and the tool checks running applications and name conflicts before it moves anything. When an operation fails partway it attempts recovery, keeps the copies, and reports their locations when recovery cannot finish safely. A migration tool that tells you where the surviving copies ended up behaves differently from one that drops them on a failed restore.

## Localization scripts, topic tags and a mixed version list

The repository layout says more about the project's shape than the feature list does. Source sits in `AppPorts/` with `AppPorts.xcodeproj/` at the top level and `AppPortsTests/` beside it, so tests are part of the tree rather than something added afterwards. `User_docs/` and `docs/` hold two documentation sets, `PRIVACY.md` and `CONTRIBUTING.md` cover governance, and `CLAUDE.md` shows an agent instruction file committed at the root. Localization is scripted rather than manual: `manage_translations.py` and `LOCALIZATION.md` sit next to `README.md` and `README_CN.md`, and the interface screenshots include a Chinese UI view, which makes language switching a shipped feature instead of a fork somebody maintains.

The last push was on 2026-10-05 and the project is published under Apache-2.0, though the documentation never says what that licence means for a bundle you have already modified through the migration path. The recorded builds also mix two conventions: v1.9.0 on 2026-09-29 carries a v prefix while 1.8.1 on 2026-09-16 and 1.8.0 on 2026-07-07 do not, so any script that assumes one tag format misses part of the list.

## Conclusion

Adopt AppPorts if your Mac is out of internal space, if you own an external drive you can treat as a permanent dependency, and if you can accept that every moved app fails while that drive is unplugged. Leave it alone if your external storage is a network share and locked migration is the reason you wanted it, or if the apps you must move are sandboxed, since the default mode refuses to re-sign those. Before moving a large application, migrate one you can reinstall cheaply and check that the drive mounts first.

## FAQ

### What special macOS permission does AppPorts need?

Its repository topics include `full-disk-access`, which points at the permission it depends on for app data directories. The README gives no grant procedure beyond moving the application into Applications for the quarantine fix.

### What happens to my Dock icons after AppPorts migrates an app?

Dock shortcuts are synchronized when you migrate and again when you restore, so existing entries follow their applications. The local stub keeps the familiar icon without the shortcut arrow an ordinary alias adds.

### Can I use AppPorts with an external drive mounted over the network?

Locked migration does not apply there, because network volumes do not use the local file locks the feature relies on. The rest of the migration still runs, and the documentation does not explain how the two cases are told apart before a move.

### Which licence is AppPorts published under?

The project code is published under Apache-2.0. Nothing in the documentation discusses obligations that apply to application bundles you have already modified through the migration path.

### What do I check when a migrated app stops opening?

Three checks in order: drive connection, data mount status, then signature. Problems left over from older re-signing methods point to a macOS 27 upgrade and repair guide on the project documentation site.

## Sources

- [License: Apache-2.0](https://github.com/wzh4869/AppPorts/blob/main/LICENSE)
- [Project website](https://appports.shimoko.com)
- [README](https://github.com/wzh4869/AppPorts/blob/main/README.md)
- [Releases](https://github.com/wzh4869/AppPorts/releases)
- [wzh4869/AppPorts on GitHub](https://github.com/wzh4869/AppPorts)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/wzh4869-appports
