Pular para conteúdo

AUR-012 - bootstrapping the consumer demo repository

Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.

demo-repo/scaffold is the sealed source of the AurumCode consumer demo repository, and tests/e2e/consumer/bootstrap/bootstrap.sh is the program that installs it into an empty checkout.

The outcome this card owns is one sentence: an empty repository receives all the scenarios, without importing private AurumCode data. Both halves are measured. The first half is a count -- every scenario the catalog declares must arrive, byte for byte. The second half is a refusal -- private material is planted on purpose and the bootstrap has to abort before any effect.

Behavior

bash tests/e2e/consumer/bootstrap/bootstrap.sh <scaffold-dir> <target-dir>

<target-dir> must be an empty checkout: an existing directory with no entries, or a path that does not exist yet. A fresh git clone or git init leaves exactly that.

On success the program writes one line, then the copy plan in sealed order:

consumer-bootstrap/bootstrapped entries=<n> scenarios=<n> tree_digest=sha256:<hex>
copied<TAB><path>

On refusal it writes a single consumer-bootstrap/<code>[:<detail>] line and exits 1. A detail is a path, a name or a count; payload bytes are never printed, so a refusal can never become the leak it is reporting.

Exit codes: 0 bootstrapped, 1 typed refusal, 64 usage, 79 inconclusive environment.

The copy itself is delegated to demo-repo/scaffold/scaffold.sh, which knows one rule -- copy the allowlist and only the allowlist. The two programs are deliberately separate: the copier is treated as untrusted, so the checkout it produced is verified again by the bootstrap after the fact.

Why git is never invoked

The card's sealed profile bootstrap-readonly-v1 pins an image with bash and a Go toolchain but no git binary, and the worker runs with a read-only rootfs and no network. An empty checkout is therefore treated as a working tree with no entries, which is what the consumer repository actually starts as, and the whole claim stays provable in the only environment that gates this card.

Contract

The allowlist

demo-repo/scaffold/allowlist.manifest is line-oriented so every field is matched by an exact expression:

schema: aurum.demo-scaffold-allowlist
version: 1
tree-root: tree
limit: <name> <value>
scenario: <id> <entry-count>
entry: <scenario-id|-> <mode> <bytes> <sha256> <path>

Entries are sorted by path. - marks an entry that belongs to no scenario. The manifest describes demo-repo/scaffold/tree, which holds the payload that reaches the consumer; scaffold.sh and the manifest itself are control files and are never copied.

What keeps private data out

An allowlist alone is data, and data can be tampered with, so three independent rules have to hold before anything is written:

  1. Path closure. Every file under tree/ must be an allowlist entry (path-not-allowlisted) and every entry must be present (entry-missing).
  2. Path policy, which no manifest can widen. An entry path must be a safe relative path of at most 128 bytes matching [A-Za-z0-9][A-Za-z0-9._/-]* with no .. segment (path-shape-denied), and no segment may name an AurumCode-private root -- .board, .git, .github, .taskmaster, .claude, .ssh, .aurum-bootstrap, credentials, secrets, internal, or a .env file (private-path-denied). A symlink is refused outright (symlink-denied), so nothing can be imported by reference.
  3. Payload screen. Bytes are read, not merely hashed. A payload carrying an AurumCode-private marker (private-marker-detected) or a value shaped like a real credential -- PEM private key headers, AKIA..., sk-..., gh?_... (credential-shape-detected) -- is refused even when the manifest seals its digest correctly.

The synthetic placeholder shape AURUM-FAKE-<NAME> is deliberately not private. The catalog contains a scenario about a leaked credential, and it has to survive the screen; that is what stops the screen from passing by refusing everything.

After the copy, path closure, digests and the payload screen are applied again to the bytes that actually landed. A copier that writes a file nobody allowlisted is caught there (unlisted-entry-copied) even though the scaffold it read was clean.

Domain limits

limit value first value beyond it
max_entries 64 65 entries -> too-many-entries
max_file_bytes 65536 65537 bytes -> file-too-large
max_total_bytes 1048576 beyond -> tree-too-large
max_path_bytes 128 129 bytes -> path-too-long
max_scenarios 32 33 scenarios -> too-many-scenarios

The limits are constants in the bootstrap and are also declared in the manifest. A manifest that declares a different number is refused with limit-divergence: the domain lives in the program, never in the data.

The sealed catalog

Six scenarios, 15 entries, all synthetic:

scenario kind what a review of it should say
clean-change clean nothing to report
docs-drift documentation the document no longer matches the code
flaky-test test quality the test depends on wall-clock timing
null-deref correctness an absent lookup result is dereferenced
secret-leak security a placeholder credential sits in source
sql-injection security a query is built by string concatenation

Sealed values of the checked-in scaffold, recomputed by the acceptance on every run:

  • scaffold input digest (allowlist plus the tree it describes): sha256:c7b1832092b391d7437481ab0129380ad3d12e6ae27cc1db73035029812e92b1
  • consumer checkout digest after a nominal bootstrap: sha256:ab5a1b68eed1799a6838406959142eefae0828e05c008a08459d9809dc7c3b59

Example (no secret involved)

Bootstrap the demo repository into an empty checkout. This is the same call the acceptance makes for the nominal-bootstrap vector, so the example cannot drift from the behavior:

out="$(mktemp -d)/consumer"
bash tests/e2e/consumer/bootstrap/bootstrap.sh demo-repo/scaffold "$out"
# consumer-bootstrap/bootstrapped entries=15 scenarios=6 \
#   tree_digest=sha256:ab5a1b68eed1799a6838406959142eefae0828e05c008a08459d9809dc7c3b59

Resealing the allowlist after a scenario changes, from the repository root:

cd demo-repo/scaffold
{
  sed -n '1,7p' allowlist.manifest
  for s in $(sed -n 's/^scenario: \([a-z0-9-]*\) .*/\1/p' allowlist.manifest); do
    printf 'scenario: %s %d\n' "$s" "$(cd tree && find "scenarios/$s" -type f | wc -l)"
  done
  (cd tree && find . -type f | sed 's|^\./||' | LC_ALL=C sort) | while read -r p; do
    case "$p" in scenarios/*/*) sc="${p#scenarios/}"; sc="${sc%%/*}" ;; *) sc='-' ;; esac
    printf 'entry: %s 0644 %s %s %s\n' "$sc" \
      "$(stat -c '%s' "tree/$p")" "$(sha256sum "tree/$p" | cut -d' ' -f1)" "$p"
  done
} > allowlist.manifest.new && mv allowlist.manifest.new allowlist.manifest

The acceptance recomputes the same values, so a manifest that drifts from the tree fails scaffold-manifest-divergence before any vector runs.

Diagnosis

observation what it means
AUR-012/AC-001/behavior-missing the scaffold or the bootstrap is absent
AUR-012/AC-001/detector-missed:<vector> a planted vector was accepted
AUR-012/AC-001/planted-undetected:<n> private material reached the copy stage
AUR-012/AC-001/MUT-001 the copier put an unlisted file in the consumer
AUR-012/AC-001/nominal:scenario-not-delivered:<path> a scenario did not arrive
AUR-012/AC-001/vector-effects:<v>:want=0:got=<n> a refusal wrote to the checkout
AUR-012/AC-001/inconclusive/... environment, never red evidence

consumer-bootstrap/inconclusive/... on stderr with exit 79 is likewise an environment result. Only exit 0 is green; exit 1 is behavioral red.

Non-goals

Running the scenarios is not part of this card. The bootstrap proves that an empty repository receives them; executing a review over them belongs to the end-to-end cards that consume this checkout.