~/.px0; set PX0_HOME to put it anywhere else.
Layout
The first four are the ones that would hurt to lose. They are also the smallest - plain Markdown and TOML, easy to back up anywhere.
memory/ is versioned for a reason the others are not: px0 writes to it on its own initiative. What an assistant has come to believe about you should be as reviewable and as revertible as anything you wrote yourself. See Memory.
Inside .state/
You do not need to touch any of this, but knowing what it holds makes px0 doctor output legible.
Two of these are not runtime internals in the usual sense. approvals/ and inbox/ hold things waiting on you rather than on px0, which is why neither is rebuildable and why the nightly pass never drops an unread inbox entry or a pending approval.
The retrieval index is derived data and is dropped and rebuilt whenever its schema changes - for instance when the tokenizer or a column changes between releases.
px0 doctor reports an empty index and names px0 brain reindex.
How versioning works
px0 keeps its own history rather than relying on git, because a store should not require you to adopt a version-control workflow to keep your library safe. Every write to a versioned file records a change: an actor (you, or the builder), a timestamp, and the new content of each file involved. A change can span several files - a workflow build that also wrote a guideline is one change, and reverts as one:user:manual actor.
Guideline history is addressable per section. A guideline heading becomes a claim id (<path>#<heading-slug>), so px0 guidelines log <path>#<anchor> walks one convention’s history without disturbing the rest of the file. See History.
Move a store to another machine
config.toml, plus the parts of .state/ that matter for continuity: version history, the schema marker, and the schedule.
Secrets are stripped, in both places they live. .state/credentials.toml is excluded, config.toml is exported with connectors.composio_api_key blanked, and config.toml’s version history is dropped from the exported manifest - otherwise the raw key would still sit in the history blobs, one command away. Blobs are content-addressed and shared, so only those nothing else references are removed. That is what makes the export safe to move over a network or drop into a backup.
The retrieval index is not exported; run px0 brain reindex after importing.
Load one back on the new machine:
--merge or --force, importing into an existing store stops rather than silently overwriting workflows you are running. An export contains no credentials, so importing never blanks the API key on the machine you are importing into - a config.toml already present is kept, and the Composio key is set separately:
px0 store verify checks that what arrived hangs together - workflows parse, guideline references resolve, version blobs exist, and any user-declared tool is well-formed - separate from px0 doctor, which asks whether the install is wired up rather than the store’s contents.
One consequence worth knowing: because config history is dropped on export, an imported store has no history for config.toml before the import. Everything else keeps its full history.
To move the store rather than copy it, move the directory and point PX0_HOME at the new location. Nothing inside the store depends on its absolute path.
Run history is per store
Run records and logs live underlogs.path, which sits outside the store and defaults to one directory shared by every store on the machine. Each record is stamped with the store that produced it, and px0 runs only lists the current store’s runs - so PX0_HOME isolates history rather than showing you another store’s runs and offering to rerun workflows this store does not have.
Point logs.path somewhere per-store if you would rather they not share a directory at all.
Sharing and syncing
Usepx0 store sync instead. It is the deliberate version of what people were doing anyway:
workflows/, guidelines/, memory/, brain/, and tools/ travel. .state/ never does - it holds the history, credentials, the retrieval index, and every queue, all of them either machine-specific or unmergeable. The shared directory is created for you on the first sync if its parent exists; a path whose parent is also missing is refused, because that is what a typo looks like.
Three rules, each because the alternative loses work:
- Nothing is overwritten silently. A file changed on both sides is written beside yours as
<name>.md.conflict-<machine>and reported. Two versions are two decisions, and px0 is not in a position to know which one you meant. The marker sits after the extension on purpose, so px0 never loads a conflict copy as a second workflow carrying the sameid. - A sync never deletes. A file missing on one side may be one that side has not pulled yet, and treating absence as deletion is how a sync loses work.
- The remote is just a directory. Whatever puts it on both machines is your business and none of px0’s.
.state/credentials.toml is never synced, and neither are captured fixtures. Avoid running the daemon on two machines against the same shared folder: both would evaluate the same schedules and fire the same workflows.
Backing up
The high-value, low-cost backup is the versioned paths plus history:brain/ is rebuildable from source URLs and output/ from reruns, so if you want a small backup, those are the two you can drop.
Deleting a store
Uninstalling px0 via the installer script (sh install.sh --uninstall) never deletes your store for you. It leaves the store as-is so you can keep your data. When you want it gone manually:

