Not Connected
Connection Status
🌐 1. Public Read
NOSTR Relay - Read public events
πŸ” 2. Personal Read/Write
NIP-42 Auth - API personal read + write uDRIVE

Astroport.ONE Developer Platform

Build decentralized applications with MULTIPASS authentication, IPFS storage, and NOSTR social features

Checking connection...

Open NOSTR Console

Quick Start: Include common.js, call connectNostr(), then use publishNote() or uploadPhotoToIPFS()

Developer Guide

Learn how to build decentralized applications with Astroport.ONE step by step

Welcome to Astroport.ONE

Build decentralized applications 10x faster with NOSTR, IPFS, and Web3 technologies.

Why Astroport.ONE?

Traditional web development requires months to build authentication, file storage, and social features. With Astroport.ONE, you get all of this in days instead of months.

What You'll Learn

  • Libraries: Understand common.js, nostr.private.js and their specific use cases
  • Event Architecture: Learn to separate events from their processing (CSS revolution analogy)
  • Smart Contracts: How Astroport stations execute your events automatically
  • Best Practices: Build scalable, maintainable decentralized applications

Quick Start (3 Steps)

Ready to test? Click the "Try It Now" button above or scroll down to the interactive examples!
Step 1: Include Libraries
<script src="https://ipfs.copylaradio.com/ipns/copylaradio.com/nostr.bundle.js"></script>
<script src="https://ipfs.copylaradio.com/ipns/copylaradio.com/common.js"></script>
Step 2: Connect User
const pubkey = await connectNostr();
Step 3: Publish Events
await publishNote('Hello Web3! ' + window.location.href, [['r', window.location.href]]);
Key Concepts
MULTIPASS

Decentralized identity with NOSTR keys. No passwords, no central database.

IPFS Storage

Files stored on IPFS with automatic metadata tracking (SHA256).

NOSTR Events

Cryptographically signed events published to decentralized relays.

Smart Contracts

Astroport stations automatically process your events according to predefined rules.

Core Libraries

Understanding which library to use and when

common.js

Purpose: Public NOSTR operations and client-side utilities

When to use: For all public-facing features in your application

πŸ“¦ Library Links:
Key Functions:
  • connectNostr() - Connect to NOSTR wallet
  • publishNote() - Publish public notes (kind 1)
  • uploadPhotoToIPFS() - Upload files to IPFS
  • sendLike() - Send reactions (kind 7)
  • hexToNpub() / npubToHex() - Format conversion
  • fetchUserFollowsWithMetadata() - Get user network
πŸ’‘ Tip: Use common.js for everything users can see or interact with publicly.
nostr.private.js

Purpose: Private/encrypted operations and sensitive data

When to use: For encrypted messages, private keys, sensitive operations

πŸ“¦ Library Links:
Key Functions:
  • encryptDM() - Encrypt direct messages (NIP-04)
  • decryptDM() - Decrypt received messages
  • signEvent() - Sign events with private key
  • generateKeys() - Generate new keypairs
⚠️ Security: Only use nostr.private.js when you need encryption or private key operations. Never expose private keys in client code.
Which Library Should I Use?
Use Case Library Reason
User authentication common.js Public operation, uses NIP-07 extension
Publishing posts common.js Public content, no encryption needed
File uploads common.js Public IPFS storage with metadata
Direct messages nostr.private.js Requires encryption (NIP-04)
Private keys nostr.private.js Sensitive operations only
Download & Include Libraries
Required Dependencies:
Core Libraries:
Include in Your HTML:
<!-- Required: NostrTools Bundle -->
<script src="https://ipfs.copylaradio.com/ipns/copylaradio.com/nostr.bundle.js"></script>

<!-- Core Library: Public Operations -->
<script src="https://ipfs.copylaradio.com/ipns/copylaradio.com/common.js"></script>

<!-- Optional: Private/Encrypted Operations -->
<script src="https://ipfs.copylaradio.com/ipns/copylaradio.com/nostr.private.js"></script>

Event-Driven Architecture

The CSS Revolution: Separating Events from Processing

The Core Principle

Just like CSS separated style from content in 1996, Astroport.ONE separates events from their processing. This allows your events to be processed by any Astroport station according to smart contract rules.

❌ Traditional Approach
// Everything coupled together
app.post('/upload', 
  authenticate(),
  validate(),
  saveToDB(),
  processFile(),
  notifyUsers()
);

Problem: 6 months of infrastructure development, hard to scale, vendor lock-in

βœ… Astroport.ONE Approach
// Event separated from processing
const event = {
  kind: 21,  // Video upload
  content: videoData,
  tags: [['url', ipfsUrl]]
};

await publishToNostr(event);
// Smart contracts handle the rest

Result: 1 week of development, scalable, interoperable

Event Flow Architecture
1. Your App

Creates NOSTR events

2. NOSTR Relay

Stores events

3. Astroport Station

Smart contracts process events

Why Separate Events from Processing?
  • Interoperability: Your events work with any Astroport-compatible application
  • Scalability: Multiple stations can process the same events in parallel
  • Maintainability: Update processing logic without changing event structure
  • Flexibility: Different stations can apply different rules to the same events
  • Future-proof: New features can be added via smart contracts without app changes
Identifying Your Events

To enable smart contract processing, you need to identify different event types in your application:

Event Kinds (NOSTR Standard)
  • kind: 1 - Text notes (posts, comments)
  • kind: 7 - Reactions (likes, emojis)
  • kind: 21 - Short videos (NIP-71)
  • kind: 22 - Long videos (NIP-71)
  • kind: 1063 - File uploads (NIP-94)
  • kind: 42 - Channel messages (NIP-28)
  • kind: 1111 - Comments on URLs (NIP-22)
Custom Event Tags

Use tags to add context that smart contracts can use:

const event = {
  kind: 1,
  content: "My post",
  tags: [
    ['t', 'myapp'],           // Topic/category
    ['g', '48.86,2.35'],     // Geolocation
    ['r', 'https://...'],     // Reference URL
    ['p', 'pubkey'],          // Reference pubkey
    ['custom', 'value']       // Custom data
  ]
};

Astroport Smart Contracts

How Astroport stations automatically process your events

What Are Smart Contracts?

Smart contracts are executable rules that run on Astroport stations. They automatically process NOSTR events according to predefined logic, without requiring manual intervention.

How It Works
  1. Your app publishes an event to a NOSTR relay
  2. Astroport station subscribes to events matching certain criteria
  3. Smart contract executes when matching event is found
  4. Result is published as a new event or stored in database
Example Use Cases
  • File Processing: Automatically generate thumbnails for uploaded images
  • Notifications: Send alerts when users are mentioned
  • Analytics: Track engagement metrics automatically
  • Moderation: Filter content based on rules
  • Payments: Process Ẑen transactions
Example: Video Upload Smart Contract

When you publish a video upload event (kind 21), a smart contract automatically:

// Your app publishes this event
const videoEvent = {
  kind: 21,
  content: "My video",
  tags: [
    ['url', '/ipfs/QmXXX'],
    ['title', 'My Video Title'],
    ['thumbnail', '/ipfs/QmYYY']
  ]
};

// Smart contract on Astroport station automatically:
// 1. Validates the event structure
// 2. Checks IPFS file exists
// 3. Generates additional thumbnails if needed
// 4. Updates video database
// 5. Notifies subscribers
// 6. Processes payment if required
πŸ’‘ Key Point: You don't need to write the smart contract yourself. Astroport stations come with pre-built smart contracts for common operations. You just need to publish the right event structure.
Important: Event Structure Matters

Smart contracts rely on event structure to know how to process them. Make sure your events follow NOSTR standards:

  • Use correct event kinds: Follow NIP standards (NIP-94 for files, NIP-71 for videos, etc.)
  • Include required tags: Smart contracts look for specific tags to extract data
  • Validate before publishing: Use hexToNpub() and validation functions from common.js
  • Document your custom tags: If you add custom tags, document them for other developers

Universal Cookie Management System

Upload cookies to enable automated scraping and data synchronization with external services

What is the Cookie System?

The Cookie System allows you to upload browser cookies (Netscape format) for specific domains. These cookies enable automated scrapers to authenticate and sync data from external websites like YouTube, Leboncoin, and more.

Upload Cookies

Quick Start:

  1. Export cookies from your browser (Netscape format)
  2. Upload via the Cookie Management interface
  3. System automatically detects domain and stores cookie
  4. Scrapers run automatically during daily refresh
Open Cookie Manager
Security Features
  • Hidden Files: Cookies stored as .DOMAIN.cookie (leading dot)
  • Private Storage: Cookies stored in your MULTIPASS directory, not on IPFS
  • NIP-42 Auth: Authentication required to upload cookies
  • Single Domain: Each cookie file must contain cookies for one domain only
  • File Permissions: 600 (read/write owner only)
Working Example: YouTube

βœ… Currently Functional: YouTube cookie system is fully operational.

How It Works:
  1. Upload .youtube.com.cookie via Cookie Manager
  2. System stores cookie in your MULTIPASS directory: ~/.zen/game/nostr/EMAIL/.youtube.com.cookie
  3. During daily refresh, youtube.com.sh automatically runs
  4. Scraper syncs your liked videos and publishes them to NOSTR
  5. Videos appear in YouTube interface
πŸ“ Files:
  • Astroport.ONE/IA/youtube.com.sh - Bash scraper script
  • UPassport/templates/youtube.html - Video display interface
Create Your Own Scraper

πŸš€ Extensible System: You can create scrapers for any domain following the same pattern as YouTube and Leboncoin.

Example: Leboncoin Scraper

The Leboncoin scraper demonstrates a more complex implementation with Python backend:

# Leboncoin scraper structure # 1. Bash script: leboncoin.fr.sh # - Called automatically during NOSTRCARD.refresh.sh # - Receives: PLAYER_EMAIL as argument # - Finds cookie: ~/.zen/game/nostr/EMAIL/.leboncoin.fr.cookie # - Calls Python backend if needed # 2. Python backend (optional): scraper_leboncoin.py # - Complex scraping logic # - Uses cookie for authentication # - Publishes results to NOSTR
Steps to Create a New Scraper:
  1. Upload Cookie: Upload .DOMAIN.cookie via Cookie Manager
  2. Create Bash Script: Create DOMAIN.sh in Astroport.ONE/IA/
    #!/bin/bash
    # DOMAIN.sh - Auto-scraper for DOMAIN
    PLAYER=$1
    COOKIE_FILE="$HOME/.zen/game/nostr/$PLAYER/.DOMAIN.cookie"
    
    if [ ! -f "$COOKIE_FILE" ]; then
        echo "Cookie not found: $COOKIE_FILE"
        exit 1
    fi
    
    # Your scraping logic here
    python3 scraper_DOMAIN.py "$COOKIE_FILE" [...args]
  3. Create Python Backend (optional): Create scraper_DOMAIN.py for complex logic
    #!/usr/bin/env python3
    # scraper_DOMAIN.py - Python backend for DOMAIN scraper
    import sys
    from get_cookie import read_cookie_from_file
    
    cookie_file = sys.argv[1]
    cookie = read_cookie_from_file(cookie_file)
    
    # Your scraping logic here
    # Use cookie for authentication
    # Publish results to NOSTR
  4. Test Locally: Run your scraper manually to verify it works
    bash Astroport.ONE/IA/DOMAIN.sh user@email.com
  5. Automatic Execution: System will run your scraper automatically during daily refresh
Reference Implementation
  • YouTube (Simple): Astroport.ONE/IA/youtube.com.sh - Uses yt-dlp
  • Leboncoin (Complex):
    • Astroport.ONE/IA/leboncoin.fr.sh - Bash wrapper
    • Astroport.ONE/IA/scraper_leboncoin.py - Python backend
Cookie File Format

Required Format: Netscape HTTP Cookie File format (exported from browser extensions)

Single-Domain Only

Each cookie file must contain cookies for one domain only (and its subdomains).

❌ Multi-Domain Files Rejected: Files containing cookies for multiple different domains will be rejected with an error message.
Example Cookie File:
# Netscape HTTP Cookie File
# https://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file! Do not edit.

.youtube.com	TRUE	/	TRUE	1796910450	__Secure-Install	uuid-value
.youtube.com	TRUE	/	TRUE	1796910450	VISITOR_INFO1_LIVE	visitor-id
.youtube.com	TRUE	/	TRUE	1793886482	PREF	preference-value
Storage Location:

Cookies are stored in your MULTIPASS directory:

~/.zen/game/nostr/EMAIL/
β”œβ”€β”€ .youtube.com.cookie      # YouTube cookies (single-domain)
β”œβ”€β”€ .leboncoin.fr.cookie     # Leboncoin cookies (single-domain)
β”œβ”€β”€ .DOMAIN.cookie           # Any domain cookie (single-domain)
└── APP/
    └── uDRIVE/              # Scraped content (videos, music, etc.)
How to Export Cookies
Browser Extensions:
  • Chrome/Edge: "Get cookies.txt LOCALLY" extension
  • Firefox: "cookies.txt" extension
Steps:
  1. Open the website you want to export cookies for
  2. Click the extension icon
  3. Filter by current site (usually automatic)
  4. Select Netscape format
  5. Export - this creates a single-domain cookie file
  6. Upload to UPlanet via Cookie Manager
⚠️ Important: Export cookies separately for each domain. Don't export all browser cookies at once - the system will reject multi-domain files.
Resources
  • Cookie Manager Interface Upload and manage your cookies
  • Full Documentation Complete cookie system documentation
  • YouTube Interface See YouTube scraper in action
  • Example Scrapers:
    • Astroport.ONE/IA/youtube.com.sh - YouTube video sync
    • Astroport.ONE/IA/leboncoin.fr.sh - Leboncoin ad scraper
    • Astroport.ONE/IA/scraper_leboncoin.py - Python backend example

Practical Examples

Real-world code examples you can use in your applications

NOSTR Console - Real-time Event Monitor

Access: Open NOSTR Console

What is it?

The NOSTR Console is a powerful real-time event monitoring tool that displays all NOSTR events from the last 48 hours, similar to a browser's network console. It's perfect for debugging, monitoring, and understanding NOSTR event flows.

Features
  • Real-time Monitoring: Automatically connects to the relay and displays events as they arrive
  • All Event Kinds: Monitors all NOSTR event kinds (0, 1, 3, 4, 5, 6, 7, and more)
  • Powerful Filtering:
    • Filter by kind (0=Metadata, 1=Text Note, 3=Contacts, etc.)
    • Filter by pubkey (hex or npub format)
    • Filter by tags (#e, #p, #t, #g, etc.)
    • Filter by geohash
    • Isolate your own events
  • Event Inspection: Click any event to see detailed JSON view with signature, tags, and content
  • Quick Actions: Copy event ID, follow pubkey, or filter replies
  • Connection Status: Detailed connection information with relay URL and authentication status
How it Works

The console uses common.js to connect to the relay and subscribes to events from the last 48 hours using multiple filters:

// The console automatically subscribes to:
- Regular events (kinds 0-7)
- Parameterized replaceable events (kinds 10000-10009)
- Other common event types
- All within the last 48 hours

// Uses window.nostrRelay from common.js
const subscription = window.nostrRelay.sub(filters);
subscription.on('event', (event) => {
    // Events are displayed in real-time
});
subscription.on('eose', () => {
    // End of stored events
});
Use Cases
  • Debugging: See what events your application is publishing
  • Monitoring: Track events from specific users or tags
  • Learning: Understand NOSTR event structure and content
  • Testing: Verify that your events are being published correctly
  • Discovery: Find interesting events and users on the relay
Tip: The console automatically connects when common.js is loaded. Click the status indicator to see detailed connection information.
Example 1: User Authentication
// Include libraries
<script src="https://ipfs.copylaradio.com/ipns/copylaradio.com/nostr.bundle.js"></script>
<script src="https://ipfs.copylaradio.com/ipns/copylaradio.com/common.js"></script>

// Connect user
async function login() {
  try {
    const pubkey = await connectNostr(true); // true = force NIP-42 auth
    console.log('User connected:', pubkey);
    
    // Convert to npub for display
    const npub = hexToNpub(pubkey);
    console.log('User npub:', npub);
    
    return pubkey;
  } catch (error) {
    console.error('Login failed:', error);
  }
}
Example 2: Publishing a Post
// Publish a simple text post
async function publishPost(content) {
  const result = await publishNote(content);
  if (result.success && result.eventId) {
    console.log('Post published:', result.eventId);
    return result.event;
  }
  throw new Error('Failed to publish: ' + result.errors.join(', '));
}

// Publish a post with tags (for smart contract processing)
async function publishPostWithTags(content, tags) {
  const result = await publishNote(content, tags);
  if (result.success && result.eventId) {
    console.log('Post with tags published:', result.eventId);
    return result.event;
  }
  throw new Error('Failed to publish: ' + result.errors.join(', '));
}

// Example: Post with geolocation and URL
await publishPostWithTags('Hello from Paris! ' + window.location.href, [
  ['g', '48.86,2.35'],  // Geolocation tag
  ['t', 'travel'],       // Topic tag
  ['r', window.location.href]  // Reference URL
]);
Example 3: File Upload
// Upload a file to IPFS
async function uploadFile(file) {
  // Upload to IPFS (automatically creates metadata)
  const result = await uploadPhotoToIPFS(file);
  
  console.log('File uploaded:', result.cid);
  console.log('IPFS URL:', result.ipfs_url);
  console.log('Metadata:', result.info);
  
  // The event is automatically published to NOSTR
  // Smart contracts will process it automatically
  
  return result;
}

// Handle file input
document.getElementById('fileInput').addEventListener('change', async (e) => {
  const file = e.target.files[0];
  if (file) {
    const result = await uploadFile(file);
    console.log('Upload complete:', result);
  }
});
Example 4: Subscribing to Events
// Subscribe to events from a specific user
async function subscribeToUser(pubkey) {
  // Connect to relay first
  await connectToRelay();
  
  // Subscribe to user's posts
  const sub = window.nostrRelay.sub([{
    kinds: [1],  // Text notes
    authors: [pubkey],
    limit: 10
  }]);
  
  sub.on('event', (event) => {
    console.log('New post:', event.content);
    // Process the event in your app
    displayPost(event);
  });
  
  return sub;
}

// Subscribe to events with specific tags (for smart contract results)
async function subscribeToTaggedEvents(tag) {
  await connectToRelay();
  
  const sub = window.nostrRelay.sub([{
    kinds: [1],
    '#t': [tag],  // Events with this tag
    limit: 50
  }]);
  
  sub.on('event', (event) => {
    // Smart contract may have added additional tags
    console.log('Event with tag:', event);
  });
  
  return sub;
}

API Reference

Complete reference for common.js functions

connectNostr(forceAuth = false)

Description: Connect to NOSTR wallet and optionally authenticate with NIP-42

Parameters:

  • forceAuth (boolean, optional): If true, forces NIP-42 authentication

Returns: Promise resolving to user's hex pubkey

const pubkey = await connectNostr(true);
publishNote(content, additionalTags = [], kind = 1, options = {})

Description: Publish a NOSTR note event

Parameters:

  • content (string): Note content
  • additionalTags (array, optional): Additional tags for the event
  • kind (number, optional): Event kind (default: 1)
  • options (object, optional): Additional options

Returns: Promise resolving to result object with success, event, eventId, etc.

const result = await publishNote('Hello!', [['t', 'greeting']]);
if (result.success) {
  console.log('Event ID:', result.eventId);
}
uploadPhotoToIPFS(file)

Description: Upload a file to IPFS and publish metadata to NOSTR

Parameters:

  • file (File): File object from input

Returns: Promise resolving to upload result with CID, IPFS URL, and metadata

const result = await uploadPhotoToIPFS(file);
console.log(result.cid, result.ipfs_url, result.info);
hexToNpub(hex) / npubToHex(npub)

Description: Convert between hex and npub (bech32) formats

Parameters:

  • hex (string): 64-character hex pubkey
  • npub (string): bech32-encoded npub

Returns: Converted string or null if invalid

const npub = hexToNpub('60c1133d148ae0d2c4b42506fb4abacac903032680b178010c942bd538643f78');
const hex = npubToHex(npub);
sendLike(eventId, authorPubkey, content = "+")

Description: Send a reaction (like) to an event (NIP-25)

Parameters:

  • eventId (string): ID of the event to react to
  • authorPubkey (string): Author's pubkey
  • content (string, optional): Reaction content (default: "+")

Returns: Promise resolving to published reaction event

await sendLike(eventId, authorPubkey, '❀️');

1. MULTIPASS Authentication

NOSTR NIP-07 NIP-42

Enable decentralized login with NOSTR keys. No passwords, no email, no central database.

Test Authentication
How Authentication Works
  1. MULTIPASS Account: You need a MULTIPASS account registered on this relay
  2. Connect: Click the button below to connect your NOSTR wallet
  3. Authenticate: Your extension will automatically send a NIP-42 event (kind 22242)
  4. Relay Acceptance: The relay accepts your NIP-42 event if you have MULTIPASS
  5. Verify: The system checks for recent authentication (valid 24h)
  6. Upload: Once authenticated, you can upload files securely
⚠️ Important: If you don't have a MULTIPASS account yet, create one here first. Without MULTIPASS, the relay will reject your authentication events.
Click button to test authentication...
// Simple integration example async function connectMultipass() { // Load common.js from IPFS const script = document.createElement('script'); script.src = 'https://ipfs.copylaradio.com/ipns/copylaradio.com/common.js'; document.head.appendChild(script); // Connect to NOSTR const pubkey = await connectNostr(); console.log('Connected:', pubkey); // Use authenticated user return pubkey; }

Upload files to IPFS and publish metadata on NOSTR. View recent uploads from the community.

Upload File Test
Image
NIP-94, kind 1063
Video
NIP-71, kind 21/22
Audio
NIP-94, kind 1063
Document
NIP-94, kind 1063
Select a file type above and upload...
Recent Uploads
Loading recent files and videos...
// File upload with automatic metadata async function uploadToIPFS(file, npub) { const formData = new FormData(); formData.append('file', file); formData.append('npub', npub); const response = await fetch('/api/fileupload', { method: 'POST', body: formData }); const result = await response.json(); // Returns: { cid, thumbnail_ipfs, info, fileName } return result; }

Add likes, reactions, comments, and shares to any content or URL. Comments and shares are linked to this page.

Comments & Shares for this Page
Loading interactions...
Test Actions
Use buttons above to post comments or share this page...
Note: Likes require an event ID. Comments and shares are attached to the current page URL.
// Send a like (NIP-25 reaction) async function sendLike(eventId, authorPubkey) { // Uses common.js function const result = await sendLike(eventId, authorPubkey, "+"); return result; } // Post a comment async function postComment(content, url) { const result = await postComment(content, url); return result; } // Share to user's feed async function shareContent(url, message) { const result = await shareCurrentPage(message); return result; }

Add real-time chat to any page. Messages are stored on NOSTR relays using NIP-28 (Public Chat Channels).

UMAP Chat Room
UMAP: 0.00, 0.00 0
Loading messages...
Geographic chat room using NIP-28 (kind 42 - Public Channel)
Channel ID: UMAP_0.00_0.00
// Subscribe to real-time messages async function subscribeToChat(hashtag) { const relay = await connectToRelay(); const sub = relay.sub([{ kinds: [1], "#t": [hashtag], limit: 50 }]); sub.on('event', event => { displayChatMessage(event); }); } // Send message async function sendMessage(content, hashtag) { await publishNote(content, [["t", hashtag]]); }

Explore existing applications and learn from their implementation.

NostrTube

Decentralized video platform with IPFS storage and NOSTR social features.

View Demo View Code
Theater Mode

Immersive video player with live comments and reactions.

View Demo View Code
Playlists

Manage video playlists stored on NOSTR (NIP-51).

View Demo View Code
Documentation & Resources

Quick Start for Developers

Add MULTIPASS authentication to your website in 3 steps:

<!-- 1. Include common.js --> <script src="https://ipfs.copylaradio.com/ipns/copylaradio.com/common.js"></script> <!-- 2. Add login button --> <button onclick="loginWithMultipass()">Connect with MULTIPASS</button> <!-- 3. Use the authenticated user --> <script> async function loginWithMultipass() { const pubkey = await connectNostr(); console.log('User connected:', pubkey); // Now you can use all NOSTR features await publishNote('Hello from my app!'); await uploadPhotoToIPFS(file); await sendLike(eventId, authorPubkey); } </script>

Secure File Upload (NIP-42 Authentication)

Ensure proper authentication before uploading to prevent 403 errors:

Authentication Flow
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Browser    │────>β”‚ NOSTR Wallet │────>β”‚    Relay     │────>β”‚   Backend    β”‚
β”‚  (Connect)   β”‚     β”‚  (NIP-42)    β”‚     β”‚ (MULTIPASS)  β”‚     β”‚   (Verify)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      Step 1:              Step 2:              Step 3:              Step 4:
  Click Connect      Send kind 22242      Check MULTIPASS    Check recent event
                     auth event           Accept/Reject       24h window

βœ… SUCCESS: User can upload files
❌ FAILURE: Create MULTIPASS account first (/g1)
                
// Method 1: Automatic authentication check (recommended) async function uploadFile(file) { // uploadPhotoToIPFS now automatically checks authentication const result = await uploadPhotoToIPFS(file); console.log('Uploaded to IPFS:', result.ipfs_url); } // Method 2: Manual authentication check async function uploadWithManualCheck(file) { // Verify authentication before upload const isAuth = await ensureAuthentication({ forceCheck: true, // Force API verification showUI: true // Show user notifications }); if (!isAuth) { console.error('Authentication failed'); return; } const result = await uploadPhotoToIPFS(file); console.log('Upload successful!', result); } // Method 3: Check authentication status via API async function checkAuth() { const status = await verifyAuthenticationWithAPI(userPubkey); console.log('Auth status:', status); // Returns: { auth_verified: true/false, message: "...", ... } }
Authentication Best Practices
  • MULTIPASS Required: The relay only accepts NIP-42 events from registered MULTIPASS accounts
  • NIP-42 Events: Authentication requires kind 22242 events on the relay
  • 24-Hour Validity: Auth events are valid for 24 hours
  • Automatic Verification: uploadPhotoToIPFS() now checks auth automatically
  • Manual Checks: Use ensureAuthentication() for custom workflows
  • Test API: Use /api/test-nostr?npub=YOUR_KEY to verify auth status
  • Relay Policy: Users without MULTIPASS or not in amisOfAmis.txt are rejected by default