Skip to content

Worked Example: Accepting One Capstone Release Candidate

This worked example uses the course capstone rather than a hypothetical app and LICENSE. You will produce two candidates, inspect the archive before extraction, compare the member contract, rehearse installation below a repository-owned root, inject two failures, and write an acceptance record bound to exact bytes.

No command writes to a host install path or publishes remotely.

Page Maps

graph LR
  family["Reproducible Research"]
  program["Deep Dive Make"]
  section["Release Engineering Artifact Contracts"]
  page["Worked Example: Accepting One Capstone Release Candidate"]
  capstone["Capstone evidence"]

  family --> program --> section --> page
  page -.applies in.-> capstone
flowchart LR
  build["produce candidate twice"] --> identity["compare exact bytes"]
  identity --> members["compare declared and observed members"]
  members --> safety["reject unsafe archive paths and types"]
  safety --> extract["extract under consumer root"]
  extract --> install["rehearse install twice"]
  install --> reject["prove corrupt and unsafe candidates fail"]
  reject --> accept["bind acceptance to digest"]

What this example does and does not claim

The capstone dist producer is intentionally small. It creates deterministic archive bytes with normalized ownership and timestamps. It does not implement signing, remote publication, system installation, or a package-manager transaction.

The example treats its archive as an internal transfer candidate with this exact payload:

app
build/bin/dyn1
build/bin/dyn2
build/include/dynamic.h

That scope matters. A public end-user package would normally add a versioned top-level directory, license, consumer documentation, and a supported install route. Passing this acceptance packet does not turn the capstone archive into that broader product.

The existing dist target archives the whole build/bin directory, including dependency files. This example repairs that package-selection defect by invoking the same deterministic producer with only the four declared payloads. The resulting evidence shows why explicit member policy is stronger than "archive whatever is nearby."

Prepare one repository-owned packet

Run every command from the bijux-masterclass repository root. Use GNU Make 4.3 or newer; on macOS that command is normally gmake.

CAPSTONE=programs/reproducible-research/deep-dive-make/capstone
PACKET=artifacts/module08-release

test ! -e "$PACKET" || {
  printf '%s\n' "Refusing to replace existing evidence: $PACKET" >&2
  exit 2
}
mkdir -p \
  "$PACKET/candidate" \
  "$PACKET/evidence" \
  "$PACKET/consumer" \
  "$PACKET/failures"

The packet separates candidate bytes, observations, consumer state, and deliberate failures. It can be removed after the exercise without touching source files.

Declare package policy independently

Create the expected regular-file list before inspecting a candidate:

printf '%s\n' \
  app \
  build/bin/dyn1 \
  build/bin/dyn2 \
  build/include/dynamic.h \
  > "$PACKET/evidence/package-members.txt"

This manifest comes from package policy. Do not replace it with output generated from the archive and then compare that output with itself.

Produce the first candidate

Build the payloads, then call the capstone's deterministic archive producer with explicit inputs:

gmake -C "$CAPSTONE" clean all

(
  cd "$CAPSTONE"
  python3 scripts/mkdist.py \
    "$OLDPWD/$PACKET/candidate/capstone.tar.gz" \
    app \
    build/bin/dyn1 \
    build/bin/dyn2 \
    build/include/dynamic.h
)

cp \
  "$PACKET/candidate/capstone.tar.gz" \
  "$PACKET/evidence/capstone-first.tar.gz"

mkdist.py sorts members, fixes modification times, normalizes uid and gid, fixes the gzip header time, and clears the gzip filename field. Those controls make output basename irrelevant to candidate bytes. They create a reproducibility claim that you still need to test.

Compute the exact candidate identity:

shasum -a 256 "$PACKET/candidate/capstone.tar.gz" \
  > "$PACKET/evidence/capstone.sha256"
cat "$PACKET/evidence/capstone.sha256"

If your environment uses sha256sum, use it consistently for generation and checking. The algorithm and sidecar format are part of the local tool contract.

Produce independently again

Recreate the payload tree and produce another candidate. The archive is published through the same candidate path, while each observation is retained under a different evidence name:

gmake -C "$CAPSTONE" clean all

(
  cd "$CAPSTONE"
  python3 scripts/mkdist.py \
    "$OLDPWD/$PACKET/candidate/capstone.tar.gz" \
    app \
    build/bin/dyn1 \
    build/bin/dyn2 \
    build/include/dynamic.h
)

cp \
  "$PACKET/candidate/capstone.tar.gz" \
  "$PACKET/evidence/capstone-second.tar.gz"
cmp \
  "$PACKET/evidence/capstone-first.tar.gz" \
  "$PACKET/evidence/capstone-second.tar.gz"

No output from cmp and exit status zero establish identical bytes for these two observed builds. They do not prove every future tool version and platform will produce the same bytes.

Keep capstone.tar.gz as the candidate under review. Later commands must inspect that file without rebuilding it.

Inspect members before extraction

Record the verbose archive view:

tar -tvzf "$PACKET/candidate/capstone.tar.gz" \
  > "$PACKET/evidence/archive-listing.txt"
cat "$PACKET/evidence/archive-listing.txt"

Now derive the observed regular-file paths and compare them with policy:

tar -tzf "$PACKET/candidate/capstone.tar.gz" \
  | sed '/\/$/d' \
  | LC_ALL=C sort \
  > "$PACKET/evidence/archive-members.txt"

cmp \
  "$PACKET/evidence/package-members.txt" \
  "$PACKET/evidence/archive-members.txt"

The terse listing is sufficient only because the producer received regular files and the next gate audits member types. A general package verifier must not infer types from trailing slashes alone.

Audit extraction safety

Use Python's archive reader to reject absolute paths, traversal components, links, and special file types before extraction:

python3 - "$PACKET/candidate/capstone.tar.gz" <<'PY'
from pathlib import PurePosixPath
import sys
import tarfile

archive = sys.argv[1]
with tarfile.open(archive, "r:gz") as bundle:
    for member in bundle.getmembers():
        path = PurePosixPath(member.name)
        if path.is_absolute() or ".." in path.parts:
            raise SystemExit(f"unsafe path: {member.name}")
        if member.issym() or member.islnk():
            raise SystemExit(f"links are not allowed: {member.name}")
        if not (member.isfile() or member.isdir()):
            raise SystemExit(f"unsupported member type: {member.name}")
print("PASS: archive member paths and types satisfy policy")
PY

This gate inspects, but does not rewrite, the candidate. An unsafe name causes rejection.

Extract under a contained consumer root

Only after the safety and member-contract gates pass. The new packet guarantees this destination does not contain an older extraction:

mkdir -p "$PACKET/consumer/extracted"
tar -xzf "$PACKET/candidate/capstone.tar.gz" \
  -C "$PACKET/consumer/extracted"

find "$PACKET/consumer/extracted" -type f -print \
  | sed "s#^$PACKET/consumer/extracted/##" \
  | LC_ALL=C sort \
  > "$PACKET/evidence/extracted-members.txt"

cmp \
  "$PACKET/evidence/package-members.txt" \
  "$PACKET/evidence/extracted-members.txt"

The second comparison proves the extracted regular-file view still matches policy. It does not replace the pre-extraction safety audit.

Rehearse a bounded install twice

The capstone does not claim a public install target. For this consumer rehearsal, define a narrow mapping for the accepted app payload:

app -> /usr/local/bin/make-capstone

Apply it below a repository-owned DESTDIR equivalent:

INSTALL_ROOT="$PACKET/consumer/install-root"
SOURCE_APP="$PACKET/consumer/extracted/app"
INSTALLED_APP="$INSTALL_ROOT/usr/local/bin/make-capstone"

install -d "$(dirname "$INSTALLED_APP")"
install -m 0755 "$SOURCE_APP" "$INSTALLED_APP"

find "$INSTALL_ROOT" -type f -print \
  | sed "s#^$INSTALL_ROOT/##" \
  | LC_ALL=C sort \
  > "$PACKET/evidence/install-first-paths.txt"
shasum -a 256 "$INSTALLED_APP" \
  > "$PACKET/evidence/install-first.sha256"

install -d "$(dirname "$INSTALLED_APP")"
install -m 0755 "$SOURCE_APP" "$INSTALLED_APP"

find "$INSTALL_ROOT" -type f -print \
  | sed "s#^$INSTALL_ROOT/##" \
  | LC_ALL=C sort \
  > "$PACKET/evidence/install-rerun-paths.txt"
shasum -a 256 "$INSTALLED_APP" \
  > "$PACKET/evidence/install-rerun.sha256"

cmp \
  "$PACKET/evidence/install-first-paths.txt" \
  "$PACKET/evidence/install-rerun-paths.txt"
cmp \
  "$PACKET/evidence/install-first.sha256" \
  "$PACKET/evidence/install-rerun.sha256"

The rehearsal proves convergence for this one-file mapping. It does not claim rollback, upgrade, removal, or package-manager ownership semantics.

Prove identity corruption is rejected

Create a corrupted copy, leaving the reviewed candidate untouched:

cp \
  "$PACKET/candidate/capstone.tar.gz" \
  "$PACKET/failures/corrupt-capstone.tar.gz"
printf 'corruption' >> "$PACKET/failures/corrupt-capstone.tar.gz"

Compare its digest with the accepted candidate's recorded digest:

accepted_digest=$(
  awk '{print $1}' "$PACKET/evidence/capstone.sha256"
)
corrupt_digest=$(
  shasum -a 256 "$PACKET/failures/corrupt-capstone.tar.gz" \
  | awk '{print $1}'
)

test "$accepted_digest" != "$corrupt_digest"

Success means the test observed different identities. The corrupt copy must not receive an acceptance record merely because its archive parser still tolerates trailing bytes.

Prove unsafe paths are rejected

Create a deliberately unsafe archive in the failures area:

python3 - "$PACKET/failures/unsafe-capstone.tar.gz" <<'PY'
from io import BytesIO
import sys
import tarfile

with tarfile.open(sys.argv[1], "w:gz") as bundle:
    payload = b"must not escape\n"
    member = tarfile.TarInfo("../escape.txt")
    member.size = len(payload)
    bundle.addfile(member, BytesIO(payload))
PY

Run the same safety logic and expect a nonzero status:

if python3 - "$PACKET/failures/unsafe-capstone.tar.gz" <<'PY'
from pathlib import PurePosixPath
import sys
import tarfile

with tarfile.open(sys.argv[1], "r:gz") as bundle:
    for member in bundle.getmembers():
        path = PurePosixPath(member.name)
        if path.is_absolute() or ".." in path.parts:
            raise SystemExit(f"unsafe path: {member.name}")
        if member.issym() or member.islnk():
            raise SystemExit(f"links are not allowed: {member.name}")
        if not (member.isfile() or member.isdir()):
            raise SystemExit(f"unsupported member type: {member.name}")
PY
then
  printf '%s\n' "FAIL: unsafe archive was accepted" >&2
  exit 1
else
  printf '%s\n' "PASS: unsafe archive was rejected"
fi

Do not extract the unsafe archive. Member inspection is enough to reject it.

Bind acceptance to exact bytes

After all successful gates, write a small local record:

candidate_digest=$(
  awk '{print $1}' "$PACKET/evidence/capstone.sha256"
)

cat > "$PACKET/evidence/acceptance.txt" <<EOF
candidate=capstone.tar.gz
sha256=$candidate_digest
member_contract=PASS
repeat_build=PASS
extraction_safety=PASS
contained_extraction=PASS
install_rerun=PASS
corruption_rejection=PASS
unsafe_path_rejection=PASS
publication=NOT_ATTEMPTED
EOF

cat "$PACKET/evidence/acceptance.txt"

The record is local evidence, not a signature. Its value is that each claim names a gate and the record binds those claims to one digest.

Read the result as a reviewer

The candidate is accepted for the narrow internal-transfer contract because:

  • two clean productions yielded identical bytes
  • the archive's regular-file members matched independent package policy
  • paths and member types passed inspection before extraction
  • extraction remained below the consumer root
  • the install rehearsal converged on the same paths and content
  • controlled corrupt and unsafe candidates were rejected
  • the acceptance record names the reviewed digest

The candidate is not accepted as a public end-user release because that contract was never tested. It still lacks a versioned package root, license, consumer documentation, supported installation interface, signature policy, and remote publication controls.

That distinction is release engineering: evidence supports a bounded claim, not a vague feeling that the archive "looks good."

End-of-example checkpoint

Before continuing, make sure you can explain:

  • why this example bypasses the capstone's broad build/bin selection
  • why the expected member list is written before archive inspection
  • why deterministic producer code still needs two observed builds
  • why member inspection must happen before extraction
  • what the one-file install rehearsal proves and what it does not
  • why the acceptance record belongs to a digest rather than a mutable filename