SOURCE / PINNED RELEASE
Made of little things.
Black Site Map Editor
- Release
- 32a57733b7e9…
- Author-recorded commit
- 3f54890f542f…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/black-site-map-editor
Archive hash verified: 7a4b1abbac7db5b0…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
# Native Black Site host integration
The adjacent `../napplet-soy` source implements this integration. The earlier
research below records the implementation's design and release boundary. It is
historical wherever it describes missing source support.
## Implemented and verified locally
- `packages/runtime/src/map-contract.ts` and `map-publication-session.ts` grant
native kind35858 metadata and narrowly scoped owner kind5 requests with viewer
consent, configured relays, exact verified signing, stale checks, bounded retry
and lifetime cancellation. Optional lineage references are supported. Admission
is explicitly metadata-only; consumers verify actual map JSON independently.
- `intent-session.ts` implements current official delivery responsibility,
catalog/default/chooser policy, retained cold delivery, source attestation,
cancellation and startup bounds. Preview and web Player surfaces wire the same
broker; the shim is pinned0.31.0 (NAP/core0.33.0).
- `websiteKinds` includes35858 for connected viewer NIP46 permissions. Creator
publishingKinds are unchanged.
- The original game now discovers/loads/plays maps and invokes the editor with
the modern INTENT URI API. The reference schema remains unchanged and its29
spec tests pass.
- `bun test tests/services/black-site-editor.test.ts` proves real signed local
upload→publish→update→reload→remix under two independent fixture identities,
actual game deployment and owner deletion. It installs a signed editor
manifest and proves game EDIT→actual broker→cold editor with the parent loaded.
It uses the real runtime/shim and consent UI, with local relay/Blossom services;
no app SDK domains are mocked. All test servers and browsers are cleaned up.
No source changes have been deployed or pushed. The installed CLI0.27.0 remains
an older compiled release. Source preview uses the command documented below;
user-facing public availability requires an explicit release/deployment.
Current editor uses the game's native kind **35858** map records, with JSON on
Blossom, NAP-RESOURCE loading, NAP-UPLOAD and NAP-OUTBOX publication. Deletion is
NIP-09 kind **5**. Scoped kind-30078 records are not a compatible substitute.
## Verified current gap
Read-only source inspection in `../napplet-soy` found:
- `packages/runtime/src/host.ts:285`: every `outbox.publish` and `relay.publish`
request goes to `NappletAppData.handle`.
- `packages/runtime/src/app-data-session.ts:32`: strict template schema accepts
only `APP_DATA_KIND`, which is 30078. Line 82 returns an explicit unsupported
publication error.
- `packages/identity/src/signer.ts`: `websiteKinds` already includes kind 5 and
30078 but lacks 35858. Connected NIP-46 accounts need map kind permission.
This is a source-policy limitation, not an editor SDK method gap. The editor
retains drafts and verified uploads on denied publication. Full completion still
requires native publication/deletion in the actual host and game consumption.
## Smallest bounded host implementation
Add a separate `packages/runtime/src/map-publication-session.ts`, retaining the
existing app-data session unchanged. Instantiate it per connected account in
`packages/runtime/src/host.ts`, route only native map templates and narrowly
validated map deletion requests to it, and close it with the account lifetime.
Do not add implementation inside the napplet. Do not authorize by editor author,
artifact hash, CLI provenance or manifest ancestry: every publisher gets the same
policy and individual viewer consent.
Mirror the existing app-data constructor's trusted dependencies:
`pubkey`, `sign`, `signal`, `consent`, configured write `relays`, and optional
`ActionIO` for testing. Reuse:
- `packages/runtime/src/action-session.ts`: `signExact`, `ActionConsent`,
`ActionIO` (`query(filters, signal, requireComplete)` and `publish(event,signal)`).
- `packages/protocol/src`: `verifiedEvent`, `SignedEvent`.
- `packages/nostr/src/relay-policy.ts`: configured/public relay validation.
- `packages/client/src/nostr.ts`: `ProtocolClient` for real IO.
- `packages/diagnostics/src`: redact errors at runtime boundaries.
Keep signing entirely in the host. `signExact` checks the returned account,
exact template and valid signature. Abort on identity/frame/request lifetime
changes; recheck after delayed signer answers before publishing. Keep bounded
signed-event retries and serialize operations per account/address, including
cross-tab Web Locks where available. Configure writes from viewer Network
settings; iframe relay hints must not silently add destinations.
## Admission rules
Strict unsigned template fields: `kind`, `content`, `tags`, `created_at` only.
Bound notes, tag count, tag lengths and timestamp skew. Suggested notes limit is
4 KiB, matching the editor's credits UI; it is a host profile bound, not a Nostr
protocol limit.
For kind 35858, require exactly one each `d`, `title`, `version`, `x`, `m`, `size`.
Use the game's map contract: ID `[a-z0-9][a-z0-9-]{0,31}`, supported version 1,
lowercase 64-character SHA-256, MIME `application/json`, size 1–262144 bytes.
Validate bounded metadata and public HTTPS server/image URLs. Permit only
reviewed tags including summary, server, image, lowercase hashtags, alt/client,
and valid remix lineage. Read `../black-site-map-schema/spec/map-event.md`,
`spec/remix-lineage.md`, and `spec/lib/map-event.mjs`/`lineage.mjs`; copies used by
the editor are under `src/vendor/`.
A metadata-only profile cannot claim blob schema validation. To claim valid-map
publication, retrieve the exact hash through the host's existing safe Blossom
resource machinery, enforce the size and hash, decode UTF-8 JSON and validate
against the game schema. Keep fetch/signing authority on the trusted host side.
For kind 5, permit only map deletion: each `a` must name
`35858:<connected-viewer>:<valid-map-id>`, and `k` must be 35858. If `e` targets
are accepted, resolve/verify signed events and confirm matching viewer, map kind
and address. Refuse unrelated kinds, foreign authors and arbitrary event IDs.
An initial profile may require the editor's one-address/one-event/one-kind shape.
Consent must name the map, viewer and relay destinations, and explain that old
relay copies and Blossom bytes may survive.
Require complete selected-relay current-map lookup before consent; capture the
current event and recheck after consent, after signing and before publication.
Reject observed changes and non-newer replacement timestamps. The native map
wire has no `previous` field: this prevents observed races but cannot promise
atomic compare-and-swap across devices or relays. The editor also checks its
loaded base before upload and publication.
Add 35858 to `websiteKinds` in `packages/identity/src/signer.ts`. Keep creator
`publishingKinds` unchanged: napplet actions never use the CLI creator signer.
Existing NIP-46 sessions may need renewed kind permission.
## Verification plan
Use `packages/runtime/src/app-data-session.test.ts` as the fixture pattern:
real `finalizeEvent`, independent secret-key fixtures, mutable relay event list,
controlled consent/signing delays, and injected `ActionIO` complete-read checks.
Add native-map session tests for supported creation/update/remix/deletion,
malformed metadata/lineage, unrelated kinds, foreign-author deletion, incomplete
reads, consent denial, modified signer output, account abort, stale revisions and
retry after lost acknowledgements. Verify no sign/publish occurs on rejected
inputs.
`tests/services/runtime-actions.test.ts` exercises the actual preview and web
runtime with injected NIP-07 and relay/Blossom fixtures. Extend it or add a native
map browser suite. Verify two independent viewers: A publishes; B anonymously
browses/loads and remixes; A reopens/updates; stale edits fail; deletion affects
only A's map. Verify game loading of the actual published JSON/event, not only a
mock shell or signer acknowledgement. Test optional-domain absence and both host
themes. Current editor host tests use mocked SDK domains and do not establish
this external integration.
Update host `docs/PROTOCOL.md`, `docs/NAP-REVIEW.md`, `docs/PUBLIC-RUNTIME.md` and
embedded creator guidance to describe the explicit bounded native-map policy,
implementation state and deployed/released state separately. Before pushing
shared-package/CLI changes, that repository's AGENTS.md requires
`bun run check:nix` (full Nix realization/checks, including KVM VM regressions).
Focused local tests remain necessary but do not replace that required gate.
## Running the updated source host
The installed `/home/robert/.local/bin/soyli` currently points at a compiled
0.27.0 release under `/home/robert/.local/share/napplet-space/releases/`. Editing
or building the source checkout does not replace that executable or a deployed
website. `apps/cli/src/preview/assets.ts` embeds preview assets in distributed
binaries; source CLI runs build the current assets in a child process.
After host implementation, run the source CLI from the editor project directory:
```sh
bun /home/robert/Projects/napplet-soy/apps/cli/src/index.ts dev
```
Use its printed hosted preview URL, then stop the session. For a standalone
current-source binary, from the host repository:
```sh
bun scripts/cli-compile.ts /tmp/soyli-black-site 0.27.0 --preview-assets
```
Use that explicit binary path from the editor project directory. Full release
builds use `bun run cli:build -- --target linux-x64` and output under
`.local/cli/<version>/soyli-linux-x64/`; they require the pinned Bun 1.3.11 and
matching distribution/installer version. Installation/release and website
rollout are separate actions. Do not overwrite the installed release directory
or claim public support merely because current-source preview passed.
## Inbound map INTENT integration
The editor now pins published @napplet/sdk 0.29.0 (NAP/core 0.33.0) and implements the official `intent.onDelivery` receiver. Its portable metadata advertises `napplet:black-site/map-edit`, `napplet:black-site/map-remix`, `napplet:black-site/map-open` and `napplet:black-site/map-new`. See map-intents.md for the strict payload contract and caller examples. Buffered cold delivery, login waiting, ownership, unsaved cancellation and receiver cleanup are fixture-tested.
Inspected soyLI runtime source has no intent router and excludes intent from its runtime domain declarations. Implement NAP-INTENT in the host repository using current official URI request normalization and `intent.deliver` binding support; do not add app-owned postMessage receivers, launch query strings or an INC substitute. Update the host shim to the published version implementing onDelivery, and retain payloads until the resolved target registers. Current published SDK semantics make result.ok mean runtime acceptance of delivery responsibility; there is no target handled acknowledgement. Source and target lifecycle, sender attestation, author-qualified handler identity, user-controlled catalog/defaults and trust/consent are host responsibilities.
Live cross-napplet verification must use a caller with the documented `intent.invoke('napplet:black-site/map-edit', {payload:{address}})` operation, a signed/discovered editor listing, and two player identities. Verify cold/warm targets, refused/absent handler, dirty target cancellation, logout/login delivery, account changes during download and exact convention routing. Passing direct receiver fixtures is not proof that the actual shell resolves and opens this editor.
