First functional commit!

This commit is contained in:
Your Name 2025-09-17 15:47:17 -04:00
parent f86e0ce7dd
commit 1b28f78f44
11 changed files with 17787 additions and 33 deletions

View File

@ -5,16 +5,16 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
## What I Look For
### 1. Routing Events (Kind 30000)
### 1. Routing Events (Kind 22222)
- Monitor all relays I'm connected to
- Look for events with `kind: 30000`
- Look for events with `kind: 22222`
- Check if `tags` contains `["p", "<my_pubkey>"]`
- These are events meant for me to process
### 2. Event Structure I Expect
```json
{
"kind": 30000,
"kind": 22222,
"pubkey": "<some_ephemeral_key>", // Not important to me
"content": "<nip44_encrypted_payload>", // This is what I need
"tags": [["p", "<my_pubkey>"]],
@ -29,7 +29,7 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
### 1. Validate
- Verify the event signature is valid
- Confirm the `p` tag contains my pubkey
- Ensure it's kind 30000
- Ensure it's kind 22222
### 2. Decrypt
- Use my private key with NIP-44 to decrypt the content
@ -37,10 +37,11 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
```json
{
"event": { /* The event to forward */ },
"routing": {
"routing": { /* Superball Instructions */
"relays": ["wss://relay1.com", "wss://relay2.com"],
"delay": 30,
"pad": "+150", // or "-50"
"padding": "+150", // or "-50"
"p": "next_superball_pubkey", // Optional - missing means final posting
"audit": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456", // Required audit tag
"payment": "eCash_token" // Optional
@ -56,8 +57,8 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
- Queue the event for delayed processing
#### Padding
- **Remove padding (`"pad": "-N"`)**: Delete N bytes worth of padding tags from the event
- **Add padding (`"pad": "+N"`)**: Create new routing wrapper with N bytes of padding tags
- **Remove padding (`"padding": "-N"`)**: Delete N bytes worth of padding tags from the event
- **Add padding (`"padding": "+N"`)**: Create new routing wrapper with N bytes of padding tags
#### Relays
- Post to ALL relays in the `relays` array
@ -69,8 +70,8 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
- **`p` field missing**: Extract inner event and post directly to relays (end chain, no padding changes)
#### Padding Logic
- **`p` field present + `pad` field**: Apply padding changes when creating routing wrapper
- **`p` field missing**: Ignore any `pad` field - cannot modify signed event
- **`p` field present + `padding` field**: Apply padding changes when creating routing wrapper
- **`p` field missing**: Ignore any `padding` field - cannot modify signed event
- **Final hop rule**: Never modify signed events, post exactly as received
#### Audit Tag Processing
@ -89,7 +90,7 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
```json
{
"kind": 30000, // Always use routing event
"kind": 22222, // Always use routing event
"pubkey": "<my_ephemeral_key>", // Generate fresh ephemeral key
"content": "<encrypted_inner_event>", // Re-encrypt with my key
"tags": [
@ -143,7 +144,7 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
## Example Processing Flow
1. **Receive**: Kind 30000 event with my pubkey in p tag
1. **Receive**: Kind 22222 event with my pubkey in p tag
2. **Decrypt**: Extract inner event + routing instructions
3. **Queue**: Schedule for delayed processing (e.g., 30 seconds + jitter)
4. **Process**: Apply padding changes and prepare for forwarding

View File

@ -20,7 +20,7 @@ Alice wants to post a message under her real identity while hiding her location
"pubkey": "alice123...",
"content": "The government is lying about inflation statistics",
"tags": [],
"created_at": 1703000000,
"created_at": 1702222200,
"id": "alice_event_id",
"sig": "alice_signature"
}
@ -34,9 +34,10 @@ Payload for Superball B (final hop - no `p` field):
"routing": {
"relays": ["wss://relay3.com", "wss://relay4.com"],
"delay": 15,
"audit": "9f8e7d6c5b4a39281726354019283746502918374650283746501928374650"
"payment": "eCash_ZYX321..." // Optional payment
// No "p" field - this means final posting
// No "pad" field - can't modify signed event
// No "audit" field - can't audit final signed event - no need
}
}
```
@ -44,7 +45,7 @@ Payload for Superball B (final hop - no `p` field):
Creates routing event:
```json
{
"kind": 30000,
"kind": 22222,
"pubkey": "ephemeral_key_2",
"content": "<encrypted_payload_for_superball_b>",
"tags": [["p", "sball_b012..."]],
@ -62,7 +63,7 @@ Payload for Superball A (continuing chain):
"routing": {
"relays": ["wss://relay2.com"],
"delay": 45,
"pad": "+200",
"padding": "+200",
"p": "sball_b012...", // Next Superball in chain
"audit": "1a2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890",
"payment": "eCash_A1B2C3..." // Optional payment
@ -73,10 +74,13 @@ Payload for Superball A (continuing chain):
Alice posts this to Relay1:
```json
{
"kind": 30000,
"kind": 22222,
"pubkey": "ephemeral_key_1",
"content": "<encrypted_payload_for_superball_a>",
"tags": [["p", "sball_a789..."]],
"tags": [
["p", "sball_a789..."],
["p", "fake audit tag..."],
],
"created_at": 1703000200,
"id": "routing_for_a",
"sig": "ephemeral_signature_1"
@ -87,12 +91,12 @@ Alice posts this to Relay1:
**T+0**: Alice posts routing event to Relay1
```
Relay1: kind 30000 event (p tag = sball_a789...)
Relay1: kind 22222 event (p tag = sball_a789...)
```
**T+5**: Superball A processes
- Decrypts payload
- Sees: relay2.com, delay 45s, pad +200
- Sees: relay2.com, delay 45s, padding +200
- Needs to ADD padding, so creates new wrapper
- Queues for 45-second delay
@ -100,7 +104,7 @@ Relay1: kind 30000 event (p tag = sball_a789...)
```
Relay2: NEW routing event (always looks the same)
{
"kind": 30000,
"kind": 22222,
"pubkey": "superball_a_ephemeral_key", // Fresh key
"content": "<newly_encrypted_payload>", // Re-encrypted
"tags": [
@ -130,7 +134,7 @@ Relay3 AND Relay4: Alice's original signed event appears exactly as she created
"pubkey": "alice123...",
"content": "The government is lying about inflation statistics",
"tags": [], // Original tags preserved
"created_at": 1703000000,
"created_at": 1702222200,
"id": "alice_event_id",
"sig": "alice_signature" // Original signature preserved
}

View File

@ -14,7 +14,7 @@ User creates and signs their normal Nostr event:
"pubkey": "user_pubkey",
"content": "Message content",
"tags": [],
"created_at": 1703000000,
"created_at": 1702222200,
"id": "event_id",
"sig": "user_signature"
}
@ -30,7 +30,7 @@ User creates routing instructions:
"wss://target-relay3.com"
],
"delay": 30,
"pad": "+150",
"padding": "+150",
"p": "superball_b_pubkey", // Next superball (optional - if missing, final posting)
"audit": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456", // Audit pubkey (always required)
"payment": "eCash_token_here" // Optional payment for processing
@ -41,7 +41,7 @@ User creates routing instructions:
User creates a routing event with NIP-44 encryption:
```json
{
"kind": 30000, // Superball routing event
"kind": 22222, // Superball routing event
"pubkey": "ephemeral_key",
"content": "<nip44_encrypted_payload>",
"tags": [
@ -61,14 +61,14 @@ The encrypted payload contains:
"pubkey": "user_pubkey",
"content": "Message content",
"tags": [],
"created_at": 1703000000,
"created_at": 1702222200,
"id": "event_id",
"sig": "user_signature"
},
"routing": {
"relays": ["wss://target-relay1.com", "wss://target-relay2.com"],
"delay": 30,
"pad": "+150",
"padding": "+150",
"p": "next_superball_pubkey", // Optional - if missing, final posting
"audit": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456", // Required audit pubkey
"payment": "eCash_token" // Optional payment
@ -78,7 +78,7 @@ The encrypted payload contains:
## Superball Processing
1. **Receive**: Monitor for kind 30000 events with p tag = own pubkey
1. **Receive**: Monitor for kind 22222 events with p tag = own pubkey
2. **Decrypt**: Use NIP-44 to decrypt the content
3. **Parse**: Extract the event and routing instructions
4. **Apply Padding**: Modify padding according to instructions
@ -96,10 +96,10 @@ Superballs **ALWAYS** create a new routing wrapper to prevent analysis of paddin
- **Metadata mixing**: Padding levels change unpredictably at each hop
### Routing Event Structure
Every forward creates a new kind 30000 event:
Every forward creates a new kind 22222 event:
```json
{
"kind": 30000,
"kind": 22222,
"pubkey": "<fresh_ephemeral_key>",
"content": "<newly_encrypted_payload>",
"tags": [
@ -133,14 +133,14 @@ The audit mechanism allows users to detect malicious Superballs that drop events
{
"relays": ["wss://relay2.com"],
"delay": 45,
"pad": "+200",
"padding": "+200",
"p": "sball_b_real_pubkey",
"audit": "a1b2c3...fake_pubkey_for_audit"
}
// Superball A should post this after 45s with +200 bytes:
{
"kind": 30000,
"kind": 22222,
"tags": [
["p", "sball_b_real_pubkey"], // Real next hop
["p", "a1b2c3...fake_pubkey"], // Audit tag (looks identical)

View File

@ -14,7 +14,7 @@ Superball provides Tor-like location privacy for Nostr users. It's a daemon that
- **Location Privacy**: Hide your network location while preserving your identity
- **Traffic Analysis Resistance**: Random delays and size padding prevent correlation
- **Simple Protocol**: Uses NIP-44 encryption with new kind 30000 for routing
- **Simple Protocol**: Uses NIP-44 encryption with new kind 22222 for routing
- **Flexible Routing**: Support for multi-hop paths through multiple daemons
- **Signature Preservation**: Original event signatures maintained for authenticity
- **Audit Security**: Detect and avoid malicious Superballs through cryptographic verification

299
SUPs.md Normal file
View File

@ -0,0 +1,299 @@
# SUPs - Superball Upgrade Possibilities
SUPs (Superball Upgrade Possibilities) describe standards for the Superball anonymity protocol, including core protocol rules, daemon behavior specifications, routing algorithms, and security mechanisms.
## SUP Types
- **Standards Track**: SUPs that affect protocol compatibility
- **Informational**: General guidelines and information
- **Process**: Procedures and governance for the SUP process
## SUP Status
- **Draft**: Initial specification under development
- **Proposed**: Ready for community review
- **Final**: Accepted and implemented
- **Replaced**: Superseded by newer SUP
- **Withdrawn**: No longer pursued
---
## SUP-1: Core Protocol Specification
**Type**: Standards Track
**Status**: Final
**Author**: Alice & Contributors
**Created**: 2024-01-17
### Abstract
This SUP defines the core Superball protocol for providing location privacy in Nostr through encrypted event routing via daemon nodes. Users can post content under their real identity while completely hiding their network location.
### Motivation
Current Nostr implementations reveal users' network locations through direct relay connections, enabling surveillance and censorship. Superball provides Tor-like anonymity while preserving message authenticity through cryptographic signatures.
### Specification
#### Event Kind
- **Kind 22222**: Superball routing event
#### Routing Event Structure
```json
{
"kind": 22222,
"pubkey": "<ephemeral_key>",
"content": "<nip44_encrypted_payload>",
"tags": [
["p", "<superball_pubkey>"],
["p", "<audit_pubkey>"],
["padding", "<random_data>"]
],
"created_at": "<timestamp>",
"id": "<event_id>",
"sig": "<ephemeral_signature>"
}
```
#### Encrypted Payload Format
```json
{
"event": { /* Original signed event or next routing event */ },
"routing": {
"relays": ["wss://relay1.com", "wss://relay2.com"],
"delay": 30,
"padding": "+150",
"p": "next_superball_pubkey", // Optional
"audit": "audit_verification_pubkey", // Required
"payment": "ecash_token" // Optional
}
}
```
#### Processing Rules
1. **Always Rewrap**: Create new routing event for every forward
2. **Audit Verification**: Include audit tag as p tag in routing event
3. **Delay Compliance**: Wait specified time plus random jitter
4. **Padding Operations**: Apply size modifications as instructed
5. **Multi-Relay Posting**: Post to all specified relays
### Rationale
The protocol uses NIP-44 encryption for simplicity over NIP-59 gift wrapping, includes mandatory audit mechanisms for security, and employs consistent rewrapping to prevent traffic analysis.
---
## SUP-2: Audit Security Mechanism
**Type**: Standards Track
**Status**: Final
**Author**: Alice & Contributors
**Created**: 2024-01-17
### Abstract
This SUP defines the audit mechanism that allows users to detect malicious Superballs through cryptographic verification of proper event forwarding.
### Motivation
Users need the ability to verify that Superballs are honestly forwarding events according to instructions, including proper delays, padding operations, and relay posting.
### Specification
#### Audit Tag Format
- **Length**: 64 hexadecimal characters
- **Format**: Appears as valid Nostr pubkey
- **Generation**: Cryptographically secure random per-hop
- **Posting**: Always included as `["p", "<audit_tag>"]` in routing events
#### User Monitoring
1. Generate unique audit tag for each routing hop
2. Monitor specified relays for audit tag appearance
3. Verify timing matches delay instructions
4. Verify event size matches padding operations
5. Build reputation scores for Superballs based on compliance
#### Privacy Properties
- Audit tags indistinguishable from real next-hop pubkeys
- Only originating user knows which tags are audit verification
- No correlation possible between different users' audit tags
### Implementation
```javascript
// Generate audit tag
const auditTag = bytesToHex(randomBytes(32));
// Monitor relay for audit appearance
const monitorAudit = async (relay, auditTag, expectedDelay, expectedSize) => {
const startTime = Date.now();
relay.subscribe([{
kinds: [22222],
"#p": [auditTag]
}], {
onevent: (event) => {
const actualDelay = (event.created_at * 1000) - startTime;
const actualSize = JSON.stringify(event).length;
// Verify compliance
const delayCompliant = Math.abs(actualDelay - expectedDelay) < TOLERANCE;
const sizeCompliant = Math.abs(actualSize - expectedSize) < PADDING_TOLERANCE;
recordSuperballReputation(event.pubkey, delayCompliant && sizeCompliant);
}
});
};
```
---
## SUP-3: Daemon Operational Rules
**Type**: Informational
**Status**: Final
**Author**: Superball Operators
**Created**: 2024-01-17
### Abstract
This SUP provides operational guidelines for Superball daemon operators, including security practices, resource management, and privacy protection protocols.
### Security Rules
1. **Never log sensitive data** - Don't store decrypted content or routing information
2. **Generate fresh keys** - Use new ephemeral keys for each forward operation
3. **Validate everything** - Check signatures, event structure, and relay URLs
4. **Rate limiting** - Prevent abuse through request throttling
5. **Memory clearing** - Immediately clear decrypted data after processing
### Privacy Rules
1. **No correlation** - Don't link input events to output events in logs
2. **Random timing** - Add jitter to specified delays
3. **Traffic mixing** - Send decoy traffic when idle (optional enhancement)
4. **Connection rotation** - Periodically reconnect to prevent fingerprinting
### Processing Rules
1. **First come, first served** - Process events in arrival order
2. **Fail silently** - Drop invalid events without response
3. **Retry logic** - Attempt relay posting 3 times before giving up
4. **Resource limits** - Drop oldest queued events if memory/queue full
---
## SUP-4: Multi-Path Routing Enhancement
**Type**: Standards Track
**Status**: Draft
**Author**: Security Researchers
**Created**: 2024-01-17
### Abstract
This SUP proposes an enhancement allowing users to send the same event through multiple independent Superball chains simultaneously for increased security against coordinated attacks.
### Motivation
Single routing chains are vulnerable to adversaries who control multiple nodes in the path. Multi-path routing increases security by requiring adversaries to control nodes across multiple independent chains.
### Specification
#### Multi-Path Event Structure
```json
{
"paths": [
{
"chain_id": "random_identifier_1",
"routing": { /* Standard routing instructions */ }
},
{
"chain_id": "random_identifier_2",
"routing": { /* Different Superballs and relays */ }
}
],
"threshold": 1 // Minimum successful deliveries required
}
```
#### Implementation Requirements
- Generate independent routing chains with no overlapping Superballs
- Use different target relays for each path
- Include chain_id in audit mechanism for path-specific monitoring
- Consider delivery successful when threshold number of paths complete
### Security Analysis
- **Resistance**: Requires adversary control of nodes across multiple chains
- **Redundancy**: Event delivery succeeds even if some paths are compromised
- **Cost**: Increases bandwidth and processing requirements
- **Detection**: Path-specific audit tags enable per-chain monitoring
---
## SUP-5: Payment Integration Specification
**Type**: Standards Track
**Status**: Proposed
**Author**: Economic Protocol Designers
**Created**: 2024-01-17
### Abstract
This SUP defines the integration of eCash payment tokens for monetized Superball services, enabling sustainable economic models for privacy infrastructure.
### Specification
#### Payment Field Format
```json
{
"payment": {
"type": "ecash",
"token": "base64_encoded_ecash_token",
"amount": 100, // satoshis
"mint": "https://mint.example.com"
}
}
```
#### Processing Rules
1. **Validate token** - Verify eCash token authenticity with mint
2. **Check amount** - Ensure payment meets minimum service fee
3. **Redeem atomically** - Claim payment only after successful forwarding
4. **Rate limits** - Higher processing priority for paid requests
#### Economic Considerations
- Free tier with limited throughput for accessibility
- Premium tiers with guaranteed processing times
- Dynamic pricing based on network congestion
- Reputation bonuses for consistent payment
---
## Future SUP Topics
### Proposed Enhancements
- **SUP-6**: Zero-Knowledge Proof Integration for Verifiable Delays
- **SUP-7**: Decentralized Superball Discovery Protocol
- **SUP-8**: Quantum-Resistant Encryption Migration
- **SUP-9**: Mobile Client Optimizations
- **SUP-10**: Governance and Protocol Upgrade Mechanisms
### Research Areas
- Traffic analysis resistance measurements
- Economic attack vectors and mitigations
- Integration with other privacy protocols
- Scalability optimizations
- Censorship resistance enhancements
---
## Contributing
SUPs follow the collaborative development process:
1. **Draft**: Create initial specification
2. **Discussion**: Community review and feedback
3. **Implementation**: Reference implementations
4. **Testing**: Security and compatibility testing
5. **Final**: Adoption by Superball ecosystem
Submit SUP proposals through the project repository with detailed technical specifications, security analysis, and implementation considerations.

190
web/login-and-profile.html Normal file
View File

@ -0,0 +1,190 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🔐 NOSTR_LOGIN_LITE - All Login Methods Test</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
background: #ffffff;
min-height: 100vh;
color: #000000;
}
</style>
</head>
<body>
<div id="login-section">
<!-- Login UI if needed -->
</div>
<div id="profile-section">
<img id="profile-picture">
<div id="profile-pubkey"></div>
<div id="profile-name"></div>
<div id="profile-about"></div>
</div>
<!-- Load the official nostr-tools bundle first -->
<script src="../lite/nostr.bundle.js"></script>
<!-- Load NOSTR_LOGIN_LITE main library (now includes NIP-46 extension) -->
<script src="../lite/nostr-lite.js"></script>
<script>
// Global variables
let nlLite = null;
let userPubkey = null;
let relayUrl = 'wss://relay.laantungir.net';
// Initialize NOSTR_LOGIN_LITE
async function initializeApp() {
// console.log('INFO', 'Initializing NOSTR_LOGIN_LITE...');
try {
await window.NOSTR_LOGIN_LITE.init({
theme: 'default',
darkMode: false,
methods: {
extension: true,
local: true,
seedphrase: true,
connect: true, // Enables "Nostr Connect" (NIP-46)
remote: true, // Also needed for "Nostr Connect" compatibility
otp: true // Enables "DM/OTP"
},
floatingTab: {
enabled: true,
hPosition: .98, // 95% from left
vPosition: 0, // 50% from top (center)
getUserInfo: true, // Fetch user profiles
getUserRelay: ['wss://relay.laantungir.net'], // Custom relays for profiles
appearance: {
style: 'minimal',
theme: 'auto',
icon: '',
text: 'Login',
iconOnly: false
},
behavior: {
hideWhenAuthenticated: false,
showUserInfo: true,
autoSlide: false,
persistent: false
},
animation: {
slideDirection: 'right' // Slide to the right when hiding
}
},
debug: true
});
nlLite = window.NOSTR_LOGIN_LITE;
console.log('SUCCESS', 'NOSTR_LOGIN_LITE initialized successfully');
window.addEventListener('nlMethodSelected', handleAuthEvent);
} catch (error) {
console.log('ERROR', `Initialization failed: ${error.message}`);
}
}
function handleAuthEvent(event) {
const { pubkey, method, error } = event.detail;
console.log('INFO', `Auth event received: method=${method}`);
if (method && pubkey) {
userPubkey = pubkey;
console.log('SUCCESS', `Login successful! Method: ${method}, Pubkey: ${pubkey}`);
loadUserProfile();
} else if (error) {
console.log('ERROR', `Authentication error: ${error}`);
}
}
// Load user profile using nostr-tools pool
async function loadUserProfile() {
if (!userPubkey) return;
console.log('INFO', `Loading profile for: ${userPubkey}`);
document.getElementById('profile-name').textContent = 'Loading profile...';
document.getElementById('profile-pubkey').textContent = userPubkey;
try {
// Create a SimplePool instance
const pool = new window.NostrTools.SimplePool();
const relays = [relayUrl, 'wss://relay.laantungir.net'];
// Get profile event (kind 0) for the user using querySync
const events = await pool.querySync(relays, {
kinds: [0],
authors: [userPubkey],
limit: 1
});
pool.close(relays); // Clean up connections
if (events.length > 0) {
console.log('SUCCESS', 'Profile event received');
const profile = JSON.parse(events[0].content);
displayProfile(profile);
} else {
console.log('INFO', 'No profile found');
document.getElementById('profile-name').textContent = 'No profile found';
document.getElementById('profile-about').textContent = 'User has not set up a profile yet.';
}
} catch (error) {
console.log('ERROR', `Profile loading failed: ${error.message}`);
document.getElementById('profile-name').textContent = 'Error loading profile';
document.getElementById('profile-about').textContent = error.message;
}
}
// Display profile data
function displayProfile(profile) {
const name = profile.name || profile.display_name || profile.displayName || 'Anonymous User';
const about = profile.about || '';
const picture = profile.picture || '';
document.getElementById('profile-name').textContent = name;
document.getElementById('profile-about').textContent = about;
if (picture) {
document.getElementById('profile-picture').src = picture;
}
console.log('SUCCESS', `Profile displayed: ${name}`);
}
// Logout function
async function logout() {
console.log('INFO', 'Logging out...');
try {
await nlLite.logout();
console.log('SUCCESS', 'Logged out successfully');
} catch (error) {
console.log('ERROR', `Logout failed: ${error.message}`);
}
}
document.addEventListener('DOMContentLoaded', () => {
// Initialize the app
setTimeout(initializeApp, 100);
});
</script>
</body>
</html>

3190
web/nostr-lite.js Normal file

File diff suppressed because it is too large Load Diff

11534
web/nostr.bundle.js Normal file

File diff suppressed because it is too large Load Diff

184
web/sign.html Normal file
View File

@ -0,0 +1,184 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NIP-07 Signing Test</title>
</head>
<body>
<div>
<div id="status"></div>
<div id="test-section" style="display:none;">
<button id="sign-button">Sign Event</button>
<button id="encrypt-button">Test NIP-04 Encrypt</button>
<button id="decrypt-button">Test NIP-04 Decrypt</button>
<div id="results"></div>
</div>
</div>
<script src="../lite/nostr.bundle.js"></script>
<script src="../lite/nostr-lite.js"></script>
<script>
let testPubkey = 'npub1damus9dqe7g7jqn45kjcjgsv0vxjqnk8cxjkf8gqjwm8t8qjm7cqm3z7l';
let ciphertext = '';
document.addEventListener('DOMContentLoaded', async () => {
await window.NOSTR_LOGIN_LITE.init({
theme: 'default',
methods: {
extension: true,
local: true,
readonly: true,
connect: true,
remote: true,
otp: true
},
floatingTab: {
enabled: true,
hPosition: 1, // 0.0-1.0 or '95%' from left
vPosition: 0, // 0.0-1.0 or '50%' from top
appearance: {
style: 'pill', // 'pill', 'square', 'circle', 'minimal'
icon: '', // Clean display without icon placeholders
text: 'Login'
},
behavior: {
hideWhenAuthenticated: false,
showUserInfo: true,
autoSlide: true
},
getUserInfo: true, // Enable profile fetching
getUserRelay: [ // Specific relays for profile fetching
'wss://relay.laantungir.net'
]
}});
// document.getElementById('login-button').addEventListener('click', () => {
// window.NOSTR_LOGIN_LITE.launch('login');
// });
window.addEventListener('nlMethodSelected', (event) => {
document.getElementById('status').textContent = `Authenticated with: ${event.detail.method}`;
document.getElementById('test-section').style.display = 'block';
});
document.getElementById('sign-button').addEventListener('click', testSigning);
document.getElementById('encrypt-button').addEventListener('click', testEncryption);
document.getElementById('decrypt-button').addEventListener('click', testDecryption);
});
async function testSigning() {
try {
console.log('=== DEBUGGING SIGN EVENT START ===');
console.log('testSigning: window.nostr is:', window.nostr);
console.log('testSigning: window.nostr constructor:', window.nostr?.constructor?.name);
console.log('testSigning: window.nostr === our facade?', window.nostr?.constructor?.name === 'WindowNostr');
// Get user public key for comparison
const userPubkey = await window.nostr.getPublicKey();
console.log('User public key:', userPubkey);
// Check auth state if our facade
if (window.nostr?.constructor?.name === 'WindowNostr') {
console.log('WindowNostr authState:', window.nostr.authState);
console.log('WindowNostr authenticatedExtension:', window.nostr.authenticatedExtension);
console.log('WindowNostr existingNostr:', window.nostr.existingNostr);
}
const event = {
kind: 1,
content: 'Hello from NIP-07!',
tags: [],
created_at: Math.floor(Date.now() / 1000)
};
console.log('=== EVENT BEING SENT TO EXTENSION ===');
console.log('Event object:', JSON.stringify(event, null, 2));
console.log('Event keys:', Object.keys(event));
console.log('Event kind type:', typeof event.kind, event.kind);
console.log('Event content type:', typeof event.content, event.content);
console.log('Event tags type:', typeof event.tags, event.tags);
console.log('Event created_at type:', typeof event.created_at, event.created_at);
console.log('Event created_at value:', event.created_at);
// Check if created_at is within reasonable bounds
const now = Math.floor(Date.now() / 1000);
const timeDiff = Math.abs(event.created_at - now);
console.log('Time difference from now (seconds):', timeDiff);
console.log('Event timestamp as Date:', new Date(event.created_at * 1000));
// Additional debugging for user-specific issues
console.log('=== USER-SPECIFIC DEBUG INFO ===');
console.log('User pubkey length:', userPubkey?.length);
console.log('User pubkey format check (hex):', /^[a-fA-F0-9]{64}$/.test(userPubkey));
// Try to get user profile info if available
try {
const profileEvent = {
kinds: [0],
authors: [userPubkey],
limit: 1
};
console.log('Would query profile with filter:', profileEvent);
} catch (profileErr) {
console.log('Profile query setup failed:', profileErr);
}
console.log('=== ABOUT TO CALL EXTENSION SIGN EVENT ===');
const signedEvent = await window.nostr.signEvent(event);
console.log('=== SIGN EVENT SUCCESSFUL ===');
console.log('Signed event:', JSON.stringify(signedEvent, null, 2));
console.log('Signed event keys:', Object.keys(signedEvent));
console.log('Signature present:', !!signedEvent.sig);
console.log('ID present:', !!signedEvent.id);
console.log('Pubkey matches user:', signedEvent.pubkey === userPubkey);
document.getElementById('results').innerHTML = `<h3>Signed Event:</h3><pre>${JSON.stringify(signedEvent, null, 2)}</pre>`;
console.log('=== DEBUGGING SIGN EVENT END ===');
} catch (error) {
console.error('=== SIGN EVENT ERROR ===');
console.error('Error message:', error.message);
console.error('Error stack:', error.stack);
console.error('Error object:', error);
document.getElementById('results').innerHTML = `<h3>Sign Error:</h3><pre>${error.message}</pre><pre>${error.stack}</pre>`;
}
}
async function testEncryption() {
try {
const plaintext = 'Secret message for testing';
const pubkey = await window.nostr.getPublicKey();
ciphertext = await window.nostr.nip04.encrypt(pubkey, plaintext);
document.getElementById('results').innerHTML = `<h3>Encrypted:</h3><pre>${ciphertext}</pre>`;
} catch (error) {
document.getElementById('results').innerHTML = `<h3>Encrypt Error:</h3><pre>${error.message}</pre>`;
}
}
async function testDecryption() {
try {
if (!ciphertext) {
document.getElementById('results').innerHTML = `<h3>Decrypt Error:</h3><pre>No ciphertext available. Run encrypt first.</pre>`;
return;
}
const pubkey = await window.nostr.getPublicKey();
const decrypted = await window.nostr.nip04.decrypt(pubkey, ciphertext);
document.getElementById('results').innerHTML = `<h3>Decrypted:</h3><pre>${decrypted}</pre>`;
} catch (error) {
document.getElementById('results').innerHTML = `<h3>Decrypt Error:</h3><pre>${error.message}</pre>`;
}
}
</script>
</body>
</html>

1077
web/superball-builder.html Normal file

File diff suppressed because it is too large Load Diff

1275
web/superball.html Normal file

File diff suppressed because it is too large Load Diff