Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions .github/demo-box-release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,60 @@ Download the one archive matching your machine:
| macOS, Apple silicon | `hello-box-1.0.0-macos-aarch64-metal.zip` |
| Windows, Intel or AMD | `hello-box-1.0.0-windows-x86_64-cpu.zip` |

Unpack it and you get two files: the box and its signed release document. Run the commands from the
directory holding them, with
[`examples/keys/example-signing-public.json`](../blob/main/examples/keys/example-signing-public.json)
from the repository:
Unpacking it gives a folder that already runs: the box under `box/`, plus `run-box.ts` and
`run_box.py` for driving it from an application instead of the terminal.

```text
scrollcase-demo/
├── box/
│ ├── <archive sha256>.zip the box — leave it zipped and named as it is
│ └── <document sha256>.release.json
├── run-box.ts
├── run_box.py
└── package.json
```

The trust key is deliberately **not** in that archive. A signature only proves where something came
from if the key does not arrive in the same package, so it is fetched from the repository:

```sh
unzip hello-box-1.0.0-<target>.zip -d scrollcase-demo
cd scrollcase-demo
mkdir keys
curl -o keys/example-signing-public.json \
https://raw.githubusercontent.com/suffro/scrollcase/main/examples/keys/example-signing-public.json
```

Then any one of these three — they perform the same checks in the same order:

```sh
# Terminal
npm install -g scrollcase
unzip hello-box-1.0.0-<target>.zip -d hello-box && cd hello-box
scrollcase verify *.release.json --public-key example-signing-public.json
scrollcase run *.release.json --public-key example-signing-public.json
scrollcase verify box/*.release.json --public-key keys/example-signing-public.json
scrollcase run box/*.release.json --public-key keys/example-signing-public.json

# Node
npm install && npx tsx run-box.ts

# Python
python -m pip install scrollcase-consumer && python run_box.py
```

On PowerShell that glob is not expanded for a command like this — use
`(Get-ChildItem box\*.release.json).FullName`, or simply type the file name you see under `box/`.

`verify` checks the signature, the archive's size and hash, the entry names and the manifest.
Adding `--self-test` extracts the box and imports with the interpreter inside it, and `run` executes
its entry point — both need the machine to match the box's target. `verify` on its own works
anywhere.

The two unpacked names are SHA-256 digests of their own contents: two builds of the same commit
The two names under `box/` are SHA-256 digests of their own contents: two builds of the same commit
produce the same names, which is what makes the archive verifiable in the first place. Keep them as
they are and side by side — `verify` finds the box by the hash its release document commits to, and
renaming or separating them breaks that. The enclosing zip exists only so the download says which
machine it is for.
renaming or separating them breaks that. The enclosing zip carries no guarantee of its own; it holds
the pair and the examples, and its name says which machine they are for.

Full walkthrough: [the demo box guide](https://scrollcase.dev/guides/demo-box).

## About the signing key

Expand Down
24 changes: 21 additions & 3 deletions .github/workflows/demo-box.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,16 @@ jobs:
# that is how `verify` finds it. Published flat, that gave six hex names on the release page and
# no way to tell which three were yours before downloading them.
#
# So each target ships as one plainly named container holding that pair. The names inside stay
# content-addressed and adjacent, which is what `verify` needs; the name outside says which
# So each target ships as one plainly named container. The pair keeps content-addressed names
# and stays adjacent under `box/`, which is what `verify` needs; the name outside says which
# machine it is for, which is what a person needs. Stored rather than compressed: the inner
# archive is already deflated, and recompressing it would cost minutes to save nothing.
#
# The consumer examples travel with it, so unpacking gives a folder that already runs three
# ways instead of source to retype. They are copied from `examples/demo-consumers/`, the same
# files the guide embeds, so the page cannot document something other than what ships. The
# trust key is deliberately absent: a signature proves nothing if the key arrives in the same
# package as what it signs.
- name: Wrap each target as one plainly named archive
shell: bash
run: |
Expand All @@ -159,7 +165,19 @@ jobs:
ls -l "$dir" >&2
exit 1
fi
zip -0 -X -j "wrapped/$BOX_ID-$BOX_VERSION-$target.zip" "${zips[0]}" "${releases[0]}"
stage="staging/$target"
mkdir -p "$stage/box"
cp "${zips[0]}" "${releases[0]}" "$stage/box/"
cp examples/demo-consumers/run-box.ts \
examples/demo-consumers/run_box.py \
examples/demo-consumers/package.json \
examples/demo-consumers/README.md "$stage/"
# Entries named explicitly rather than with `.`, so the archive carries no `./` prefix
# and no stray dotfile the runner happens to leave in the staging directory.
( cd "$stage" && zip -0 -X -r -q \
"$GITHUB_WORKSPACE/wrapped/$BOX_ID-$BOX_VERSION-$target.zip" \
box run-box.ts run_box.py package.json README.md )
unzip -l "wrapped/$BOX_ID-$BOX_VERSION-$target.zip"
done
ls -l wrapped

Expand Down
Loading
Loading