Back to Napcraft
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.

docs/napcraft-brief.md
# Napcraft

A small shared creative island: create a persistent world, share its world code,
walk around together, place and remove colorful blocks, and return later.

## Boundary and data flow

- Hard domains: `cvm` for authoritative durable blocks, `webrtc` for live players.
- Optional `storage` keeps recently visited world codes and unresolved commands;
  unavailable storage leaves a visible reminder to keep the world code.
- Optional `identity` resets viewer-scoped UI when the account changes. Creation
  requires host account consent; guests can join and build in a shared world.
- Soy backend module `worlds` stores a bounded 18 × 18 × 12 voxel island. Commands
  compare the expected cell value, retain exact retry intents, and query after
  conflicts. Provider revision checks additionally serialize concurrent changes.
- Codes carry the instance and pinned release; an update never migrates a world.
- Open WebRTC rendezvous is scoped to the world. Peer messages are bounded cosmetic
  presence and invalidations; only backend queries may change confirmed blocks.
- No direct network, browser persistence, signing, host DOM or relay escape hatch.
- New worlds are public shared creative spaces: anyone with their code can edit.
  The code is a locator, not a password. The owner alone can lock/unlock building.

## Visual direction

Considered a first-person neon quarry and a toy-like isometric garden. Chosen:
a floating craft-table island, lavender sky, mint grass, apricot sand, coral bricks,
blue glass and plum shadows. Small cube gardeners, chunky beveled blocks, tiny
flowers and drifting clouds give this creation its own identity.

The interface uses ink-purple text, white paper panels, square-soft corners,
chunky lilac buttons, block swatches and restrained spring feedback. The island
fills the play surface, with a compact world chip above and a material tray below.
All colors are app-owned; host themes never recolor terrain or the HUD.

Desktop: WASD/arrows, hover to aim, click a visible block to build on its column,
right-click to erase its top block, Shift-click to aim only, Space/place, X/remove, 1–6
materials, drag empty sky to pan, zoom buttons. Touch: D-pad plus large build and
erase buttons; tap a block to aim. World selection, help and sharing use dialogs.
At 200 × 160 show a useful enlarge notice; at 320 × 560 retain the whole control
set. Check 900 × 600, fullscreen, 390 × 844, 360 × 640 and 844 × 390.

Cover: an island with a little coral-and-glass pavilion, trees, two gardeners and
a highlighted build location. Clip: two independent players in a real shared world
walk, select materials and stack/remove blocks. Practice is explicitly temporary
and uses the same rendering/building rules. Persistent-world proof comes from
independent browser contexts and durable-backend tests.

## Shared editing response contract

Keep the existing playful palette. Local blocks appear immediately with dashed
pending outlines and a save count; provider confirmation removes that marker.
Touch build/erase starts on press. Keep up to 24 pending edits and journal exact
requests through NAP-STORAGE. The provider remains authoritative; peer messages
only trigger bounded refreshes. See [responsive editing](responsive-editing.md)
for batch/delta rules, conflict/retry semantics and measured latency budgets.

## Visible friends and direct building

Keep the existing CVM/storage authority and compatible WebRTC presence protocol.
Players get a transport-derived color, short friend label and a spread-out starting
spot. Draw gardeners and their labels above terrain so tall builds cannot hide
friends; separate overlapping labels. Keep the live-player count visible on phones.
Presence remains cosmetic, bounded and transient, never authoritative world data.
Desktop picking follows the visible top/side faces, previews the column on hover,
and commits one edit on a click release. A drag pans without editing; cancellation,
middle-click and empty-sky clicks do not build. Touch retains tap-to-aim plus the
existing immediate Build/Erase buttons. Test real clicks, drags, touch, two visible
moving players, leaving/rejoining, and the existing conflict/recovery scenarios.

## Consent and unfinished saves

Keep the same app-owned garden styling and SDK/CVM/storage boundaries. Allow two
minutes for host consent and extension signing, without changing handler limits
or the saved request's expiry. Show a waiting state while approval is pending.
Replace the generic retry dead end with a persistent recovery explanation, exact
request details, safe retry while valid, and a recovered-code check. Keep a durable
copy before reviewing old edits or setting aside an expired creation. A kept
record is never replayed automatically. Creating a different world requires an
explicit acknowledgement that the old world may exist; never mint a replacement
intent as an automatic timeout/retry action. Preserve records across reloads.
Test delayed approval/signing, a committed creation with lost reply, expired legacy
creation, deliberate separate-world consent, two-player edits and reloads.

## Public world directory

Purpose: choose a shared island from the opening screen without passing a code.
Keep listed worlds available when empty, as requested. Use the shipped
`soy.app-data/1` public NIP-78 convention through OUTBOX for signed advertisements
(`worlds`, schema `napcraft.world-directory`, version 1, payload `{code}`). The
world's pinned CVM release/instance remains authoritative; no backend change.
Use the supplied app-data helper's host-policy observer (the pinned SDK does not
export shell.onReady); identity and OUTBOX calls use SDK wrappers. No relay escape
hatch or new NAP. Directory scope survives builds; local recovery scope is unchanged.

Preserve cvm/webrtc requirements. Directory policy/outbox/identity are optional:
explain unavailability and keep direct joins/practice working. Guests can browse
and join. A signed-in player explicitly lists the current saved world from Invite,
with host approval and a clear anyone-can-join/edit notice. Existing worlds are
never automatically advertised. Listing removal affects that player's public
advertisement, not world state or other players' copies. Label who shared it,
without claiming verified ownership or current occupancy.

The opening panel becomes a pastel island directory: compact heading, readable
world rows, 44px Join buttons, search within the recent loaded view and Refresh.
Keep create, code join, practice, recents and recovery reachable. App-owned colors
and existing minimum 300×340 stay. The panel scrolls on phones and short frames;
test 200×160 notice, 320×560, 900×600, full screen and touch portrait/landscape.
Loading, empty, incomplete, invalid-record, signed-out and failed-list/publish
states are explicit. Deterministic advertisement IDs, current-revision checks and
retained retry templates prevent duplicate advertisements; publication failures
never recreate or remove a world. Test anonymous discovery, two identities,
reopening, listing/removal, denied consent, stale revisions and malformed records.