# Keeping it current (/docs/haus/keeping-it-current)



Every change to a haus machine is two beats: &#x2A;*edit the file, run one command.**
Pulling a newer haus, undoing yesterday and rebuilding on new hardware are all
that loop, started from different places.

## The everyday loop [#the-everyday-loop]

```sh
haus edit       # your host file, in $EDITOR
# …change something…
haus rebuild    # build first, then switch
```

For a one-line change, skip the editor: `haus set theme.accent teal` writes the
setting, type-checks it and rebuilds in one go. The [`haus`
reference](/docs/haus/reference/haus) has the rest. On ⌘Space the
[launcher](/docs/haus/rooms/launcher) carries **Rebuild System**, which runs
`haus rebuild` in a floating terminal, and **Nix Config**, which opens your host
file.

## Pulling a newer haus [#pulling-a-newer-haus]

```sh
haus update
```

haus lives upstream, pinned to an exact commit in your config's `flake.lock`.
**Nothing about your machine changes until you run this**: no background
updater, no surprise on a Monday. It moves that one pin to the latest haus,
upgrades the family's Homebrew apps alongside it, prints what changed upstream,
and rebuilds. One of those apps, scruff, can trail its release by a few minutes,
and a release whose formula stopped building never arrives and nothing says so;
[scruff's install page](/docs/scruff/install) has the detail. `haus status` says
where you stand: your current generation, the revision you're pinned to, and
whether upstream has moved past it. The machinery behind the pin is [How the
flakes fit together](/docs/haus/internals/flakes).

## When a change goes wrong [#when-a-change-goes-wrong]

```sh
haus rollback      # back to the previous generation, atomically
haus generations   # what you can roll back to
haus doctor        # health check, when it isn't obvious what broke
```

Every switch creates a generation to step back to. When it's a symptom rather
than a change (a dead bar, a launcher that won't answer), [Troubleshooting](/docs/haus/reference/troubleshooting) has the
known quirks and the command for each.

<Callout type="warn" title="Rollback doesn't rewind macOS's own settings">
  A generation holds everything Nix wrote (packages, services, login agents), but
  **not** the macOS preferences a rebuild set imperatively: the Dock, Finder, the
  keyboard. `haus capture` before a risky change snapshots those, and `haus
  revert-settings` writes them back.

  It restores what the snapshot holds, which is not quite the same as undoing the
  rebuild: the restore merges, so a setting you had already chosen goes back to
  your value, and one the rebuild added where you had nothing stays. Snapshot the
  domains you care about by name (`haus capture com.apple.universalaccess`, say)
  rather than relying on the default three, and expect the additions to need
  their own line in your host file.
</Callout>

## A second Mac [#a-second-mac]

```sh
cd ~/.config/nix && git pull
haus rebuild
```

Your config is a git repo, so a second Mac catches up the way any checkout does,
assuming you pushed it somewhere both machines can reach. Both read the same pin
from the same lock, so they converge on the same system, not roughly the same
one.

## A new Mac, or a wiped one [#a-new-mac-or-a-wiped-one]

The payoff for describing a machine as text is a restore, not a weekend in
System Settings. It assumes one thing, that `~/.config/nix` is **pushed
somewhere you can clone it from**. If it isn't, that's today's five-minute job.

<Callout title="No remote yet? One command.">
  `~/.config/nix` is already a git repo with a first commit in it, so all it needs
  is somewhere to go:

  ```sh
  cd ~/.config/nix
  gh repo create nix-config --private --source=. --remote=origin --push
  ```

  Private, because the config names your hostnames, your paths and the ids of your
  secrets, though never their values. `gh` comes with the [Development
  room](/docs/haus/rooms/development)'s git pack, and wants one `gh auth login`.
</Callout>

```sh
# clones your config into ~/.config/nix and skips the interview
curl -fsSL https://hausfold.co/haus.sh | bash -s -- --from <your-config-remote>
```

On a brand-new Mac the Xcode Command Line Tools are missing, so the first run
opens Apple's installer dialog and stops. Approve it and run the line again.

It installs [Determinate Nix](https://docs.determinate.systems/) for you and
finishes by printing [the build and switch
pair](/docs/haus/install#build-and-switch) with your hostname filled in. When
that lands, `haus` is on your `PATH`, and `haus doctor` is the check that it all
came back.

<Callout title="New hostname? Name the host you want.">
  Your config declares each machine by hostname, and a new one isn't picked up on
  its own. Either build the old host by name (`--flake .#<oldhostname>`) or copy
  `hosts/<old>/` to `hosts/<new>/` and add it to `flake.nix` beside the first.
  Your identity and app choices come along either way.

  ```sh
  nix eval ~/.config/nix#darwinConfigurations --apply builtins.attrNames
  ```
</Callout>

### What comes back on its own [#what-comes-back-on-its-own]

That switch restores every app in `haus.roster` naming a Homebrew cask or
formula or a nixpkgs package, the whole terminal, the macOS defaults haus
manages down to the Caps-Lock remap and ⌘Space, and the launchd agents behind
tiling, the bar and the launcher. That last group has a catch on macOS Tahoe and
later: **Background Task Management can quietly refuse to start them on a fresh
machine**, and `haus btm` prints the one thing to click.

App Store entries are the exception. Installing from an `appStoreId` is opt-in
(`haus.appStore.install`), and even switched on it only fetches apps your Apple
Account already owns. Buying a paid one is still yours to do, once, in App
Store.app.

### The bits Nix doesn't carry [#the-bits-nix-doesnt-carry]

A handful of things never enter your config, on purpose. Walk this once per
machine:

* **Secrets.** Your config declares *which* secrets exist, never their values.
  `haus-secret --check` asks for the ones this Mac's rooms need; `secretspec
  check` does the same inside a project of your own. They sit in this Mac's
  login keychain by default, which is why they don't ride along in the clone.
  Point `haus.secrets.provider` at a cloud vault and they follow you instead, at
  the cost of one login to bootstrap it.
* **Commit signing keys.** Your config names the key *id*; the key *material* is
  yours to import, with whatever agent unlocks it.
* **The permission grants.** macOS ties Accessibility, Full Disk Access and the
  rest to the machine, so a restored Mac starts with none of them.
  [`haus permissions`](/docs/haus/reference/haus#what-needs-a-person-and-what-runs-itself)
  walks the ones this generation actually needs, one card at a time;
  `haus permissions --list` just reports.
* **Apps you installed by hand.** They won't reappear. Reinstall the ones you
  want, and consider a [roster entry](/docs/haus/rooms/apps) so next time they
  do.
* **Logins and licences.** Nix restores the app, not your session; sign back
  into the handful that need it, the App Store among them.

Every app you move off that list and into `haus.roster` is one less thing to
remember next migration. The goal is a Mac you could wipe on a Tuesday without
flinching.

Adopting a Mac you set up by hand is a different move, and
[install](/docs/haus/install) handles it deliberately: your casks are kept, your
dotfiles are backed up rather than clobbered, and nothing activates until you
say so.
