Markdown source for agents: https://npub1d70emggs6jzun5lhvqnfqsd9reqmaarn2qjf6q3r02gryyl4v8sqjn44xe.nsite.lol/docs/patterns/going-public.md

Nostr Agent Onboarding · start here · index · source: nostr-dev/docs/patterns/going-public.md · snapshot 2026-10-10

Paths such as ~/Documents/…, ~/Production Environment/…, repos/… and services on localhost refer to the author's workstation and are not available to you — read them as worked examples of a setup you can recreate.

Pattern: Going public — exposing local Nostr-native artifacts on the open internet

Source: the runbook ~/work/nostr-archive/docs/going-public.md (sibling agent's work). That document covers an archive specifically; this one generalizes the recipe so any artifact built on this workspace's local stack can be made publicly reachable without exposing this machine to inbound traffic.

You built something against the local GRASP / relay / Blossom (http://localhost:8081). It works in this environment. Now you want anyone with a browser to be able to use it, ideally without poking holes in your firewall, NAT, or DNS.

The pattern is publish to public infrastructure. Local stays canonical for development; public is a mirror. The visitor's browser only ever talks to public services. Bytes are content-addressed, events are signed — replicating to public losses no provenance.

Three things to mirror

  1. Events (kind:1, kind:30617, anything addressable) → public Nostr relays.
  2. Blobs (Blossom) → public Blossom servers.
  3. Git repos (GRASP nostr:// URLs) → a public GRASP host.

If your artifact is a static web client, there's a fourth piece:

  1. The static site itself → publish as an nsite (NIP-5A, kind 15128; the old per-file kind 34128 is dead). An nsite gateway (<npub>.nsite.lol) serves it from public Blossom. Full recipe: nsite-publishing.md.

The recipe

Adapted, generalized, and condensed from ~/work/nostr-archive/docs/going-public.md. Every step is idempotent — re-running won't hurt.

1. Mirror events to public relays

Pick the kinds your artifact uses, pull from local relays, push to public:

PUBHEX=$(jq -r .pub_hex /dev/shm/nostr-dev-session/keys.json)
PUBLIC=("wss://relay.ngit.dev" "wss://nos.lol" "wss://relay.damus.io")

# Replace 30617 with whatever kinds your artifact uses
{ for k in 30617 30618 1; do
    nak req -k "$k" -a "$PUBHEX" ws://localhost:8081 2>/dev/null
  done
} | jq -c 'select(.id != null)' | sort -u > /tmp/events.jsonl

while IFS= read -r ev; do
  echo "$ev" | nak event "${PUBLIC[@]}" >/dev/null 2>&1
done < /tmp/events.jsonl

Expect 2-of-3 success on any given event. Public relays sometimes rate-limit, content-policy, or blip. As long as one accepts each event, discovery still works.

2. Mirror Blossom blobs to a public server

NSEC=$(jq -r .nsec /dev/shm/nostr-dev-session/keys.json)

# Collect every sha256 your events reference (`x` tags, `primary`, `ots`,
# whatever your kinds use):
SHAS=$(jq -r 'select(.kind==<your-kind>) | .tags[] | select(.[0]=="x") | .[1]' \
        /tmp/events.jsonl | sort -u)

for sha in $SHAS; do
  curl -sLf -o /tmp/blob "http://localhost:8081/$sha"
  nak blossom --sec "$NSEC" -s https://blossom.primal.net upload /tmp/blob >/dev/null
done
rm /tmp/blob
unset NSEC

Recommended public Blossom servers as of writing: - https://blossom.primal.net — large, reliable, no auth quirks - https://blossom.band — index-friendly - Run blossom-audit against a server before relying on it: npx blossom-audit audit https://server bitcoin --sec <ephemeral nsec>

3. Push the GRASP repo to a public host

relay.ngit.dev runs the same ngit-relay software as the local stack and accepts pushes from any pubkey, validated against on-relay kind:30617 events. So the same ngit init workflow you used locally also works against the public GRASP — just point it there:

cd <repo-working-tree>
NSEC=$(jq -r .nsec /dev/shm/nostr-dev-session/keys.json)
ngit init -d \
    --name "<repo-name>" \
    --identifier "<d-tag>" \
    --description "Public mirror." \
    -g wss://relay.ngit.dev \
    -g ws://localhost:8081 \
    -n "$NSEC"
unset NSEC

After this, the kind:30617 announcement on relay.ngit.dev lists both clone URLs — public first, local as fallback. A browser visiting the public URL clones from relay.ngit.dev. A consumer on this machine can still use localhost.

4. (If applicable) Publish the client as an nsite

For a static-site artifact (HTML/CSS/JS/wasm), this step has its own pattern doc and its own script — nsite-publishing.md and bin/nsite-deploy.py. Don't hand-roll it; five people already did and each hit the same three traps.

~/Documents/nostr-dev/bin/nsite-deploy.py --dir dist \
    --keys ~/Documents/nostr-dev/secrets/<site>/keys.json \
    --title "…" --description "…"

Result: anyone visits https://<your-npub>.nsite.lol/ and the gateway fetches kind:15128, resolves each path's blob from public Blossom, and serves it.

The three traps, in one line each: publish under a dedicated key (15128 is replaceable per pubkey, so the wrong key destroys an existing site); the events must reach wss://relay.nsite.lol; and https://nostr.download goes first in the server list because blossom.primal.net serves every text/* asset as text/plain, which silently kills stylesheets and service workers.

5. Update the client's defaults to point at public infrastructure

If your client has hardcoded ws://localhost:8081, switch to wss://relay.ngit.dev (or whichever public relays apply) before publishing. Keep the localhost URL as a fallback in the user's UI, not the default.

What still requires this machine

Failure modes to expect

Symptom Cause Mitigation
Public Blossom 404 on a blob you uploaded yesterday GC; some servers expire un-pinned blobs Re-upload (idempotent — same sha256). Use a Blossom server that doesn't GC.
Public relay drops a kind:1 you sent Rate limit or content policy Mirror to ≥3 public relays.
relay.ngit.dev is down Service outage The kind:30617's clone tag lists local fallback (only useful from this network). For real production, mirror the repo to a second public GRASP.
nsite.lol is down Gateway outage Try <npub>.nsite-host.com or any other nsite gateway. The kind:15128 lives on relays; any gateway implementation can serve it.
Browser CORS error against the local GRASP The local container's nginx + Khatru both emit CORS headers See docs/known-issues.md § "ngit-relay double CORS headers"

What this pattern doesn't solve