Skip to content

hzrd149/nsite-gateway

Repository files navigation

nsite-gateway

A Deno + Hono gateway that serves static websites published on Nostr (the nsite protocol).

Sites are identified by site manifest events (kind 15128 for root sites, kind 35128 for named sites, and kind 5128 for snapshots) and blobs served via Blossom.

Configuring

All configuration is done through the .env file. Start by copying the example file and modifying it.

cp .env.example .env

Environment Variables

Variable Default Description
LOOKUP_RELAYS wss://user.kindpag.es,wss://purplepag.es Comma-separated relays used to look up a user's NIP-65 relay list (kind 10002) and blossom server list (kind 10063)
NOSTR_RELAYS (none) Extra relays added to every event query, supplemental to the user's own outbox relays
CACHE_RELAYS (auto-detect ws://localhost:4869) Relays to persist all fetched events to (a local Nostr cache relay). Auto-detected if a relay is running on localhost:4869
BLOSSOM_SERVERS (none) Comma-separated fallback blossom servers used when a user has no 10063 event and the manifest has no server tags
BLOSSOM_PROXY (auto-detect http://localhost:24242) Optional upstream blossom proxy checked first for every blob (see Blossom Proxy). Auto-detected if a proxy is running on localhost:24242
MAX_FILE_SIZE 128 MB Maximum blob size to serve (e.g. "2 MB"). Enforced via Content-Length header and during streaming
CACHE_PATH (Deno default KV location) File path for the persistent Deno KV store (e.g. ./data/cache). Omit to use Deno's default location
CACHE_TIME 3600 TTL in seconds for all KV cache entries (DNS lookups, blob server hints, user profiles)
BLOB_SERVER_TTL 604800 TTL in seconds for the preferred verified blob source cache (["blob-server", sha256]), allowing trusted sources to stick around longer than general cache entries
BLOB_BAD_SOURCE_TTL 86400 TTL in seconds for a bad (sha256, server) verification result before that source is eligible to be retried for the blob
VERIFY_WORKER_POOL_MAX 4 Maximum number of blob verification workers created dynamically to hash responses in parallel
PUBLIC_DOMAIN (none) The gateway's own public domain. When set, it is used for constructing canonical site URLs on the homepage and status pages
NSITE_HOST 0.0.0.0 IP address the server binds to
NSITE_PORT 3000 Port the server listens on
ONION_HOST (none) If set to a .onion URL, every nsite response includes an Onion-Location header pointing to the Tor mirror
CURATION_USER (none) Hex pubkey of a curator whose NIP-51 mute list (kind 10000) is loaded into the in-memory event store at startup and refreshed on a timer. Sites whose author pubkey appears in public p tags on that list are omitted from the home page (encrypted mutes in content are not applied).
CURATION_REFRESH 600 (10 min) How often to re-fetch the curator mute list, in seconds

Running with Deno

deno task start

For local development with file watching:

deno task dev

The Deno tasks already include the required flags (--unstable-kv, --env-file=.env, and the necessary permission flags).

If NOSTR_RELAYS is set, the gateway will bulk-fetch all known site manifests (kinds 15128, 35128, and 5128) from those relays at startup, pre-populating the in-memory event store, and then re-check those relays every 10 minutes for newer manifest events.

If CURATION_USER is set, the gateway loads that user's mute list via the same event loader used elsewhere, keeps it in eventStore, and refreshes it on the interval given by CURATION_REFRESH in seconds (independent of NOSTR_RELAYS).

Cache Backends

Deno KV (metadata cache)

The gateway uses Deno KV to cache:

  • DNS resolution results (hostname → pubkey + site identifier)
  • Blob server hints — the last successful server for each blob (tried first on subsequent requests)
  • Blob server lists — the full ordered server list for each blob
  • User profiles (kind 0) — author display names shown on the homepage and status pages

To enable persistent caching, set CACHE_PATH:

CACHE_PATH="./data/cache"

If CACHE_PATH is omitted, Deno uses its default local KV location.

Nostr Event Cache

All fetched Nostr events are held in an in-memory event store for the process lifetime. To persist events across restarts, point CACHE_RELAYS at a local Nostr relay:

CACHE_RELAYS="ws://localhost:4869"

If a relay is already running on localhost:4869, it will be detected and used automatically.

HTTP Cache

All nsite responses include strong ETags (the blob's sha256 hash) and Cache-Control: public, max-age=3600. Conditional requests with If-None-Match are handled — matching ETags return 304 Not Modified without fetching the blob at all.

Running Directly from JSR

You can run the published package without cloning this repository:

deno run --unstable-kv --env-file=.env --allow-env --allow-net --allow-read --allow-write jsr:@hzrd149/nsite-gateway

Running with Docker Compose

The included docker-compose.yml sets up a full production stack:

  • nsite-gateway — the gateway itself
  • Caddy — TLS termination and reverse proxy
  • flower-cache — local blossom proxy (wired as BLOSSOM_PROXY)
git clone https://github.com/hzrd149/nsite-gateway.git
cd nsite-gateway
docker compose up

Persistent Deno KV caching is enabled via a Docker volume mounted at /cache.

Note: You must create a Caddyfile before starting the stack — the docker-compose.yml mounts ./Caddyfile into the Caddy container but the file is not included in the repository.

Once running, the gateway is accessible at http://localhost:3000.

Running with Docker

docker run --rm -it --name nsite -p 3000:3000 ghcr.io/hzrd149/nsite-gateway

Note: The default image CMD does not include --unstable-kv or --allow-write, so Deno KV caching is inactive. Use a custom entrypoint or the docker compose setup if you need persistent caching.

Hostname Resolution

The gateway resolves incoming hostnames to a Nostr site using three strategies (in order):

  1. npub subdomainnpub1abc....nsite.example.com: the leftmost label is a valid bech32 npub, decoded to a hex pubkey. Serves the root site (kind 15128).
  2. Snapshot labelv<50-char-base36-event-id>.nsite.example.com: the leftmost label starts with v and is followed by a 50-character base36 snapshot event id. Serves the exact snapshot event (kind 5128).
  3. Named site label — a 50-character base36-encoded pubkey followed by a 1–13 character site identifier (e.g. <base36pubkey><identifier>.nsite.example.com). Serves a named site (kind 35128).
  4. CNAME resolution — if the hostname doesn't parse directly as an nsite label, the gateway resolves CNAME records. This enables custom domains like myblog.com → npub1abc....nsite.example.com.

Homepage

The gateway serves a built-in homepage at the root domain that displays all currently cached sites as a card grid. Each card shows the site title, description, author name, path count, and last-updated time.

To replace the built-in homepage with your own, place an index.html file in the public/ directory at the project root.

Status Dashboard

The gateway serves a built-in status dashboard at /status:

  • GET /status — table of all site manifests currently loaded in the event store, with titles, authors, path counts, and last-updated timestamps.
  • GET /status/:address — detailed view for any npub, naddr, nprofile, raw hex pubkey, or v<snapshotIdB36> snapshot id: site metadata, relays, blossom servers, full path table with cached server info, and the raw manifest JSON.

Status pages are always Cache-Control: no-store.

Onion Header

If you operate a Tor mirror, set ONION_HOST and the gateway will include an Onion-Location header in every nsite response:

ONION_HOST="http://examplehiddenservice.onion"

Blossom Proxy

You can configure a BLOSSOM_PROXY server that will be checked first for all blob requests before falling back to other servers. When set, the gateway will:

  1. Check the proxy first for each blob request
  2. Include BUD-10 discovery hints as query parameters:
    • xs parameters: domain names of all known blossom servers (server hints)
    • as parameter: the author's pubkey (author hint)

This allows the proxy to locate blobs on other servers if it doesn't have them cached.

BLOSSOM_PROXY="https://blossom-proxy.example.com"

The proxy URL is constructed as:

<BLOSSOM_PROXY>/<sha256>?xs=server1.com&xs=server2.com&as=<pubkey>

The blossom proxy specification is defined in BUD-11. For a reference implementation, see flower-cache.

If a proxy is already running on localhost:24242, it will be detected and used automatically without setting BLOSSOM_PROXY.

About

Typescript gateway for simple static websites on nostr

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors