Configuration files¶
No config files are hand-written — every file below is created and updated by forest commands. This page documents what they contain so you can read them in review and commit the right ones.
Workspace registry — committed¶
Checkout forest.yaml — shared, committed¶
One per checkout, at .forest/checkouts/<name>/forest.yaml:
project: demo
remotes:
origin:
url: s3://my-bucket/prefix
region: us-east-2 # optional; also endpoint, profile, key_file, known_hosts
stages:
raw:
remote_path: demo/raw # optional; defaults to <checkout>/<stage>
sync_by: subdirectory # subdirectory | directory | file
direction: both # push | pull | both (default: both, omitted)
direction restricts which transfer verbs may touch a stage: bare
forest push/forest pull (and --all) skip a wrong-direction stage with
an info note, while explicitly selecting one (--stage, unit IDs, --id)
is an error. There is no flag override — changing direction is a deliberate
forest.yaml edit (ADR 0022).
Checkout local.yaml — per-machine, gitignored¶
active_remote: origin
stage_paths:
raw: ../data/raw # relative resolves from the workspace root
Checkout sync_state.json — per-machine, gitignored¶
One per checkout, written by push and pull after each verified
transfer. Each entry records what moved, when, and the evidence forest
uses for skip decisions, dirty-pull guards, and force-with-lease checks
(trimmed real entry):
{
"remote": "origin",
"stage": "raw",
"unit_id": "plot-01",
"direction": "push",
"timestamp": "2026-07-02T22:32:44.641369+00:00",
"file_count": 2,
"total_bytes": 73,
"checksum": "42ca3c31aea092c220ba8b85369bca1a666a91d35d6578cc4c5db925ff25b10e",
"file_snapshot": { "notes.txt": 43, "temps.csv": 30 },
"file_hashes": {
"notes.txt": "01642dd14166e068f3c35fe75b82d187cf67969041ea814e94f7ead1b589f121",
"temps.csv": "b150ba5104379dc6d21c4d082ba75a2fad36c4acc8ad1176eaba8d340a1aabbb"
}
}
checksum— aggregate over the sortedrelpath:size:sha256lines; content-sensitive, so a same-size edit changes it.file_snapshot—{relative path: size}for every file in the unit at transfer time; the fast comparisonstatususes by default.file_hashes— per-file SHA-256, checked bystatus --checksum, the dirty-pull guard, push's skip decision, and pull's proof that the local copy used for a live remote comparison is still clean.
Entries written before forest recorded content hashes lack these fields; they read as stale, re-push once after upgrading, and heal to the full shape.
Which files does git see?¶
| File | Scope | Git |
|---|---|---|
.forest/config.yaml |
shared | committed |
.forest/checkouts/<name>/forest.yaml |
shared | committed |
.forest/HEAD |
user-local | gitignored |
.forest/checkouts/<name>/local.yaml |
user-local | gitignored |
.forest/checkouts/<name>/sync_state.json |
user-local | gitignored |
The .gitignore entries are managed by forest — forest init writes
them. A fresh clone restores the local
half with forest bind + forest remote use + forest pull
(worked example).