SOURCE / PINNED RELEASE
Made of little things.
Napcraft
- Release
- ee47ac6a25df…
- Author-recorded commit
- 410dea87e109…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-52f9e22f5ce
Archive hash verified: 9e6e0cabb18d0d46…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
# Consent and recovery release — 2026-09-26
Napcraft now lets a player take time approving backend account use and signing,
and explains uncertain saves without quietly replacing a creation request.
## Tooling and preserved changes
`soyli update` confirmed the installed 0.23.4 executable is current.
`soyli skills update` updated one support document with no conflicts. Source,
dependencies, configuration, publication binding and selected identity were
preserved. The existing local change to `docs/examples/backend-client.ts` was
reviewed and retained exactly: account-bound calls allow 120 seconds for consent
and signing, while describe/discovery calls retain their 15-second UI timeout.
Napcraft and its shared editor already import this helper, so the built app uses
the fix. Provider execution limits and intent expiry are unchanged.
The updated guidance documents the host consent fix and soyLI's narrow validation
of historical public backend metadata. No Git history rewrite is needed. No
accounts were switched or created for publication; the selected remote signer is
`npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf`.
## Recovery behavior
- Waiting buttons explicitly say approval or a reply is pending.
- An uncertain creation keeps its original request ID, payload and expiry. A
valid retry obtains the same creation receipt; it does not create a replacement.
- The returned world code is stored before the pending request is cleared.
- An expired creation explains that the world may already exist. Keeping it
aside must successfully store a recovery record before clearing the active
marker. Practice and joining are then available.
- A known code is read and its world name/edit count shown for explicit
recognition. The original request remains in the linked recovery record.
- Creating a separate world requires acknowledging that the old one may exist.
Retained records are never automatically sent, changed or overwritten.
- Older edit recovery makes a durable copy before the existing safe review;
actor changes and expiry still prevent unsafe replay. Read-only revision
conflicts get up to two bounded retries with fresh query IDs.
## Backend decision
The deployed `worlds` module is active at revision 1, release
`86a73266cceaa724009833c313051c49940c45efc2537dcc27c73eaeed8f6728`.
All three source hashes in its signed receipt match the local manifest, handler
and schemas. `soyli backend check` produces the same compiled artifact
`68ff977382555abde828d449fbe227d5c100ac0ef6ca3b29aab0c615b3099f18`.
No backend source or deployment change is needed. Existing world codes retain
their release and instance.
## Verification before publication
Final HTML: 57,243 bytes, SHA-256
`e3df4c86501215286650d66b64ed708da53fe28013a9083b558bee37bbac5720`.
| Check | Result |
| --- | --- |
| TypeScript, build, guidance/backend/sync tests | Passed; 21 test assertions |
| Backend compilation | Passed; 6 operations, unchanged artifact |
| Reference conformance | 5 passed, 0 failed, 5 skipped (manifest/wire/lifecycle evidence) |
| Native consent/recovery scenario | 19 passed with two independent players, 50 ms latency / 15 ms jitter, local TURN |
| Existing edit correctness | 22 passed, including simultaneous edits, lost replies, reloads and actor changes |
| Responsive host checks | Seven sizes from 200×160 through 1440×900, portrait/landscape touch, host themes and missing optional domains passed |
| Final soyLI check | Passed; matching artifact hash |
| Promotional media | New landscape/portrait PNGs inspected; 7-second VP8 clip decoded and played fully in Listing |
The consent test waits 20 seconds at the real host consent dialog, then delays a
real disposable NIP-07 signature by 25 seconds. It deliberately loses a successful
creation reply and proves the identical retry returns the same instance. It
also verifies two-player edits and reload persistence, exact retention of an
expired legacy request, explicit separate-world consent and code recognition.
The final 19-check run passed with bounded query-conflict retry and deterministic
conflict coverage; exploratory late-read waits informed that coverage.
`tests/public-consent.mjs` prepares a representative unfinished request in the
previous public build, waits for the new publication, and checks the storage
boundary, slow consent/signing, independent public browser processes and reloads.
## Public-host verification
The final public consent scenario passed all 7 assertions against napplet.soy and
the active provider. It used two separate Chromium processes with the website's
normal public ICE configuration; there was no local backend or TURN substitution.
One signed-in disposable test player created a shared world and a guest joined.
Both saw each other, both edited, and both reloaded and recovered the two confirmed
edits. The actual host dialog stayed open for **20,004 ms**, followed by a real
NIP-07 test signature delayed **25,004 ms**. Session binding and creation succeeded.
This verifies the deployed consent/signing fix through the normal browser path.
The public in-page dialog is **Allow network connection**, with **Connect**, and
a separate **Allow multiplayer connections** dialog with **Allow**. These labels
differ from preview fixtures; the test approves only these scoped capabilities.
Unapproved-dialog runs retained the creation request and showed Napcraft's
recovery explanation. They are not evidence of a backend connection failure.
`tests/public-return.mjs` passed another **4 assertions** after closing both
original browser processes. Fresh desktop and 390×844 touch browsers reopened
the world, saw each other, and simultaneously built on different selected
columns. Both changes converged; after closing both browsers again, a third
fresh browser recovered all **5 confirmed edits**. The initial count was 3:
an earlier same-cell test correctly coalesced two identical competing placements
into one edit. Selection checks wait for a rendered frame before acting.
The phone uses the website's **Open player** control: the inline listing card is
shorter than Napcraft's documented minimum. Both public screenshots were visually
inspected, including the two avatars, presence labels and phone touch controls.
These are browser touch emulation checks, not physical-device tests.
Public result files and screenshots are kept locally under `.napplet-space/`;
they are not bundled into the app. Cross-build storage migration remains the
explicit limitation below, not a passed assertion.
## Publication receipts
Published commit `2043aa2d79f2fe00c6c17aa5a5e4f0e9cfaf9760` with the selected
remote signer. The HTML, source archive, cover, gameplay clip, descriptor,
snapshot and current-release announcement all received receipts. All four
configured mirrors acknowledged the release. The website initially reported
`announced_pending_index` / `superseded`; a subsequent `soyli status --refresh`
returned `indexed`, `websiteReady: true`, `websiteStatus: ready`, with no error.
This was indexing delay, not a backend deployment failure.
[Published Napcraft release](https://napplet.soy/r/307dcea38bb90d0ac0feac78b463a57a9e55096449b777976dde3fd0331fef25).
[Previous release for same-browser recovery](https://napplet.soy/r/4d1006fa12b413a59ef607f75f4e09d010b6a7e9b8b12369cb66238059bcfef9).
A post-publication `soyli backend describe worlds --json` still reports release
`86a73266cceaa724009833c313051c49940c45efc2537dcc27c73eaeed8f6728`,
revision 1, enabled. No deployment was performed.
Publication includes the refreshed [cover](https://blossom.napplet.soy/42b64e4500a0af6d7db66fb3c0acca3f6faa6ecd5d13cb7adcdf609a5ba6493c)
and [gameplay clip](https://blossom.napplet.soy/02dead686675af8e391dbe9b7742715a7f4c5bbdd3db33f6f506ce8f4a9c8680).
The post-release evidence/scripts checkpoint does not change the published HTML
or require a backend deployment.
## Tooling follow-ups
1. Provide consented storage migration/export between verified releases, or a
documented stable storage scope for recovery journals. Build-scoped data is
durable on reload but inaccessible to an updated game.
2. Expose a safe provider creation-receipt lookup for an expired uncertain
request. The existing module operations cannot discover a missing instance.
3. Include public-host consent selectors and full-player mobile navigation in
testing guidance alongside soyLI preview fixtures. Startup checks and local
multiplayer alone do not cover these public UI differences.
## Platform limits and coverage boundaries
The provider offers no creation-history or receipt lookup for an expired request
whose instance/code never reached the player. Napcraft cannot safely infer whether
that world exists. It keeps the original evidence and offers recognized-code
recovery or deliberate separate-world creation; automatic replacement would risk
a duplicate. Retry expiry and the provider's execution/RPC limits still apply.
Recovery records use optional, viewer- and build-scoped host storage in the
current browser. Public testing found that an update cannot read a pending request
saved by the previous build. Reopening that pinned release in the same browser
still returns the exact original record: it was not deleted. This agrees with
`docs/design-patterns.md` (napplet identity plus aggregate hash). The available
storage SDK has no prior-build migration/read API. Napcraft cannot bypass that
boundary. Recent-world lists and recovery copies have the same limitation.
For existing players, retain world codes and reopen the previous pinned release
in the same browser/account to access its state. Do not interpret an empty new
release's local list as proof that no world exists. A provider receipt/world
lookup or host-mediated storage migration/export is needed for complete recovery
when an earlier creation's code never arrived. This is a platform follow-up, not
a reason to redeploy the unchanged worlds handler or silently mint a replacement.
The first public cross-release assertion failed on this boundary. The subsequent
scenario explicitly records the limitation, verifies that the old request remains
in the old release, and seeds a separate representative legacy request in the new
build to exercise its recovery UI. That is same-build recovery coverage, not a
claim of automatic data migration.
Records are not a cloud backup; without working storage the app cannot durably
set a request aside. Confirmed shared worlds live in the backend independently
and remain accessible from any release/browser with their code.
The local network tests use the real soyLI sandbox and isolated backend with
forced local TURN, not the public provider. Test identities use real signatures
through a disposable NIP-07 shim; an installed extension's own popup UI and
physical iOS/Android devices are not covered. Skipped conformance checks remain
unverified. Public-network evidence is recorded separately below when available.
