Skip to content

The annotated script, as it ran

The kit's own script, demo.sh, played all 16 steps of Demo 1 in a clean Ubuntu 24.04 container with no Nix and no network. Its log, unedited:

# container-run.sh good  2026-11-07T11:05:48Z  image mirror.gcr.io/library/ubuntu@sha256:fe81560990fae86dc4743202b2cb4d3df98d0bcf949173cecef15ce1127ef852
== /etc/os-release
PRETTY_NAME="Ubuntu 24.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.5 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
== uname
Linux 7.0.0-1017-azure x86_64
== user
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu)
== is there a /nix in this container?
no: test -e /nix fails; ls says:
ls: cannot access '/nix': No such file or directory
== tools the host image lacks
jq: absent
curl: absent
nix: absent
patchelf: absent
== unpack
singular v1.1.0
commit 1107167c6aa2cf1e131adc3426a319eabdb5ee93
cardano-node 10.7.0
kit v1.1.0-2
tutorial-stepper 0.4.1
== run demo.sh
2026-11-07T11:05:51Z
Singular Demo 1: three actors on a private chain
Mode: continuous
Processing window: 120 seconds; reclaim window: 60 seconds.
The transcript and each actor's receipts stay in /out/singular-demo-run
Starting the private chain and the three wallets.

Step 1: Carol creates the registry
Carol starts a registry for the group. Its token identifies it for everyone; creating it gives Carol no exclusive right to fold requests. Each actor signs in their own directory. The processing and reclaim windows are fixed when the registry is created.

Step 2: join using the public token
Carol publishes only the registry token. Alice and Bob each make their own settings file, with their own journal. The token and the chain's web API are all a reader needs. The private chain has no public explorer; the recorded preprod run links its public transaction IDs to an explorer.

Step 3: Alice books her key
Alice registers the key alice with a payload and a protected deposit of 3 ada. The booking creates a pending request; it does not change the registry yet. She sends nothing to a folder: the request is public on the chain. It must be folded before its processing deadline, on the next page.

Step 4: Bob folds Alice's booking
Bob needs the public token, chain data and his own wallet. He does not need to know which requests are pending, and he receives no file from Alice's wallet or journal: `open-datum fold` finds the registry's pending requests on the chain and folds the ones it can. The fold creates Alice's holding with its payload and deposit. The receipt lists what was folded, what was skipped and why, and how many foldable requests were left. Here it folds Alice's registration, skips nothing and leaves nothing.

Step 5: Alice reads her key
Alice reads her key back from the chain. `open-datum inspect` answers `live`: the holding carries her payload and her protected deposit. The read signs nothing and needs only the registry token and the chain's web API, from her settings file. Its `outputRef` is the holding she spends to change the payload.

Step 6: Alice changes the payload
The controller signs a direct update of the holding. It spends the previous holding and creates its replacement with the same protected control and deposit. No request is booked and no fold is needed.

Step 7: read the key again
Alice reads the key again. It is still `live`, with the new payload and the same deposit, in the holding the update created. The previous holding has been spent.

Step 8: Alice books permanent termination
Termination spends Alice's holding and donates its protected deposit to the treasury. It creates a pending termination carrying the key's active token and approval. This is permanent, has no reclaim window and cannot be undone. A later fold burns those tokens; the key can never be registered again.

Step 9: Bob folds the termination
Anyone can fold the public termination request. Bob first reads the key: `open-datum inspect` answers `pending-termination`, at the request Alice booked. Then he folds whatever is pending, as on step 4: the fold finds the termination, burns its approval and active token, and Bob receives the request's value after his fee. Alice's protected deposit has already gone to the treasury.

Step 10: read the terminated key
Alice reads her key with `singular registry inspect`, which knows no application. It answers `terminal`: the key was registered and terminated, and can never be registered again. A key nobody registered answers `never-registered`. Both answers are read from the registry's state output on the chain.

Step 11: clean up an expired registration
Alice books a different key, late, and nobody folds it: no fold runs between this booking and its cleanup, so no fold finds it while it can still be folded. After both its processing and reclaim windows expire, anyone may clean it up. The refund goes to the registration's refund address. Cleanup is a separate command from reclaim and applies to registrations, not permanent termination.

Step 12: Bob books his own key with external signing
Bob prepares a registration for the key bob, signs its bundle with his own wallet, then submits it. Building alone sends nothing; exit status 19 means the bundle is waiting for signatures. Each part stays in Bob's directory.

Step 13: Alice folds Bob's booking
Alice folds with her own wallet whatever is pending, which is Bob's registration. The fold is open to everyone and does not need Bob's key, payload file or journal, nor the reference of his booking.

Step 14: Alice takes back an unprocessed registration
Alice books the key unfolded, and nobody folds it: no fold runs before the reclaim, so no fold finds it. Once its processing deadline passes, the refund address can sign a reclaim during its reclaim window. This request disappears without ever registering the key, and its locked value returns to Alice.

Step 15: book one more key
The reclaimed request was never folded. Alice now books a fresh key, dave, so Bob can demonstrate an externally signed fold.

Step 16: Bob folds the final registration with external signing
Bob builds a fold of whatever is pending, which is the registration of dave, signs its bundle with his own wallet, then submits it. The receipt records the confirmed transaction and its outputs.
87 commands played
ok    carol/receipts/01-create.json
ok    carol/receipts/01-create.json
ok    alice/receipts/03-insert-alice.json
ok    bob/receipts/04-fold-insert.json
ok    alice/receipts/06-update.json
ok    alice/receipts/08-terminate.json
ok    bob/receipts/09-fold-terminate.json
ok    alice/receipts/11a-insert-late.json
ok    bob/receipts/11d-cleanup.json
ok    alice/receipts/13-fold-bob.json
ok    alice/receipts/14a-insert-unfolded.json
ok    alice/receipts/14c-reclaim.json
ok    alice/receipts/15-insert-dave.json
ok    alice/receipts/03-insert-alice.json
ok    alice/receipts/06-update.json
ok    alice/receipts/08-terminate.json
ok    bob/receipts/12-insert-bob.json
ok    bob/receipts/12-insert-bob.submitted.json
ok    bob/receipts/16a-fold-dave.json
ok    bob/receipts/16a-fold-dave.submitted.json
ok    bob/receipts/04-fold-insert.json: folded the registration of alice, skipped nothing, left 0
ok    bob/receipts/09-fold-terminate.json: folded the termination of alice, skipped nothing, left 0
ok    alice/receipts/13-fold-bob.json: folded the registration of bob, skipped nothing, left 0
ok    bob/receipts/16a-fold-dave.json: folded the registration of dave, skipped nothing, left 0
ok    alice/receipts/05-read-alice.json: live
ok    alice/receipts/07-read-alice.json: live
ok    bob/receipts/09-read-alice.json: pending-termination
ok    alice/receipts/10-read-alice.json: terminal
ok    alice/receipts/10-read-nobody.json: never-registered
All lifecycle receipts and reads passed.
Lifecycle steps passed: 16/16
All 16 steps passed.
2026-11-07T11:13:55Z
== demo.sh exit status: 0
== transaction tools on the demo's own bundles
first bundle: /out/singular-demo-run/bob/bundles/insert-bob.bundle
second bundle: /out/singular-demo-run/bob/bundles/fold-dave.bundle
TXTOOLS inspect-exit=0
TXTOOLS fee-lines=1
+- fee
TXTOOLS diff-same-exit=0
TXTOOLS diff-different-exit=1
TXTOOLS diff-different-A-lines=16 B-lines=16
== anything left running?
kit processes still running: 0
(scan done)
== docker run exit status: 0