Skip to content

Commit 007f891

Browse files
authored
docs: correct two claims this repository makes that do not hold (#64)
Both were found by someone following the documentation exactly. Neither is a code change; the code was doing what it does and the documents described something else. 1. THE MODE IS NOT 644 OR 755. `FileMeta::executable` claimed "a file is either mode 644 or 755, and no other permission bit is replicated". The second half is true. The first half is not. Materialization creates the file with `fs::write`, giving `0666 & ~umask`, and then ORs in `0o111`. The mode therefore depends on the RECEIVING host's umask. Observed on hetz and droppy on 2026-08-23: 0664 for a plain file and 0775 for an executable one, both under umask 002. Only the executable BIT replicates. Nothing may depend on the exact figure. The comment now gives the umask table and says so, and notes that st2's render deliberately writes an exact mode instead, so the two systems differ on purpose. 2. THE CHECKSUM STEP HAS NEVER BEEN RUNNABLE. "Upgrading Fabric Safely" told the operator to fetch a combined `SHA256SUMS` from the release. No release has ever had one: `git log -S'SHA256SUMS' -- .github/workflows/release.yml` returns nothing, because the workflow publishes one `.sha256` PER ASSET and never a combined manifest. So the documented verification could not complete against any release this project has published, since b7ce527 added it. It stopped nobody until tonight, when `hetz.root` followed it exactly, could not fetch the file, and refused to substitute a different checksum rather than improvise a verification step. That refusal was correct and it is the only reason this was found. The step now fetches the per-asset `.sha256`. It takes field one rather than running `shasum -c`, because the published file still carries the builder's `dist/` path and a check against it fails on the path, not on the hash. The workflow is left alone. It was never wrong; the document described a release layout that never existed. Agent: Silber.fabric
1 parent 42fb6b3 commit 007f891

2 files changed

Lines changed: 25 additions & 6 deletions

File tree

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,8 @@ Upgrading the fabric binary under a running daemon — especially on a remote
355355
machine reached only over `fabric shell` — must be done lockout-safe: a botched
356356
restart can sever the only path back to the box. Follow this order.
357357

358-
Download a release asset directly, verify it against the release's combined
359-
`SHA256SUMS` manifest, and stage both the old and new binaries with same-directory
358+
Download a release asset directly, verify it against that asset's published
359+
`.sha256`, and stage both the old and new binaries with same-directory
360360
renames. A release archive contains exactly one member named literal `fabric`
361361
(not `./fabric`); verify that shape before extracting:
362362

@@ -377,9 +377,12 @@ download_dir="$(mktemp -d)"
377377
trap 'rm -rf "$download_dir"' EXIT
378378

379379
curl --fail --location "$release_url/$asset" --output "$download_dir/$asset"
380-
curl --fail --location "$release_url/SHA256SUMS" --output "$download_dir/SHA256SUMS"
380+
curl --fail --location "$release_url/$asset.sha256" --output "$download_dir/$asset.sha256"
381381

382-
expected="$(awk -v asset="$asset" '$2 == asset { print $1 }' "$download_dir/SHA256SUMS")"
382+
# The release publishes ONE .sha256 per asset, not a combined manifest. The file
383+
# holds "<hash> dist/<archive>", with the builder's path still in it, so
384+
# `shasum -c` fails here. Take field one and compare it ourselves.
385+
expected="$(awk 'NR == 1 { print $1 }' "$download_dir/$asset.sha256")"
383386
test -n "$expected"
384387
if command -v sha256sum >/dev/null 2>&1; then
385388
actual="$(sha256sum "$download_dir/$asset" | awk '{ print $1 }')"

src/sync/manifest.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,24 @@ pub struct FileMeta {
5656
pub hash: ContentHash,
5757
/// File size in bytes (informational; the hash is the identity).
5858
pub size: u64,
59-
/// Whether the file is executable, the way git tracks it: a file is either
60-
/// mode 644 or 755, and no other permission bit is replicated.
59+
/// Whether the file is executable, the way git tracks it. ONLY this bit
60+
/// replicates; no other permission bit crosses.
61+
///
62+
/// The resulting MODE is not fixed, and an earlier version of this comment
63+
/// claimed it was. Materialization creates the file with `fs::write`, which
64+
/// yields `0666 & ~umask`, and then ORs in `0o111`. So the mode depends on
65+
/// the RECEIVING host's umask:
66+
///
67+
/// | receiver umask | plain file | executable file |
68+
/// | --- | --- | --- |
69+
/// | 022 | 0644 | 0755 |
70+
/// | 002 | 0664 | 0775 |
71+
///
72+
/// Observed on hetz and droppy on 2026-08-23: 0664 and 0775, both umask
73+
/// 002. Do NOT depend on an exact mode here; depend only on the executable
74+
/// bit. st2's render deliberately differs: it writes an exact mode that is
75+
/// immune to umask, so the two systems do not agree on the other bits and
76+
/// are not meant to.
6177
///
6278
/// Defaulted so a peer that predates this field still parses. Note the
6379
/// asymmetry: adding a field with a default is safe, removing one is not.

0 commit comments

Comments
 (0)