Every file fastpace touches gets recorded in a signed install-manifest. Uninstall reads that manifest and removes only what fastpace itself placed — your own hooks, agents, skills, and settings entries are provably untouched. This guide is the long-form version of the scope picker on the home page.
Claude wires hooks/agents/skills by exact filename —
e.g. .claude/hooks/pre-tool-use.js,
.claude/hooks/post-tool-use.js. We can't namespace
fastpace's files with an fp- prefix the way we do
slash commands; Claude expects specific names and we have to
ship those names. So fastpace ends up writing files into
.claude/ at paths a user might independently want
to own.
The pre-2026-05-15 uninstall sidestepped this by wiping the
whole .claude/ directory — which destroyed any
non-fastpace hooks/agents/skills the user added on their own.
The manifest fixes that: install records exactly what fastpace
placed; uninstall removes only those entries.
A user who put their own pre-tool-use.js in
.claude/hooks/ before fastpace init ran
keeps their file after both init AND uninstall. The
packages/fp/test/uninstall.test.js suite asserts
this end-to-end.
Two manifests, identical schema, two locations:
~/.fastpace/install-manifest.json (user-global scope) <repo>/fastpace/install-manifest.json (per-repo scope)
Each is a JSON document signed by the F0.1 install identity (Ed25519). The schema:
{
"schema_version": 1,
"installed_by": "fastpace v0.46.2",
"installed_at": "2026-05-15T08:14:00.000Z",
"scope": "user" | "repo",
"synthesised": false,
"files": [
{
"path": ".claude/hooks/pre-tool-use.js",
"kind": "hook" | "agent" | "skill" | "settings",
"owner": "fastpace",
"pre_existing": false,
"sha256_at_install": "ab12..."
}
],
"settings_patches": [
{
"path": ".claude/settings.json",
"added_keys": ["hooks.PreToolUse[0]", "hooks.PreToolUse[1]"],
"original_values": { "hooks.PreToolUse[0]": null }
}
],
"signed_by": { "install_id": "i_xxxxxxxx", "fingerprint": "sha256:..." },
"entry_hash": "<sha256 over canonical-JSON exclusive of entry_hash+signature>",
"signature": "<base64 ed25519 of entry_hash>"
} sha256_at_install — lets
uninstall detect "user modified this file" and either prompt
or skip instead of silently deleting their customisation.
pre_existing — reserved slot
for files that already lived at the install path. For v1,
install REFUSES to overwrite pre-existing files (see Part 4),
so this is always false for files in the
manifest. The slot lets future flows record restore-on-uninstall
if the policy ever changes.
settings_patches[] — exact
JSONPath of every key fastpace added to settings.json, plus
the value that was there before (or null for
net-new). Uninstall --settings does a precise
reverse-merge from this rather than nuking the whole file.
A tampered manifest could trick uninstall into deleting
arbitrary files. Signing closes that. Uninstall verifies the
signature before any destructive action; if verification fails,
it refuses to act unless you explicitly pass
--skip-manifest-verify (loud warning, recommended
only with --dry-run for inspection).
Verification is rotation-aware. After
fastpace identity rotate, the current key changes,
but the archived key in ~/.fastpace/identity/archive/
still validates manifests signed before the rotation. You can
rotate keys as often as compliance requires without invalidating
prior installs.
Default scope is gentle: bare fastpace uninstall
touches only the user-global manifest. The four optional scope
flags compose, with two shorthand collapses.
| Command | Touches | Prompt |
|---|---|---|
fastpace uninstall | user-global manifest only (gentle default) | type "yes" |
--repo | also: current repo's .claude/ files | type "yes" |
--settings | also: reverse-merge fastpace keys out of settings.json | type "yes" |
--fastpace-dir | also: remove the repo's fastpace/ dir (audit log LOST) | type "yes" |
--user-identity | also: remove ~/.fastpace/ (Ed25519 keypair + license cache) | type "yes" |
--all | shorthand: --repo --settings --fastpace-dir | type "yes" |
--everything / --nuke | shorthand: --all --user-identity | double-confirm |
--force / -y | non-interactive (CI / scripts) | — |
--dry-run | print the plan, change nothing | — |
--skip-manifest-verify | proceed even if signature is invalid (loud warning) | — |
--keep-modified | default for modified files: keep | — |
--force-delete-modified | delete modified files without prompting | — |
sha256_at_install → delete silently
[k]eep / [d]elete / [s]how diff / [A]ll-keep / [D]elete-all.
Under --force the default is keep
(less destructive); pass --force-delete-modified
to override
Most-recoverable first, identity last:
fastpace/ directory removal
The manifest model only works if install never silently
replaces user-owned files. fastpace init now
refuses to overwrite. The behaviour at install time, per file:
sha256_at_install
The collision summary at the end of fastpace init
looks like:
fastpace init
✗ skipped 2 files that already existed (your versions kept,
fastpace will not touch them):
• .claude/hooks/pre-tool-use.js
• .claude/agents/reviewer.md
To adopt fastpace's versions instead: back yours up, delete
the file, then run fastpace init --reinstall. Skipped files don't enter the manifest, so uninstall correctly never touches them — that's the safety guarantee end-to-end.
When a hook was in yesterday's bundled set but isn't today's,
fastpace update:
sha256_at_install
(you didn't customise it)
Customisations are preserved; truly orphan unmodified files go away without prompting. From the customisation moment forward, fastpace doesn't manage that file, and uninstall doesn't touch it either.
Users on a pre-manifest build who run fastpace update
get a synthesised manifest: update walks today's
bundled file set against on-disk content and claims anything
that exists with its current SHA-256. Imperfect — a
user-customised file that happens to live at a fastpace-bundled
path will be claimed — but the modification check in uninstall
protects them: the next uninstall sees the SHA-256 has drifted
from what's bundled today, classifies it as modified, and
prompts before doing anything.
Removing fastpace from a machine in one sitting:
# 1. Clean the repo + reverse-merge settings.json + delete fastpace/ fastpace uninstall --all # 2. Remove the per-user identity + license cache (optional but # completes the "no traces left" goal) fastpace uninstall --user-identity # 3. Remove the CLI itself npm uninstall -g @fastpace-ai/fp
Or in one command (the --nuke shorthand):
fastpace uninstall --nuke npm uninstall -g @fastpace-ai/fp
Nothing about fastpace phones home. Removing the CLI removes
the binary; no server-side state to clean up. The licensing
service auto-prunes inactive install_ids after 90
days as housekeeping — no action required from you.
If the manifest has been tampered with (or corrupted), uninstall refuses to act and prints:
✗ repo-scope install-manifest failed signature verification:
entry_hash does not match recomputation (manifest tampered with)
Recovery: fastpace uninstall --dry-run --skip-manifest-verify
to inspect without touching anything; then pass --skip-manifest-verify
to force.
Always start with --dry-run — it prints the plan
without changing anything, so you can see what would happen
before committing.
--user-identity
Deleting ~/.fastpace/ destroys the Ed25519 keypair
that signs the audit chain. Past audit logs and manifests
remain verifiable only against the archived public key, which
also lives in ~/.fastpace/identity/archive/ — so
deleting ~/.fastpace takes the archive with it.
If you might need forensic verification of past audit logs later (e.g. for an audit two years out), export the identity first:
fastpace identity show --json > identity.json # Stash identity.json somewhere durable (S3, password manager, etc.). fastpace uninstall --user-identity
Users on the prior (wipe-the-repo) fastpace uninstall
will see a one-time notice the first time they run uninstall
after upgrading:
NOTE: in this version 'fastpace uninstall' no longer touches the current repo by default. Pass --repo to also remove .claude/ files in this directory. Pass --all for repo + settings + fastpace/ removal (the old default behaviour).
The notice is tracked in ~/.fastpace/notices-seen.json
so it fires once and then shuts up.
.claude/ and fastpace/--user-identity