diff --git a/docs/guides/demo-box.md b/docs/guides/demo-box.md index 048d3b0..c62f4b7 100644 --- a/docs/guides/demo-box.md +++ b/docs/guides/demo-box.md @@ -32,26 +32,34 @@ The demo box is the .zip you find inside it, next to the .rel Once you have downloaded the demo, follow these steps: -1. **Install scrollcase and unzip the demo:** +1. **Install scrollcase and unpack the demo into a folder of its own:** ```sh npm install -g scrollcase # this will install it globally -unzip hello-box-1.0.0-.zip -d hello-box -cd hello-box +mkdir scrollcase-demo && cd scrollcase-demo +unzip ../hello-box-1.0.0-.zip -d box ``` -> **hello-box** contains 2 files: the **demo box** `.zip` to run, and its matching `.release.json`.
+> **box/** now holds 2 files: the **demo box** `.zip` to run, and its matching `.release.json`.
--- -2. **Download the demo public key into the same folder:** +2. **Download the demo public key, next to the box rather than inside it:** ```sh -curl -O https://raw.githubusercontent.com/suffro/scrollcase/main/examples/keys/example-signing-public.json +mkdir keys +curl -o keys/example-signing-public.json \ + https://raw.githubusercontent.com/suffro/scrollcase/main/examples/keys/example-signing-public.json ``` or alternatively here's its GitHub link: [`example-signing-public.json`](https://github.com/suffro/scrollcase/blob/main/examples/keys/example-signing-public.json) +::: tip Why the key lives outside the box +A signature only proves where something came from if the key does not travel with it. Keeping the +key in its own folder — and downloading it from the repository rather than the release — is the +habit to carry into a real project, where the key will not be a demo key. +::: + --- 3. **Verify and run the box:** @@ -60,25 +68,26 @@ or alternatively here's its GitHub link: [`example-signing-public.json`](https:/ ```sh -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 ``` ```powershell -scrollcase verify (Get-ChildItem *.release.json).Name --public-key example-signing-public.json -scrollcase run (Get-ChildItem *.release.json).Name --public-key example-signing-public.json +scrollcase verify (Get-ChildItem box\*.release.json).FullName --public-key keys\example-signing-public.json +scrollcase run (Get-ChildItem box\*.release.json).FullName --public-key keys\example-signing-public.json ``` -> The `*.release.json` above is a real shell glob, not a placeholder — your shell replaces it -> with the one release document in the folder. PowerShell does not expand globs for a command like +> The `box/*.release.json` above is a real shell glob, not a placeholder — your shell replaces +> it with the one release document in the folder. PowerShell does not expand globs for a command like > this, which is why it needs `Get-ChildItem`. Either way you can always type the file name you see -> after unzipping. +> after unzipping. You never name the box archive: `verify` finds it beside the release document, +> under the hash that document commits to. @@ -86,6 +95,100 @@ scrollcase run (Get-ChildItem *.release.json).Name --public-key example-signing- 4. **Check out the results, that's it.** +At this point the folder looks like this — the box untouched in its own directory, the key beside +it, nothing loose: + +```text +scrollcase-demo/ +├── box/ +│ ├── .zip # the demo box, left exactly as downloaded +│ └── .release.json +└── keys/ + └── example-signing-public.json +``` + +## Run it from your own app + +The CLI is the quickest way to see a box work, but an application does not shell out to it: both +consumers expose the same verify-then-run semantics as a library, and they take the very files you +just downloaded. Same folder, same key, nothing rebuilt. + + + + +```sh +npm install scrollcase +npm install --save-dev tsx typescript +``` + +```ts +// run-box.ts +import { readdirSync } from 'node:fs'; +import { join } from 'node:path'; +import { runBox } from 'scrollcase/consumer'; + +const release = readdirSync('box').find((name) => name.endsWith('.release.json'))!; + +runBox(join('box', release), { + publicPath: 'keys/example-signing-public.json', + stdout: 'inherit', + stderr: 'inherit', + onPrepared: ({ boxId, version, targetId }) => { + console.log(`Running ${boxId} ${version} (${targetId})`); + }, +}).then((result) => { + process.exitCode = result.exitCode ?? 1; +}); +``` + +```sh +npx tsx run-box.ts +``` + + + + +```sh +python -m pip install scrollcase-consumer +``` + +```python +# run_box.py +from pathlib import Path + +from scrollcase_consumer import PreparedBox, run_box + +release = next(Path("box").glob("*.release.json")) + + +def report(prepared: PreparedBox) -> None: + print(f"Running {prepared.box_id} {prepared.version} ({prepared.target_id})", flush=True) + + +result = run_box( + release, + public_key_path="keys/example-signing-public.json", + on_prepared=report, +) +raise SystemExit(result.exit_code or 0) +``` + +```sh +python run_box.py +``` + + + + +Drop either file at the top of `scrollcase-demo/` and run it from there. `runBox` verifies the +signature, extracts to a private temporary directory, executes, and cleans up after itself — the +same chain `scrollcase run` performs, minus the terminal. `onPrepared` fires after verification and +before execution, which is how an application shows what it is about to run without repeating the +trust chain itself. + +The Python package is published separately: `npm install scrollcase` does not install it, and +`pip install scrollcase-consumer` needs no Node at all. Full surface in the +[Library APIs reference](/reference/api). ## What just happened