EVERYDAY USE

Sites and project directories

Park PHP projects, link app ports to folder domains, and enable local HTTPS.

Project directories

Open Settings → Sites, then use the + menu beside Projects → Project Directories and add a parent directory. Its immediate child folders become project domains:

~/Projects/shop       → shop.test
~/Projects/dashboard  → dashboard.test

Enable or pause each directory with its toggle. Removing a parked directory removes its automatically discovered routes and preserves project files. Paused directories retain their project settings. Explicit links remain independent of directory parking.

morrow site park "$HOME/Projects"
morrow site directories
morrow site directory "$HOME/Projects" off
morrow site directory "$HOME/Projects" on
morrow site unpark "$HOME/Projects"

Folder names are converted to lowercase DNS labels; spaces and unsupported characters become hyphens. Duplicate domains across directories show a conflict instead of silently choosing a project. Use Project Settings to choose a unique hostname. Hidden folders, node_modules, vendor, build, and dist are skipped during automatic discovery.

Git worktree hosting

Under Sites → + → Project Directories, each directory has an Include Git worktrees switch. Turn it off to exclude linked Git worktree folders from automatic local hosting. The choice is saved per directory and also applies to worktrees discovered later. It starts enabled so existing sites keep working after an update.

Excluded worktrees stay listed as Worktree excluded from local hosting. Their files, custom domains, HTTPS preferences, folder runtime selections, and individual ignore choices are preserved. Turn the switch back on to restore their routes. Regular repositories and submodules are unaffected. Detection reads Git's worktree metadata; it does not run Git or require Git to be installed.

morrow site directory "$HOME/Projects" --worktrees off
morrow site directory "$HOME/Projects" --worktrees on
morrow site directories

This switch controls automatically discovered sites only. To host one worktree explicitly while excluding the rest, use Link Project or morrow site link /path/to/worktree. Explicit links stay independent of the directory preference. Morrow only discovers immediate child folders of a parked directory; it does not search every worktree registered with a repository.

Changing this preference while Sites is running reloads the affected routes and updates the directory monitor to the current Morrow CLI. Your project files and unrelated services remain intact.

Ignore folders for local hosting

For a folder that should not receive a local domain, open its project menu and choose Ignore for .test hosting (the label follows your configured suffix). Morrow keeps the project settings and folder, removes its route from the web server, and shows Ignored for local hosting. Directory refreshes retain this choice. Choose Include in .test hosting to restore it.

morrow site ignore dotfiles.test
morrow site include dotfiles.test

Ignoring never deletes the application folder. Removing a parked directory is a separate action that un-registers its discovered routes while preserving all folders.

Start hosting

Click the play icon beside Web server in Sites, or:

morrow site start
morrow site stop
morrow site refresh
morrow site list

Morrow reuses existing Caddy and dnsmasq binaries or downloads complete distributions from the configured catalog. No package manager is required. Services use separate user-owned launchd jobs. PHP projects use complete PHP-FPM installations; choose a default in Sites or a per-project version in Project Settings.

morrow site php
morrow site php 8.4
morrow site php --install

The install option reuses a complete PHP-FPM installation or downloads a complete PHP distribution from the catalog. Only complete native distributions are installed. A standalone PHP CLI without PHP-FPM is not sufficient for web hosting. This choice is separate from your ordinary CLI runtime default.

The directory watcher continues when the menu bar app closes. Changes are checked approximately every three seconds while hosting is enabled. Start-at-login can be changed in Hosting Settings. morrow site watch also runs the watcher in the foreground while hosting is enabled.

PHP and static roots

Laravel projects use public as their document root. Other PHP projects are detected by an index.php in the root or public directory. Static sites use a detected public directory or their folder root. Change the root in Project Settings; it must remain inside the project folder.

Package-based Node projects and Go projects are detected as application servers and wait for a linked port. Morrow does not serve their source folders as static websites automatically. Static mode blocks PHP source and common private project files; Caddy also handles PHP through its FastCGI handler when PHP mode is selected. This is local development hosting, not a sandbox for untrusted project code.

A project can register a development server without Morrow owning that process:

cd "$HOME/Projects/dashboard"
morrow site link --port 3000

This creates dashboard.test → 127.0.0.1:3000. The command works for Node, Go, Python, Rust, Java, or any HTTP application. Keep starting the application with its own development command.

For a Nuxt project whose dev script already invokes Nuxt:

{
  "scripts": {
    "predev": "morrow site link --port 3000",
    "dev": "nuxt dev --host 127.0.0.1 --port 3000"
  }
}

Run the one-time hosting setup first, then npm run dev. A Go project can similarly run morrow site link --port 8081 before its own startup command. Apps must actually listen on the specified port; setting a port in Morrow does not change the application's configuration.

morrow site link /path/to/project --port 4100 --domain dashboard.test --https
morrow site unlink dashboard.test

A domain without dots is placed under the current development suffix. Explicit links persist after the application's process exits, showing Waiting for app until its port opens again. Morrow does not start or stop these external app processes. WebSockets pass through Caddy; frameworks such as Vite may require the exact hostname and secure HMR settings in their dev-server configuration.

Change the domain suffix

Open the configuration icon beside Web server for Hosting Settings, or:

morrow site configure --suffix morrow.test

Supported namespaces are test, internal, localhost, and their subdomains, such as morrow.test. Public domain suffixes and .local multicast DNS are intentionally outside this feature.

Automatically named routes change with the suffix. Explicit custom hostnames remain as entered; their previous namespace remains managed. New suffixes need system setup again. Changing a suffix creates matching local certificates for HTTPS sites.

Clean URLs and system DNS

The app reads the helper's native authorization status, including permission revoked in System Settings. An allowed helper does not trigger another approval prompt. Pending requests are recovered when the app reopens, while routing is verified separately before setup is reported as ready. Review permission and cleanup controls in General → Background access.

Services use unprivileged listener ports by default: HTTP 8080, HTTPS 8443, and DNS 5354. Before system setup, Morrow displays URLs with those ports. A hostname also needs working local DNS. Setup preserves other resolvers unless you explicitly select replacement.

The Local Domains card shows routing and certificate status. Set Up starts hosting if needed, registers the native setup helper for one-time macOS approval for DNS and ports 80/443, and trusts the development CA when a project has HTTPS enabled. If approval is needed, Approve Morrow opens the macOS background-permission settings; setup resumes after approval. Once configured, the setup button disappears. Validation, gateway logs, and repair live in its … menu.

If a previous resolver remains after removing Herd/Valet, the primary action becomes Replace Setup. It explicitly backs up the previous resolver before replacing it. Stop any other web server using 80/443 first; Morrow still refuses to redirect an occupied web-server port. You can also choose another private suffix.

The CLI requires an explicit replacement option:

sudo morrow site setup --http-port 8080 --https-port 8443 --dns-port 5354 --suffixes test --replace-resolvers

Resolver backups are private root-owned JSON files in /Library/Application Support/Morrow Network Backups. They retain the original bytes and permissions. Removing Morrow routing restores a saved resolver only if that resolver still contains Morrow's configuration. Backups remain available afterward.

Morrow uses launchd socket activation for standard localhost ports. launchd opens HTTP 80, HTTPS 443, and TCP/UDP DNS 53 on 127.0.0.1, then gives those sockets to a small gateway running as your Mac user. It forwards only to Morrow's private local listeners. PHP, DNS, Caddy, the gateway, and the directory watcher run without root privileges.

This setup does not install packet-filter rules, a VPN, or a network extension. Private domains and local connections are outside Private Relay's internet relay path. Migrating an older Morrow setup removes its PF anchor, releases only Morrow's PF token, and removes its dedicated DNS alias; other tools' filtering state is preserved. macOS may take a short time to refresh Private Relay's status.

Unrelated resolvers, web servers, network DNS settings, and project files are preserved. Resolver replacement only applies to the namespaces explicitly selected for setup. Pending setup is reported rather than counted as complete.

For CLI setup as an administrator, provide the settings you selected:

sudo morrow site setup --http-port 8080 --https-port 8443 --dns-port 5354 --suffixes test

With system setup complete, addresses become http://shop.test or https://shop.test, without explicit listener ports. Removing only Morrow's system integration:

sudo morrow site setup --remove

Stop Sites before changing listener ports, then rerun system setup with matching values. Linked app ports must differ from the proxy listeners.

HTTPS on or off

Use the exact hostname displayed on the project card. Automatic folder names are normalized to DNS labels: a folder named karti.dev initially receives karti-dev.test. To use the dotted address instead, edit Project Settings → Domain, or rename that route from Terminal:

morrow site rename karti-dev.test karti.dev.test

Renaming preserves the project ID, folder, document root, runtime, and HTTPS preference. The chosen name becomes a custom domain and is retained during directory discovery. Certificates are issued for configured hostnames; an unconfigured spelling can fail the TLS handshake even when another spelling of the project's hostname works.

Toggle HTTPS per project, or:

morrow site secure shop.test
morrow site unsecure shop.test
morrow site configure --https on

The global setting controls new sites. Caddy's internal CA issues and automatically renews certificates for explicitly configured project hostnames. New site certificates are valid for up to 368 days. Existing certificates adopt the new lifetime when renewed, subject to the remaining validity of the certificate chain. Morrow does not request public certificates for these development domains.

Local hosting uses HTTP/1.1 and HTTP/2. The clean-port gateway forwards TCP, so Morrow does not advertise HTTP/3 on its private HTTPS listener. HTTPS responses also clear previously cached alternate-service routes, keeping browsers on the displayed project address.

The project's HTTPS switch and a small padlock beside its domain show that HTTPS is enabled. Enabling or disabling it does not show a success toast or confirmation card. An enabled, active route with a trusted certificate is checked quietly; connection failures show an actionable error card on Sites. A padlock with an amber accent explains missing setup, stopped hosting, ignored hosting, or a failed connection in its tooltip. The menu dropdown contains no toast notifications.

If macOS needs setup-helper approval, General and Sites show a compact reminder above their content with Review Permissions…. The reminder follows actual permission status and disappears once access is allowed. An existing helper that is no longer registered offers Retry Setup. Neither reminder covers other controls.

Enable HTTPS on a site, then choose Set Up or Finish Setup in Local Domains. Setup skips certificate changes when the CA is already trusted. Advanced users can still run:

morrow site trust

Validate HTTPS in the Local Domains … menu reports its result inside the card. Validation uses native certificate trust evaluation without launching a certificate application, opening a browser, changing the selected page, or adding a page-wide activity/error panel. Only explicit setup or certificate trust changes need system approval. The app no longer elevates through AppleScript or shell scripts.

morrow site check-https
morrow site check-https shop.test
morrow site certificates
morrow site renew-https shop.test

The project row shows its certificate expiry date. Use Renew HTTPS Certificate in the project’s … menu to issue a fresh certificate immediately. Renewal briefly restarts the web server and keeps the existing trusted root, so it does not ask for trust again. Run morrow site renew-https without a hostname to renew every active HTTPS site in one restart.

After routing is configured, validation checks the normal project hostname and HTTPS port 443, including system DNS and forwarding, and shows the expiry of the certificate served there. Before setup, it checks the private listener directly and explains the remaining setup step. HTTP redirects such as 302 still establish a working TLS connection; the check does not verify an application's business logic.

If HTTPS connects but your app sends you back to sign in or returns a session or CSRF error, check the application's session lifetime and cookie settings for the displayed HTTPS hostname. Renewing the site's certificate does not change that application's session or cookie lifetime.

Before system routing is enabled, use the displayed address such as https://shop.test:8443. The default HTTPS switch applies only to new sites; existing projects have their own HTTPS toggles.

This installs the public root certificate into this user's login keychain trust settings and may prompt for authorization. Private CA keys remain in Morrow's local web/caddy-data directory and are not included in iCloud setup sync. Clients using separate trust stores may need to import the public root certificate separately. Turning off HTTPS changes the route back to HTTP; it does not remove the trusted CA.

Project PHP and Node versions

Open Project Settings → Folder versions → Choose to select PHP, Node.js, or another runtime for the project folder. The same editor is available under Runtimes → Folder Versions. PHP hosting uses that folder's selected PHP-FPM pool; Terminal commands use the same folder selection through Morrow's wrappers or morrow tool exec. Node selection chooses the development runtime; start the Node app separately and link its port to a local domain.

Parent folder choices apply to subfolders, with closer choices taking precedence per runtime. Existing explicit PHP route choices are retained as folder selections where the folder has one consistent PHP version. See Folder Versions for installation policy and shell setup.

Logs and actual status

Sites reports live launchd PIDs and listener readiness. PHP projects also need their owned PHP-FPM process and socket; proxy projects need a listening upstream port. These checks indicate routing/runtime readiness, not an application-specific database or business-logic health check.

Use the project card's terminal icon or View Routing Log to open Caddy output in Morrow's searchable log viewer. Files live under ~/Library/Application Support/Morrow/web; DNS, PHP-FPM, and watcher output have separate log files there.

morrow site logs shop.test
morrow site open shop.test

Site and parked-directory configuration is local in this first implementation; it is not yet part of the portable iCloud workspace blueprint.

Native helper and release signing

The setup daemon is bundled with Morrow and registered through SMAppService. macOS retains its approval. It accepts only a private regular request file owned by the console user, validates the listener ports and private suffixes, and exposes no arbitrary command/path execution. Public setup results contain status, never credentials.

The gateway is copied to a root-protected application-support location and runs as the Mac user. It keeps working if the GUI app moves. The helper and gateway are distinct roles: only installation metadata and resolver changes use root privileges.

Customer builds must be Developer ID signed and notarized. Ad-hoc development builds can have additional macOS approval restrictions. See Contributing for release build inputs.

Edit this page on GitHub ↗
Search all 17 guides esc to close