- 
                Notifications
    You must be signed in to change notification settings 
- Fork 707
Add Nostr Web NIPs — Decentralized Web Publishing via Nostr #2092
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Open
      
      
            KarimHassan
  wants to merge
  10
  commits into
  nostr-protocol:master
  
    
      
        
          
  
    
      Choose a base branch
      
     
    
      
        
      
      
        
          
          
        
        
          
            
              
              
              
  
           
        
        
          
            
              
              
           
        
       
     
  
        
          
            
          
            
          
        
       
    
      
from
Shugur-Network:nostr-web
  
      
      
   
  
    
  
  
  
 
  
      
    base: master
Could not load branches
            
              
  
    Branch not found: {{ refName }}
  
            
                
      Loading
              
            Could not load tags
            
            
              Nothing to show
            
              
  
            
                
      Loading
              
            Are you sure you want to change the base?
            Some commits from the old base branch may be removed from the timeline,
            and old review comments may become outdated.
          
          
      
        
          +607
        
        
          −0
        
        
          
        
      
    
  
  
     Open
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            10 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      71ffba4
              
                Add NIP-XX: Time Capsules specification
              
              
                KarimHassan 2166c5f
              
                Refactor: Redefine Time Capsule
              
              
                KarimHassan 0d0b254
              
                Add support for nostr web
              
              
                KarimHassan a2fa618
              
                Remove NIP-XX specification for Time-Lock Encrypted Messages (Time Ca…
              
              
                KarimHassan 3792e22
              
                Refactor NIP-ZZ: Update DNS TXT record format and improve clarity in …
              
              
                KarimHassan 1e53eb7
              
                Fix relay URLs in NIP-ZZ examples for consistency and clarity
              
              
                KarimHassan 0ad82be
              
                Update MIME type examples and add image asset reference in NIP-YY doc…
              
              
                KarimHassan e7aa560
              
                Refactor NIP-YY: Update site index versioning and route mapping for i…
              
              
                KarimHassan 0351518
              
                Update NIP-ZZ: Change DNS TXT record format to semicolon-separated ke…
              
              
                KarimHassan 19ea627
              
                Refactor NIP-YY: Remove route from Page Manifest for clarity
              
              
                KarimHassan File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,264 @@ | ||
| # NIP-YY | ||
|  | ||
| ## Nostr Web Pages | ||
|  | ||
| `draft` `optional` | ||
|  | ||
| This NIP defines a set of event kinds for hosting static websites on Nostr, enabling censorship-resistant web publishing through the Nostr protocol. | ||
|  | ||
| ## Abstract | ||
|  | ||
| Nostr Web Pages (NWP) allows publishing static websites as Nostr events. Web assets (HTML, CSS, JavaScript, fonts, etc.) are stored as regular events (kind 1125), page manifests (1126) define routes, the entrypoint (11126) provides the current entry to a site, and the site index (31126) maps routes using addressable events. This enables decentralized, verifiable, and censorship-resistant websites. | ||
|  | ||
| ## Motivation | ||
|  | ||
| Traditional web hosting relies on centralized servers that can be censored, taken down, or compromised. By publishing websites as Nostr events: | ||
|  | ||
| - Content becomes censorship-resistant through relay replication | ||
| - Sites are independently verifiable via cryptographic signatures | ||
| - No central origin servers are required | ||
| - Publishing works with existing Nostr infrastructure | ||
|  | ||
| ## Event Kinds | ||
|  | ||
| This NIP defines the following event kinds: | ||
|  | ||
| | Kind | Description | Type | | ||
| | ------- | ------------- | ----------- | | ||
| | `1125` | Asset | Regular | | ||
| | `1126` | Page Manifest | Regular | | ||
| | `31126` | Site Index | Addressable | | ||
| | `11126` | Entrypoint | Replaceable | | ||
|  | ||
| ### Regular Assets (1125) | ||
|  | ||
| Content-addressed assets for web pages. All web assets (HTML, CSS, JavaScript, fonts, etc.) use kind `1125` with MIME type specified in the `m` tag. | ||
|  | ||
| **Required tags:** | ||
|  | ||
| - `m` - MIME type (e.g., `text/html`, `text/css`, `text/javascript`, `application/wasm`, `font/woff2`, 'image/png', etc.) | ||
| - `x` - Hex-encoded SHA-256 hash of the `content` field (for content deduplication) | ||
|  | ||
| **Optional tags:** | ||
|  | ||
| - `alt` - Alternative text or description for the asset | ||
|  | ||
| **Example HTML asset:** | ||
|  | ||
| ```json | ||
| { | ||
| "kind": 1125, | ||
| "pubkey": "<site-pubkey>", | ||
| "created_at": 1234567890, | ||
| "tags": [ | ||
| ["m", "text/html"], | ||
| ["x", "a1b2c3..."], | ||
| ["alt", "Home Page"] | ||
| ], | ||
| "content": "<!DOCTYPE html><html>...</html>", | ||
| "id": "<event-id>", | ||
| "sig": "<signature>" | ||
| } | ||
| ``` | ||
|  | ||
| ### Page Manifest (1126) | ||
|  | ||
| Regular event that links assets for a specific page. Each page version is a separate event. | ||
|  | ||
| **Required tags:** | ||
|  | ||
| - `e` - Asset event IDs (kind 1125): `["e", "<event-id>", "<recommended relay URL, optional>"]` | ||
| - Assets are identified by their event ID; MIME type is specified in the asset's `m` tag | ||
|  | ||
| **Optional tags:** | ||
|  | ||
| - `title` - Page title | ||
| - `description` - Page description | ||
|  | ||
| **Example:** | ||
|  | ||
| ```json | ||
| { | ||
| "kind": 1126, | ||
| "pubkey": "<site-pubkey>", | ||
| "created_at": 1234567890, | ||
| "tags": [ | ||
| ["e", "<html-event-id>", "wss://relay.example.com"], | ||
| ["e", "<css-event-id-1>", "wss://relay.example.com"], | ||
| ["e", "<css-event-id-2>", "wss://relay.example.com"], | ||
| ["e", "<js-event-id>", "wss://relay.example.com"], | ||
| ["e", "<image-event-id>", "wss://relay.example.com"], | ||
| ["title", "Home"], | ||
| ["description", "Welcome to my Nostr Web site"] | ||
| ], | ||
| "content": "", | ||
| "id": "<event-id>", | ||
| "sig": "<signature>" | ||
| } | ||
| ``` | ||
|  | ||
| ### Site Index (31126) | ||
|  | ||
| Addressable event that maps routes to their current page manifest IDs. The `d` tag is used for versioning. | ||
|  | ||
| **Required tags:** | ||
|  | ||
| - `d` - Version identifier (e.g., `"v1.2.3"`, `"v1.0.0"`, `"main"`, `"staging"`) | ||
| - `rt` - Route mapping: `["rt", "<route-path>", "<page-manifest-event-id>", "<relay-url, optional>", "<marker, optional>"]` | ||
| - One `rt` tag per route | ||
| - Route path (e.g., `"/"`, `"/about"`, `"/blog/post-1"`) | ||
| - Page manifest event ID (kind 1126) | ||
| - Optional relay URL hint | ||
| - Optional marker: `"default"` or `"404"` to indicate special routes | ||
| - If no route is marked as `"default"`, clients SHOULD use the `"/"` route as default | ||
| - If no route is marked as `"404"`, clients SHOULD display a generic error page | ||
|  | ||
| **Optional tags:** | ||
|  | ||
| - `title` - Site title | ||
| - `description` - Site description | ||
|  | ||
| **Example:** | ||
|  | ||
| ```json | ||
| { | ||
| "kind": 31126, | ||
| "pubkey": "<site-pubkey>", | ||
| "created_at": 1234567890, | ||
| "tags": [ | ||
| ["d", "v1.2.3"], | ||
| [ | ||
| "rt", | ||
| "/", | ||
| "<page-manifest-event-id-1>", | ||
| "wss://relay1.example.com", | ||
| "default" | ||
| ], | ||
| ["rt", "/about", "<page-manifest-event-id-2>", "wss://relay1.example.com"], | ||
| [ | ||
| "rt", | ||
| "/blog/post-1", | ||
| "<page-manifest-event-id-3>", | ||
| "wss://relay2.example.com" | ||
| ], | ||
| ["rt", "/404", "<page-manifest-event-id-4>", "", "404"], | ||
| ["title", "My Nostr Website"], | ||
| ["description", "A decentralized website on Nostr"] | ||
| ], | ||
| "content": "", | ||
| "id": "<event-id>", | ||
| "sig": "<signature>" | ||
| } | ||
| ``` | ||
|  | ||
| ### Entrypoint (11126) | ||
|  | ||
| Replaceable event that points to the current site index. Only the latest event per author is stored by relays. | ||
|  | ||
| **Required tags:** | ||
|  | ||
| - `a` - Address coordinates to the current site index: `["a", "31126:<pubkey>:<d-tag>", "<relay-url>"]` | ||
| - The `<d-tag>` is the version identifier used in the site index's `d` tag (e.g., `"v1.2.3"`) | ||
|  | ||
| **Example:** | ||
|  | ||
| ```json | ||
| { | ||
| "kind": 11126, | ||
| "pubkey": "<site-pubkey>", | ||
| "created_at": 1234567890, | ||
| "tags": [["a", "31126:<site-pubkey>:v1.2.3", "wss://relay1.example.com"]], | ||
| "content": "", | ||
| "id": "<event-id>", | ||
| "sig": "<signature>" | ||
| } | ||
| ``` | ||
|  | ||
| ## Client Behavior | ||
|  | ||
| ### Publishing | ||
|  | ||
| 1. Generate asset events (kind 1125) with SHA-256 hashes and MIME types | ||
| 2. Publish assets to relays | ||
| 3. Create page manifest (1126) for each page, referencing asset event IDs | ||
| 4. Create/update site index (31126) with route-to-manifest mapping using `rt` tags | ||
| 5. Update entrypoint (11126) to point to the current site index version | ||
| 6. Generate DNS TXT record (see NIP-ZZ) | ||
|  | ||
| ### Fetching and Rendering | ||
|  | ||
| 1. Query DNS for `_nweb.<domain>` TXT record to get site pubkey and relays | ||
| 2. Fetch entrypoint (11126) from relays: `{"kinds": [11126], "authors": ["<pubkey>"]}` | ||
| 3. Extract site index address from the `a` tag in entrypoint | ||
| 4. Fetch site index (31126) using the address coordinates | ||
| 5. Parse site index tags to extract routes (`rt`), identifying which route is marked as `"default"` and which is marked as `"404"` | ||
| 6. Get page manifest ID for requested route from the `rt` tags | ||
| - If route not found and a `"default"` route exists, use the default route | ||
| - If route not found and no `"default"` route exists, use the `"/"` route | ||
| - If the `"/"` route doesn't exist, display a 404 error | ||
| - If a `"404"` route exists, display the 404 page | ||
| - If no `"404"` route exists, display a generic client-generated error page | ||
| 7. Fetch page manifest (1126): `{"ids": ["<manifest-id>"]}` | ||
| 8. Fetch all referenced assets (kind 1125) by event ID | ||
| 9. Parse each asset's `m` tag to determine MIME type (HTML, CSS, JavaScript, etc.) | ||
| 10. Assemble HTML with CSS and JS references | ||
| 11. Render in sandboxed environment | ||
|  | ||
| ### Security Considerations | ||
|  | ||
| **Author Verification:** | ||
|  | ||
| - All events MUST be authored by the pubkey specified in DNS TXT record | ||
| - Clients MUST reject events from other pubkeys | ||
|  | ||
| **Content Addressing:** | ||
|  | ||
| - All assets (kind 1125) MUST include `x` tag with SHA-256 hash of the content | ||
| - Enables content deduplication: relays MAY use the `x` tag to identify and deduplicate identical content | ||
| - Allows content sharing: multiple sites can reference the same asset by its hash | ||
| - Site indexes (31126) use the `d` tag for versioning | ||
| - The `d` tag SHOULD follow semantic versioning (e.g., `"v1.2.3"`) or use environment identifiers (e.g., `"main"`, `"staging"`) | ||
| - Allows publishers to reference specific versions via the entrypoint | ||
|  | ||
| **Sandboxing:** | ||
|  | ||
| - Content SHOULD be rendered in isolated environment (iframe sandbox) | ||
| - Network requests outside Nostr SHOULD be blocked | ||
|  | ||
| ## Caching | ||
|  | ||
| **DNS Records:** | ||
|  | ||
| - Cache only as offline fallback | ||
| - Always attempt fresh DNS lookup | ||
|  | ||
| **Entrypoint (11126):** | ||
|  | ||
| - Always fetch fresh (TTL = 0) | ||
| - Required to get current site index | ||
|  | ||
| **Site Index (31126):** | ||
|  | ||
| - Cache with short TTL (30-60 seconds) | ||
| - Required to detect site updates | ||
|  | ||
| **Page Manifests (1126):** | ||
|  | ||
| - Cache with reasonable TTL (content-addressed by ID) | ||
| - Validate against current site index | ||
|  | ||
| **Regular Assets (1125):** | ||
|  | ||
| - Cache with reasonable TTL (content-addressed) | ||
| - Use SHA-256 hash (from `x` tag) for cache key validation | ||
| - Can be indexed by MIME type (from `m` tag) for efficient queries | ||
|  | ||
| ## Reference Implementation | ||
|  | ||
| - Website: nweb.shugur.com | ||
| - Browser extension: | ||
| - Chrome Web Store: [nostr-web-browser](https://chromewebstore.google.com/detail/nostr-web-browser/hhdngjdmlabdachflbdfapkogadodkif) | ||
| - Firefox Add-on: [nostr-web-browser on AMO](https://addons.mozilla.org/en-US/firefox/addon/nostr-web-browser/) | ||
| - Publisher CLI: | ||
| - [nw-publisher on npm](https://www.npmjs.com/package/nw-publisher) (Recommended) | ||
| - [publisher](https://github.com/Shugur-Network/nw-nips/tree/main/publisher) | ||
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.