Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ PACKAGE_NOTARY_PROFILE ?= $(RELEASE_NOTARY_PROFILE)
FORCE ?= 0

.DEFAULT_GOAL := help
.PHONY: help doctor test guest runtime app build run run-ephemeral reset update-omarchy package package-preflight release release-preflight clean clean-all clean-guest
.PHONY: help doctor test guest runtime app build run run-ephemeral reset backup update-omarchy package package-preflight release release-preflight clean clean-all clean-guest

help:
@printf '%s\n' \
Expand All @@ -41,6 +41,8 @@ help:
'Storage:' \
' make run-ephemeral Run without retaining VM changes' \
' make reset Open the confirmed factory-reset flow' \
' make backup DEST=/path/to/empty/folder' \
' Copy the saved VM into DEST (APFS clone)' \
' make clean Remove all project builds and build caches' \
' make clean-all Also remove VM data and stale temporary files'

Expand Down Expand Up @@ -93,6 +95,10 @@ run-ephemeral: app
reset: app
@$(ROOT)/macos/open-qemu-gpu.sh --reset-storage

backup:
@[[ -n "$(strip $(DEST))" ]] || { echo 'error: DEST=/path/to/empty/folder is required' >&2; exit 1; }
@$(ROOT)/macos/backup-qemu-storage.sh "$(DEST)"

update-omarchy:
@[[ -n "$(strip $(OMARCHY_RELEASE))" ]] || { echo 'error: OMARCHY_RELEASE=x.y.z is required' >&2; exit 1; }
@$(ROOT)/guest/scripts/update-upstream-pin.py \
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,24 @@ the same Mac; guest SSH authentication is still required.

Normal launches keep one persistent VM under `~/Library/Application Support/Try Omarchy/VM/v1`. Removing the app does not remove this data. The start menu can reset it, and requires confirmation before replacing a disk that is incompatible with a new factory guest build.

### Backing up before an update

A new Try Omarchy guest build cannot boot an existing VM. The start menu then asks you to **Reset Omarchy**, which erases the disk. Automatic disk migration between releases is not reliable yet, so `v0.1.0` disks are not moved onto `v0.2.0`.

**Keep your files.** Before resetting, copy anything you need out of Omarchy with the shared folder, or with SSH if you enabled it. Import those files into the new VM after Reset.

**Keep the whole VM.** Quit Try Omarchy, then use **Backup…** on the start menu. The copy is an APFS clone of the working disk, so it stays sparse and is nearly instant on the same volume. The destination becomes a valid workspace: choose it later as **VM Location**. Restoring that copy still needs the **same** Try Omarchy version — it will not boot on a newer guest.

From a checkout, the same copy is:

```sh
make backup DEST="$HOME/Backups/Try Omarchy"
```

`DEST` must be an empty folder on a local APFS volume, or a path that can be created as one. Do not copy the live folder in Finder: Finder copies can expand the sparse working disk to its full size (around 24 GB) and land the 0600 workspace marker as 0644, so the launcher then refuses to open it.

Factory images under `images/` are not part of the backup. The matching Try Omarchy build rematerializes them.

### Choosing where the VM lives

**Change…** on the start menu's **VM Location** row moves the VM to any folder
Expand Down Expand Up @@ -333,7 +351,7 @@ Run the complete contract and native test suite with:
make test
```

Run `make help` for component builds, persistent-storage reset, ephemeral mode, and cleanup commands.
Run `make help` for component builds, persistent-storage reset, VM backup, ephemeral mode, and cleanup commands.

To reclaim development build space, run:

Expand Down
6 changes: 5 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,11 @@ disk remain unchanged. Normal user launches use one private writable disk under
`~/Library/Application Support/Try Omarchy/VM/v1`. Its factory-image identity is immutable:
the launcher never pairs a saved root filesystem with a different bundled kernel
or initramfs. When a guest build changes, the start menu asks for an explicitly
confirmed factory reset before creating the replacement disk. A compatible
confirmed factory reset before creating the replacement disk. **Backup…** copies
recognized persistent disks into an empty APFS folder with `cp -c`, preserving
sparse allocation and the 0600/0700 modes the launcher requires. The copy is a
valid workspace for the same guest identity; it does not make an older disk boot
on a newer factory image. A compatible
legacy identity-keyed disk can be migrated into the single workspace without
discarding its contents. If several recognized legacy disks exist, normal launch
stops at the start menu; confirmed reset safely removes them before publishing
Expand Down
4 changes: 3 additions & 1 deletion macos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ and specialized development runs can opt into identity-keyed parallel disks by
setting `OMARCHY_QEMU_GPU_DEVELOPMENT_MULTI_DISK=1`; release behavior leaves it
unset. The disk's guest-build identity is immutable so an older root filesystem
can never boot with incompatible bundled kernel modules. A changed guest build
requires the user-facing, confirmed Reset Omarchy flow.
requires the user-facing, confirmed Reset Omarchy flow. **Backup…** (or
`make backup DEST=…`) APFS-clones that workspace into an empty folder so the
original can be restored with the matching app version after a reset.

The start menu can move that workspace to any APFS folder the user picks; the
folder is used exactly as chosen, never with a folder created inside it — a
Expand Down
15 changes: 15 additions & 0 deletions macos/Sources/OmarchyVMHelper/QEMUGPULauncher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@ enum QEMUGPUStorageOption: String, Equatable {
case ephemeral = "--ephemeral"
case resetStorage = "--reset-storage"
case resetStorageOnly = "--reset-storage-only"
case backupStorageOnly = "--backup-storage-only"
}

/// Build-only controls and user-facing integration values must never leak
/// into an ordinary app launch or a storage reset through the parent process.
enum QEMUGPURuntimeEnvironment {
static let inspectOnlyKey = "OMARCHY_QEMU_GPU_INSPECT_ONLY"
static let dryRunKey = "OMARCHY_QEMU_GPU_DRY_RUN"
/// Kept byte-identical to `QEMU_PERSISTENT_STORAGE_BACKUP_ROOT_ENV` in
/// qemu-persistent-storage.sh. Change one and you must change both;
/// StorageLocationContractTests pins them together.
static let backupRootKey = "OMARCHY_QEMU_GPU_BACKUP_ROOT"

static func sanitizedForLaunch(_ base: [String: String]) -> [String: String] {
var environment = base
environment.removeValue(forKey: inspectOnlyKey)
environment.removeValue(forKey: dryRunKey)
environment.removeValue(forKey: backupRootKey)
return environment
}

Expand All @@ -34,6 +40,15 @@ enum QEMUGPURuntimeEnvironment {
}
return environment
}

static func sanitizedForBackup(
_ base: [String: String],
destination: String
) -> [String: String] {
var environment = sanitizedForReset(base)
environment[backupRootKey] = destination
return environment
}
}

enum QEMUGPUStorageSpaceEstimate {
Expand Down
Loading