@xtr-dev/rondevu-server 0.5.14 → 0.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +3 -3
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/src/app.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -2824,7 +2824,7 @@ function createApp(storage, config) {
|
|
|
2824
2824
|
return config.corsOrigins[0];
|
|
2825
2825
|
},
|
|
2826
2826
|
allowMethods: ["GET", "POST", "OPTIONS"],
|
|
2827
|
-
allowHeaders: ["Content-Type", "Origin", "X-
|
|
2827
|
+
allowHeaders: ["Content-Type", "Origin", "X-PublicKey", "X-Timestamp", "X-Nonce", "X-Signature"],
|
|
2828
2828
|
exposeHeaders: ["Content-Type"],
|
|
2829
2829
|
credentials: false,
|
|
2830
2830
|
maxAge: 86400
|
|
@@ -2833,7 +2833,7 @@ function createApp(storage, config) {
|
|
|
2833
2833
|
return c.json({
|
|
2834
2834
|
version: config.version,
|
|
2835
2835
|
name: "Rondevu",
|
|
2836
|
-
description: "WebRTC signaling with RPC interface and
|
|
2836
|
+
description: "WebRTC signaling with RPC interface and Ed25519 signature-based authentication"
|
|
2837
2837
|
}, 200);
|
|
2838
2838
|
});
|
|
2839
2839
|
app.get("/health", (c) => {
|
|
@@ -2889,7 +2889,7 @@ function createApp(storage, config) {
|
|
|
2889
2889
|
}
|
|
2890
2890
|
|
|
2891
2891
|
// src/config.ts
|
|
2892
|
-
var BUILD_VERSION = true ? "0.5.
|
|
2892
|
+
var BUILD_VERSION = true ? "0.5.14" : "unknown";
|
|
2893
2893
|
function loadConfig() {
|
|
2894
2894
|
function parsePositiveInt(value, defaultValue, name, min = 1) {
|
|
2895
2895
|
const parsed = parseInt(value || defaultValue, 10);
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/storage/hash-id.ts", "../src/storage/memory.ts", "../src/storage/sqlite.ts", "../src/storage/mysql.ts", "../src/storage/postgres.ts", "../src/index.ts", "../src/app.ts", "../src/crypto.ts", "../node_modules/@noble/ed25519/index.js", "../src/rpc.ts", "../src/config.ts", "../src/storage/factory.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Generates a unique offer ID using SHA-256 hash\n * Combines SDP content with timestamp and random bytes for uniqueness\n * Uses Web Crypto API for compatibility with both Node.js and Cloudflare Workers\n *\n * @param sdp - The WebRTC SDP offer\n * @returns Unique SHA-256 hash ID\n */\nexport async function generateOfferHash(sdp: string): Promise<string> {\n // Generate random bytes for uniqueness (8 bytes = 64 bits of randomness)\n const randomBytes = crypto.getRandomValues(new Uint8Array(8));\n const randomHex = Array.from(randomBytes).map(b => b.toString(16).padStart(2, '0')).join('');\n\n // Include SDP, timestamp, and random bytes for uniqueness\n const hashInput = {\n sdp,\n timestamp: Date.now(),\n nonce: randomHex\n };\n\n // Create non-prettified JSON string\n const jsonString = JSON.stringify(hashInput);\n\n // Convert string to Uint8Array for hashing\n const encoder = new TextEncoder();\n const data = encoder.encode(jsonString);\n\n // Generate SHA-256 hash\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\n // Convert hash to hex string\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n\n return hashHex;\n}\n", "import {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\n\ninterface RateLimit {\n count: number;\n resetTime: number;\n}\n\ninterface NonceEntry {\n expiresAt: number;\n}\n\n/**\n * In-memory storage adapter for rondevu signaling system\n * Data is not persisted - all data is lost on server restart\n * Best for development, testing, or ephemeral deployments\n */\nexport class MemoryStorage implements Storage {\n // Primary storage\n private offers = new Map<string, Offer>();\n private iceCandidates = new Map<string, IceCandidate[]>(); // offerId \u2192 candidates\n private rateLimits = new Map<string, RateLimit>();\n private nonces = new Map<string, NonceEntry>();\n\n // Secondary indexes for efficient lookups\n private offersByPublicKey = new Map<string, Set<string>>(); // publicKey \u2192 offer IDs\n private offersByTag = new Map<string, Set<string>>(); // tag \u2192 offer IDs\n private offersByAnswerer = new Map<string, Set<string>>(); // answerer publicKey \u2192 offer IDs\n\n // Auto-increment counter for ICE candidates\n private iceCandidateIdCounter = 0;\n\n constructor() {}\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n const created: Offer[] = [];\n const now = Date.now();\n\n for (const request of offers) {\n const id = request.id || await generateOfferHash(request.sdp);\n\n const offer: Offer = {\n id,\n publicKey: request.publicKey,\n tags: request.tags,\n sdp: request.sdp,\n createdAt: now,\n expiresAt: request.expiresAt,\n lastSeen: now,\n };\n\n // Store offer\n this.offers.set(id, offer);\n\n // Update publicKey index\n if (!this.offersByPublicKey.has(request.publicKey)) {\n this.offersByPublicKey.set(request.publicKey, new Set());\n }\n this.offersByPublicKey.get(request.publicKey)!.add(id);\n\n // Update tag indexes\n for (const tag of request.tags) {\n if (!this.offersByTag.has(tag)) {\n this.offersByTag.set(tag, new Set());\n }\n this.offersByTag.get(tag)!.add(id);\n }\n\n created.push(offer);\n }\n\n return created;\n }\n\n async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {\n const now = Date.now();\n const offerIds = this.offersByPublicKey.get(publicKey);\n if (!offerIds) return [];\n\n const offers: Offer[] = [];\n for (const id of offerIds) {\n const offer = this.offers.get(id);\n if (offer && offer.expiresAt > now) {\n offers.push(offer);\n }\n }\n\n return offers.sort((a, b) => b.lastSeen - a.lastSeen);\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const offer = this.offers.get(offerId);\n if (!offer || offer.expiresAt <= Date.now()) {\n return null;\n }\n return offer;\n }\n\n async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {\n const offer = this.offers.get(offerId);\n if (!offer || offer.publicKey !== ownerPublicKey) {\n return false;\n }\n\n this.removeOfferFromIndexes(offer);\n this.offers.delete(offerId);\n this.iceCandidates.delete(offerId);\n\n return true;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n let count = 0;\n\n for (const [id, offer] of this.offers) {\n if (offer.expiresAt < now) {\n this.removeOfferFromIndexes(offer);\n this.offers.delete(id);\n this.iceCandidates.delete(id);\n count++;\n }\n }\n\n return count;\n }\n\n async answerOffer(\n offerId: string,\n answererPublicKey: string,\n answerSdp: string,\n matchedTags?: string[]\n ): Promise<{ success: boolean; error?: string }> {\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return { success: false, error: 'Offer not found or expired' };\n }\n\n if (offer.answererPublicKey) {\n return { success: false, error: 'Offer already answered' };\n }\n\n // Update offer with answer\n const now = Date.now();\n offer.answererPublicKey = answererPublicKey;\n offer.answerSdp = answerSdp;\n offer.answeredAt = now;\n offer.matchedTags = matchedTags;\n\n // Update answerer index\n if (!this.offersByAnswerer.has(answererPublicKey)) {\n this.offersByAnswerer.set(answererPublicKey, new Set());\n }\n this.offersByAnswerer.get(answererPublicKey)!.add(offerId);\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {\n const now = Date.now();\n const offerIds = this.offersByPublicKey.get(offererPublicKey);\n if (!offerIds) return [];\n\n const offers: Offer[] = [];\n for (const id of offerIds) {\n const offer = this.offers.get(id);\n if (offer && offer.answererPublicKey && offer.expiresAt > now) {\n offers.push(offer);\n }\n }\n\n return offers.sort((a, b) => (b.answeredAt || 0) - (a.answeredAt || 0));\n }\n\n async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {\n const now = Date.now();\n const offerIds = this.offersByAnswerer.get(answererPublicKey);\n if (!offerIds) return [];\n\n const offers: Offer[] = [];\n for (const id of offerIds) {\n const offer = this.offers.get(id);\n if (offer && offer.expiresAt > now) {\n offers.push(offer);\n }\n }\n\n return offers.sort((a, b) => (b.answeredAt || 0) - (a.answeredAt || 0));\n }\n\n // ===== Discovery =====\n\n async discoverOffers(\n tags: string[],\n excludePublicKey: string | null,\n limit: number,\n offset: number\n ): Promise<Offer[]> {\n if (tags.length === 0) return [];\n\n const now = Date.now();\n const matchingOfferIds = new Set<string>();\n\n // Find all offers matching any tag (OR logic)\n for (const tag of tags) {\n const offerIds = this.offersByTag.get(tag);\n if (offerIds) {\n for (const id of offerIds) {\n matchingOfferIds.add(id);\n }\n }\n }\n\n // Filter and collect matching offers\n const offers: Offer[] = [];\n for (const id of matchingOfferIds) {\n const offer = this.offers.get(id);\n if (\n offer &&\n offer.expiresAt > now &&\n !offer.answererPublicKey &&\n (!excludePublicKey || offer.publicKey !== excludePublicKey)\n ) {\n offers.push(offer);\n }\n }\n\n // Sort by created_at descending and apply pagination\n offers.sort((a, b) => b.createdAt - a.createdAt);\n return offers.slice(offset, offset + limit);\n }\n\n async getRandomOffer(\n tags: string[],\n excludePublicKey: string | null\n ): Promise<Offer | null> {\n if (tags.length === 0) return null;\n\n const now = Date.now();\n const matchingOffers: Offer[] = [];\n\n // Find all offers matching any tag (OR logic)\n const matchingOfferIds = new Set<string>();\n for (const tag of tags) {\n const offerIds = this.offersByTag.get(tag);\n if (offerIds) {\n for (const id of offerIds) {\n matchingOfferIds.add(id);\n }\n }\n }\n\n // Collect matching offers\n for (const id of matchingOfferIds) {\n const offer = this.offers.get(id);\n if (\n offer &&\n offer.expiresAt > now &&\n !offer.answererPublicKey &&\n (!excludePublicKey || offer.publicKey !== excludePublicKey)\n ) {\n matchingOffers.push(offer);\n }\n }\n\n if (matchingOffers.length === 0) return null;\n\n // Return random offer\n const randomIndex = Math.floor(Math.random() * matchingOffers.length);\n return matchingOffers[randomIndex];\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n publicKey: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n const baseTimestamp = Date.now();\n\n if (!this.iceCandidates.has(offerId)) {\n this.iceCandidates.set(offerId, []);\n }\n\n const candidateList = this.iceCandidates.get(offerId)!;\n\n for (let i = 0; i < candidates.length; i++) {\n const candidate: IceCandidate = {\n id: ++this.iceCandidateIdCounter,\n offerId,\n publicKey,\n role,\n candidate: candidates[i],\n createdAt: baseTimestamp + i,\n };\n candidateList.push(candidate);\n }\n\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n const candidates = this.iceCandidates.get(offerId) || [];\n\n return candidates\n .filter(c => c.role === targetRole && (since === undefined || c.createdAt > since))\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n async getIceCandidatesForMultipleOffers(\n offerIds: string[],\n publicKey: string,\n since?: number\n ): Promise<Map<string, IceCandidate[]>> {\n const result = new Map<string, IceCandidate[]>();\n\n if (offerIds.length === 0) return result;\n if (offerIds.length > 1000) {\n throw new Error('Too many offer IDs (max 1000)');\n }\n\n for (const offerId of offerIds) {\n const offer = this.offers.get(offerId);\n if (!offer) continue;\n\n const candidates = this.iceCandidates.get(offerId) || [];\n\n // Determine which role's candidates to return\n // If user is offerer, return answerer candidates and vice versa\n const isOfferer = offer.publicKey === publicKey;\n const isAnswerer = offer.answererPublicKey === publicKey;\n\n if (!isOfferer && !isAnswerer) continue;\n\n const targetRole = isOfferer ? 'answerer' : 'offerer';\n\n const filteredCandidates = candidates\n .filter(c => c.role === targetRole && (since === undefined || c.createdAt > since))\n .sort((a, b) => a.createdAt - b.createdAt);\n\n if (filteredCandidates.length > 0) {\n result.set(offerId, filteredCandidates);\n }\n }\n\n return result;\n }\n\n // ===== Rate Limiting =====\n\n async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {\n const now = Date.now();\n const existing = this.rateLimits.get(identifier);\n\n if (!existing || existing.resetTime < now) {\n // New window or expired - reset count\n this.rateLimits.set(identifier, {\n count: 1,\n resetTime: now + windowMs,\n });\n return true;\n }\n\n // Increment count in existing window\n existing.count++;\n return existing.count <= limit;\n }\n\n async deleteExpiredRateLimits(now: number): Promise<number> {\n let count = 0;\n for (const [identifier, rateLimit] of this.rateLimits) {\n if (rateLimit.resetTime < now) {\n this.rateLimits.delete(identifier);\n count++;\n }\n }\n return count;\n }\n\n // ===== Nonce Tracking (Replay Protection) =====\n\n async checkAndMarkNonce(nonceKey: string, expiresAt: number): Promise<boolean> {\n if (this.nonces.has(nonceKey)) {\n return false; // Nonce already used - replay attack\n }\n\n this.nonces.set(nonceKey, { expiresAt });\n return true; // Nonce is new - allowed\n }\n\n async deleteExpiredNonces(now: number): Promise<number> {\n let count = 0;\n for (const [key, entry] of this.nonces) {\n if (entry.expiresAt < now) {\n this.nonces.delete(key);\n count++;\n }\n }\n return count;\n }\n\n async close(): Promise<void> {\n // Clear all data\n this.offers.clear();\n this.iceCandidates.clear();\n this.rateLimits.clear();\n this.nonces.clear();\n this.offersByPublicKey.clear();\n this.offersByTag.clear();\n this.offersByAnswerer.clear();\n }\n\n // ===== Count Methods (for resource limits) =====\n\n async getOfferCount(): Promise<number> {\n return this.offers.size;\n }\n\n async getOfferCountByPublicKey(publicKey: string): Promise<number> {\n const offerIds = this.offersByPublicKey.get(publicKey);\n return offerIds ? offerIds.size : 0;\n }\n\n async getIceCandidateCount(offerId: string): Promise<number> {\n const candidates = this.iceCandidates.get(offerId);\n return candidates ? candidates.length : 0;\n }\n\n // ===== Helper Methods =====\n\n private removeOfferFromIndexes(offer: Offer): void {\n // Remove from publicKey index\n const publicKeyOffers = this.offersByPublicKey.get(offer.publicKey);\n if (publicKeyOffers) {\n publicKeyOffers.delete(offer.id);\n if (publicKeyOffers.size === 0) {\n this.offersByPublicKey.delete(offer.publicKey);\n }\n }\n\n // Remove from tag indexes\n for (const tag of offer.tags) {\n const tagOffers = this.offersByTag.get(tag);\n if (tagOffers) {\n tagOffers.delete(offer.id);\n if (tagOffers.size === 0) {\n this.offersByTag.delete(tag);\n }\n }\n }\n\n // Remove from answerer index\n if (offer.answererPublicKey) {\n const answererOffers = this.offersByAnswerer.get(offer.answererPublicKey);\n if (answererOffers) {\n answererOffers.delete(offer.id);\n if (answererOffers.size === 0) {\n this.offersByAnswerer.delete(offer.answererPublicKey);\n }\n }\n }\n }\n}\n", "import Database from 'better-sqlite3';\nimport {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\n\n/**\n * SQLite storage adapter for rondevu signaling system\n * Supports both file-based and in-memory databases\n */\nexport class SQLiteStorage implements Storage {\n private db: Database.Database;\n\n /**\n * Creates a new SQLite storage instance\n * @param path Path to SQLite database file, or ':memory:' for in-memory database\n */\n constructor(path: string = ':memory:') {\n this.db = new Database(path);\n this.initializeDatabase();\n }\n\n /**\n * Initializes database schema with Ed25519 public key identity\n */\n private initializeDatabase(): void {\n this.db.exec(`\n -- Identities table (Ed25519 public key as identity)\n CREATE TABLE IF NOT EXISTS identities (\n public_key TEXT PRIMARY KEY,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n last_used INTEGER NOT NULL,\n CHECK(length(public_key) = 64)\n );\n\n CREATE INDEX IF NOT EXISTS idx_identities_expires ON identities(expires_at);\n\n -- WebRTC signaling offers with tags\n CREATE TABLE IF NOT EXISTS offers (\n id TEXT PRIMARY KEY,\n public_key TEXT NOT NULL,\n tags TEXT NOT NULL,\n sdp TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n last_seen INTEGER NOT NULL,\n answerer_public_key TEXT,\n answer_sdp TEXT,\n answered_at INTEGER,\n matched_tags TEXT,\n FOREIGN KEY (public_key) REFERENCES identities(public_key) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_offers_public_key ON offers(public_key);\n CREATE INDEX IF NOT EXISTS idx_offers_expires ON offers(expires_at);\n CREATE INDEX IF NOT EXISTS idx_offers_last_seen ON offers(last_seen);\n CREATE INDEX IF NOT EXISTS idx_offers_answerer ON offers(answerer_public_key);\n\n -- ICE candidates table\n CREATE TABLE IF NOT EXISTS ice_candidates (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n offer_id TEXT NOT NULL,\n public_key TEXT NOT NULL,\n role TEXT NOT NULL CHECK(role IN ('offerer', 'answerer')),\n candidate TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n FOREIGN KEY (offer_id) REFERENCES offers(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_ice_offer ON ice_candidates(offer_id);\n CREATE INDEX IF NOT EXISTS idx_ice_public_key ON ice_candidates(public_key);\n CREATE INDEX IF NOT EXISTS idx_ice_created ON ice_candidates(created_at);\n\n -- Rate limits table (for distributed rate limiting)\n CREATE TABLE IF NOT EXISTS rate_limits (\n identifier TEXT PRIMARY KEY,\n count INTEGER NOT NULL,\n reset_time INTEGER NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_rate_limits_reset ON rate_limits(reset_time);\n\n -- Nonces table (for replay attack prevention)\n CREATE TABLE IF NOT EXISTS nonces (\n nonce_key TEXT PRIMARY KEY,\n expires_at INTEGER NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_nonces_expires ON nonces(expires_at);\n `);\n\n // Enable foreign keys\n this.db.pragma('foreign_keys = ON');\n }\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n const created: Offer[] = [];\n\n // Generate hash-based IDs for all offers first\n const offersWithIds = await Promise.all(\n offers.map(async (offer) => ({\n ...offer,\n id: offer.id || await generateOfferHash(offer.sdp),\n }))\n );\n\n // Use transaction for atomic creation\n const transaction = this.db.transaction((offersWithIds: (CreateOfferRequest & { id: string })[]) => {\n const offerStmt = this.db.prepare(`\n INSERT INTO offers (id, public_key, tags, sdp, created_at, expires_at, last_seen)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const offer of offersWithIds) {\n const now = Date.now();\n\n // Insert offer with JSON-serialized tags\n offerStmt.run(\n offer.id,\n offer.publicKey,\n JSON.stringify(offer.tags),\n offer.sdp,\n now,\n offer.expiresAt,\n now\n );\n\n created.push({\n id: offer.id,\n publicKey: offer.publicKey,\n tags: offer.tags,\n sdp: offer.sdp,\n createdAt: now,\n expiresAt: offer.expiresAt,\n lastSeen: now,\n });\n }\n });\n\n transaction(offersWithIds);\n return created;\n }\n\n async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE public_key = ? AND expires_at > ?\n ORDER BY last_seen DESC\n `);\n\n const rows = stmt.all(publicKey, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE id = ? AND expires_at > ?\n `);\n\n const row = stmt.get(offerId, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToOffer(row);\n }\n\n async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {\n const stmt = this.db.prepare(`\n DELETE FROM offers\n WHERE id = ? AND public_key = ?\n `);\n\n const result = stmt.run(offerId, ownerPublicKey);\n return result.changes > 0;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM offers WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n async answerOffer(\n offerId: string,\n answererPublicKey: string,\n answerSdp: string,\n matchedTags?: string[]\n ): Promise<{ success: boolean; error?: string }> {\n // Check if offer exists and is not expired\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return {\n success: false,\n error: 'Offer not found or expired'\n };\n }\n\n // Check if offer already has an answerer\n if (offer.answererPublicKey) {\n return {\n success: false,\n error: 'Offer already answered'\n };\n }\n\n // Update offer with answer\n const stmt = this.db.prepare(`\n UPDATE offers\n SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?\n WHERE id = ? AND answerer_public_key IS NULL\n `);\n\n const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;\n const result = stmt.run(answererPublicKey, answerSdp, Date.now(), matchedTagsJson, offerId);\n\n if (result.changes === 0) {\n return {\n success: false,\n error: 'Offer already answered (race condition)'\n };\n }\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE public_key = ? AND answerer_public_key IS NOT NULL AND expires_at > ?\n ORDER BY answered_at DESC\n `);\n\n const rows = stmt.all(offererPublicKey, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE answerer_public_key = ? AND expires_at > ?\n ORDER BY answered_at DESC\n `);\n\n const rows = stmt.all(answererPublicKey, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n // ===== Discovery =====\n\n async discoverOffers(\n tags: string[],\n excludePublicKey: string | null,\n limit: number,\n offset: number\n ): Promise<Offer[]> {\n if (tags.length === 0) {\n return [];\n }\n\n // Build query with JSON tag matching (OR logic)\n // SQLite: Use json_each() to expand tags array and check if any tag matches\n const placeholders = tags.map(() => '?').join(',');\n\n let query = `\n SELECT DISTINCT o.* FROM offers o, json_each(o.tags) as t\n WHERE t.value IN (${placeholders})\n AND o.expires_at > ?\n AND o.answerer_public_key IS NULL\n `;\n\n const params: any[] = [...tags, Date.now()];\n\n if (excludePublicKey) {\n query += ' AND o.public_key != ?';\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY o.created_at DESC LIMIT ? OFFSET ?';\n params.push(limit, offset);\n\n const stmt = this.db.prepare(query);\n const rows = stmt.all(...params) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getRandomOffer(\n tags: string[],\n excludePublicKey: string | null\n ): Promise<Offer | null> {\n if (tags.length === 0) {\n return null;\n }\n\n // Build query with JSON tag matching (OR logic)\n const placeholders = tags.map(() => '?').join(',');\n\n let query = `\n SELECT DISTINCT o.* FROM offers o, json_each(o.tags) as t\n WHERE t.value IN (${placeholders})\n AND o.expires_at > ?\n AND o.answerer_public_key IS NULL\n `;\n\n const params: any[] = [...tags, Date.now()];\n\n if (excludePublicKey) {\n query += ' AND o.public_key != ?';\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY RANDOM() LIMIT 1';\n\n const stmt = this.db.prepare(query);\n const row = stmt.get(...params) as any;\n\n return row ? this.rowToOffer(row) : null;\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n publicKey: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n const stmt = this.db.prepare(`\n INSERT INTO ice_candidates (offer_id, public_key, role, candidate, created_at)\n VALUES (?, ?, ?, ?, ?)\n `);\n\n const baseTimestamp = Date.now();\n const transaction = this.db.transaction((candidates: any[]) => {\n for (let i = 0; i < candidates.length; i++) {\n stmt.run(\n offerId,\n publicKey,\n role,\n JSON.stringify(candidates[i]),\n baseTimestamp + i\n );\n }\n });\n\n transaction(candidates);\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n let query = `\n SELECT * FROM ice_candidates\n WHERE offer_id = ? AND role = ?\n `;\n\n const params: any[] = [offerId, targetRole];\n\n if (since !== undefined) {\n query += ' AND created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY created_at ASC';\n\n const stmt = this.db.prepare(query);\n const rows = stmt.all(...params) as any[];\n\n return rows.map(row => ({\n id: row.id,\n offerId: row.offer_id,\n publicKey: row.public_key,\n role: row.role,\n candidate: JSON.parse(row.candidate),\n createdAt: row.created_at,\n }));\n }\n\n async getIceCandidatesForMultipleOffers(\n offerIds: string[],\n publicKey: string,\n since?: number\n ): Promise<Map<string, IceCandidate[]>> {\n const result = new Map<string, IceCandidate[]>();\n\n // Return empty map if no offer IDs provided\n if (offerIds.length === 0) {\n return result;\n }\n\n // Validate array contains only strings\n if (!Array.isArray(offerIds) || !offerIds.every(id => typeof id === 'string')) {\n throw new Error('Invalid offer IDs: must be array of strings');\n }\n\n // Prevent DoS attacks from extremely large IN clauses\n if (offerIds.length > 1000) {\n throw new Error('Too many offer IDs (max 1000)');\n }\n\n // Build query that fetches candidates from the OTHER peer only\n // For each offer, determine if user is offerer or answerer and get opposite role\n const placeholders = offerIds.map(() => '?').join(',');\n\n let query = `\n SELECT ic.*, o.public_key as offer_public_key\n FROM ice_candidates ic\n INNER JOIN offers o ON o.id = ic.offer_id\n WHERE ic.offer_id IN (${placeholders})\n AND (\n (o.public_key = ? AND ic.role = 'answerer')\n OR (o.answerer_public_key = ? AND ic.role = 'offerer')\n )\n `;\n\n const params: any[] = [...offerIds, publicKey, publicKey];\n\n if (since !== undefined) {\n query += ' AND ic.created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY ic.created_at ASC';\n\n const stmt = this.db.prepare(query);\n const rows = stmt.all(...params) as any[];\n\n // Group candidates by offer_id\n for (const row of rows) {\n const candidate: IceCandidate = {\n id: row.id,\n offerId: row.offer_id,\n publicKey: row.public_key,\n role: row.role,\n candidate: JSON.parse(row.candidate),\n createdAt: row.created_at,\n };\n\n if (!result.has(row.offer_id)) {\n result.set(row.offer_id, []);\n }\n result.get(row.offer_id)!.push(candidate);\n }\n\n return result;\n }\n\n // ===== Rate Limiting =====\n\n async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {\n const now = Date.now();\n const resetTime = now + windowMs;\n\n // Atomic UPSERT: Insert or increment count, reset if expired\n // This prevents TOCTOU race conditions by doing check+increment in single operation\n const result = this.db.prepare(`\n INSERT INTO rate_limits (identifier, count, reset_time)\n VALUES (?, 1, ?)\n ON CONFLICT(identifier) DO UPDATE SET\n count = CASE\n WHEN reset_time < ? THEN 1\n ELSE count + 1\n END,\n reset_time = CASE\n WHEN reset_time < ? THEN ?\n ELSE reset_time\n END\n RETURNING count\n `).get(identifier, resetTime, now, now, resetTime) as { count: number };\n\n // Check if limit exceeded\n return result.count <= limit;\n }\n\n async deleteExpiredRateLimits(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM rate_limits WHERE reset_time < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n // ===== Nonce Tracking (Replay Protection) =====\n\n async checkAndMarkNonce(nonceKey: string, expiresAt: number): Promise<boolean> {\n try {\n // Atomic INSERT - if nonce already exists, this will fail with UNIQUE constraint\n // This prevents replay attacks by ensuring each nonce is only used once\n const stmt = this.db.prepare(`\n INSERT INTO nonces (nonce_key, expires_at)\n VALUES (?, ?)\n `);\n stmt.run(nonceKey, expiresAt);\n return true; // Nonce is new, request allowed\n } catch (error: any) {\n // SQLITE_CONSTRAINT error code for UNIQUE constraint violation\n if (error?.code === 'SQLITE_CONSTRAINT') {\n return false; // Nonce already used, replay attack detected\n }\n throw error; // Other errors should propagate\n }\n }\n\n async deleteExpiredNonces(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM nonces WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n async close(): Promise<void> {\n this.db.close();\n }\n\n // ===== Count Methods (for resource limits) =====\n\n async getOfferCount(): Promise<number> {\n const result = this.db.prepare('SELECT COUNT(*) as count FROM offers').get() as { count: number };\n return result.count;\n }\n\n async getOfferCountByPublicKey(publicKey: string): Promise<number> {\n const result = this.db.prepare('SELECT COUNT(*) as count FROM offers WHERE public_key = ?').get(publicKey) as { count: number };\n return result.count;\n }\n\n async getIceCandidateCount(offerId: string): Promise<number> {\n const result = this.db.prepare('SELECT COUNT(*) as count FROM ice_candidates WHERE offer_id = ?').get(offerId) as { count: number };\n return result.count;\n }\n\n // ===== Helper Methods =====\n\n /**\n * Helper method to convert database row to Offer object\n */\n private rowToOffer(row: any): Offer {\n return {\n id: row.id,\n publicKey: row.public_key,\n tags: JSON.parse(row.tags),\n sdp: row.sdp,\n createdAt: row.created_at,\n expiresAt: row.expires_at,\n lastSeen: row.last_seen,\n answererPublicKey: row.answerer_public_key || undefined,\n answerSdp: row.answer_sdp || undefined,\n answeredAt: row.answered_at || undefined,\n matchedTags: row.matched_tags ? JSON.parse(row.matched_tags) : undefined,\n };\n }\n}\n", "import mysql, { Pool, RowDataPacket, ResultSetHeader } from 'mysql2/promise';\nimport {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\n\n/**\n * MySQL storage adapter for rondevu signaling system\n * Uses Ed25519 public key as identity (no usernames, no secrets)\n */\nexport class MySQLStorage implements Storage {\n private pool: Pool;\n\n private constructor(pool: Pool) {\n this.pool = pool;\n }\n\n /**\n * Creates a new MySQL storage instance with connection pooling\n * @param connectionString MySQL connection URL\n * @param poolSize Maximum number of connections in the pool\n */\n static async create(connectionString: string, poolSize: number = 10): Promise<MySQLStorage> {\n const pool = mysql.createPool({\n uri: connectionString,\n waitForConnections: true,\n connectionLimit: poolSize,\n queueLimit: 0,\n enableKeepAlive: true,\n keepAliveInitialDelay: 10000,\n });\n\n const storage = new MySQLStorage(pool);\n await storage.initializeDatabase();\n return storage;\n }\n\n private async initializeDatabase(): Promise<void> {\n const conn = await this.pool.getConnection();\n try {\n await conn.query(`\n CREATE TABLE IF NOT EXISTS identities (\n public_key CHAR(64) PRIMARY KEY,\n created_at BIGINT NOT NULL,\n expires_at BIGINT NOT NULL,\n last_used BIGINT NOT NULL,\n INDEX idx_identities_expires (expires_at)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n\n await conn.query(`\n CREATE TABLE IF NOT EXISTS offers (\n id VARCHAR(64) PRIMARY KEY,\n public_key CHAR(64) NOT NULL,\n tags JSON NOT NULL,\n sdp MEDIUMTEXT NOT NULL,\n created_at BIGINT NOT NULL,\n expires_at BIGINT NOT NULL,\n last_seen BIGINT NOT NULL,\n answerer_public_key CHAR(64),\n answer_sdp MEDIUMTEXT,\n answered_at BIGINT,\n matched_tags JSON,\n INDEX idx_offers_public_key (public_key),\n INDEX idx_offers_expires (expires_at),\n INDEX idx_offers_last_seen (last_seen),\n INDEX idx_offers_answerer (answerer_public_key),\n FOREIGN KEY (public_key) REFERENCES identities(public_key) ON DELETE CASCADE\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n\n await conn.query(`\n CREATE TABLE IF NOT EXISTS ice_candidates (\n id BIGINT AUTO_INCREMENT PRIMARY KEY,\n offer_id VARCHAR(64) NOT NULL,\n public_key CHAR(64) NOT NULL,\n role ENUM('offerer', 'answerer') NOT NULL,\n candidate JSON NOT NULL,\n created_at BIGINT NOT NULL,\n INDEX idx_ice_offer (offer_id),\n INDEX idx_ice_public_key (public_key),\n INDEX idx_ice_created (created_at),\n FOREIGN KEY (offer_id) REFERENCES offers(id) ON DELETE CASCADE\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n\n await conn.query(`\n CREATE TABLE IF NOT EXISTS rate_limits (\n identifier VARCHAR(255) PRIMARY KEY,\n count INT NOT NULL,\n reset_time BIGINT NOT NULL,\n INDEX idx_rate_limits_reset (reset_time)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n\n await conn.query(`\n CREATE TABLE IF NOT EXISTS nonces (\n nonce_key VARCHAR(255) PRIMARY KEY,\n expires_at BIGINT NOT NULL,\n INDEX idx_nonces_expires (expires_at)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n } finally {\n conn.release();\n }\n }\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n if (offers.length === 0) return [];\n\n const created: Offer[] = [];\n const now = Date.now();\n\n const conn = await this.pool.getConnection();\n try {\n await conn.beginTransaction();\n\n for (const request of offers) {\n const id = request.id || await generateOfferHash(request.sdp);\n\n await conn.query(\n `INSERT INTO offers (id, public_key, tags, sdp, created_at, expires_at, last_seen)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n [id, request.publicKey, JSON.stringify(request.tags), request.sdp, now, request.expiresAt, now]\n );\n\n created.push({\n id,\n publicKey: request.publicKey,\n tags: request.tags,\n sdp: request.sdp,\n createdAt: now,\n expiresAt: request.expiresAt,\n lastSeen: now,\n });\n }\n\n await conn.commit();\n } catch (error) {\n await conn.rollback();\n throw error;\n } finally {\n conn.release();\n }\n\n return created;\n }\n\n async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT * FROM offers WHERE public_key = ? AND expires_at > ? ORDER BY last_seen DESC`,\n [publicKey, Date.now()]\n );\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT * FROM offers WHERE id = ? AND expires_at > ?`,\n [offerId, Date.now()]\n );\n return rows.length > 0 ? this.rowToOffer(rows[0]) : null;\n }\n\n async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {\n const [result] = await this.pool.query<ResultSetHeader>(\n `DELETE FROM offers WHERE id = ? AND public_key = ?`,\n [offerId, ownerPublicKey]\n );\n return result.affectedRows > 0;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n const [result] = await this.pool.query<ResultSetHeader>(\n `DELETE FROM offers WHERE expires_at < ?`,\n [now]\n );\n return result.affectedRows;\n }\n\n async answerOffer(\n offerId: string,\n answererPublicKey: string,\n answerSdp: string,\n matchedTags?: string[]\n ): Promise<{ success: boolean; error?: string }> {\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return { success: false, error: 'Offer not found or expired' };\n }\n\n if (offer.answererPublicKey) {\n return { success: false, error: 'Offer already answered' };\n }\n\n const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;\n const [result] = await this.pool.query<ResultSetHeader>(\n `UPDATE offers SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?\n WHERE id = ? AND answerer_public_key IS NULL`,\n [answererPublicKey, answerSdp, Date.now(), matchedTagsJson, offerId]\n );\n\n if (result.affectedRows === 0) {\n return { success: false, error: 'Offer already answered (race condition)' };\n }\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT * FROM offers\n WHERE public_key = ? AND answerer_public_key IS NOT NULL AND expires_at > ?\n ORDER BY answered_at DESC`,\n [offererPublicKey, Date.now()]\n );\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT * FROM offers\n WHERE answerer_public_key = ? AND expires_at > ?\n ORDER BY answered_at DESC`,\n [answererPublicKey, Date.now()]\n );\n return rows.map(row => this.rowToOffer(row));\n }\n\n // ===== Discovery =====\n\n async discoverOffers(\n tags: string[],\n excludePublicKey: string | null,\n limit: number,\n offset: number\n ): Promise<Offer[]> {\n if (tags.length === 0) return [];\n\n const tagArray = JSON.stringify(tags);\n\n let query = `\n SELECT DISTINCT o.* FROM offers o\n WHERE JSON_OVERLAPS(o.tags, ?)\n AND o.expires_at > ?\n AND o.answerer_public_key IS NULL\n `;\n const params: any[] = [tagArray, Date.now()];\n\n if (excludePublicKey) {\n query += ' AND o.public_key != ?';\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY o.created_at DESC LIMIT ? OFFSET ?';\n params.push(limit, offset);\n\n const [rows] = await this.pool.query<RowDataPacket[]>(query, params);\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getRandomOffer(\n tags: string[],\n excludePublicKey: string | null\n ): Promise<Offer | null> {\n if (tags.length === 0) return null;\n\n const tagArray = JSON.stringify(tags);\n\n let query = `\n SELECT DISTINCT o.* FROM offers o\n WHERE JSON_OVERLAPS(o.tags, ?)\n AND o.expires_at > ?\n AND o.answerer_public_key IS NULL\n `;\n const params: any[] = [tagArray, Date.now()];\n\n if (excludePublicKey) {\n query += ' AND o.public_key != ?';\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY RAND() LIMIT 1';\n\n const [rows] = await this.pool.query<RowDataPacket[]>(query, params);\n return rows.length > 0 ? this.rowToOffer(rows[0]) : null;\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n publicKey: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n if (candidates.length === 0) return 0;\n\n const baseTimestamp = Date.now();\n const values = candidates.map((c, i) => [\n offerId,\n publicKey,\n role,\n JSON.stringify(c),\n baseTimestamp + i,\n ]);\n\n await this.pool.query(\n `INSERT INTO ice_candidates (offer_id, public_key, role, candidate, created_at)\n VALUES ?`,\n [values]\n );\n\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n let query = `SELECT * FROM ice_candidates WHERE offer_id = ? AND role = ?`;\n const params: any[] = [offerId, targetRole];\n\n if (since !== undefined) {\n query += ' AND created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY created_at ASC';\n\n const [rows] = await this.pool.query<RowDataPacket[]>(query, params);\n return rows.map(row => this.rowToIceCandidate(row));\n }\n\n async getIceCandidatesForMultipleOffers(\n offerIds: string[],\n publicKey: string,\n since?: number\n ): Promise<Map<string, IceCandidate[]>> {\n const result = new Map<string, IceCandidate[]>();\n\n if (offerIds.length === 0) return result;\n if (offerIds.length > 1000) {\n throw new Error('Too many offer IDs (max 1000)');\n }\n\n const placeholders = offerIds.map(() => '?').join(',');\n\n let query = `\n SELECT ic.*, o.public_key as offer_public_key\n FROM ice_candidates ic\n INNER JOIN offers o ON o.id = ic.offer_id\n WHERE ic.offer_id IN (${placeholders})\n AND (\n (o.public_key = ? AND ic.role = 'answerer')\n OR (o.answerer_public_key = ? AND ic.role = 'offerer')\n )\n `;\n const params: any[] = [...offerIds, publicKey, publicKey];\n\n if (since !== undefined) {\n query += ' AND ic.created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY ic.created_at ASC';\n\n const [rows] = await this.pool.query<RowDataPacket[]>(query, params);\n\n for (const row of rows) {\n const candidate = this.rowToIceCandidate(row);\n if (!result.has(row.offer_id)) {\n result.set(row.offer_id, []);\n }\n result.get(row.offer_id)!.push(candidate);\n }\n\n return result;\n }\n\n // ===== Rate Limiting =====\n\n async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {\n const now = Date.now();\n const resetTime = now + windowMs;\n\n await this.pool.query(\n `INSERT INTO rate_limits (identifier, count, reset_time)\n VALUES (?, 1, ?)\n ON DUPLICATE KEY UPDATE\n count = IF(reset_time < ?, 1, count + 1),\n reset_time = IF(reset_time < ?, ?, reset_time)`,\n [identifier, resetTime, now, now, resetTime]\n );\n\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT count FROM rate_limits WHERE identifier = ?`,\n [identifier]\n );\n\n return rows.length > 0 && rows[0].count <= limit;\n }\n\n async deleteExpiredRateLimits(now: number): Promise<number> {\n const [result] = await this.pool.query<ResultSetHeader>(\n `DELETE FROM rate_limits WHERE reset_time < ?`,\n [now]\n );\n return result.affectedRows;\n }\n\n // ===== Nonce Tracking (Replay Protection) =====\n\n async checkAndMarkNonce(nonceKey: string, expiresAt: number): Promise<boolean> {\n try {\n await this.pool.query(\n `INSERT INTO nonces (nonce_key, expires_at) VALUES (?, ?)`,\n [nonceKey, expiresAt]\n );\n return true;\n } catch (error: any) {\n if (error.code === 'ER_DUP_ENTRY') {\n return false;\n }\n throw error;\n }\n }\n\n async deleteExpiredNonces(now: number): Promise<number> {\n const [result] = await this.pool.query<ResultSetHeader>(\n `DELETE FROM nonces WHERE expires_at < ?`,\n [now]\n );\n return result.affectedRows;\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n // ===== Count Methods (for resource limits) =====\n\n async getOfferCount(): Promise<number> {\n const [rows] = await this.pool.query<RowDataPacket[]>('SELECT COUNT(*) as count FROM offers');\n return Number(rows[0].count);\n }\n\n async getOfferCountByPublicKey(publicKey: string): Promise<number> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n 'SELECT COUNT(*) as count FROM offers WHERE public_key = ?',\n [publicKey]\n );\n return Number(rows[0].count);\n }\n\n async getIceCandidateCount(offerId: string): Promise<number> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n 'SELECT COUNT(*) as count FROM ice_candidates WHERE offer_id = ?',\n [offerId]\n );\n return Number(rows[0].count);\n }\n\n // ===== Helper Methods =====\n\n private rowToOffer(row: RowDataPacket): Offer {\n return {\n id: row.id,\n publicKey: row.public_key,\n tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : row.tags,\n sdp: row.sdp,\n createdAt: Number(row.created_at),\n expiresAt: Number(row.expires_at),\n lastSeen: Number(row.last_seen),\n answererPublicKey: row.answerer_public_key || undefined,\n answerSdp: row.answer_sdp || undefined,\n answeredAt: row.answered_at ? Number(row.answered_at) : undefined,\n matchedTags: row.matched_tags ? (typeof row.matched_tags === 'string' ? JSON.parse(row.matched_tags) : row.matched_tags) : undefined,\n };\n }\n\n private rowToIceCandidate(row: RowDataPacket): IceCandidate {\n return {\n id: Number(row.id),\n offerId: row.offer_id,\n publicKey: row.public_key,\n role: row.role as 'offerer' | 'answerer',\n candidate: typeof row.candidate === 'string' ? JSON.parse(row.candidate) : row.candidate,\n createdAt: Number(row.created_at),\n };\n }\n}\n", "import { Pool } from 'pg';\nimport {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\n\n/**\n * PostgreSQL storage adapter for rondevu signaling system\n * Uses Ed25519 public key as identity (no usernames, no secrets)\n */\nexport class PostgreSQLStorage implements Storage {\n private pool: Pool;\n\n private constructor(pool: Pool) {\n this.pool = pool;\n }\n\n /**\n * Creates a new PostgreSQL storage instance with connection pooling\n * @param connectionString PostgreSQL connection URL\n * @param poolSize Maximum number of connections in the pool\n */\n static async create(connectionString: string, poolSize: number = 10): Promise<PostgreSQLStorage> {\n const pool = new Pool({\n connectionString,\n max: poolSize,\n idleTimeoutMillis: 30000,\n connectionTimeoutMillis: 5000,\n });\n\n const storage = new PostgreSQLStorage(pool);\n await storage.initializeDatabase();\n return storage;\n }\n\n private async initializeDatabase(): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(`\n CREATE TABLE IF NOT EXISTS identities (\n public_key CHAR(64) PRIMARY KEY,\n created_at BIGINT NOT NULL,\n expires_at BIGINT NOT NULL,\n last_used BIGINT NOT NULL\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_identities_expires ON identities(expires_at)`);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS offers (\n id VARCHAR(64) PRIMARY KEY,\n public_key CHAR(64) NOT NULL REFERENCES identities(public_key) ON DELETE CASCADE,\n tags JSONB NOT NULL,\n sdp TEXT NOT NULL,\n created_at BIGINT NOT NULL,\n expires_at BIGINT NOT NULL,\n last_seen BIGINT NOT NULL,\n answerer_public_key CHAR(64),\n answer_sdp TEXT,\n answered_at BIGINT,\n matched_tags JSONB\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_public_key ON offers(public_key)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_expires ON offers(expires_at)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_last_seen ON offers(last_seen)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_answerer ON offers(answerer_public_key)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_tags ON offers USING GIN(tags)`);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS ice_candidates (\n id BIGSERIAL PRIMARY KEY,\n offer_id VARCHAR(64) NOT NULL REFERENCES offers(id) ON DELETE CASCADE,\n public_key CHAR(64) NOT NULL,\n role VARCHAR(8) NOT NULL CHECK (role IN ('offerer', 'answerer')),\n candidate JSONB NOT NULL,\n created_at BIGINT NOT NULL\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_ice_offer ON ice_candidates(offer_id)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_ice_public_key ON ice_candidates(public_key)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_ice_created ON ice_candidates(created_at)`);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS rate_limits (\n identifier VARCHAR(255) PRIMARY KEY,\n count INT NOT NULL,\n reset_time BIGINT NOT NULL\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_rate_limits_reset ON rate_limits(reset_time)`);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS nonces (\n nonce_key VARCHAR(255) PRIMARY KEY,\n expires_at BIGINT NOT NULL\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_nonces_expires ON nonces(expires_at)`);\n } finally {\n client.release();\n }\n }\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n if (offers.length === 0) return [];\n\n const created: Offer[] = [];\n const now = Date.now();\n\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n\n for (const request of offers) {\n const id = request.id || await generateOfferHash(request.sdp);\n\n await client.query(\n `INSERT INTO offers (id, public_key, tags, sdp, created_at, expires_at, last_seen)\n VALUES ($1, $2, $3, $4, $5, $6, $7)`,\n [id, request.publicKey, JSON.stringify(request.tags), request.sdp, now, request.expiresAt, now]\n );\n\n created.push({\n id,\n publicKey: request.publicKey,\n tags: request.tags,\n sdp: request.sdp,\n createdAt: now,\n expiresAt: request.expiresAt,\n lastSeen: now,\n });\n }\n\n await client.query('COMMIT');\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n\n return created;\n }\n\n async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {\n const result = await this.pool.query(\n `SELECT * FROM offers WHERE public_key = $1 AND expires_at > $2 ORDER BY last_seen DESC`,\n [publicKey, Date.now()]\n );\n return result.rows.map(row => this.rowToOffer(row));\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const result = await this.pool.query(\n `SELECT * FROM offers WHERE id = $1 AND expires_at > $2`,\n [offerId, Date.now()]\n );\n return result.rows.length > 0 ? this.rowToOffer(result.rows[0]) : null;\n }\n\n async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {\n const result = await this.pool.query(\n `DELETE FROM offers WHERE id = $1 AND public_key = $2`,\n [offerId, ownerPublicKey]\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n const result = await this.pool.query(\n `DELETE FROM offers WHERE expires_at < $1`,\n [now]\n );\n return result.rowCount ?? 0;\n }\n\n async answerOffer(\n offerId: string,\n answererPublicKey: string,\n answerSdp: string,\n matchedTags?: string[]\n ): Promise<{ success: boolean; error?: string }> {\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return { success: false, error: 'Offer not found or expired' };\n }\n\n if (offer.answererPublicKey) {\n return { success: false, error: 'Offer already answered' };\n }\n\n const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;\n const result = await this.pool.query(\n `UPDATE offers SET answerer_public_key = $1, answer_sdp = $2, answered_at = $3, matched_tags = $4\n WHERE id = $5 AND answerer_public_key IS NULL`,\n [answererPublicKey, answerSdp, Date.now(), matchedTagsJson, offerId]\n );\n\n if ((result.rowCount ?? 0) === 0) {\n return { success: false, error: 'Offer already answered (race condition)' };\n }\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {\n const result = await this.pool.query(\n `SELECT * FROM offers\n WHERE public_key = $1 AND answerer_public_key IS NOT NULL AND expires_at > $2\n ORDER BY answered_at DESC`,\n [offererPublicKey, Date.now()]\n );\n return result.rows.map(row => this.rowToOffer(row));\n }\n\n async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {\n const result = await this.pool.query(\n `SELECT * FROM offers\n WHERE answerer_public_key = $1 AND expires_at > $2\n ORDER BY answered_at DESC`,\n [answererPublicKey, Date.now()]\n );\n return result.rows.map(row => this.rowToOffer(row));\n }\n\n // ===== Discovery =====\n\n async discoverOffers(\n tags: string[],\n excludePublicKey: string | null,\n limit: number,\n offset: number\n ): Promise<Offer[]> {\n if (tags.length === 0) return [];\n\n let query = `\n SELECT DISTINCT o.* FROM offers o\n WHERE o.tags ?| $1\n AND o.expires_at > $2\n AND o.answerer_public_key IS NULL\n `;\n const params: any[] = [tags, Date.now()];\n let paramIndex = 3;\n\n if (excludePublicKey) {\n query += ` AND o.public_key != $${paramIndex}`;\n params.push(excludePublicKey);\n paramIndex++;\n }\n\n query += ` ORDER BY o.created_at DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;\n params.push(limit, offset);\n\n const result = await this.pool.query(query, params);\n return result.rows.map(row => this.rowToOffer(row));\n }\n\n async getRandomOffer(\n tags: string[],\n excludePublicKey: string | null\n ): Promise<Offer | null> {\n if (tags.length === 0) return null;\n\n let query = `\n SELECT DISTINCT o.* FROM offers o\n WHERE o.tags ?| $1\n AND o.expires_at > $2\n AND o.answerer_public_key IS NULL\n `;\n const params: any[] = [tags, Date.now()];\n let paramIndex = 3;\n\n if (excludePublicKey) {\n query += ` AND o.public_key != $${paramIndex}`;\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY RANDOM() LIMIT 1';\n\n const result = await this.pool.query(query, params);\n return result.rows.length > 0 ? this.rowToOffer(result.rows[0]) : null;\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n publicKey: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n if (candidates.length === 0) return 0;\n\n const baseTimestamp = Date.now();\n const client = await this.pool.connect();\n\n try {\n await client.query('BEGIN');\n\n for (let i = 0; i < candidates.length; i++) {\n await client.query(\n `INSERT INTO ice_candidates (offer_id, public_key, role, candidate, created_at)\n VALUES ($1, $2, $3, $4, $5)`,\n [offerId, publicKey, role, JSON.stringify(candidates[i]), baseTimestamp + i]\n );\n }\n\n await client.query('COMMIT');\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n let query = `SELECT * FROM ice_candidates WHERE offer_id = $1 AND role = $2`;\n const params: any[] = [offerId, targetRole];\n\n if (since !== undefined) {\n query += ' AND created_at > $3';\n params.push(since);\n }\n\n query += ' ORDER BY created_at ASC';\n\n const result = await this.pool.query(query, params);\n return result.rows.map(row => this.rowToIceCandidate(row));\n }\n\n async getIceCandidatesForMultipleOffers(\n offerIds: string[],\n publicKey: string,\n since?: number\n ): Promise<Map<string, IceCandidate[]>> {\n const resultMap = new Map<string, IceCandidate[]>();\n\n if (offerIds.length === 0) return resultMap;\n if (offerIds.length > 1000) {\n throw new Error('Too many offer IDs (max 1000)');\n }\n\n let query = `\n SELECT ic.*, o.public_key as offer_public_key\n FROM ice_candidates ic\n INNER JOIN offers o ON o.id = ic.offer_id\n WHERE ic.offer_id = ANY($1)\n AND (\n (o.public_key = $2 AND ic.role = 'answerer')\n OR (o.answerer_public_key = $2 AND ic.role = 'offerer')\n )\n `;\n const params: any[] = [offerIds, publicKey];\n\n if (since !== undefined) {\n query += ' AND ic.created_at > $3';\n params.push(since);\n }\n\n query += ' ORDER BY ic.created_at ASC';\n\n const result = await this.pool.query(query, params);\n\n for (const row of result.rows) {\n const candidate = this.rowToIceCandidate(row);\n if (!resultMap.has(row.offer_id)) {\n resultMap.set(row.offer_id, []);\n }\n resultMap.get(row.offer_id)!.push(candidate);\n }\n\n return resultMap;\n }\n\n // ===== Rate Limiting =====\n\n async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {\n const now = Date.now();\n const resetTime = now + windowMs;\n\n const result = await this.pool.query(\n `INSERT INTO rate_limits (identifier, count, reset_time)\n VALUES ($1, 1, $2)\n ON CONFLICT (identifier) DO UPDATE SET\n count = CASE\n WHEN rate_limits.reset_time < $3 THEN 1\n ELSE rate_limits.count + 1\n END,\n reset_time = CASE\n WHEN rate_limits.reset_time < $3 THEN $2\n ELSE rate_limits.reset_time\n END\n RETURNING count`,\n [identifier, resetTime, now]\n );\n\n return result.rows[0].count <= limit;\n }\n\n async deleteExpiredRateLimits(now: number): Promise<number> {\n const result = await this.pool.query(\n `DELETE FROM rate_limits WHERE reset_time < $1`,\n [now]\n );\n return result.rowCount ?? 0;\n }\n\n // ===== Nonce Tracking (Replay Protection) =====\n\n async checkAndMarkNonce(nonceKey: string, expiresAt: number): Promise<boolean> {\n try {\n await this.pool.query(\n `INSERT INTO nonces (nonce_key, expires_at) VALUES ($1, $2)`,\n [nonceKey, expiresAt]\n );\n return true;\n } catch (error: any) {\n if (error.code === '23505') {\n return false;\n }\n throw error;\n }\n }\n\n async deleteExpiredNonces(now: number): Promise<number> {\n const result = await this.pool.query(\n `DELETE FROM nonces WHERE expires_at < $1`,\n [now]\n );\n return result.rowCount ?? 0;\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n // ===== Count Methods (for resource limits) =====\n\n async getOfferCount(): Promise<number> {\n const result = await this.pool.query('SELECT COUNT(*) as count FROM offers');\n return Number(result.rows[0].count);\n }\n\n async getOfferCountByPublicKey(publicKey: string): Promise<number> {\n const result = await this.pool.query(\n 'SELECT COUNT(*) as count FROM offers WHERE public_key = $1',\n [publicKey]\n );\n return Number(result.rows[0].count);\n }\n\n async getIceCandidateCount(offerId: string): Promise<number> {\n const result = await this.pool.query(\n 'SELECT COUNT(*) as count FROM ice_candidates WHERE offer_id = $1',\n [offerId]\n );\n return Number(result.rows[0].count);\n }\n\n // ===== Helper Methods =====\n\n private rowToOffer(row: any): Offer {\n return {\n id: row.id,\n publicKey: row.public_key.trim(),\n tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : row.tags,\n sdp: row.sdp,\n createdAt: Number(row.created_at),\n expiresAt: Number(row.expires_at),\n lastSeen: Number(row.last_seen),\n answererPublicKey: row.answerer_public_key?.trim() || undefined,\n answerSdp: row.answer_sdp || undefined,\n answeredAt: row.answered_at ? Number(row.answered_at) : undefined,\n matchedTags: row.matched_tags || undefined,\n };\n }\n\n private rowToIceCandidate(row: any): IceCandidate {\n return {\n id: Number(row.id),\n offerId: row.offer_id,\n publicKey: row.public_key.trim(),\n role: row.role as 'offerer' | 'answerer',\n candidate: typeof row.candidate === 'string' ? JSON.parse(row.candidate) : row.candidate,\n createdAt: Number(row.created_at),\n };\n }\n}\n", "import { serve } from '@hono/node-server';\nimport { createApp } from './app.ts';\nimport { loadConfig, runCleanup } from './config.ts';\nimport { createStorage } from './storage/factory.ts';\nimport { Storage } from './storage/types.ts';\n\nasync function main() {\n const config = loadConfig();\n\n console.log('Starting Rondevu server...');\n console.log('Configuration:', {\n port: config.port,\n storageType: config.storageType,\n storagePath: config.storageType === 'sqlite' ? config.storagePath : undefined,\n databaseUrl: config.databaseUrl ? '[configured]' : undefined,\n dbPoolSize: ['mysql', 'postgres'].includes(config.storageType) ? config.dbPoolSize : undefined,\n offerDefaultTtl: `${config.offerDefaultTtl}ms`,\n cleanupInterval: `${config.cleanupInterval}ms`,\n version: config.version,\n });\n\n const storage: Storage = await createStorage({\n type: config.storageType,\n sqlitePath: config.storagePath,\n connectionString: config.databaseUrl,\n poolSize: config.dbPoolSize,\n });\n console.log(`Using ${config.storageType} storage`);\n\n // Periodic cleanup\n const cleanupTimer = setInterval(async () => {\n try {\n const result = await runCleanup(storage, Date.now());\n const total = result.offers + result.rateLimits + result.nonces;\n if (total > 0) {\n console.log(`Cleanup: ${result.offers} offers, ${result.rateLimits} rate limits, ${result.nonces} nonces`);\n }\n } catch (err) {\n console.error('Cleanup error:', err);\n }\n }, config.cleanupInterval);\n\n const app = createApp(storage, config);\n\n const server = serve({\n fetch: app.fetch,\n port: config.port,\n });\n\n console.log(`Server running on http://localhost:${config.port}`);\n console.log('Ready to accept connections');\n\n // Graceful shutdown handler\n const shutdown = async () => {\n console.log('\\nShutting down gracefully...');\n clearInterval(cleanupTimer);\n await storage.close();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n", "import { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { Storage } from './storage/types.ts';\nimport { Config } from './config.ts';\nimport { handleRpc, RpcRequest } from './rpc.ts';\n\n/**\n * Creates the Hono application with RPC interface\n */\nexport function createApp(storage: Storage, config: Config) {\n const app = new Hono();\n\n // Enable CORS\n app.use('/*', cors({\n origin: (origin) => {\n if (config.corsOrigins.length === 1 && config.corsOrigins[0] === '*') {\n return origin;\n }\n if (config.corsOrigins.includes(origin)) {\n return origin;\n }\n return config.corsOrigins[0];\n },\n allowMethods: ['GET', 'POST', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Origin', 'X-Name', 'X-Timestamp', 'X-Nonce', 'X-Signature'],\n exposeHeaders: ['Content-Type'],\n credentials: false,\n maxAge: 86400,\n }));\n\n // Root endpoint - server info\n app.get('/', (c) => {\n return c.json({\n version: config.version,\n name: 'Rondevu',\n description: 'WebRTC signaling with RPC interface and HMAC signature-based authentication',\n }, 200);\n });\n\n // Health check\n app.get('/health', (c) => {\n return c.json({\n status: 'ok',\n timestamp: Date.now(),\n version: config.version,\n }, 200);\n });\n\n /**\n * POST /rpc\n * RPC endpoint - accepts batch method calls only\n */\n app.post('/rpc', async (c) => {\n try {\n const body = await c.req.json();\n\n // Only accept batch arrays\n if (!Array.isArray(body)) {\n return c.json([{\n success: false,\n error: 'Request must be an array of RPC calls',\n errorCode: 'INVALID_PARAMS'\n }], 400);\n }\n\n const requests: RpcRequest[] = body;\n\n // Validate requests\n if (requests.length === 0) {\n return c.json([{\n success: false,\n error: 'Empty request array',\n errorCode: 'INVALID_PARAMS'\n }], 400);\n }\n\n if (requests.length > config.maxBatchSize) {\n return c.json([{\n success: false,\n error: `Too many requests in batch (max ${config.maxBatchSize})`,\n errorCode: 'BATCH_TOO_LARGE'\n }], 413); // 413 Payload Too Large\n }\n\n // Handle RPC (pass context for auth headers)\n const responses = await handleRpc(requests, c, storage, config);\n\n // Always return array\n return c.json(responses, 200);\n } catch (err) {\n console.error('RPC error:', err);\n\n // Distinguish between JSON parse errors and validation errors\n const errorMsg = err instanceof SyntaxError\n ? 'Invalid JSON in request body'\n : 'Request must be valid JSON array';\n\n return c.json([{\n success: false,\n error: errorMsg,\n errorCode: 'INVALID_PARAMS'\n }], 400);\n }\n });\n\n // 404 for all other routes\n app.all('*', (c) => {\n return c.json({\n error: 'Not found. Use POST /rpc for all API calls.',\n }, 404);\n });\n\n return app;\n}\n", "/**\n * Crypto utilities for Ed25519 signature verification and validation\n * Uses @noble/ed25519 for Ed25519 operations and Web Crypto API for SHA-512\n */\n\nimport { Buffer } from 'node:buffer';\nimport * as ed from '@noble/ed25519';\n\n// Configure @noble/ed25519 to use Web Crypto API's SHA-512\n// Required for both Node.js and Cloudflare Workers compatibility\ned.hashes.sha512Async = async (message: Uint8Array): Promise<Uint8Array> => {\n const hashBuffer = await crypto.subtle.digest('SHA-512', message);\n return new Uint8Array(hashBuffer);\n};\n\n/**\n * Convert hex string to byte array with validation\n */\nfunction hexToBytes(hex: string): Uint8Array {\n if (hex.length % 2 !== 0) {\n throw new Error('Hex string must have even length');\n }\n\n for (let i = 0; i < hex.length; i++) {\n const c = hex[i].toLowerCase();\n if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) {\n throw new Error(`Invalid hex character at position ${i}: '${hex[i]}'`);\n }\n }\n\n const match = hex.match(/.{1,2}/g);\n if (!match) {\n throw new Error('Invalid hex string format');\n }\n\n return new Uint8Array(match.map(byte => {\n const parsed = parseInt(byte, 16);\n if (isNaN(parsed)) {\n throw new Error(`Invalid hex byte: ${byte}`);\n }\n return parsed;\n }));\n}\n\n/**\n * Canonical JSON serialization with sorted keys.\n * Ensures deterministic output regardless of property insertion order.\n * Must match client's canonicalJSON implementation exactly.\n */\nfunction canonicalJSON(obj: any, depth: number = 0): string {\n const MAX_DEPTH = 100;\n\n if (depth > MAX_DEPTH) {\n throw new Error('Object nesting too deep for canonicalization');\n }\n\n if (obj === null) return 'null';\n if (obj === undefined) return JSON.stringify(undefined);\n\n const type = typeof obj;\n\n if (type === 'function') throw new Error('Functions are not supported in RPC parameters');\n if (type === 'symbol' || type === 'bigint') throw new Error(`${type} is not supported in RPC parameters`);\n if (type === 'number' && !Number.isFinite(obj)) throw new Error('NaN and Infinity are not supported in RPC parameters');\n\n if (type !== 'object') return JSON.stringify(obj);\n\n if (Array.isArray(obj)) {\n return '[' + obj.map(item => canonicalJSON(item, depth + 1)).join(',') + ']';\n }\n\n const sortedKeys = Object.keys(obj).sort();\n const pairs = sortedKeys.map(key => JSON.stringify(key) + ':' + canonicalJSON(obj[key], depth + 1));\n return '{' + pairs.join(',') + '}';\n}\n\n/**\n * Build the message string for signing.\n * Format: timestamp:nonce:method:canonicalJSON(params || {})\n */\nexport function buildSignatureMessage(timestamp: number, nonce: string, method: string, params?: any): string {\n // Validate nonce is UUID v4 format\n if (nonce.length !== 36) {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n\n if (nonce[8] !== '-' || nonce[13] !== '-' || nonce[18] !== '-' || nonce[23] !== '-') {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n\n if (nonce[14] !== '4') {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n\n const variant = nonce[19].toLowerCase();\n if (variant !== '8' && variant !== '9' && variant !== 'a' && variant !== 'b') {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n\n const hexChars = nonce.replace(/-/g, '');\n for (let i = 0; i < hexChars.length; i++) {\n const c = hexChars[i].toLowerCase();\n if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n }\n\n const paramsStr = canonicalJSON(params || {});\n return `${timestamp}:${nonce}:${method}:${paramsStr}`;\n}\n\n// ===== Ed25519 Public Key Identity =====\n\nconst ED25519_PUBLIC_KEY_LENGTH = 32; // 32 bytes = 64 hex chars\nconst ED25519_SIGNATURE_LENGTH = 64; // 64 bytes\n\n/**\n * Validates an Ed25519 public key format.\n * @param publicKey 64-character lowercase hex string (32 bytes)\n */\nexport function validatePublicKey(publicKey: string): { valid: boolean; error?: string } {\n if (typeof publicKey !== 'string') {\n return { valid: false, error: 'Public key must be a string' };\n }\n\n if (publicKey.length !== ED25519_PUBLIC_KEY_LENGTH * 2) {\n return { valid: false, error: `Public key must be ${ED25519_PUBLIC_KEY_LENGTH * 2} hex characters (${ED25519_PUBLIC_KEY_LENGTH} bytes)` };\n }\n\n for (let i = 0; i < publicKey.length; i++) {\n const c = publicKey[i];\n if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) {\n return { valid: false, error: `Invalid hex character at position ${i}: '${c}' (use lowercase hex)` };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Verify an Ed25519 signature.\n * @param publicKey Signer's public key (64-char hex)\n * @param message Message that was signed\n * @param signature Signature to verify (base64 encoded)\n */\nexport async function verifyEd25519Signature(\n publicKey: string,\n message: string,\n signature: string\n): Promise<boolean> {\n try {\n const pkValidation = validatePublicKey(publicKey);\n if (!pkValidation.valid) {\n console.error('Ed25519 verification error: invalid public key:', pkValidation.error);\n return false;\n }\n\n const publicKeyBytes = hexToBytes(publicKey);\n const encoder = new TextEncoder();\n const messageBytes = encoder.encode(message);\n const signatureBytes = Buffer.from(signature, 'base64');\n\n if (signatureBytes.length !== ED25519_SIGNATURE_LENGTH) {\n console.error(`Ed25519 verification error: signature length ${signatureBytes.length}, expected ${ED25519_SIGNATURE_LENGTH}`);\n return false;\n }\n\n return await ed.verifyAsync(signatureBytes, messageBytes, publicKeyBytes);\n } catch (error) {\n console.error('Ed25519 verification error:', error);\n return false;\n }\n}\n\n// ===== Tag Validation =====\n\nconst TAG_MIN_LENGTH = 1;\nconst TAG_MAX_LENGTH = 64;\nconst TAG_REGEX = /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/;\n\n/**\n * Validates a single tag format.\n * Rules: 1-64 chars, lowercase alphanumeric with optional dots/dashes,\n * must start and end with alphanumeric character.\n */\nexport function validateTag(tag: string): { valid: boolean; error?: string } {\n if (typeof tag !== 'string') {\n return { valid: false, error: 'Tag must be a string' };\n }\n\n if (tag.length < TAG_MIN_LENGTH) {\n return { valid: false, error: `Tag must be at least ${TAG_MIN_LENGTH} character` };\n }\n\n if (tag.length > TAG_MAX_LENGTH) {\n return { valid: false, error: `Tag must be at most ${TAG_MAX_LENGTH} characters` };\n }\n\n if (tag.length === 1) {\n if (!/^[a-z0-9]$/.test(tag)) {\n return { valid: false, error: 'Tag must be lowercase alphanumeric' };\n }\n return { valid: true };\n }\n\n if (!TAG_REGEX.test(tag)) {\n return { valid: false, error: 'Tag must be lowercase alphanumeric with optional dots/dashes, and start/end with alphanumeric' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validates an array of tags.\n * @param tags Array of tags to validate\n * @param maxTags Maximum number of tags allowed (default: 20)\n */\nexport function validateTags(tags: string[], maxTags: number = 20): { valid: boolean; error?: string } {\n if (!Array.isArray(tags)) {\n return { valid: false, error: 'Tags must be an array' };\n }\n\n if (tags.length === 0) {\n return { valid: false, error: 'At least one tag is required' };\n }\n\n if (tags.length > maxTags) {\n return { valid: false, error: `Maximum ${maxTags} tags allowed` };\n }\n\n for (let i = 0; i < tags.length; i++) {\n const result = validateTag(tags[i]);\n if (!result.valid) {\n return { valid: false, error: `Tag ${i + 1}: ${result.error}` };\n }\n }\n\n const uniqueTags = new Set(tags);\n if (uniqueTags.size !== tags.length) {\n return { valid: false, error: 'Duplicate tags are not allowed' };\n }\n\n return { valid: true };\n}\n", "/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */\n/**\n * 5KB JS implementation of ed25519 EdDSA signatures.\n * Compliant with RFC8032, FIPS 186-5 & ZIP215.\n * @module\n * @example\n * ```js\nimport * as ed from '@noble/ed25519';\n(async () => {\n const secretKey = ed.utils.randomSecretKey();\n const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);\n const pubKey = await ed.getPublicKeyAsync(secretKey); // Sync methods are also present\n const signature = await ed.signAsync(message, secretKey);\n const isValid = await ed.verifyAsync(signature, message, pubKey);\n})();\n```\n */\n/**\n * Curve params. ed25519 is twisted edwards curve. Equation is \u2212x\u00B2 + y\u00B2 = -a + dx\u00B2y\u00B2.\n * * P = `2n**255n - 19n` // field over which calculations are done\n * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points\n * * h = 8 // cofactor\n * * a = `Fp.create(BigInt(-1))` // equation param\n * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param\n * * Gx, Gy are coordinates of Generator / base point\n */\nconst ed25519_CURVE = {\n p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,\n n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,\n h: 8n,\n a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,\n d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,\n Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,\n Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,\n};\nconst { p: P, n: N, Gx, Gy, a: _a, d: _d, h } = ed25519_CURVE;\nconst L = 32; // field / group byte length\nconst L2 = 64;\n// Helpers and Precomputes sections are reused between libraries\n// ## Helpers\n// ----------\nconst captureTrace = (...args) => {\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(...args);\n }\n};\nconst err = (message = '') => {\n const e = new Error(message);\n captureTrace(e, err);\n throw e;\n};\nconst isBig = (n) => typeof n === 'bigint'; // is big integer\nconst isStr = (s) => typeof s === 'string'; // is string\nconst isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n/** Asserts something is Uint8Array. */\nconst abytes = (value, length, title = '') => {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n err(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n};\n/** create Uint8Array */\nconst u8n = (len) => new Uint8Array(len);\nconst u8fr = (buf) => Uint8Array.from(buf);\nconst padh = (n, pad) => n.toString(16).padStart(pad, '0');\nconst bytesToHex = (b) => Array.from(abytes(b))\n .map((e) => padh(e, 2))\n .join('');\nconst C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters\nconst _ch = (ch) => {\n if (ch >= C._0 && ch <= C._9)\n return ch - C._0; // '2' => 50-48\n if (ch >= C.A && ch <= C.F)\n return ch - (C.A - 10); // 'B' => 66-(65-10)\n if (ch >= C.a && ch <= C.f)\n return ch - (C.a - 10); // 'b' => 98-(97-10)\n return;\n};\nconst hexToBytes = (hex) => {\n const e = 'hex invalid';\n if (!isStr(hex))\n return err(e);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n return err(e);\n const array = u8n(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n // treat each char as ASCII\n const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16\n const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char\n if (n1 === undefined || n2 === undefined)\n return err(e);\n array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9\n }\n return array;\n};\nconst cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments\nconst subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined, consider polyfill');\n// prettier-ignore\nconst concatBytes = (...arrs) => {\n const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length\n let pad = 0; // walk through each array,\n arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type\n return r;\n};\n/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */\nconst randomBytes = (len = L) => {\n const c = cr();\n return c.getRandomValues(u8n(len));\n};\nconst big = BigInt;\nconst assertRange = (n, min, max, msg = 'bad number: out of range') => (isBig(n) && min <= n && n < max ? n : err(msg));\n/** modular division */\nconst M = (a, b = P) => {\n const r = a % b;\n return r >= 0n ? r : b + r;\n};\nconst modN = (a) => M(a, N);\n/** Modular inversion using euclidean GCD (non-CT). No negative exponent for now. */\n// prettier-ignore\nconst invert = (num, md) => {\n if (num === 0n || md <= 0n)\n err('no inverse n=' + num + ' mod=' + md);\n let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;\n while (a !== 0n) {\n const q = b / a, r = b % a;\n const m = x - u * q, n = y - v * q;\n b = a, a = r, x = u, y = v, u = m, v = n;\n }\n return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point\n};\nconst callHash = (name) => {\n // @ts-ignore\n const fn = hashes[name];\n if (typeof fn !== 'function')\n err('hashes.' + name + ' not set');\n return fn;\n};\nconst hash = (msg) => callHash('sha512')(msg);\nconst apoint = (p) => (p instanceof Point ? p : err('Point expected'));\n// ## End of Helpers\n// -----------------\nconst B256 = 2n ** 256n;\n/** Point in XYZT extended coordinates. */\nclass Point {\n static BASE;\n static ZERO;\n X;\n Y;\n Z;\n T;\n constructor(X, Y, Z, T) {\n const max = B256;\n this.X = assertRange(X, 0n, max);\n this.Y = assertRange(Y, 0n, max);\n this.Z = assertRange(Z, 1n, max);\n this.T = assertRange(T, 0n, max);\n Object.freeze(this);\n }\n static CURVE() {\n return ed25519_CURVE;\n }\n static fromAffine(p) {\n return new Point(p.x, p.y, 1n, M(p.x * p.y));\n }\n /** RFC8032 5.1.3: Uint8Array to Point. */\n static fromBytes(hex, zip215 = false) {\n const d = _d;\n // Copy array to not mess it up.\n const normed = u8fr(abytes(hex, L));\n // adjust first LE byte = last BE byte\n const lastByte = hex[31];\n normed[31] = lastByte & ~0x80;\n const y = bytesToNumLE(normed);\n // zip215=true: 0 <= y < 2^256\n // zip215=false, RFC8032: 0 <= y < 2^255-19\n const max = zip215 ? B256 : P;\n assertRange(y, 0n, max);\n const y2 = M(y * y); // y\u00B2\n const u = M(y2 - 1n); // u=y\u00B2-1\n const v = M(d * y2 + 1n); // v=dy\u00B2+1\n let { isValid, value: x } = uvRatio(u, v); // (uv\u00B3)(uv\u2077)^(p-5)/8; square root\n if (!isValid)\n err('bad point: y not sqrt'); // not square root: bad point\n const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate\n const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit\n if (!zip215 && x === 0n && isLastByteOdd)\n err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1\n if (isLastByteOdd !== isXOdd)\n x = M(-x);\n return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy\n }\n static fromHex(hex, zip215) {\n return Point.fromBytes(hexToBytes(hex), zip215);\n }\n get x() {\n return this.toAffine().x;\n }\n get y() {\n return this.toAffine().y;\n }\n /** Checks if the point is valid and on-curve. */\n assertValidity() {\n const a = _a;\n const d = _d;\n const p = this;\n if (p.is0())\n return err('bad point: ZERO'); // TODO: optimize, with vars below?\n // Equation in affine coordinates: ax\u00B2 + y\u00B2 = 1 + dx\u00B2y\u00B2\n // Equation in projective coordinates (X/Z, Y/Z, Z): (aX\u00B2 + Y\u00B2)Z\u00B2 = Z\u2074 + dX\u00B2Y\u00B2\n const { X, Y, Z, T } = p;\n const X2 = M(X * X); // X\u00B2\n const Y2 = M(Y * Y); // Y\u00B2\n const Z2 = M(Z * Z); // Z\u00B2\n const Z4 = M(Z2 * Z2); // Z\u2074\n const aX2 = M(X2 * a); // aX\u00B2\n const left = M(Z2 * M(aX2 + Y2)); // (aX\u00B2 + Y\u00B2)Z\u00B2\n const right = M(Z4 + M(d * M(X2 * Y2))); // Z\u2074 + dX\u00B2Y\u00B2\n if (left !== right)\n return err('bad point: equation left != right (1)');\n // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T\n const XY = M(X * Y);\n const ZT = M(Z * T);\n if (XY !== ZT)\n return err('bad point: equation left != right (2)');\n return this;\n }\n /** Equality check: compare points P&Q. */\n equals(other) {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality\n const X1Z2 = M(X1 * Z2);\n const X2Z1 = M(X2 * Z1);\n const Y1Z2 = M(Y1 * Z2);\n const Y2Z1 = M(Y2 * Z1);\n return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;\n }\n is0() {\n return this.equals(I);\n }\n /** Flip point over y coordinate. */\n negate() {\n return new Point(M(-this.X), this.Y, this.Z, M(-this.T));\n }\n /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */\n double() {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const a = _a;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd\n const A = M(X1 * X1);\n const B = M(Y1 * Y1);\n const C = M(2n * M(Z1 * Z1));\n const D = M(a * A);\n const x1y1 = X1 + Y1;\n const E = M(M(x1y1 * x1y1) - A - B);\n const G = D + B;\n const F = G - C;\n const H = D - B;\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */\n add(other) {\n const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;\n const { X: X2, Y: Y2, Z: Z2, T: T2 } = apoint(other); // doesn't check if other on-curve\n const a = _a;\n const d = _d;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3\n const A = M(X1 * X2);\n const B = M(Y1 * Y2);\n const C = M(T1 * d * T2);\n const D = M(Z1 * Z2);\n const E = M((X1 + Y1) * (X2 + Y2) - A - B);\n const F = M(D - C);\n const G = M(D + C);\n const H = M(B - a * A);\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n subtract(other) {\n return this.add(apoint(other).negate());\n }\n /**\n * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.\n * Uses {@link wNAF} for base point.\n * Uses fake point to mitigate side-channel leakage.\n * @param n scalar by which point is multiplied\n * @param safe safe mode guards against timing attacks; unsafe mode is faster\n */\n multiply(n, safe = true) {\n if (!safe && (n === 0n || this.is0()))\n return I;\n assertRange(n, 1n, N);\n if (n === 1n)\n return this;\n if (this.equals(G))\n return wNAF(n).p;\n // init result point & fake point\n let p = I;\n let f = G;\n for (let d = this; n > 0n; d = d.double(), n >>= 1n) {\n // if bit is present, add to point\n // if not present, add to fake, for timing safety\n if (n & 1n)\n p = p.add(d);\n else if (safe)\n f = f.add(d);\n }\n return p;\n }\n multiplyUnsafe(scalar) {\n return this.multiply(scalar, false);\n }\n /** Convert point to 2d xy affine point. (X, Y, Z) \u220B (x=X/Z, y=Y/Z) */\n toAffine() {\n const { X, Y, Z } = this;\n // fast-paths for ZERO point OR Z=1\n if (this.equals(I))\n return { x: 0n, y: 1n };\n const iz = invert(Z, P);\n // (Z * Z^-1) must be 1, otherwise bad math\n if (M(Z * iz) !== 1n)\n err('invalid inverse');\n // x = X*Z^-1; y = Y*Z^-1\n const x = M(X * iz);\n const y = M(Y * iz);\n return { x, y };\n }\n toBytes() {\n const { x, y } = this.assertValidity().toAffine();\n const b = numTo32bLE(y);\n // store sign in first LE byte\n b[31] |= x & 1n ? 0x80 : 0;\n return b;\n }\n toHex() {\n return bytesToHex(this.toBytes());\n }\n clearCofactor() {\n return this.multiply(big(h), false);\n }\n isSmallOrder() {\n return this.clearCofactor().is0();\n }\n isTorsionFree() {\n // Multiply by big number N. We can't `mul(N)` because of checks. Instead, we `mul(N/2)*2+1`\n let p = this.multiply(N / 2n, false).double();\n if (N % 2n)\n p = p.add(this);\n return p.is0();\n }\n}\n/** Generator / base point */\nconst G = new Point(Gx, Gy, 1n, M(Gx * Gy));\n/** Identity / zero point */\nconst I = new Point(0n, 1n, 1n, 0n);\n// Static aliases\nPoint.BASE = G;\nPoint.ZERO = I;\nconst numTo32bLE = (num) => hexToBytes(padh(assertRange(num, 0n, B256), L2)).reverse();\nconst bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));\nconst pow2 = (x, power) => {\n // pow2(x, 4) == x^(2^4)\n let r = x;\n while (power-- > 0n) {\n r *= r;\n r %= P;\n }\n return r;\n};\n// prettier-ignore\nconst pow_2_252_3 = (x) => {\n const x2 = (x * x) % P; // x^2, bits 1\n const b2 = (x2 * x) % P; // x^3, bits 11\n const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111\n const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111\n const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)\n const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)\n const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)\n const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)\n const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)\n const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)\n const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)\n const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.\n return { pow_p_5_8, b2 };\n};\nconst RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // \u221A-1\n// for sqrt comp\n// prettier-ignore\nconst uvRatio = (u, v) => {\n const v3 = M(v * v * v); // v\u00B3\n const v7 = M(v3 * v3 * v); // v\u2077\n const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv\u2077)^(p-5)/8\n let x = M(u * v3 * pow); // (uv\u00B3)(uv\u2077)^(p-5)/8\n const vx2 = M(v * x * x); // vx\u00B2\n const root1 = x; // First root candidate\n const root2 = M(x * RM1); // Second root candidate; RM1 is \u221A-1\n const useRoot1 = vx2 === u; // If vx\u00B2 = u (mod p), x is a square root\n const useRoot2 = vx2 === M(-u); // If vx\u00B2 = -u, set x <-- x * 2^((p-1)/4)\n const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx\u00B2 = -u\u221A-1\n if (useRoot1)\n x = root1;\n if (useRoot2 || noRoot)\n x = root2; // We return root2 anyway, for const-time\n if ((M(x) & 1n) === 1n)\n x = M(-x); // edIsNegative\n return { isValid: useRoot1 || useRoot2, value: x };\n};\n// N == L, just weird naming\nconst modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian\n/** hashes.sha512 should conform to the interface. */\n// TODO: rename\nconst sha512a = (...m) => hashes.sha512Async(concatBytes(...m)); // Async SHA512\nconst sha512s = (...m) => callHash('sha512')(concatBytes(...m));\n// RFC8032 5.1.5\nconst hash2extK = (hashed) => {\n // slice creates a copy, unlike subarray\n const head = hashed.slice(0, L);\n head[0] &= 248; // Clamp bits: 0b1111_1000\n head[31] &= 127; // 0b0111_1111\n head[31] |= 64; // 0b0100_0000\n const prefix = hashed.slice(L, L2); // secret key \"prefix\"\n const scalar = modL_LE(head); // modular division over curve order\n const point = G.multiply(scalar); // public key point\n const pointBytes = point.toBytes(); // point serialized to Uint8Array\n return { head, prefix, scalar, point, pointBytes };\n};\n// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.\nconst getExtendedPublicKeyAsync = (secretKey) => sha512a(abytes(secretKey, L)).then(hash2extK);\nconst getExtendedPublicKey = (secretKey) => hash2extK(sha512s(abytes(secretKey, L)));\n/** Creates 32-byte ed25519 public key from 32-byte secret key. Async. */\nconst getPublicKeyAsync = (secretKey) => getExtendedPublicKeyAsync(secretKey).then((p) => p.pointBytes);\n/** Creates 32-byte ed25519 public key from 32-byte secret key. To use, set `hashes.sha512` first. */\nconst getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;\nconst hashFinishA = (res) => sha512a(res.hashable).then(res.finish);\nconst hashFinishS = (res) => res.finish(sha512s(res.hashable));\n// Code, shared between sync & async sign\nconst _sign = (e, rBytes, msg) => {\n const { pointBytes: P, scalar: s } = e;\n const r = modL_LE(rBytes); // r was created outside, reduce it modulo L\n const R = G.multiply(r).toBytes(); // R = [r]B\n const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l\n return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)\n };\n return { hashable, finish };\n};\n/**\n * Signs message using secret key. Async.\n * Follows RFC8032 5.1.6.\n */\nconst signAsync = async (message, secretKey) => {\n const m = abytes(message);\n const e = await getExtendedPublicKeyAsync(secretKey);\n const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\n/**\n * Signs message using secret key. To use, set `hashes.sha512` first.\n * Follows RFC8032 5.1.6.\n */\nconst sign = (message, secretKey) => {\n const m = abytes(message);\n const e = getExtendedPublicKey(secretKey);\n const rBytes = sha512s(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishS(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\nconst defaultVerifyOpts = { zip215: true };\nconst _verify = (sig, msg, pub, opts = defaultVerifyOpts) => {\n sig = abytes(sig, L2); // Signature hex str/Bytes, must be 64 bytes\n msg = abytes(msg); // Message hex str/Bytes\n pub = abytes(pub, L);\n const { zip215 } = opts; // switch between zip215 and rfc8032 verif\n let A;\n let R;\n let s;\n let SB;\n let hashable = Uint8Array.of();\n try {\n A = Point.fromBytes(pub, zip215); // public key A decoded\n R = Point.fromBytes(sig.slice(0, L), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P\n s = bytesToNumLE(sig.slice(L, L2)); // Decode second half as an integer S\n SB = G.multiply(s, false); // in the range 0 <= s < L\n hashable = concatBytes(R.toBytes(), A.toBytes(), msg); // dom2(F, C) || R || A || PH(M)\n }\n catch (error) { }\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n if (SB == null)\n return false; // false if try-catch catched an error\n if (!zip215 && A.isSmallOrder())\n return false; // false for SBS: Strongly Binding Signature\n const k = modL_LE(hashed); // decode in little-endian, modulo L\n const RkA = R.add(A.multiply(k, false)); // [8]R + [8][k]A'\n return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'\n };\n return { hashable, finish };\n};\n/** Verifies signature on message and public key. Async. Follows RFC8032 5.1.7. */\nconst verifyAsync = async (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishA(_verify(signature, message, publicKey, opts));\n/** Verifies signature on message and public key. To use, set `hashes.sha512` first. Follows RFC8032 5.1.7. */\nconst verify = (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishS(_verify(signature, message, publicKey, opts));\n/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */\nconst etc = {\n bytesToHex: bytesToHex,\n hexToBytes: hexToBytes,\n concatBytes: concatBytes,\n mod: M,\n invert: invert,\n randomBytes: randomBytes,\n};\nconst hashes = {\n sha512Async: async (message) => {\n const s = subtle();\n const m = concatBytes(message);\n return u8n(await s.digest('SHA-512', m.buffer));\n },\n sha512: undefined,\n};\n// FIPS 186 B.4.1 compliant key generation produces private keys\n// with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1\nconst randomSecretKey = (seed = randomBytes(L)) => seed;\nconst keygen = (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = getPublicKey(secretKey);\n return { secretKey, publicKey };\n};\nconst keygenAsync = async (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = await getPublicKeyAsync(secretKey);\n return { secretKey, publicKey };\n};\n/** ed25519-specific key utilities. */\nconst utils = {\n getExtendedPublicKeyAsync: getExtendedPublicKeyAsync,\n getExtendedPublicKey: getExtendedPublicKey,\n randomSecretKey: randomSecretKey,\n};\n// ## Precomputes\n// --------------\nconst W = 8; // W is window size\nconst scalarBits = 256;\nconst pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop\nconst pwindowSize = 2 ** (W - 1); // 128 for W=8\nconst precompute = () => {\n const points = [];\n let p = G;\n let b = p;\n for (let w = 0; w < pwindows; w++) {\n b = p;\n points.push(b);\n for (let i = 1; i < pwindowSize; i++) {\n b = b.add(p);\n points.push(b);\n } // i=1, bc we skip 0\n p = b.double();\n }\n return points;\n};\nlet Gpows = undefined; // precomputes for base point G\n// const-time negate\nconst ctneg = (cnd, p) => {\n const n = p.negate();\n return cnd ? n : p;\n};\n/**\n * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by\n * caching multiples of G (base point). Cache is stored in 32MB of RAM.\n * Any time `G.multiply` is done, precomputes are used.\n * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.\n *\n * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,\n * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.\n *\n * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().\n */\nconst wNAF = (n) => {\n const comp = Gpows || (Gpows = precompute());\n let p = I;\n let f = G; // f must be G, or could become I in the end\n const pow_2_w = 2 ** W; // 256 for W=8\n const maxNum = pow_2_w; // 256 for W=8\n const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111\n const shiftBy = big(W); // 8 for W=8\n for (let w = 0; w < pwindows; w++) {\n let wbits = Number(n & mask); // extract W bits.\n n >>= shiftBy; // shift number by W bits.\n // We use negative indexes to reduce size of precomputed table by 2x.\n // Instead of needing precomputes 0..256, we only calculate them for 0..128.\n // If an index > 128 is found, we do (256-index) - where 256 is next window.\n // Naive: index +127 => 127, +224 => 224\n // Optimized: index +127 => 127, +224 => 256-32\n if (wbits > pwindowSize) {\n wbits -= maxNum;\n n += 1n;\n }\n const off = w * pwindowSize;\n const offF = off; // offsets, evaluate both\n const offP = off + Math.abs(wbits) - 1;\n const isEven = w % 2 !== 0; // conditions, evaluate both\n const isNeg = wbits < 0;\n if (wbits === 0) {\n // off == I: can't add it. Adding random offF instead.\n f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point\n }\n else {\n p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point\n }\n }\n if (n !== 0n)\n err('invalid wnaf');\n return { p, f }; // return both real and fake points for JIT\n};\n// !! Remove the export to easily use in REPL / browser console\nexport { etc, getPublicKey, getPublicKeyAsync, hash, hashes, keygen, keygenAsync, Point, sign, signAsync, utils, verify, verifyAsync, };\n", "import { Context } from 'hono';\nimport { Storage } from './storage/types.ts';\nimport { Config } from './config.ts';\nimport {\n validateTags,\n validatePublicKey,\n verifyEd25519Signature,\n buildSignatureMessage,\n} from './crypto.ts';\n\n// Constants (non-configurable)\nconst MAX_PAGE_SIZE = 100;\nconst REQUEST_RATE_WINDOW = 1000; // 1 second in milliseconds\n\n/**\n * Check JSON object depth to prevent stack overflow from deeply nested objects\n */\nfunction getJsonDepth(obj: any, maxDepth: number, currentDepth = 0): number {\n if (obj === null || typeof obj !== 'object') {\n return currentDepth;\n }\n\n if (currentDepth >= maxDepth) {\n return currentDepth + 1;\n }\n\n let maxChildDepth = currentDepth;\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const childDepth = getJsonDepth(obj[key], maxDepth, currentDepth + 1);\n maxChildDepth = Math.max(maxChildDepth, childDepth);\n\n if (maxChildDepth > maxDepth) {\n return maxChildDepth;\n }\n }\n }\n\n return maxChildDepth;\n}\n\n/**\n * Validate parameter is a non-empty string\n */\nfunction validateStringParam(value: any, paramName: string): void {\n if (typeof value !== 'string' || value.length === 0) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `${paramName} must be a non-empty string`);\n }\n}\n\n/**\n * Standard error codes for RPC responses\n */\nexport const ErrorCodes = {\n // Authentication errors\n AUTH_REQUIRED: 'AUTH_REQUIRED',\n INVALID_CREDENTIALS: 'INVALID_CREDENTIALS',\n\n // Validation errors\n INVALID_PUBLIC_KEY: 'INVALID_PUBLIC_KEY',\n INVALID_TAG: 'INVALID_TAG',\n INVALID_SDP: 'INVALID_SDP',\n INVALID_PARAMS: 'INVALID_PARAMS',\n MISSING_PARAMS: 'MISSING_PARAMS',\n\n // Resource errors\n OFFER_NOT_FOUND: 'OFFER_NOT_FOUND',\n OFFER_ALREADY_ANSWERED: 'OFFER_ALREADY_ANSWERED',\n OFFER_NOT_ANSWERED: 'OFFER_NOT_ANSWERED',\n NO_AVAILABLE_OFFERS: 'NO_AVAILABLE_OFFERS',\n\n // Authorization errors\n NOT_AUTHORIZED: 'NOT_AUTHORIZED',\n OWNERSHIP_MISMATCH: 'OWNERSHIP_MISMATCH',\n\n // Limit errors\n TOO_MANY_OFFERS: 'TOO_MANY_OFFERS',\n SDP_TOO_LARGE: 'SDP_TOO_LARGE',\n BATCH_TOO_LARGE: 'BATCH_TOO_LARGE',\n RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',\n TOO_MANY_OFFERS_PER_USER: 'TOO_MANY_OFFERS_PER_USER',\n STORAGE_FULL: 'STORAGE_FULL',\n TOO_MANY_ICE_CANDIDATES: 'TOO_MANY_ICE_CANDIDATES',\n\n // Generic errors\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n UNKNOWN_METHOD: 'UNKNOWN_METHOD',\n} as const;\n\n/**\n * Custom error class with error code support\n */\nexport class RpcError extends Error {\n constructor(\n public errorCode: string,\n message: string\n ) {\n super(message);\n this.name = 'RpcError';\n }\n}\n\n/**\n * RPC request format (body only - auth in headers)\n */\nexport interface RpcRequest {\n method: string;\n params?: any;\n clientIp?: string;\n}\n\n/**\n * RPC response format\n */\nexport interface RpcResponse {\n success: boolean;\n result?: any;\n error?: string;\n errorCode?: string;\n}\n\n/**\n * RPC Method Parameter Interfaces\n */\nexport interface DiscoverParams {\n tags: string[];\n limit?: number;\n offset?: number;\n}\n\nexport interface PublishOfferParams {\n tags: string[];\n offers: Array<{ sdp: string }>;\n ttl?: number;\n}\n\nexport interface DeleteOfferParams {\n offerId: string;\n}\n\nexport interface AnswerOfferParams {\n offerId: string;\n sdp: string;\n matchedTags?: string[];\n}\n\nexport interface GetOfferAnswerParams {\n offerId: string;\n}\n\nexport interface PollParams {\n since?: number;\n}\n\nexport interface AddIceCandidatesParams {\n offerId: string;\n candidates: any[];\n}\n\nexport interface GetIceCandidatesParams {\n offerId: string;\n since?: number;\n}\n\n/**\n * RPC method handler\n */\ntype RpcHandler<TParams = any> = (\n params: TParams,\n publicKey: string,\n timestamp: number,\n signature: string,\n storage: Storage,\n config: Config,\n request: RpcRequest\n) => Promise<any>;\n\n/**\n * Validate timestamp for replay attack prevention\n */\nfunction validateTimestamp(timestamp: number, config: Config): void {\n const now = Date.now();\n\n if (now - timestamp > config.timestampMaxAge) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'Timestamp too old');\n }\n\n if (timestamp - now > config.timestampMaxFuture) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'Timestamp too far in future');\n }\n}\n\n/**\n * Verify request signature using Ed25519\n * Stateless verification - no identity registration required\n */\nasync function verifyRequestSignature(\n publicKey: string,\n timestamp: number,\n nonce: string,\n signature: string,\n method: string,\n params: any,\n storage: Storage,\n config: Config\n): Promise<void> {\n // Validate timestamp first\n validateTimestamp(timestamp, config);\n\n // Validate public key format\n const pkValidation = validatePublicKey(publicKey);\n if (!pkValidation.valid) {\n throw new RpcError(ErrorCodes.INVALID_PUBLIC_KEY, pkValidation.error || 'Invalid public key');\n }\n\n // Build message and verify Ed25519 signature\n const message = buildSignatureMessage(timestamp, nonce, method, params);\n const isValid = await verifyEd25519Signature(publicKey, message, signature);\n\n if (!isValid) {\n throw new RpcError(ErrorCodes.INVALID_CREDENTIALS, 'Invalid signature');\n }\n\n // Check nonce uniqueness AFTER successful signature verification\n const nonceKey = `nonce:${publicKey}:${nonce}`;\n const nonceExpiresAt = timestamp + config.timestampMaxAge;\n const nonceIsNew = await storage.checkAndMarkNonce(nonceKey, nonceExpiresAt);\n\n if (!nonceIsNew) {\n throw new RpcError(ErrorCodes.INVALID_CREDENTIALS, 'Nonce already used (replay attack detected)');\n }\n}\n\n/**\n * RPC Method Handlers\n */\nconst handlers: Record<string, RpcHandler> = {\n /**\n * Discover offers by tags\n */\n async discover(params: DiscoverParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { tags, limit, offset } = params;\n\n const tagsValidation = validateTags(tags);\n if (!tagsValidation.valid) {\n throw new RpcError(ErrorCodes.INVALID_TAG, tagsValidation.error || 'Invalid tags');\n }\n\n // Mode 1: Paginated discovery\n if (limit !== undefined) {\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit < 0) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'limit must be a non-negative integer');\n }\n if (offset !== undefined && (typeof offset !== 'number' || !Number.isInteger(offset) || offset < 0)) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'offset must be a non-negative integer');\n }\n\n const pageLimit = Math.min(Math.max(1, limit), MAX_PAGE_SIZE);\n const pageOffset = Math.max(0, offset || 0);\n\n const excludePublicKey = publicKey || null;\n\n const offers = await storage.discoverOffers(\n tags,\n excludePublicKey,\n pageLimit,\n pageOffset\n );\n\n return {\n offers: offers.map(offer => ({\n offerId: offer.id,\n publicKey: offer.publicKey,\n tags: offer.tags,\n sdp: offer.sdp,\n createdAt: offer.createdAt,\n expiresAt: offer.expiresAt,\n })),\n count: offers.length,\n limit: pageLimit,\n offset: pageOffset,\n };\n }\n\n // Mode 2: Random discovery\n const excludePublicKey = publicKey || null;\n const offer = await storage.getRandomOffer(tags, excludePublicKey);\n\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'No offers found matching tags');\n }\n\n return {\n offerId: offer.id,\n publicKey: offer.publicKey,\n tags: offer.tags,\n sdp: offer.sdp,\n createdAt: offer.createdAt,\n expiresAt: offer.expiresAt,\n };\n },\n\n /**\n * Publish offers with tags\n */\n async publishOffer(params: PublishOfferParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { tags, offers, ttl } = params;\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required for offer publishing');\n }\n\n const tagsValidation = validateTags(tags);\n if (!tagsValidation.valid) {\n throw new RpcError(ErrorCodes.INVALID_TAG, tagsValidation.error || 'Invalid tags');\n }\n\n if (!offers || !Array.isArray(offers) || offers.length === 0) {\n throw new RpcError(ErrorCodes.MISSING_PARAMS, 'Must provide at least one offer');\n }\n\n if (offers.length > config.maxOffersPerRequest) {\n throw new RpcError(\n ErrorCodes.TOO_MANY_OFFERS,\n `Too many offers (max ${config.maxOffersPerRequest})`\n );\n }\n\n const userOfferCount = await storage.getOfferCountByPublicKey(publicKey);\n if (userOfferCount + offers.length > config.maxOffersPerUser) {\n throw new RpcError(\n ErrorCodes.TOO_MANY_OFFERS_PER_USER,\n `User offer limit exceeded. You have ${userOfferCount} offers, limit is ${config.maxOffersPerUser}.`\n );\n }\n\n const totalOfferCount = await storage.getOfferCount();\n if (totalOfferCount + offers.length > config.maxTotalOffers) {\n throw new RpcError(\n ErrorCodes.STORAGE_FULL,\n `Server offer limit reached (${config.maxTotalOffers}). Try again later.`\n );\n }\n\n offers.forEach((offer, index) => {\n if (!offer || typeof offer !== 'object') {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `Invalid offer at index ${index}: must be an object`);\n }\n if (!offer.sdp || typeof offer.sdp !== 'string') {\n throw new RpcError(ErrorCodes.INVALID_SDP, `Invalid offer at index ${index}: missing or invalid SDP`);\n }\n if (!offer.sdp.trim()) {\n throw new RpcError(ErrorCodes.INVALID_SDP, `Invalid offer at index ${index}: SDP cannot be empty`);\n }\n if (offer.sdp.length > config.maxSdpSize) {\n throw new RpcError(ErrorCodes.SDP_TOO_LARGE, `SDP too large at index ${index} (max ${config.maxSdpSize} bytes)`);\n }\n });\n\n if (ttl !== undefined) {\n if (typeof ttl !== 'number' || isNaN(ttl) || ttl < 0) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'TTL must be a non-negative number');\n }\n }\n\n const now = Date.now();\n const offerTtl =\n ttl !== undefined\n ? Math.min(Math.max(ttl, config.offerMinTtl), config.offerMaxTtl)\n : config.offerDefaultTtl;\n const expiresAt = now + offerTtl;\n\n const offerRequests = offers.map(offer => ({\n publicKey,\n tags,\n sdp: offer.sdp,\n expiresAt,\n }));\n\n const createdOffers = await storage.createOffers(offerRequests);\n\n return {\n publicKey,\n tags,\n offers: createdOffers.map(offer => ({\n offerId: offer.id,\n sdp: offer.sdp,\n createdAt: offer.createdAt,\n expiresAt: offer.expiresAt,\n })),\n createdAt: now,\n expiresAt,\n };\n },\n\n /**\n * Delete an offer by ID\n */\n async deleteOffer(params: DeleteOfferParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId } = params;\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n validateStringParam(offerId, 'offerId');\n\n const deleted = await storage.deleteOffer(offerId, publicKey);\n if (!deleted) {\n throw new RpcError(ErrorCodes.NOT_AUTHORIZED, 'Offer not found or not owned by this identity');\n }\n\n return { success: true };\n },\n\n /**\n * Answer an offer\n */\n async answerOffer(params: AnswerOfferParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId, sdp, matchedTags } = params;\n\n validateStringParam(offerId, 'offerId');\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n if (!sdp || typeof sdp !== 'string' || sdp.length === 0) {\n throw new RpcError(ErrorCodes.INVALID_SDP, 'Invalid SDP');\n }\n\n if (sdp.length > config.maxSdpSize) {\n throw new RpcError(ErrorCodes.SDP_TOO_LARGE, `SDP too large (max ${config.maxSdpSize} bytes)`);\n }\n\n if (matchedTags !== undefined && !Array.isArray(matchedTags)) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'matchedTags must be an array');\n }\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');\n }\n\n if (offer.answererPublicKey) {\n throw new RpcError(ErrorCodes.OFFER_ALREADY_ANSWERED, 'Offer already answered');\n }\n\n if (matchedTags && matchedTags.length > 0) {\n const offerTagSet = new Set(offer.tags);\n const invalidTags = matchedTags.filter(tag => !offerTagSet.has(tag));\n if (invalidTags.length > 0) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `matchedTags contains tags not on offer: ${invalidTags.join(', ')}`);\n }\n }\n\n await storage.answerOffer(offerId, publicKey, sdp, matchedTags);\n\n return { success: true, offerId };\n },\n\n /**\n * Get answer for an offer\n */\n async getOfferAnswer(params: GetOfferAnswerParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId } = params;\n\n validateStringParam(offerId, 'offerId');\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');\n }\n\n if (offer.publicKey !== publicKey) {\n throw new RpcError(ErrorCodes.NOT_AUTHORIZED, 'Not authorized to access this offer');\n }\n\n if (!offer.answererPublicKey || !offer.answerSdp) {\n throw new RpcError(ErrorCodes.OFFER_NOT_ANSWERED, 'Offer not yet answered');\n }\n\n return {\n sdp: offer.answerSdp,\n offerId: offer.id,\n answererPublicKey: offer.answererPublicKey,\n answeredAt: offer.answeredAt,\n };\n },\n\n /**\n * Combined polling for answers and ICE candidates\n */\n async poll(params: PollParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { since } = params;\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n if (since !== undefined && (typeof since !== 'number' || since < 0 || !Number.isFinite(since))) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'Invalid since parameter: must be a non-negative number');\n }\n const sinceTimestamp = since !== undefined ? since : 0;\n\n const answeredOffers = await storage.getAnsweredOffers(publicKey);\n const filteredAnswers = answeredOffers.filter(\n (offer) => offer.answeredAt && offer.answeredAt > sinceTimestamp\n );\n\n const ownedOffers = await storage.getOffersByPublicKey(publicKey);\n const answeredByUser = await storage.getOffersAnsweredBy(publicKey);\n\n const allOfferIds = [\n ...ownedOffers.map(offer => offer.id),\n ...answeredByUser.map(offer => offer.id),\n ];\n const offerIds = [...new Set(allOfferIds)];\n\n const iceCandidatesMap = await storage.getIceCandidatesForMultipleOffers(\n offerIds,\n publicKey,\n sinceTimestamp\n );\n\n const iceCandidatesByOffer: Record<string, any[]> = {};\n for (const [offerId, candidates] of iceCandidatesMap.entries()) {\n iceCandidatesByOffer[offerId] = candidates;\n }\n\n return {\n answers: filteredAnswers.map((offer) => ({\n offerId: offer.id,\n answererPublicKey: offer.answererPublicKey,\n sdp: offer.answerSdp,\n answeredAt: offer.answeredAt,\n })),\n iceCandidates: iceCandidatesByOffer,\n };\n },\n\n /**\n * Add ICE candidates\n */\n async addIceCandidates(params: AddIceCandidatesParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId, candidates } = params;\n\n validateStringParam(offerId, 'offerId');\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n if (!Array.isArray(candidates) || candidates.length === 0) {\n throw new RpcError(ErrorCodes.MISSING_PARAMS, 'Missing or invalid required parameter: candidates');\n }\n\n if (candidates.length > config.maxCandidatesPerRequest) {\n throw new RpcError(\n ErrorCodes.INVALID_PARAMS,\n `Too many candidates (max ${config.maxCandidatesPerRequest})`\n );\n }\n\n candidates.forEach((candidate, index) => {\n if (!candidate || typeof candidate !== 'object') {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `Invalid candidate at index ${index}: must be an object`);\n }\n\n const depth = getJsonDepth(candidate, config.maxCandidateDepth + 1);\n if (depth > config.maxCandidateDepth) {\n throw new RpcError(\n ErrorCodes.INVALID_PARAMS,\n `Candidate at index ${index} too deeply nested (max depth ${config.maxCandidateDepth})`\n );\n }\n\n let candidateJson: string;\n try {\n candidateJson = JSON.stringify(candidate);\n } catch (e) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `Candidate at index ${index} is not serializable`);\n }\n\n if (candidateJson.length > config.maxCandidateSize) {\n throw new RpcError(\n ErrorCodes.INVALID_PARAMS,\n `Candidate at index ${index} too large (max ${config.maxCandidateSize} bytes)`\n );\n }\n });\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');\n }\n\n const currentCandidateCount = await storage.getIceCandidateCount(offerId);\n if (currentCandidateCount + candidates.length > config.maxIceCandidatesPerOffer) {\n throw new RpcError(\n ErrorCodes.TOO_MANY_ICE_CANDIDATES,\n `ICE candidate limit exceeded for offer. Current: ${currentCandidateCount}, limit: ${config.maxIceCandidatesPerOffer}.`\n );\n }\n\n const role = offer.publicKey === publicKey ? 'offerer' : 'answerer';\n const count = await storage.addIceCandidates(\n offerId,\n publicKey,\n role,\n candidates\n );\n\n return { count, offerId };\n },\n\n /**\n * Get ICE candidates\n */\n async getIceCandidates(params: GetIceCandidatesParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId, since } = params;\n\n validateStringParam(offerId, 'offerId');\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n if (since !== undefined && (typeof since !== 'number' || since < 0 || !Number.isFinite(since))) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'Invalid since parameter: must be a non-negative number');\n }\n const sinceTimestamp = since !== undefined ? since : 0;\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');\n }\n\n const isOfferer = offer.publicKey === publicKey;\n const isAnswerer = offer.answererPublicKey === publicKey;\n\n if (!isOfferer && !isAnswerer) {\n throw new RpcError(ErrorCodes.NOT_AUTHORIZED, 'Not authorized to access ICE candidates for this offer');\n }\n\n const role = isOfferer ? 'answerer' : 'offerer';\n\n const candidates = await storage.getIceCandidates(\n offerId,\n role,\n sinceTimestamp\n );\n\n return {\n candidates: candidates.map((c: any) => ({\n candidate: c.candidate,\n createdAt: c.createdAt,\n })),\n offerId,\n };\n },\n};\n\n// Methods that don't require authentication\nconst UNAUTHENTICATED_METHODS = new Set(['discover']);\n\n/**\n * Handle RPC batch request with header-based authentication\n */\nexport async function handleRpc(\n requests: RpcRequest[],\n ctx: Context,\n storage: Storage,\n config: Config\n): Promise<RpcResponse[]> {\n const responses: RpcResponse[] = [];\n\n const clientIp =\n ctx.req.header('cf-connecting-ip') ||\n ctx.req.header('x-real-ip') ||\n ctx.req.header('x-forwarded-for')?.split(',')[0].trim() ||\n undefined;\n\n // General request rate limiting (per IP per second)\n if (clientIp) {\n const rateLimitKey = `req:${clientIp}`;\n const allowed = await storage.checkRateLimit(\n rateLimitKey,\n config.requestsPerIpPerSecond,\n REQUEST_RATE_WINDOW\n );\n\n if (!allowed) {\n return requests.map(() => ({\n success: false,\n error: `Rate limit exceeded. Maximum ${config.requestsPerIpPerSecond} requests per second per IP.`,\n errorCode: ErrorCodes.RATE_LIMIT_EXCEEDED,\n }));\n }\n }\n\n // Read auth headers (X-PublicKey instead of X-Name)\n const publicKey = ctx.req.header('X-PublicKey');\n const timestampHeader = ctx.req.header('X-Timestamp');\n const nonce = ctx.req.header('X-Nonce');\n const signature = ctx.req.header('X-Signature');\n\n const timestamp = timestampHeader ? parseInt(timestampHeader, 10) : 0;\n\n // Pre-calculate total operations\n let totalOperations = 0;\n for (const request of requests) {\n const { method, params } = request;\n if (method === 'publishOffer' && params?.offers && Array.isArray(params.offers)) {\n totalOperations += params.offers.length;\n } else if (method === 'addIceCandidates' && params?.candidates && Array.isArray(params.candidates)) {\n totalOperations += params.candidates.length;\n } else {\n totalOperations += 1;\n }\n }\n\n if (totalOperations > config.maxTotalOperations) {\n return requests.map(() => ({\n success: false,\n error: `Total operations across batch exceed limit: ${totalOperations} > ${config.maxTotalOperations}`,\n errorCode: ErrorCodes.BATCH_TOO_LARGE,\n }));\n }\n\n // Process all requests\n for (const request of requests) {\n try {\n const { method, params } = request;\n\n if (!method || typeof method !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid method',\n errorCode: ErrorCodes.INVALID_PARAMS,\n });\n continue;\n }\n\n const handler = handlers[method];\n if (!handler) {\n responses.push({\n success: false,\n error: `Unknown method: ${method}`,\n errorCode: ErrorCodes.UNKNOWN_METHOD,\n });\n continue;\n }\n\n const requiresAuth = !UNAUTHENTICATED_METHODS.has(method);\n\n if (requiresAuth) {\n if (!publicKey || typeof publicKey !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid X-PublicKey header',\n errorCode: ErrorCodes.AUTH_REQUIRED,\n });\n continue;\n }\n\n if (!timestampHeader || typeof timestampHeader !== 'string' || isNaN(timestamp)) {\n responses.push({\n success: false,\n error: 'Missing or invalid X-Timestamp header',\n errorCode: ErrorCodes.AUTH_REQUIRED,\n });\n continue;\n }\n\n if (!nonce || typeof nonce !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid X-Nonce header (use crypto.randomUUID())',\n errorCode: ErrorCodes.AUTH_REQUIRED,\n });\n continue;\n }\n\n if (!signature || typeof signature !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid X-Signature header',\n errorCode: ErrorCodes.AUTH_REQUIRED,\n });\n continue;\n }\n\n // Verify Ed25519 signature\n await verifyRequestSignature(\n publicKey,\n timestamp,\n nonce,\n signature,\n method,\n params,\n storage,\n config\n );\n\n const result = await handler(\n params || {},\n publicKey,\n timestamp,\n signature,\n storage,\n config,\n { ...request, clientIp }\n );\n\n responses.push({\n success: true,\n result,\n });\n } else {\n // Execute handler without strict auth requirement\n const result = await handler(\n params || {},\n publicKey || '',\n 0,\n '',\n storage,\n config,\n { ...request, clientIp }\n );\n\n responses.push({\n success: true,\n result,\n });\n }\n } catch (err) {\n if (err instanceof RpcError) {\n responses.push({\n success: false,\n error: err.message,\n errorCode: err.errorCode,\n });\n } else {\n console.error('Unexpected RPC error:', err);\n responses.push({\n success: false,\n error: 'Internal server error',\n errorCode: ErrorCodes.INTERNAL_ERROR,\n });\n }\n }\n }\n\n return responses;\n}\n", "import { Storage } from './storage/types.ts';\nimport { StorageType } from './storage/factory.ts';\n\n// Version is injected at build time via esbuild define\ndeclare const RONDEVU_VERSION: string;\nconst BUILD_VERSION = typeof RONDEVU_VERSION !== 'undefined' ? RONDEVU_VERSION : 'unknown';\n\n/**\n * Application configuration\n * Reads from environment variables with sensible defaults\n */\nexport interface Config {\n port: number;\n storageType: StorageType;\n storagePath: string;\n databaseUrl: string;\n dbPoolSize: number;\n corsOrigins: string[];\n version: string;\n offerDefaultTtl: number;\n offerMaxTtl: number;\n offerMinTtl: number;\n cleanupInterval: number;\n maxOffersPerRequest: number;\n maxBatchSize: number;\n maxSdpSize: number;\n maxCandidateSize: number;\n maxCandidateDepth: number;\n maxCandidatesPerRequest: number;\n maxTotalOperations: number;\n timestampMaxAge: number; // Max age for timestamps (replay protection)\n timestampMaxFuture: number; // Max future tolerance for timestamps (clock skew)\n // Resource limits (for abuse prevention)\n maxOffersPerUser: number; // Max concurrent offers per user\n maxTotalOffers: number; // Max total offers in storage\n maxIceCandidatesPerOffer: number; // Max ICE candidates per offer\n requestsPerIpPerSecond: number; // Rate limit: requests per IP per second\n}\n\n/**\n * Loads configuration from environment variables\n */\nexport function loadConfig(): Config {\n // Helper to safely parse and validate integer config values\n function parsePositiveInt(value: string | undefined, defaultValue: string, name: string, min = 1): number {\n const parsed = parseInt(value || defaultValue, 10);\n if (isNaN(parsed)) {\n throw new Error(`${name} must be a valid integer (got: ${value})`);\n }\n if (parsed < min) {\n throw new Error(`${name} must be >= ${min} (got: ${parsed})`);\n }\n return parsed;\n }\n\n const config = {\n port: parsePositiveInt(process.env.PORT, '3000', 'PORT', 1),\n storageType: (process.env.STORAGE_TYPE || 'memory') as StorageType,\n storagePath: process.env.STORAGE_PATH || ':memory:',\n databaseUrl: process.env.DATABASE_URL || '',\n dbPoolSize: parsePositiveInt(process.env.DB_POOL_SIZE, '10', 'DB_POOL_SIZE', 1),\n corsOrigins: process.env.CORS_ORIGINS\n ? process.env.CORS_ORIGINS.split(',').map(o => o.trim())\n : ['*'],\n version: process.env.VERSION || BUILD_VERSION,\n offerDefaultTtl: parsePositiveInt(process.env.OFFER_DEFAULT_TTL, '60000', 'OFFER_DEFAULT_TTL', 1000),\n offerMaxTtl: parsePositiveInt(process.env.OFFER_MAX_TTL, '86400000', 'OFFER_MAX_TTL', 1000),\n offerMinTtl: parsePositiveInt(process.env.OFFER_MIN_TTL, '60000', 'OFFER_MIN_TTL', 1000),\n cleanupInterval: parsePositiveInt(process.env.CLEANUP_INTERVAL, '60000', 'CLEANUP_INTERVAL', 1000),\n maxOffersPerRequest: parsePositiveInt(process.env.MAX_OFFERS_PER_REQUEST, '100', 'MAX_OFFERS_PER_REQUEST', 1),\n maxBatchSize: parsePositiveInt(process.env.MAX_BATCH_SIZE, '100', 'MAX_BATCH_SIZE', 1),\n maxSdpSize: parsePositiveInt(process.env.MAX_SDP_SIZE, String(64 * 1024), 'MAX_SDP_SIZE', 1024), // Min 1KB\n maxCandidateSize: parsePositiveInt(process.env.MAX_CANDIDATE_SIZE, String(4 * 1024), 'MAX_CANDIDATE_SIZE', 256), // Min 256 bytes\n maxCandidateDepth: parsePositiveInt(process.env.MAX_CANDIDATE_DEPTH, '10', 'MAX_CANDIDATE_DEPTH', 1),\n maxCandidatesPerRequest: parsePositiveInt(process.env.MAX_CANDIDATES_PER_REQUEST, '100', 'MAX_CANDIDATES_PER_REQUEST', 1),\n maxTotalOperations: parsePositiveInt(process.env.MAX_TOTAL_OPERATIONS, '1000', 'MAX_TOTAL_OPERATIONS', 1),\n timestampMaxAge: parsePositiveInt(process.env.TIMESTAMP_MAX_AGE, '60000', 'TIMESTAMP_MAX_AGE', 1000), // Min 1 second\n timestampMaxFuture: parsePositiveInt(process.env.TIMESTAMP_MAX_FUTURE, '60000', 'TIMESTAMP_MAX_FUTURE', 1000), // Min 1 second\n // Resource limits\n maxOffersPerUser: parsePositiveInt(process.env.MAX_OFFERS_PER_USER, '1000', 'MAX_OFFERS_PER_USER', 1),\n maxTotalOffers: parsePositiveInt(process.env.MAX_TOTAL_OFFERS, '100000', 'MAX_TOTAL_OFFERS', 1),\n maxIceCandidatesPerOffer: parsePositiveInt(process.env.MAX_ICE_CANDIDATES_PER_OFFER, '50', 'MAX_ICE_CANDIDATES_PER_OFFER', 1),\n requestsPerIpPerSecond: parsePositiveInt(process.env.REQUESTS_PER_IP_PER_SECOND, '50', 'REQUESTS_PER_IP_PER_SECOND', 1),\n };\n\n return config;\n}\n\n/**\n * Default config values (shared between Node and Workers)\n */\nexport const CONFIG_DEFAULTS = {\n offerDefaultTtl: 60000,\n offerMaxTtl: 86400000,\n offerMinTtl: 60000,\n cleanupInterval: 60000,\n maxOffersPerRequest: 100,\n maxBatchSize: 100,\n maxSdpSize: 64 * 1024,\n maxCandidateSize: 4 * 1024,\n maxCandidateDepth: 10,\n maxCandidatesPerRequest: 100,\n maxTotalOperations: 1000,\n timestampMaxAge: 60000,\n timestampMaxFuture: 60000,\n // Resource limits\n maxOffersPerUser: 1000,\n maxTotalOffers: 100000,\n maxIceCandidatesPerOffer: 50,\n requestsPerIpPerSecond: 50,\n} as const;\n\n/**\n * Build config for Cloudflare Workers from env vars\n */\nexport function buildWorkerConfig(env: {\n OFFER_DEFAULT_TTL?: string;\n OFFER_MAX_TTL?: string;\n OFFER_MIN_TTL?: string;\n MAX_OFFERS_PER_REQUEST?: string;\n MAX_BATCH_SIZE?: string;\n CORS_ORIGINS?: string;\n VERSION?: string;\n}): Config {\n return {\n port: 0, // Not used in Workers\n storageType: 'sqlite', // D1 is SQLite-compatible\n storagePath: '', // Not used with D1\n databaseUrl: '', // Not used with D1\n dbPoolSize: 10, // Not used with D1\n corsOrigins: env.CORS_ORIGINS?.split(',').map(o => o.trim()) ?? ['*'],\n version: env.VERSION ?? 'unknown',\n offerDefaultTtl: env.OFFER_DEFAULT_TTL ? parseInt(env.OFFER_DEFAULT_TTL, 10) : CONFIG_DEFAULTS.offerDefaultTtl,\n offerMaxTtl: env.OFFER_MAX_TTL ? parseInt(env.OFFER_MAX_TTL, 10) : CONFIG_DEFAULTS.offerMaxTtl,\n offerMinTtl: env.OFFER_MIN_TTL ? parseInt(env.OFFER_MIN_TTL, 10) : CONFIG_DEFAULTS.offerMinTtl,\n cleanupInterval: CONFIG_DEFAULTS.cleanupInterval,\n maxOffersPerRequest: env.MAX_OFFERS_PER_REQUEST ? parseInt(env.MAX_OFFERS_PER_REQUEST, 10) : CONFIG_DEFAULTS.maxOffersPerRequest,\n maxBatchSize: env.MAX_BATCH_SIZE ? parseInt(env.MAX_BATCH_SIZE, 10) : CONFIG_DEFAULTS.maxBatchSize,\n maxSdpSize: CONFIG_DEFAULTS.maxSdpSize,\n maxCandidateSize: CONFIG_DEFAULTS.maxCandidateSize,\n maxCandidateDepth: CONFIG_DEFAULTS.maxCandidateDepth,\n maxCandidatesPerRequest: CONFIG_DEFAULTS.maxCandidatesPerRequest,\n maxTotalOperations: CONFIG_DEFAULTS.maxTotalOperations,\n timestampMaxAge: CONFIG_DEFAULTS.timestampMaxAge,\n timestampMaxFuture: CONFIG_DEFAULTS.timestampMaxFuture,\n // Resource limits\n maxOffersPerUser: CONFIG_DEFAULTS.maxOffersPerUser,\n maxTotalOffers: CONFIG_DEFAULTS.maxTotalOffers,\n maxIceCandidatesPerOffer: CONFIG_DEFAULTS.maxIceCandidatesPerOffer,\n requestsPerIpPerSecond: CONFIG_DEFAULTS.requestsPerIpPerSecond,\n };\n}\n\n/**\n * Run cleanup of expired entries (shared between Node and Workers)\n * @returns Object with counts of deleted items\n */\nexport async function runCleanup(storage: Storage, now: number): Promise<{\n offers: number;\n rateLimits: number;\n nonces: number;\n}> {\n const offers = await storage.deleteExpiredOffers(now);\n const rateLimits = await storage.deleteExpiredRateLimits(now);\n const nonces = await storage.deleteExpiredNonces(now);\n\n return { offers, rateLimits, nonces };\n}\n", "import { Storage } from './types.ts';\n\n/**\n * Supported storage backend types\n */\nexport type StorageType = 'memory' | 'sqlite' | 'mysql' | 'postgres';\n\n/**\n * Configuration for creating a storage backend\n */\nexport interface StorageConfig {\n type: StorageType;\n /** SQLite database path (default: ':memory:') */\n sqlitePath?: string;\n /** Connection string for MySQL/PostgreSQL */\n connectionString?: string;\n /** Connection pool size for MySQL/PostgreSQL (default: 10) */\n poolSize?: number;\n}\n\n/**\n * Creates a storage backend based on configuration\n * Uses dynamic imports to avoid loading unused dependencies\n */\nexport async function createStorage(config: StorageConfig): Promise<Storage> {\n switch (config.type) {\n case 'memory': {\n const { MemoryStorage } = await import('./memory.ts');\n return new MemoryStorage();\n }\n\n case 'sqlite': {\n const { SQLiteStorage } = await import('./sqlite.ts');\n return new SQLiteStorage(config.sqlitePath || ':memory:');\n }\n\n case 'mysql': {\n if (!config.connectionString) {\n throw new Error('MySQL storage requires DATABASE_URL connection string');\n }\n const { MySQLStorage } = await import('./mysql.ts');\n return MySQLStorage.create(config.connectionString, config.poolSize || 10);\n }\n\n case 'postgres': {\n if (!config.connectionString) {\n throw new Error('PostgreSQL storage requires DATABASE_URL connection string');\n }\n const { PostgreSQLStorage } = await import('./postgres.ts');\n return PostgreSQLStorage.create(config.connectionString, config.poolSize || 10);\n }\n\n default:\n throw new Error(`Unsupported storage type: ${config.type}`);\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Generates a unique offer ID using SHA-256 hash\n * Combines SDP content with timestamp and random bytes for uniqueness\n * Uses Web Crypto API for compatibility with both Node.js and Cloudflare Workers\n *\n * @param sdp - The WebRTC SDP offer\n * @returns Unique SHA-256 hash ID\n */\nexport async function generateOfferHash(sdp: string): Promise<string> {\n // Generate random bytes for uniqueness (8 bytes = 64 bits of randomness)\n const randomBytes = crypto.getRandomValues(new Uint8Array(8));\n const randomHex = Array.from(randomBytes).map(b => b.toString(16).padStart(2, '0')).join('');\n\n // Include SDP, timestamp, and random bytes for uniqueness\n const hashInput = {\n sdp,\n timestamp: Date.now(),\n nonce: randomHex\n };\n\n // Create non-prettified JSON string\n const jsonString = JSON.stringify(hashInput);\n\n // Convert string to Uint8Array for hashing\n const encoder = new TextEncoder();\n const data = encoder.encode(jsonString);\n\n // Generate SHA-256 hash\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\n // Convert hash to hex string\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n\n return hashHex;\n}\n", "import {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\n\ninterface RateLimit {\n count: number;\n resetTime: number;\n}\n\ninterface NonceEntry {\n expiresAt: number;\n}\n\n/**\n * In-memory storage adapter for rondevu signaling system\n * Data is not persisted - all data is lost on server restart\n * Best for development, testing, or ephemeral deployments\n */\nexport class MemoryStorage implements Storage {\n // Primary storage\n private offers = new Map<string, Offer>();\n private iceCandidates = new Map<string, IceCandidate[]>(); // offerId \u2192 candidates\n private rateLimits = new Map<string, RateLimit>();\n private nonces = new Map<string, NonceEntry>();\n\n // Secondary indexes for efficient lookups\n private offersByPublicKey = new Map<string, Set<string>>(); // publicKey \u2192 offer IDs\n private offersByTag = new Map<string, Set<string>>(); // tag \u2192 offer IDs\n private offersByAnswerer = new Map<string, Set<string>>(); // answerer publicKey \u2192 offer IDs\n\n // Auto-increment counter for ICE candidates\n private iceCandidateIdCounter = 0;\n\n constructor() {}\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n const created: Offer[] = [];\n const now = Date.now();\n\n for (const request of offers) {\n const id = request.id || await generateOfferHash(request.sdp);\n\n const offer: Offer = {\n id,\n publicKey: request.publicKey,\n tags: request.tags,\n sdp: request.sdp,\n createdAt: now,\n expiresAt: request.expiresAt,\n lastSeen: now,\n };\n\n // Store offer\n this.offers.set(id, offer);\n\n // Update publicKey index\n if (!this.offersByPublicKey.has(request.publicKey)) {\n this.offersByPublicKey.set(request.publicKey, new Set());\n }\n this.offersByPublicKey.get(request.publicKey)!.add(id);\n\n // Update tag indexes\n for (const tag of request.tags) {\n if (!this.offersByTag.has(tag)) {\n this.offersByTag.set(tag, new Set());\n }\n this.offersByTag.get(tag)!.add(id);\n }\n\n created.push(offer);\n }\n\n return created;\n }\n\n async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {\n const now = Date.now();\n const offerIds = this.offersByPublicKey.get(publicKey);\n if (!offerIds) return [];\n\n const offers: Offer[] = [];\n for (const id of offerIds) {\n const offer = this.offers.get(id);\n if (offer && offer.expiresAt > now) {\n offers.push(offer);\n }\n }\n\n return offers.sort((a, b) => b.lastSeen - a.lastSeen);\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const offer = this.offers.get(offerId);\n if (!offer || offer.expiresAt <= Date.now()) {\n return null;\n }\n return offer;\n }\n\n async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {\n const offer = this.offers.get(offerId);\n if (!offer || offer.publicKey !== ownerPublicKey) {\n return false;\n }\n\n this.removeOfferFromIndexes(offer);\n this.offers.delete(offerId);\n this.iceCandidates.delete(offerId);\n\n return true;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n let count = 0;\n\n for (const [id, offer] of this.offers) {\n if (offer.expiresAt < now) {\n this.removeOfferFromIndexes(offer);\n this.offers.delete(id);\n this.iceCandidates.delete(id);\n count++;\n }\n }\n\n return count;\n }\n\n async answerOffer(\n offerId: string,\n answererPublicKey: string,\n answerSdp: string,\n matchedTags?: string[]\n ): Promise<{ success: boolean; error?: string }> {\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return { success: false, error: 'Offer not found or expired' };\n }\n\n if (offer.answererPublicKey) {\n return { success: false, error: 'Offer already answered' };\n }\n\n // Update offer with answer\n const now = Date.now();\n offer.answererPublicKey = answererPublicKey;\n offer.answerSdp = answerSdp;\n offer.answeredAt = now;\n offer.matchedTags = matchedTags;\n\n // Update answerer index\n if (!this.offersByAnswerer.has(answererPublicKey)) {\n this.offersByAnswerer.set(answererPublicKey, new Set());\n }\n this.offersByAnswerer.get(answererPublicKey)!.add(offerId);\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {\n const now = Date.now();\n const offerIds = this.offersByPublicKey.get(offererPublicKey);\n if (!offerIds) return [];\n\n const offers: Offer[] = [];\n for (const id of offerIds) {\n const offer = this.offers.get(id);\n if (offer && offer.answererPublicKey && offer.expiresAt > now) {\n offers.push(offer);\n }\n }\n\n return offers.sort((a, b) => (b.answeredAt || 0) - (a.answeredAt || 0));\n }\n\n async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {\n const now = Date.now();\n const offerIds = this.offersByAnswerer.get(answererPublicKey);\n if (!offerIds) return [];\n\n const offers: Offer[] = [];\n for (const id of offerIds) {\n const offer = this.offers.get(id);\n if (offer && offer.expiresAt > now) {\n offers.push(offer);\n }\n }\n\n return offers.sort((a, b) => (b.answeredAt || 0) - (a.answeredAt || 0));\n }\n\n // ===== Discovery =====\n\n async discoverOffers(\n tags: string[],\n excludePublicKey: string | null,\n limit: number,\n offset: number\n ): Promise<Offer[]> {\n if (tags.length === 0) return [];\n\n const now = Date.now();\n const matchingOfferIds = new Set<string>();\n\n // Find all offers matching any tag (OR logic)\n for (const tag of tags) {\n const offerIds = this.offersByTag.get(tag);\n if (offerIds) {\n for (const id of offerIds) {\n matchingOfferIds.add(id);\n }\n }\n }\n\n // Filter and collect matching offers\n const offers: Offer[] = [];\n for (const id of matchingOfferIds) {\n const offer = this.offers.get(id);\n if (\n offer &&\n offer.expiresAt > now &&\n !offer.answererPublicKey &&\n (!excludePublicKey || offer.publicKey !== excludePublicKey)\n ) {\n offers.push(offer);\n }\n }\n\n // Sort by created_at descending and apply pagination\n offers.sort((a, b) => b.createdAt - a.createdAt);\n return offers.slice(offset, offset + limit);\n }\n\n async getRandomOffer(\n tags: string[],\n excludePublicKey: string | null\n ): Promise<Offer | null> {\n if (tags.length === 0) return null;\n\n const now = Date.now();\n const matchingOffers: Offer[] = [];\n\n // Find all offers matching any tag (OR logic)\n const matchingOfferIds = new Set<string>();\n for (const tag of tags) {\n const offerIds = this.offersByTag.get(tag);\n if (offerIds) {\n for (const id of offerIds) {\n matchingOfferIds.add(id);\n }\n }\n }\n\n // Collect matching offers\n for (const id of matchingOfferIds) {\n const offer = this.offers.get(id);\n if (\n offer &&\n offer.expiresAt > now &&\n !offer.answererPublicKey &&\n (!excludePublicKey || offer.publicKey !== excludePublicKey)\n ) {\n matchingOffers.push(offer);\n }\n }\n\n if (matchingOffers.length === 0) return null;\n\n // Return random offer\n const randomIndex = Math.floor(Math.random() * matchingOffers.length);\n return matchingOffers[randomIndex];\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n publicKey: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n const baseTimestamp = Date.now();\n\n if (!this.iceCandidates.has(offerId)) {\n this.iceCandidates.set(offerId, []);\n }\n\n const candidateList = this.iceCandidates.get(offerId)!;\n\n for (let i = 0; i < candidates.length; i++) {\n const candidate: IceCandidate = {\n id: ++this.iceCandidateIdCounter,\n offerId,\n publicKey,\n role,\n candidate: candidates[i],\n createdAt: baseTimestamp + i,\n };\n candidateList.push(candidate);\n }\n\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n const candidates = this.iceCandidates.get(offerId) || [];\n\n return candidates\n .filter(c => c.role === targetRole && (since === undefined || c.createdAt > since))\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n async getIceCandidatesForMultipleOffers(\n offerIds: string[],\n publicKey: string,\n since?: number\n ): Promise<Map<string, IceCandidate[]>> {\n const result = new Map<string, IceCandidate[]>();\n\n if (offerIds.length === 0) return result;\n if (offerIds.length > 1000) {\n throw new Error('Too many offer IDs (max 1000)');\n }\n\n for (const offerId of offerIds) {\n const offer = this.offers.get(offerId);\n if (!offer) continue;\n\n const candidates = this.iceCandidates.get(offerId) || [];\n\n // Determine which role's candidates to return\n // If user is offerer, return answerer candidates and vice versa\n const isOfferer = offer.publicKey === publicKey;\n const isAnswerer = offer.answererPublicKey === publicKey;\n\n if (!isOfferer && !isAnswerer) continue;\n\n const targetRole = isOfferer ? 'answerer' : 'offerer';\n\n const filteredCandidates = candidates\n .filter(c => c.role === targetRole && (since === undefined || c.createdAt > since))\n .sort((a, b) => a.createdAt - b.createdAt);\n\n if (filteredCandidates.length > 0) {\n result.set(offerId, filteredCandidates);\n }\n }\n\n return result;\n }\n\n // ===== Rate Limiting =====\n\n async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {\n const now = Date.now();\n const existing = this.rateLimits.get(identifier);\n\n if (!existing || existing.resetTime < now) {\n // New window or expired - reset count\n this.rateLimits.set(identifier, {\n count: 1,\n resetTime: now + windowMs,\n });\n return true;\n }\n\n // Increment count in existing window\n existing.count++;\n return existing.count <= limit;\n }\n\n async deleteExpiredRateLimits(now: number): Promise<number> {\n let count = 0;\n for (const [identifier, rateLimit] of this.rateLimits) {\n if (rateLimit.resetTime < now) {\n this.rateLimits.delete(identifier);\n count++;\n }\n }\n return count;\n }\n\n // ===== Nonce Tracking (Replay Protection) =====\n\n async checkAndMarkNonce(nonceKey: string, expiresAt: number): Promise<boolean> {\n if (this.nonces.has(nonceKey)) {\n return false; // Nonce already used - replay attack\n }\n\n this.nonces.set(nonceKey, { expiresAt });\n return true; // Nonce is new - allowed\n }\n\n async deleteExpiredNonces(now: number): Promise<number> {\n let count = 0;\n for (const [key, entry] of this.nonces) {\n if (entry.expiresAt < now) {\n this.nonces.delete(key);\n count++;\n }\n }\n return count;\n }\n\n async close(): Promise<void> {\n // Clear all data\n this.offers.clear();\n this.iceCandidates.clear();\n this.rateLimits.clear();\n this.nonces.clear();\n this.offersByPublicKey.clear();\n this.offersByTag.clear();\n this.offersByAnswerer.clear();\n }\n\n // ===== Count Methods (for resource limits) =====\n\n async getOfferCount(): Promise<number> {\n return this.offers.size;\n }\n\n async getOfferCountByPublicKey(publicKey: string): Promise<number> {\n const offerIds = this.offersByPublicKey.get(publicKey);\n return offerIds ? offerIds.size : 0;\n }\n\n async getIceCandidateCount(offerId: string): Promise<number> {\n const candidates = this.iceCandidates.get(offerId);\n return candidates ? candidates.length : 0;\n }\n\n // ===== Helper Methods =====\n\n private removeOfferFromIndexes(offer: Offer): void {\n // Remove from publicKey index\n const publicKeyOffers = this.offersByPublicKey.get(offer.publicKey);\n if (publicKeyOffers) {\n publicKeyOffers.delete(offer.id);\n if (publicKeyOffers.size === 0) {\n this.offersByPublicKey.delete(offer.publicKey);\n }\n }\n\n // Remove from tag indexes\n for (const tag of offer.tags) {\n const tagOffers = this.offersByTag.get(tag);\n if (tagOffers) {\n tagOffers.delete(offer.id);\n if (tagOffers.size === 0) {\n this.offersByTag.delete(tag);\n }\n }\n }\n\n // Remove from answerer index\n if (offer.answererPublicKey) {\n const answererOffers = this.offersByAnswerer.get(offer.answererPublicKey);\n if (answererOffers) {\n answererOffers.delete(offer.id);\n if (answererOffers.size === 0) {\n this.offersByAnswerer.delete(offer.answererPublicKey);\n }\n }\n }\n }\n}\n", "import Database from 'better-sqlite3';\nimport {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\n\n/**\n * SQLite storage adapter for rondevu signaling system\n * Supports both file-based and in-memory databases\n */\nexport class SQLiteStorage implements Storage {\n private db: Database.Database;\n\n /**\n * Creates a new SQLite storage instance\n * @param path Path to SQLite database file, or ':memory:' for in-memory database\n */\n constructor(path: string = ':memory:') {\n this.db = new Database(path);\n this.initializeDatabase();\n }\n\n /**\n * Initializes database schema with Ed25519 public key identity\n */\n private initializeDatabase(): void {\n this.db.exec(`\n -- Identities table (Ed25519 public key as identity)\n CREATE TABLE IF NOT EXISTS identities (\n public_key TEXT PRIMARY KEY,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n last_used INTEGER NOT NULL,\n CHECK(length(public_key) = 64)\n );\n\n CREATE INDEX IF NOT EXISTS idx_identities_expires ON identities(expires_at);\n\n -- WebRTC signaling offers with tags\n CREATE TABLE IF NOT EXISTS offers (\n id TEXT PRIMARY KEY,\n public_key TEXT NOT NULL,\n tags TEXT NOT NULL,\n sdp TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n last_seen INTEGER NOT NULL,\n answerer_public_key TEXT,\n answer_sdp TEXT,\n answered_at INTEGER,\n matched_tags TEXT,\n FOREIGN KEY (public_key) REFERENCES identities(public_key) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_offers_public_key ON offers(public_key);\n CREATE INDEX IF NOT EXISTS idx_offers_expires ON offers(expires_at);\n CREATE INDEX IF NOT EXISTS idx_offers_last_seen ON offers(last_seen);\n CREATE INDEX IF NOT EXISTS idx_offers_answerer ON offers(answerer_public_key);\n\n -- ICE candidates table\n CREATE TABLE IF NOT EXISTS ice_candidates (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n offer_id TEXT NOT NULL,\n public_key TEXT NOT NULL,\n role TEXT NOT NULL CHECK(role IN ('offerer', 'answerer')),\n candidate TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n FOREIGN KEY (offer_id) REFERENCES offers(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_ice_offer ON ice_candidates(offer_id);\n CREATE INDEX IF NOT EXISTS idx_ice_public_key ON ice_candidates(public_key);\n CREATE INDEX IF NOT EXISTS idx_ice_created ON ice_candidates(created_at);\n\n -- Rate limits table (for distributed rate limiting)\n CREATE TABLE IF NOT EXISTS rate_limits (\n identifier TEXT PRIMARY KEY,\n count INTEGER NOT NULL,\n reset_time INTEGER NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_rate_limits_reset ON rate_limits(reset_time);\n\n -- Nonces table (for replay attack prevention)\n CREATE TABLE IF NOT EXISTS nonces (\n nonce_key TEXT PRIMARY KEY,\n expires_at INTEGER NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_nonces_expires ON nonces(expires_at);\n `);\n\n // Enable foreign keys\n this.db.pragma('foreign_keys = ON');\n }\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n const created: Offer[] = [];\n\n // Generate hash-based IDs for all offers first\n const offersWithIds = await Promise.all(\n offers.map(async (offer) => ({\n ...offer,\n id: offer.id || await generateOfferHash(offer.sdp),\n }))\n );\n\n // Use transaction for atomic creation\n const transaction = this.db.transaction((offersWithIds: (CreateOfferRequest & { id: string })[]) => {\n const offerStmt = this.db.prepare(`\n INSERT INTO offers (id, public_key, tags, sdp, created_at, expires_at, last_seen)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const offer of offersWithIds) {\n const now = Date.now();\n\n // Insert offer with JSON-serialized tags\n offerStmt.run(\n offer.id,\n offer.publicKey,\n JSON.stringify(offer.tags),\n offer.sdp,\n now,\n offer.expiresAt,\n now\n );\n\n created.push({\n id: offer.id,\n publicKey: offer.publicKey,\n tags: offer.tags,\n sdp: offer.sdp,\n createdAt: now,\n expiresAt: offer.expiresAt,\n lastSeen: now,\n });\n }\n });\n\n transaction(offersWithIds);\n return created;\n }\n\n async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE public_key = ? AND expires_at > ?\n ORDER BY last_seen DESC\n `);\n\n const rows = stmt.all(publicKey, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE id = ? AND expires_at > ?\n `);\n\n const row = stmt.get(offerId, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToOffer(row);\n }\n\n async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {\n const stmt = this.db.prepare(`\n DELETE FROM offers\n WHERE id = ? AND public_key = ?\n `);\n\n const result = stmt.run(offerId, ownerPublicKey);\n return result.changes > 0;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM offers WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n async answerOffer(\n offerId: string,\n answererPublicKey: string,\n answerSdp: string,\n matchedTags?: string[]\n ): Promise<{ success: boolean; error?: string }> {\n // Check if offer exists and is not expired\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return {\n success: false,\n error: 'Offer not found or expired'\n };\n }\n\n // Check if offer already has an answerer\n if (offer.answererPublicKey) {\n return {\n success: false,\n error: 'Offer already answered'\n };\n }\n\n // Update offer with answer\n const stmt = this.db.prepare(`\n UPDATE offers\n SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?\n WHERE id = ? AND answerer_public_key IS NULL\n `);\n\n const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;\n const result = stmt.run(answererPublicKey, answerSdp, Date.now(), matchedTagsJson, offerId);\n\n if (result.changes === 0) {\n return {\n success: false,\n error: 'Offer already answered (race condition)'\n };\n }\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE public_key = ? AND answerer_public_key IS NOT NULL AND expires_at > ?\n ORDER BY answered_at DESC\n `);\n\n const rows = stmt.all(offererPublicKey, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE answerer_public_key = ? AND expires_at > ?\n ORDER BY answered_at DESC\n `);\n\n const rows = stmt.all(answererPublicKey, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n // ===== Discovery =====\n\n async discoverOffers(\n tags: string[],\n excludePublicKey: string | null,\n limit: number,\n offset: number\n ): Promise<Offer[]> {\n if (tags.length === 0) {\n return [];\n }\n\n // Build query with JSON tag matching (OR logic)\n // SQLite: Use json_each() to expand tags array and check if any tag matches\n const placeholders = tags.map(() => '?').join(',');\n\n let query = `\n SELECT DISTINCT o.* FROM offers o, json_each(o.tags) as t\n WHERE t.value IN (${placeholders})\n AND o.expires_at > ?\n AND o.answerer_public_key IS NULL\n `;\n\n const params: any[] = [...tags, Date.now()];\n\n if (excludePublicKey) {\n query += ' AND o.public_key != ?';\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY o.created_at DESC LIMIT ? OFFSET ?';\n params.push(limit, offset);\n\n const stmt = this.db.prepare(query);\n const rows = stmt.all(...params) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getRandomOffer(\n tags: string[],\n excludePublicKey: string | null\n ): Promise<Offer | null> {\n if (tags.length === 0) {\n return null;\n }\n\n // Build query with JSON tag matching (OR logic)\n const placeholders = tags.map(() => '?').join(',');\n\n let query = `\n SELECT DISTINCT o.* FROM offers o, json_each(o.tags) as t\n WHERE t.value IN (${placeholders})\n AND o.expires_at > ?\n AND o.answerer_public_key IS NULL\n `;\n\n const params: any[] = [...tags, Date.now()];\n\n if (excludePublicKey) {\n query += ' AND o.public_key != ?';\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY RANDOM() LIMIT 1';\n\n const stmt = this.db.prepare(query);\n const row = stmt.get(...params) as any;\n\n return row ? this.rowToOffer(row) : null;\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n publicKey: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n const stmt = this.db.prepare(`\n INSERT INTO ice_candidates (offer_id, public_key, role, candidate, created_at)\n VALUES (?, ?, ?, ?, ?)\n `);\n\n const baseTimestamp = Date.now();\n const transaction = this.db.transaction((candidates: any[]) => {\n for (let i = 0; i < candidates.length; i++) {\n stmt.run(\n offerId,\n publicKey,\n role,\n JSON.stringify(candidates[i]),\n baseTimestamp + i\n );\n }\n });\n\n transaction(candidates);\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n let query = `\n SELECT * FROM ice_candidates\n WHERE offer_id = ? AND role = ?\n `;\n\n const params: any[] = [offerId, targetRole];\n\n if (since !== undefined) {\n query += ' AND created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY created_at ASC';\n\n const stmt = this.db.prepare(query);\n const rows = stmt.all(...params) as any[];\n\n return rows.map(row => ({\n id: row.id,\n offerId: row.offer_id,\n publicKey: row.public_key,\n role: row.role,\n candidate: JSON.parse(row.candidate),\n createdAt: row.created_at,\n }));\n }\n\n async getIceCandidatesForMultipleOffers(\n offerIds: string[],\n publicKey: string,\n since?: number\n ): Promise<Map<string, IceCandidate[]>> {\n const result = new Map<string, IceCandidate[]>();\n\n // Return empty map if no offer IDs provided\n if (offerIds.length === 0) {\n return result;\n }\n\n // Validate array contains only strings\n if (!Array.isArray(offerIds) || !offerIds.every(id => typeof id === 'string')) {\n throw new Error('Invalid offer IDs: must be array of strings');\n }\n\n // Prevent DoS attacks from extremely large IN clauses\n if (offerIds.length > 1000) {\n throw new Error('Too many offer IDs (max 1000)');\n }\n\n // Build query that fetches candidates from the OTHER peer only\n // For each offer, determine if user is offerer or answerer and get opposite role\n const placeholders = offerIds.map(() => '?').join(',');\n\n let query = `\n SELECT ic.*, o.public_key as offer_public_key\n FROM ice_candidates ic\n INNER JOIN offers o ON o.id = ic.offer_id\n WHERE ic.offer_id IN (${placeholders})\n AND (\n (o.public_key = ? AND ic.role = 'answerer')\n OR (o.answerer_public_key = ? AND ic.role = 'offerer')\n )\n `;\n\n const params: any[] = [...offerIds, publicKey, publicKey];\n\n if (since !== undefined) {\n query += ' AND ic.created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY ic.created_at ASC';\n\n const stmt = this.db.prepare(query);\n const rows = stmt.all(...params) as any[];\n\n // Group candidates by offer_id\n for (const row of rows) {\n const candidate: IceCandidate = {\n id: row.id,\n offerId: row.offer_id,\n publicKey: row.public_key,\n role: row.role,\n candidate: JSON.parse(row.candidate),\n createdAt: row.created_at,\n };\n\n if (!result.has(row.offer_id)) {\n result.set(row.offer_id, []);\n }\n result.get(row.offer_id)!.push(candidate);\n }\n\n return result;\n }\n\n // ===== Rate Limiting =====\n\n async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {\n const now = Date.now();\n const resetTime = now + windowMs;\n\n // Atomic UPSERT: Insert or increment count, reset if expired\n // This prevents TOCTOU race conditions by doing check+increment in single operation\n const result = this.db.prepare(`\n INSERT INTO rate_limits (identifier, count, reset_time)\n VALUES (?, 1, ?)\n ON CONFLICT(identifier) DO UPDATE SET\n count = CASE\n WHEN reset_time < ? THEN 1\n ELSE count + 1\n END,\n reset_time = CASE\n WHEN reset_time < ? THEN ?\n ELSE reset_time\n END\n RETURNING count\n `).get(identifier, resetTime, now, now, resetTime) as { count: number };\n\n // Check if limit exceeded\n return result.count <= limit;\n }\n\n async deleteExpiredRateLimits(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM rate_limits WHERE reset_time < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n // ===== Nonce Tracking (Replay Protection) =====\n\n async checkAndMarkNonce(nonceKey: string, expiresAt: number): Promise<boolean> {\n try {\n // Atomic INSERT - if nonce already exists, this will fail with UNIQUE constraint\n // This prevents replay attacks by ensuring each nonce is only used once\n const stmt = this.db.prepare(`\n INSERT INTO nonces (nonce_key, expires_at)\n VALUES (?, ?)\n `);\n stmt.run(nonceKey, expiresAt);\n return true; // Nonce is new, request allowed\n } catch (error: any) {\n // SQLITE_CONSTRAINT error code for UNIQUE constraint violation\n if (error?.code === 'SQLITE_CONSTRAINT') {\n return false; // Nonce already used, replay attack detected\n }\n throw error; // Other errors should propagate\n }\n }\n\n async deleteExpiredNonces(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM nonces WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n async close(): Promise<void> {\n this.db.close();\n }\n\n // ===== Count Methods (for resource limits) =====\n\n async getOfferCount(): Promise<number> {\n const result = this.db.prepare('SELECT COUNT(*) as count FROM offers').get() as { count: number };\n return result.count;\n }\n\n async getOfferCountByPublicKey(publicKey: string): Promise<number> {\n const result = this.db.prepare('SELECT COUNT(*) as count FROM offers WHERE public_key = ?').get(publicKey) as { count: number };\n return result.count;\n }\n\n async getIceCandidateCount(offerId: string): Promise<number> {\n const result = this.db.prepare('SELECT COUNT(*) as count FROM ice_candidates WHERE offer_id = ?').get(offerId) as { count: number };\n return result.count;\n }\n\n // ===== Helper Methods =====\n\n /**\n * Helper method to convert database row to Offer object\n */\n private rowToOffer(row: any): Offer {\n return {\n id: row.id,\n publicKey: row.public_key,\n tags: JSON.parse(row.tags),\n sdp: row.sdp,\n createdAt: row.created_at,\n expiresAt: row.expires_at,\n lastSeen: row.last_seen,\n answererPublicKey: row.answerer_public_key || undefined,\n answerSdp: row.answer_sdp || undefined,\n answeredAt: row.answered_at || undefined,\n matchedTags: row.matched_tags ? JSON.parse(row.matched_tags) : undefined,\n };\n }\n}\n", "import mysql, { Pool, RowDataPacket, ResultSetHeader } from 'mysql2/promise';\nimport {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\n\n/**\n * MySQL storage adapter for rondevu signaling system\n * Uses Ed25519 public key as identity (no usernames, no secrets)\n */\nexport class MySQLStorage implements Storage {\n private pool: Pool;\n\n private constructor(pool: Pool) {\n this.pool = pool;\n }\n\n /**\n * Creates a new MySQL storage instance with connection pooling\n * @param connectionString MySQL connection URL\n * @param poolSize Maximum number of connections in the pool\n */\n static async create(connectionString: string, poolSize: number = 10): Promise<MySQLStorage> {\n const pool = mysql.createPool({\n uri: connectionString,\n waitForConnections: true,\n connectionLimit: poolSize,\n queueLimit: 0,\n enableKeepAlive: true,\n keepAliveInitialDelay: 10000,\n });\n\n const storage = new MySQLStorage(pool);\n await storage.initializeDatabase();\n return storage;\n }\n\n private async initializeDatabase(): Promise<void> {\n const conn = await this.pool.getConnection();\n try {\n await conn.query(`\n CREATE TABLE IF NOT EXISTS identities (\n public_key CHAR(64) PRIMARY KEY,\n created_at BIGINT NOT NULL,\n expires_at BIGINT NOT NULL,\n last_used BIGINT NOT NULL,\n INDEX idx_identities_expires (expires_at)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n\n await conn.query(`\n CREATE TABLE IF NOT EXISTS offers (\n id VARCHAR(64) PRIMARY KEY,\n public_key CHAR(64) NOT NULL,\n tags JSON NOT NULL,\n sdp MEDIUMTEXT NOT NULL,\n created_at BIGINT NOT NULL,\n expires_at BIGINT NOT NULL,\n last_seen BIGINT NOT NULL,\n answerer_public_key CHAR(64),\n answer_sdp MEDIUMTEXT,\n answered_at BIGINT,\n matched_tags JSON,\n INDEX idx_offers_public_key (public_key),\n INDEX idx_offers_expires (expires_at),\n INDEX idx_offers_last_seen (last_seen),\n INDEX idx_offers_answerer (answerer_public_key),\n FOREIGN KEY (public_key) REFERENCES identities(public_key) ON DELETE CASCADE\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n\n await conn.query(`\n CREATE TABLE IF NOT EXISTS ice_candidates (\n id BIGINT AUTO_INCREMENT PRIMARY KEY,\n offer_id VARCHAR(64) NOT NULL,\n public_key CHAR(64) NOT NULL,\n role ENUM('offerer', 'answerer') NOT NULL,\n candidate JSON NOT NULL,\n created_at BIGINT NOT NULL,\n INDEX idx_ice_offer (offer_id),\n INDEX idx_ice_public_key (public_key),\n INDEX idx_ice_created (created_at),\n FOREIGN KEY (offer_id) REFERENCES offers(id) ON DELETE CASCADE\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n\n await conn.query(`\n CREATE TABLE IF NOT EXISTS rate_limits (\n identifier VARCHAR(255) PRIMARY KEY,\n count INT NOT NULL,\n reset_time BIGINT NOT NULL,\n INDEX idx_rate_limits_reset (reset_time)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n\n await conn.query(`\n CREATE TABLE IF NOT EXISTS nonces (\n nonce_key VARCHAR(255) PRIMARY KEY,\n expires_at BIGINT NOT NULL,\n INDEX idx_nonces_expires (expires_at)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n `);\n } finally {\n conn.release();\n }\n }\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n if (offers.length === 0) return [];\n\n const created: Offer[] = [];\n const now = Date.now();\n\n const conn = await this.pool.getConnection();\n try {\n await conn.beginTransaction();\n\n for (const request of offers) {\n const id = request.id || await generateOfferHash(request.sdp);\n\n await conn.query(\n `INSERT INTO offers (id, public_key, tags, sdp, created_at, expires_at, last_seen)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n [id, request.publicKey, JSON.stringify(request.tags), request.sdp, now, request.expiresAt, now]\n );\n\n created.push({\n id,\n publicKey: request.publicKey,\n tags: request.tags,\n sdp: request.sdp,\n createdAt: now,\n expiresAt: request.expiresAt,\n lastSeen: now,\n });\n }\n\n await conn.commit();\n } catch (error) {\n await conn.rollback();\n throw error;\n } finally {\n conn.release();\n }\n\n return created;\n }\n\n async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT * FROM offers WHERE public_key = ? AND expires_at > ? ORDER BY last_seen DESC`,\n [publicKey, Date.now()]\n );\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT * FROM offers WHERE id = ? AND expires_at > ?`,\n [offerId, Date.now()]\n );\n return rows.length > 0 ? this.rowToOffer(rows[0]) : null;\n }\n\n async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {\n const [result] = await this.pool.query<ResultSetHeader>(\n `DELETE FROM offers WHERE id = ? AND public_key = ?`,\n [offerId, ownerPublicKey]\n );\n return result.affectedRows > 0;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n const [result] = await this.pool.query<ResultSetHeader>(\n `DELETE FROM offers WHERE expires_at < ?`,\n [now]\n );\n return result.affectedRows;\n }\n\n async answerOffer(\n offerId: string,\n answererPublicKey: string,\n answerSdp: string,\n matchedTags?: string[]\n ): Promise<{ success: boolean; error?: string }> {\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return { success: false, error: 'Offer not found or expired' };\n }\n\n if (offer.answererPublicKey) {\n return { success: false, error: 'Offer already answered' };\n }\n\n const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;\n const [result] = await this.pool.query<ResultSetHeader>(\n `UPDATE offers SET answerer_public_key = ?, answer_sdp = ?, answered_at = ?, matched_tags = ?\n WHERE id = ? AND answerer_public_key IS NULL`,\n [answererPublicKey, answerSdp, Date.now(), matchedTagsJson, offerId]\n );\n\n if (result.affectedRows === 0) {\n return { success: false, error: 'Offer already answered (race condition)' };\n }\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT * FROM offers\n WHERE public_key = ? AND answerer_public_key IS NOT NULL AND expires_at > ?\n ORDER BY answered_at DESC`,\n [offererPublicKey, Date.now()]\n );\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT * FROM offers\n WHERE answerer_public_key = ? AND expires_at > ?\n ORDER BY answered_at DESC`,\n [answererPublicKey, Date.now()]\n );\n return rows.map(row => this.rowToOffer(row));\n }\n\n // ===== Discovery =====\n\n async discoverOffers(\n tags: string[],\n excludePublicKey: string | null,\n limit: number,\n offset: number\n ): Promise<Offer[]> {\n if (tags.length === 0) return [];\n\n const tagArray = JSON.stringify(tags);\n\n let query = `\n SELECT DISTINCT o.* FROM offers o\n WHERE JSON_OVERLAPS(o.tags, ?)\n AND o.expires_at > ?\n AND o.answerer_public_key IS NULL\n `;\n const params: any[] = [tagArray, Date.now()];\n\n if (excludePublicKey) {\n query += ' AND o.public_key != ?';\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY o.created_at DESC LIMIT ? OFFSET ?';\n params.push(limit, offset);\n\n const [rows] = await this.pool.query<RowDataPacket[]>(query, params);\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getRandomOffer(\n tags: string[],\n excludePublicKey: string | null\n ): Promise<Offer | null> {\n if (tags.length === 0) return null;\n\n const tagArray = JSON.stringify(tags);\n\n let query = `\n SELECT DISTINCT o.* FROM offers o\n WHERE JSON_OVERLAPS(o.tags, ?)\n AND o.expires_at > ?\n AND o.answerer_public_key IS NULL\n `;\n const params: any[] = [tagArray, Date.now()];\n\n if (excludePublicKey) {\n query += ' AND o.public_key != ?';\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY RAND() LIMIT 1';\n\n const [rows] = await this.pool.query<RowDataPacket[]>(query, params);\n return rows.length > 0 ? this.rowToOffer(rows[0]) : null;\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n publicKey: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n if (candidates.length === 0) return 0;\n\n const baseTimestamp = Date.now();\n const values = candidates.map((c, i) => [\n offerId,\n publicKey,\n role,\n JSON.stringify(c),\n baseTimestamp + i,\n ]);\n\n await this.pool.query(\n `INSERT INTO ice_candidates (offer_id, public_key, role, candidate, created_at)\n VALUES ?`,\n [values]\n );\n\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n let query = `SELECT * FROM ice_candidates WHERE offer_id = ? AND role = ?`;\n const params: any[] = [offerId, targetRole];\n\n if (since !== undefined) {\n query += ' AND created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY created_at ASC';\n\n const [rows] = await this.pool.query<RowDataPacket[]>(query, params);\n return rows.map(row => this.rowToIceCandidate(row));\n }\n\n async getIceCandidatesForMultipleOffers(\n offerIds: string[],\n publicKey: string,\n since?: number\n ): Promise<Map<string, IceCandidate[]>> {\n const result = new Map<string, IceCandidate[]>();\n\n if (offerIds.length === 0) return result;\n if (offerIds.length > 1000) {\n throw new Error('Too many offer IDs (max 1000)');\n }\n\n const placeholders = offerIds.map(() => '?').join(',');\n\n let query = `\n SELECT ic.*, o.public_key as offer_public_key\n FROM ice_candidates ic\n INNER JOIN offers o ON o.id = ic.offer_id\n WHERE ic.offer_id IN (${placeholders})\n AND (\n (o.public_key = ? AND ic.role = 'answerer')\n OR (o.answerer_public_key = ? AND ic.role = 'offerer')\n )\n `;\n const params: any[] = [...offerIds, publicKey, publicKey];\n\n if (since !== undefined) {\n query += ' AND ic.created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY ic.created_at ASC';\n\n const [rows] = await this.pool.query<RowDataPacket[]>(query, params);\n\n for (const row of rows) {\n const candidate = this.rowToIceCandidate(row);\n if (!result.has(row.offer_id)) {\n result.set(row.offer_id, []);\n }\n result.get(row.offer_id)!.push(candidate);\n }\n\n return result;\n }\n\n // ===== Rate Limiting =====\n\n async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {\n const now = Date.now();\n const resetTime = now + windowMs;\n\n await this.pool.query(\n `INSERT INTO rate_limits (identifier, count, reset_time)\n VALUES (?, 1, ?)\n ON DUPLICATE KEY UPDATE\n count = IF(reset_time < ?, 1, count + 1),\n reset_time = IF(reset_time < ?, ?, reset_time)`,\n [identifier, resetTime, now, now, resetTime]\n );\n\n const [rows] = await this.pool.query<RowDataPacket[]>(\n `SELECT count FROM rate_limits WHERE identifier = ?`,\n [identifier]\n );\n\n return rows.length > 0 && rows[0].count <= limit;\n }\n\n async deleteExpiredRateLimits(now: number): Promise<number> {\n const [result] = await this.pool.query<ResultSetHeader>(\n `DELETE FROM rate_limits WHERE reset_time < ?`,\n [now]\n );\n return result.affectedRows;\n }\n\n // ===== Nonce Tracking (Replay Protection) =====\n\n async checkAndMarkNonce(nonceKey: string, expiresAt: number): Promise<boolean> {\n try {\n await this.pool.query(\n `INSERT INTO nonces (nonce_key, expires_at) VALUES (?, ?)`,\n [nonceKey, expiresAt]\n );\n return true;\n } catch (error: any) {\n if (error.code === 'ER_DUP_ENTRY') {\n return false;\n }\n throw error;\n }\n }\n\n async deleteExpiredNonces(now: number): Promise<number> {\n const [result] = await this.pool.query<ResultSetHeader>(\n `DELETE FROM nonces WHERE expires_at < ?`,\n [now]\n );\n return result.affectedRows;\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n // ===== Count Methods (for resource limits) =====\n\n async getOfferCount(): Promise<number> {\n const [rows] = await this.pool.query<RowDataPacket[]>('SELECT COUNT(*) as count FROM offers');\n return Number(rows[0].count);\n }\n\n async getOfferCountByPublicKey(publicKey: string): Promise<number> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n 'SELECT COUNT(*) as count FROM offers WHERE public_key = ?',\n [publicKey]\n );\n return Number(rows[0].count);\n }\n\n async getIceCandidateCount(offerId: string): Promise<number> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n 'SELECT COUNT(*) as count FROM ice_candidates WHERE offer_id = ?',\n [offerId]\n );\n return Number(rows[0].count);\n }\n\n // ===== Helper Methods =====\n\n private rowToOffer(row: RowDataPacket): Offer {\n return {\n id: row.id,\n publicKey: row.public_key,\n tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : row.tags,\n sdp: row.sdp,\n createdAt: Number(row.created_at),\n expiresAt: Number(row.expires_at),\n lastSeen: Number(row.last_seen),\n answererPublicKey: row.answerer_public_key || undefined,\n answerSdp: row.answer_sdp || undefined,\n answeredAt: row.answered_at ? Number(row.answered_at) : undefined,\n matchedTags: row.matched_tags ? (typeof row.matched_tags === 'string' ? JSON.parse(row.matched_tags) : row.matched_tags) : undefined,\n };\n }\n\n private rowToIceCandidate(row: RowDataPacket): IceCandidate {\n return {\n id: Number(row.id),\n offerId: row.offer_id,\n publicKey: row.public_key,\n role: row.role as 'offerer' | 'answerer',\n candidate: typeof row.candidate === 'string' ? JSON.parse(row.candidate) : row.candidate,\n createdAt: Number(row.created_at),\n };\n }\n}\n", "import { Pool } from 'pg';\nimport {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\n\n/**\n * PostgreSQL storage adapter for rondevu signaling system\n * Uses Ed25519 public key as identity (no usernames, no secrets)\n */\nexport class PostgreSQLStorage implements Storage {\n private pool: Pool;\n\n private constructor(pool: Pool) {\n this.pool = pool;\n }\n\n /**\n * Creates a new PostgreSQL storage instance with connection pooling\n * @param connectionString PostgreSQL connection URL\n * @param poolSize Maximum number of connections in the pool\n */\n static async create(connectionString: string, poolSize: number = 10): Promise<PostgreSQLStorage> {\n const pool = new Pool({\n connectionString,\n max: poolSize,\n idleTimeoutMillis: 30000,\n connectionTimeoutMillis: 5000,\n });\n\n const storage = new PostgreSQLStorage(pool);\n await storage.initializeDatabase();\n return storage;\n }\n\n private async initializeDatabase(): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(`\n CREATE TABLE IF NOT EXISTS identities (\n public_key CHAR(64) PRIMARY KEY,\n created_at BIGINT NOT NULL,\n expires_at BIGINT NOT NULL,\n last_used BIGINT NOT NULL\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_identities_expires ON identities(expires_at)`);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS offers (\n id VARCHAR(64) PRIMARY KEY,\n public_key CHAR(64) NOT NULL REFERENCES identities(public_key) ON DELETE CASCADE,\n tags JSONB NOT NULL,\n sdp TEXT NOT NULL,\n created_at BIGINT NOT NULL,\n expires_at BIGINT NOT NULL,\n last_seen BIGINT NOT NULL,\n answerer_public_key CHAR(64),\n answer_sdp TEXT,\n answered_at BIGINT,\n matched_tags JSONB\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_public_key ON offers(public_key)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_expires ON offers(expires_at)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_last_seen ON offers(last_seen)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_answerer ON offers(answerer_public_key)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_offers_tags ON offers USING GIN(tags)`);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS ice_candidates (\n id BIGSERIAL PRIMARY KEY,\n offer_id VARCHAR(64) NOT NULL REFERENCES offers(id) ON DELETE CASCADE,\n public_key CHAR(64) NOT NULL,\n role VARCHAR(8) NOT NULL CHECK (role IN ('offerer', 'answerer')),\n candidate JSONB NOT NULL,\n created_at BIGINT NOT NULL\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_ice_offer ON ice_candidates(offer_id)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_ice_public_key ON ice_candidates(public_key)`);\n await client.query(`CREATE INDEX IF NOT EXISTS idx_ice_created ON ice_candidates(created_at)`);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS rate_limits (\n identifier VARCHAR(255) PRIMARY KEY,\n count INT NOT NULL,\n reset_time BIGINT NOT NULL\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_rate_limits_reset ON rate_limits(reset_time)`);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS nonces (\n nonce_key VARCHAR(255) PRIMARY KEY,\n expires_at BIGINT NOT NULL\n )\n `);\n\n await client.query(`CREATE INDEX IF NOT EXISTS idx_nonces_expires ON nonces(expires_at)`);\n } finally {\n client.release();\n }\n }\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n if (offers.length === 0) return [];\n\n const created: Offer[] = [];\n const now = Date.now();\n\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n\n for (const request of offers) {\n const id = request.id || await generateOfferHash(request.sdp);\n\n await client.query(\n `INSERT INTO offers (id, public_key, tags, sdp, created_at, expires_at, last_seen)\n VALUES ($1, $2, $3, $4, $5, $6, $7)`,\n [id, request.publicKey, JSON.stringify(request.tags), request.sdp, now, request.expiresAt, now]\n );\n\n created.push({\n id,\n publicKey: request.publicKey,\n tags: request.tags,\n sdp: request.sdp,\n createdAt: now,\n expiresAt: request.expiresAt,\n lastSeen: now,\n });\n }\n\n await client.query('COMMIT');\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n\n return created;\n }\n\n async getOffersByPublicKey(publicKey: string): Promise<Offer[]> {\n const result = await this.pool.query(\n `SELECT * FROM offers WHERE public_key = $1 AND expires_at > $2 ORDER BY last_seen DESC`,\n [publicKey, Date.now()]\n );\n return result.rows.map(row => this.rowToOffer(row));\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const result = await this.pool.query(\n `SELECT * FROM offers WHERE id = $1 AND expires_at > $2`,\n [offerId, Date.now()]\n );\n return result.rows.length > 0 ? this.rowToOffer(result.rows[0]) : null;\n }\n\n async deleteOffer(offerId: string, ownerPublicKey: string): Promise<boolean> {\n const result = await this.pool.query(\n `DELETE FROM offers WHERE id = $1 AND public_key = $2`,\n [offerId, ownerPublicKey]\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n const result = await this.pool.query(\n `DELETE FROM offers WHERE expires_at < $1`,\n [now]\n );\n return result.rowCount ?? 0;\n }\n\n async answerOffer(\n offerId: string,\n answererPublicKey: string,\n answerSdp: string,\n matchedTags?: string[]\n ): Promise<{ success: boolean; error?: string }> {\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return { success: false, error: 'Offer not found or expired' };\n }\n\n if (offer.answererPublicKey) {\n return { success: false, error: 'Offer already answered' };\n }\n\n const matchedTagsJson = matchedTags ? JSON.stringify(matchedTags) : null;\n const result = await this.pool.query(\n `UPDATE offers SET answerer_public_key = $1, answer_sdp = $2, answered_at = $3, matched_tags = $4\n WHERE id = $5 AND answerer_public_key IS NULL`,\n [answererPublicKey, answerSdp, Date.now(), matchedTagsJson, offerId]\n );\n\n if ((result.rowCount ?? 0) === 0) {\n return { success: false, error: 'Offer already answered (race condition)' };\n }\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererPublicKey: string): Promise<Offer[]> {\n const result = await this.pool.query(\n `SELECT * FROM offers\n WHERE public_key = $1 AND answerer_public_key IS NOT NULL AND expires_at > $2\n ORDER BY answered_at DESC`,\n [offererPublicKey, Date.now()]\n );\n return result.rows.map(row => this.rowToOffer(row));\n }\n\n async getOffersAnsweredBy(answererPublicKey: string): Promise<Offer[]> {\n const result = await this.pool.query(\n `SELECT * FROM offers\n WHERE answerer_public_key = $1 AND expires_at > $2\n ORDER BY answered_at DESC`,\n [answererPublicKey, Date.now()]\n );\n return result.rows.map(row => this.rowToOffer(row));\n }\n\n // ===== Discovery =====\n\n async discoverOffers(\n tags: string[],\n excludePublicKey: string | null,\n limit: number,\n offset: number\n ): Promise<Offer[]> {\n if (tags.length === 0) return [];\n\n let query = `\n SELECT DISTINCT o.* FROM offers o\n WHERE o.tags ?| $1\n AND o.expires_at > $2\n AND o.answerer_public_key IS NULL\n `;\n const params: any[] = [tags, Date.now()];\n let paramIndex = 3;\n\n if (excludePublicKey) {\n query += ` AND o.public_key != $${paramIndex}`;\n params.push(excludePublicKey);\n paramIndex++;\n }\n\n query += ` ORDER BY o.created_at DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;\n params.push(limit, offset);\n\n const result = await this.pool.query(query, params);\n return result.rows.map(row => this.rowToOffer(row));\n }\n\n async getRandomOffer(\n tags: string[],\n excludePublicKey: string | null\n ): Promise<Offer | null> {\n if (tags.length === 0) return null;\n\n let query = `\n SELECT DISTINCT o.* FROM offers o\n WHERE o.tags ?| $1\n AND o.expires_at > $2\n AND o.answerer_public_key IS NULL\n `;\n const params: any[] = [tags, Date.now()];\n let paramIndex = 3;\n\n if (excludePublicKey) {\n query += ` AND o.public_key != $${paramIndex}`;\n params.push(excludePublicKey);\n }\n\n query += ' ORDER BY RANDOM() LIMIT 1';\n\n const result = await this.pool.query(query, params);\n return result.rows.length > 0 ? this.rowToOffer(result.rows[0]) : null;\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n publicKey: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n if (candidates.length === 0) return 0;\n\n const baseTimestamp = Date.now();\n const client = await this.pool.connect();\n\n try {\n await client.query('BEGIN');\n\n for (let i = 0; i < candidates.length; i++) {\n await client.query(\n `INSERT INTO ice_candidates (offer_id, public_key, role, candidate, created_at)\n VALUES ($1, $2, $3, $4, $5)`,\n [offerId, publicKey, role, JSON.stringify(candidates[i]), baseTimestamp + i]\n );\n }\n\n await client.query('COMMIT');\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n let query = `SELECT * FROM ice_candidates WHERE offer_id = $1 AND role = $2`;\n const params: any[] = [offerId, targetRole];\n\n if (since !== undefined) {\n query += ' AND created_at > $3';\n params.push(since);\n }\n\n query += ' ORDER BY created_at ASC';\n\n const result = await this.pool.query(query, params);\n return result.rows.map(row => this.rowToIceCandidate(row));\n }\n\n async getIceCandidatesForMultipleOffers(\n offerIds: string[],\n publicKey: string,\n since?: number\n ): Promise<Map<string, IceCandidate[]>> {\n const resultMap = new Map<string, IceCandidate[]>();\n\n if (offerIds.length === 0) return resultMap;\n if (offerIds.length > 1000) {\n throw new Error('Too many offer IDs (max 1000)');\n }\n\n let query = `\n SELECT ic.*, o.public_key as offer_public_key\n FROM ice_candidates ic\n INNER JOIN offers o ON o.id = ic.offer_id\n WHERE ic.offer_id = ANY($1)\n AND (\n (o.public_key = $2 AND ic.role = 'answerer')\n OR (o.answerer_public_key = $2 AND ic.role = 'offerer')\n )\n `;\n const params: any[] = [offerIds, publicKey];\n\n if (since !== undefined) {\n query += ' AND ic.created_at > $3';\n params.push(since);\n }\n\n query += ' ORDER BY ic.created_at ASC';\n\n const result = await this.pool.query(query, params);\n\n for (const row of result.rows) {\n const candidate = this.rowToIceCandidate(row);\n if (!resultMap.has(row.offer_id)) {\n resultMap.set(row.offer_id, []);\n }\n resultMap.get(row.offer_id)!.push(candidate);\n }\n\n return resultMap;\n }\n\n // ===== Rate Limiting =====\n\n async checkRateLimit(identifier: string, limit: number, windowMs: number): Promise<boolean> {\n const now = Date.now();\n const resetTime = now + windowMs;\n\n const result = await this.pool.query(\n `INSERT INTO rate_limits (identifier, count, reset_time)\n VALUES ($1, 1, $2)\n ON CONFLICT (identifier) DO UPDATE SET\n count = CASE\n WHEN rate_limits.reset_time < $3 THEN 1\n ELSE rate_limits.count + 1\n END,\n reset_time = CASE\n WHEN rate_limits.reset_time < $3 THEN $2\n ELSE rate_limits.reset_time\n END\n RETURNING count`,\n [identifier, resetTime, now]\n );\n\n return result.rows[0].count <= limit;\n }\n\n async deleteExpiredRateLimits(now: number): Promise<number> {\n const result = await this.pool.query(\n `DELETE FROM rate_limits WHERE reset_time < $1`,\n [now]\n );\n return result.rowCount ?? 0;\n }\n\n // ===== Nonce Tracking (Replay Protection) =====\n\n async checkAndMarkNonce(nonceKey: string, expiresAt: number): Promise<boolean> {\n try {\n await this.pool.query(\n `INSERT INTO nonces (nonce_key, expires_at) VALUES ($1, $2)`,\n [nonceKey, expiresAt]\n );\n return true;\n } catch (error: any) {\n if (error.code === '23505') {\n return false;\n }\n throw error;\n }\n }\n\n async deleteExpiredNonces(now: number): Promise<number> {\n const result = await this.pool.query(\n `DELETE FROM nonces WHERE expires_at < $1`,\n [now]\n );\n return result.rowCount ?? 0;\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n // ===== Count Methods (for resource limits) =====\n\n async getOfferCount(): Promise<number> {\n const result = await this.pool.query('SELECT COUNT(*) as count FROM offers');\n return Number(result.rows[0].count);\n }\n\n async getOfferCountByPublicKey(publicKey: string): Promise<number> {\n const result = await this.pool.query(\n 'SELECT COUNT(*) as count FROM offers WHERE public_key = $1',\n [publicKey]\n );\n return Number(result.rows[0].count);\n }\n\n async getIceCandidateCount(offerId: string): Promise<number> {\n const result = await this.pool.query(\n 'SELECT COUNT(*) as count FROM ice_candidates WHERE offer_id = $1',\n [offerId]\n );\n return Number(result.rows[0].count);\n }\n\n // ===== Helper Methods =====\n\n private rowToOffer(row: any): Offer {\n return {\n id: row.id,\n publicKey: row.public_key.trim(),\n tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : row.tags,\n sdp: row.sdp,\n createdAt: Number(row.created_at),\n expiresAt: Number(row.expires_at),\n lastSeen: Number(row.last_seen),\n answererPublicKey: row.answerer_public_key?.trim() || undefined,\n answerSdp: row.answer_sdp || undefined,\n answeredAt: row.answered_at ? Number(row.answered_at) : undefined,\n matchedTags: row.matched_tags || undefined,\n };\n }\n\n private rowToIceCandidate(row: any): IceCandidate {\n return {\n id: Number(row.id),\n offerId: row.offer_id,\n publicKey: row.public_key.trim(),\n role: row.role as 'offerer' | 'answerer',\n candidate: typeof row.candidate === 'string' ? JSON.parse(row.candidate) : row.candidate,\n createdAt: Number(row.created_at),\n };\n }\n}\n", "import { serve } from '@hono/node-server';\nimport { createApp } from './app.ts';\nimport { loadConfig, runCleanup } from './config.ts';\nimport { createStorage } from './storage/factory.ts';\nimport { Storage } from './storage/types.ts';\n\nasync function main() {\n const config = loadConfig();\n\n console.log('Starting Rondevu server...');\n console.log('Configuration:', {\n port: config.port,\n storageType: config.storageType,\n storagePath: config.storageType === 'sqlite' ? config.storagePath : undefined,\n databaseUrl: config.databaseUrl ? '[configured]' : undefined,\n dbPoolSize: ['mysql', 'postgres'].includes(config.storageType) ? config.dbPoolSize : undefined,\n offerDefaultTtl: `${config.offerDefaultTtl}ms`,\n cleanupInterval: `${config.cleanupInterval}ms`,\n version: config.version,\n });\n\n const storage: Storage = await createStorage({\n type: config.storageType,\n sqlitePath: config.storagePath,\n connectionString: config.databaseUrl,\n poolSize: config.dbPoolSize,\n });\n console.log(`Using ${config.storageType} storage`);\n\n // Periodic cleanup\n const cleanupTimer = setInterval(async () => {\n try {\n const result = await runCleanup(storage, Date.now());\n const total = result.offers + result.rateLimits + result.nonces;\n if (total > 0) {\n console.log(`Cleanup: ${result.offers} offers, ${result.rateLimits} rate limits, ${result.nonces} nonces`);\n }\n } catch (err) {\n console.error('Cleanup error:', err);\n }\n }, config.cleanupInterval);\n\n const app = createApp(storage, config);\n\n const server = serve({\n fetch: app.fetch,\n port: config.port,\n });\n\n console.log(`Server running on http://localhost:${config.port}`);\n console.log('Ready to accept connections');\n\n // Graceful shutdown handler\n const shutdown = async () => {\n console.log('\\nShutting down gracefully...');\n clearInterval(cleanupTimer);\n await storage.close();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n", "import { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { Storage } from './storage/types.ts';\nimport { Config } from './config.ts';\nimport { handleRpc, RpcRequest } from './rpc.ts';\n\n/**\n * Creates the Hono application with RPC interface\n */\nexport function createApp(storage: Storage, config: Config) {\n const app = new Hono();\n\n // Enable CORS\n app.use('/*', cors({\n origin: (origin) => {\n if (config.corsOrigins.length === 1 && config.corsOrigins[0] === '*') {\n return origin;\n }\n if (config.corsOrigins.includes(origin)) {\n return origin;\n }\n return config.corsOrigins[0];\n },\n allowMethods: ['GET', 'POST', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Origin', 'X-PublicKey', 'X-Timestamp', 'X-Nonce', 'X-Signature'],\n exposeHeaders: ['Content-Type'],\n credentials: false,\n maxAge: 86400,\n }));\n\n // Root endpoint - server info\n app.get('/', (c) => {\n return c.json({\n version: config.version,\n name: 'Rondevu',\n description: 'WebRTC signaling with RPC interface and Ed25519 signature-based authentication',\n }, 200);\n });\n\n // Health check\n app.get('/health', (c) => {\n return c.json({\n status: 'ok',\n timestamp: Date.now(),\n version: config.version,\n }, 200);\n });\n\n /**\n * POST /rpc\n * RPC endpoint - accepts batch method calls only\n */\n app.post('/rpc', async (c) => {\n try {\n const body = await c.req.json();\n\n // Only accept batch arrays\n if (!Array.isArray(body)) {\n return c.json([{\n success: false,\n error: 'Request must be an array of RPC calls',\n errorCode: 'INVALID_PARAMS'\n }], 400);\n }\n\n const requests: RpcRequest[] = body;\n\n // Validate requests\n if (requests.length === 0) {\n return c.json([{\n success: false,\n error: 'Empty request array',\n errorCode: 'INVALID_PARAMS'\n }], 400);\n }\n\n if (requests.length > config.maxBatchSize) {\n return c.json([{\n success: false,\n error: `Too many requests in batch (max ${config.maxBatchSize})`,\n errorCode: 'BATCH_TOO_LARGE'\n }], 413); // 413 Payload Too Large\n }\n\n // Handle RPC (pass context for auth headers)\n const responses = await handleRpc(requests, c, storage, config);\n\n // Always return array\n return c.json(responses, 200);\n } catch (err) {\n console.error('RPC error:', err);\n\n // Distinguish between JSON parse errors and validation errors\n const errorMsg = err instanceof SyntaxError\n ? 'Invalid JSON in request body'\n : 'Request must be valid JSON array';\n\n return c.json([{\n success: false,\n error: errorMsg,\n errorCode: 'INVALID_PARAMS'\n }], 400);\n }\n });\n\n // 404 for all other routes\n app.all('*', (c) => {\n return c.json({\n error: 'Not found. Use POST /rpc for all API calls.',\n }, 404);\n });\n\n return app;\n}\n", "/**\n * Crypto utilities for Ed25519 signature verification and validation\n * Uses @noble/ed25519 for Ed25519 operations and Web Crypto API for SHA-512\n */\n\nimport { Buffer } from 'node:buffer';\nimport * as ed from '@noble/ed25519';\n\n// Configure @noble/ed25519 to use Web Crypto API's SHA-512\n// Required for both Node.js and Cloudflare Workers compatibility\ned.hashes.sha512Async = async (message: Uint8Array): Promise<Uint8Array> => {\n const hashBuffer = await crypto.subtle.digest('SHA-512', message);\n return new Uint8Array(hashBuffer);\n};\n\n/**\n * Convert hex string to byte array with validation\n */\nfunction hexToBytes(hex: string): Uint8Array {\n if (hex.length % 2 !== 0) {\n throw new Error('Hex string must have even length');\n }\n\n for (let i = 0; i < hex.length; i++) {\n const c = hex[i].toLowerCase();\n if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) {\n throw new Error(`Invalid hex character at position ${i}: '${hex[i]}'`);\n }\n }\n\n const match = hex.match(/.{1,2}/g);\n if (!match) {\n throw new Error('Invalid hex string format');\n }\n\n return new Uint8Array(match.map(byte => {\n const parsed = parseInt(byte, 16);\n if (isNaN(parsed)) {\n throw new Error(`Invalid hex byte: ${byte}`);\n }\n return parsed;\n }));\n}\n\n/**\n * Canonical JSON serialization with sorted keys.\n * Ensures deterministic output regardless of property insertion order.\n * Must match client's canonicalJSON implementation exactly.\n */\nfunction canonicalJSON(obj: any, depth: number = 0): string {\n const MAX_DEPTH = 100;\n\n if (depth > MAX_DEPTH) {\n throw new Error('Object nesting too deep for canonicalization');\n }\n\n if (obj === null) return 'null';\n if (obj === undefined) return JSON.stringify(undefined);\n\n const type = typeof obj;\n\n if (type === 'function') throw new Error('Functions are not supported in RPC parameters');\n if (type === 'symbol' || type === 'bigint') throw new Error(`${type} is not supported in RPC parameters`);\n if (type === 'number' && !Number.isFinite(obj)) throw new Error('NaN and Infinity are not supported in RPC parameters');\n\n if (type !== 'object') return JSON.stringify(obj);\n\n if (Array.isArray(obj)) {\n return '[' + obj.map(item => canonicalJSON(item, depth + 1)).join(',') + ']';\n }\n\n const sortedKeys = Object.keys(obj).sort();\n const pairs = sortedKeys.map(key => JSON.stringify(key) + ':' + canonicalJSON(obj[key], depth + 1));\n return '{' + pairs.join(',') + '}';\n}\n\n/**\n * Build the message string for signing.\n * Format: timestamp:nonce:method:canonicalJSON(params || {})\n */\nexport function buildSignatureMessage(timestamp: number, nonce: string, method: string, params?: any): string {\n // Validate nonce is UUID v4 format\n if (nonce.length !== 36) {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n\n if (nonce[8] !== '-' || nonce[13] !== '-' || nonce[18] !== '-' || nonce[23] !== '-') {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n\n if (nonce[14] !== '4') {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n\n const variant = nonce[19].toLowerCase();\n if (variant !== '8' && variant !== '9' && variant !== 'a' && variant !== 'b') {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n\n const hexChars = nonce.replace(/-/g, '');\n for (let i = 0; i < hexChars.length; i++) {\n const c = hexChars[i].toLowerCase();\n if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) {\n throw new Error('Nonce must be a valid UUID v4 (use crypto.randomUUID())');\n }\n }\n\n const paramsStr = canonicalJSON(params || {});\n return `${timestamp}:${nonce}:${method}:${paramsStr}`;\n}\n\n// ===== Ed25519 Public Key Identity =====\n\nconst ED25519_PUBLIC_KEY_LENGTH = 32; // 32 bytes = 64 hex chars\nconst ED25519_SIGNATURE_LENGTH = 64; // 64 bytes\n\n/**\n * Validates an Ed25519 public key format.\n * @param publicKey 64-character lowercase hex string (32 bytes)\n */\nexport function validatePublicKey(publicKey: string): { valid: boolean; error?: string } {\n if (typeof publicKey !== 'string') {\n return { valid: false, error: 'Public key must be a string' };\n }\n\n if (publicKey.length !== ED25519_PUBLIC_KEY_LENGTH * 2) {\n return { valid: false, error: `Public key must be ${ED25519_PUBLIC_KEY_LENGTH * 2} hex characters (${ED25519_PUBLIC_KEY_LENGTH} bytes)` };\n }\n\n for (let i = 0; i < publicKey.length; i++) {\n const c = publicKey[i];\n if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) {\n return { valid: false, error: `Invalid hex character at position ${i}: '${c}' (use lowercase hex)` };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Verify an Ed25519 signature.\n * @param publicKey Signer's public key (64-char hex)\n * @param message Message that was signed\n * @param signature Signature to verify (base64 encoded)\n */\nexport async function verifyEd25519Signature(\n publicKey: string,\n message: string,\n signature: string\n): Promise<boolean> {\n try {\n const pkValidation = validatePublicKey(publicKey);\n if (!pkValidation.valid) {\n console.error('Ed25519 verification error: invalid public key:', pkValidation.error);\n return false;\n }\n\n const publicKeyBytes = hexToBytes(publicKey);\n const encoder = new TextEncoder();\n const messageBytes = encoder.encode(message);\n const signatureBytes = Buffer.from(signature, 'base64');\n\n if (signatureBytes.length !== ED25519_SIGNATURE_LENGTH) {\n console.error(`Ed25519 verification error: signature length ${signatureBytes.length}, expected ${ED25519_SIGNATURE_LENGTH}`);\n return false;\n }\n\n return await ed.verifyAsync(signatureBytes, messageBytes, publicKeyBytes);\n } catch (error) {\n console.error('Ed25519 verification error:', error);\n return false;\n }\n}\n\n// ===== Tag Validation =====\n\nconst TAG_MIN_LENGTH = 1;\nconst TAG_MAX_LENGTH = 64;\nconst TAG_REGEX = /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/;\n\n/**\n * Validates a single tag format.\n * Rules: 1-64 chars, lowercase alphanumeric with optional dots/dashes,\n * must start and end with alphanumeric character.\n */\nexport function validateTag(tag: string): { valid: boolean; error?: string } {\n if (typeof tag !== 'string') {\n return { valid: false, error: 'Tag must be a string' };\n }\n\n if (tag.length < TAG_MIN_LENGTH) {\n return { valid: false, error: `Tag must be at least ${TAG_MIN_LENGTH} character` };\n }\n\n if (tag.length > TAG_MAX_LENGTH) {\n return { valid: false, error: `Tag must be at most ${TAG_MAX_LENGTH} characters` };\n }\n\n if (tag.length === 1) {\n if (!/^[a-z0-9]$/.test(tag)) {\n return { valid: false, error: 'Tag must be lowercase alphanumeric' };\n }\n return { valid: true };\n }\n\n if (!TAG_REGEX.test(tag)) {\n return { valid: false, error: 'Tag must be lowercase alphanumeric with optional dots/dashes, and start/end with alphanumeric' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validates an array of tags.\n * @param tags Array of tags to validate\n * @param maxTags Maximum number of tags allowed (default: 20)\n */\nexport function validateTags(tags: string[], maxTags: number = 20): { valid: boolean; error?: string } {\n if (!Array.isArray(tags)) {\n return { valid: false, error: 'Tags must be an array' };\n }\n\n if (tags.length === 0) {\n return { valid: false, error: 'At least one tag is required' };\n }\n\n if (tags.length > maxTags) {\n return { valid: false, error: `Maximum ${maxTags} tags allowed` };\n }\n\n for (let i = 0; i < tags.length; i++) {\n const result = validateTag(tags[i]);\n if (!result.valid) {\n return { valid: false, error: `Tag ${i + 1}: ${result.error}` };\n }\n }\n\n const uniqueTags = new Set(tags);\n if (uniqueTags.size !== tags.length) {\n return { valid: false, error: 'Duplicate tags are not allowed' };\n }\n\n return { valid: true };\n}\n", "/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */\n/**\n * 5KB JS implementation of ed25519 EdDSA signatures.\n * Compliant with RFC8032, FIPS 186-5 & ZIP215.\n * @module\n * @example\n * ```js\nimport * as ed from '@noble/ed25519';\n(async () => {\n const secretKey = ed.utils.randomSecretKey();\n const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);\n const pubKey = await ed.getPublicKeyAsync(secretKey); // Sync methods are also present\n const signature = await ed.signAsync(message, secretKey);\n const isValid = await ed.verifyAsync(signature, message, pubKey);\n})();\n```\n */\n/**\n * Curve params. ed25519 is twisted edwards curve. Equation is \u2212x\u00B2 + y\u00B2 = -a + dx\u00B2y\u00B2.\n * * P = `2n**255n - 19n` // field over which calculations are done\n * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points\n * * h = 8 // cofactor\n * * a = `Fp.create(BigInt(-1))` // equation param\n * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param\n * * Gx, Gy are coordinates of Generator / base point\n */\nconst ed25519_CURVE = {\n p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,\n n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,\n h: 8n,\n a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,\n d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,\n Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,\n Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,\n};\nconst { p: P, n: N, Gx, Gy, a: _a, d: _d, h } = ed25519_CURVE;\nconst L = 32; // field / group byte length\nconst L2 = 64;\n// Helpers and Precomputes sections are reused between libraries\n// ## Helpers\n// ----------\nconst captureTrace = (...args) => {\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(...args);\n }\n};\nconst err = (message = '') => {\n const e = new Error(message);\n captureTrace(e, err);\n throw e;\n};\nconst isBig = (n) => typeof n === 'bigint'; // is big integer\nconst isStr = (s) => typeof s === 'string'; // is string\nconst isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n/** Asserts something is Uint8Array. */\nconst abytes = (value, length, title = '') => {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n err(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n};\n/** create Uint8Array */\nconst u8n = (len) => new Uint8Array(len);\nconst u8fr = (buf) => Uint8Array.from(buf);\nconst padh = (n, pad) => n.toString(16).padStart(pad, '0');\nconst bytesToHex = (b) => Array.from(abytes(b))\n .map((e) => padh(e, 2))\n .join('');\nconst C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters\nconst _ch = (ch) => {\n if (ch >= C._0 && ch <= C._9)\n return ch - C._0; // '2' => 50-48\n if (ch >= C.A && ch <= C.F)\n return ch - (C.A - 10); // 'B' => 66-(65-10)\n if (ch >= C.a && ch <= C.f)\n return ch - (C.a - 10); // 'b' => 98-(97-10)\n return;\n};\nconst hexToBytes = (hex) => {\n const e = 'hex invalid';\n if (!isStr(hex))\n return err(e);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n return err(e);\n const array = u8n(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n // treat each char as ASCII\n const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16\n const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char\n if (n1 === undefined || n2 === undefined)\n return err(e);\n array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9\n }\n return array;\n};\nconst cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments\nconst subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined, consider polyfill');\n// prettier-ignore\nconst concatBytes = (...arrs) => {\n const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length\n let pad = 0; // walk through each array,\n arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type\n return r;\n};\n/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */\nconst randomBytes = (len = L) => {\n const c = cr();\n return c.getRandomValues(u8n(len));\n};\nconst big = BigInt;\nconst assertRange = (n, min, max, msg = 'bad number: out of range') => (isBig(n) && min <= n && n < max ? n : err(msg));\n/** modular division */\nconst M = (a, b = P) => {\n const r = a % b;\n return r >= 0n ? r : b + r;\n};\nconst modN = (a) => M(a, N);\n/** Modular inversion using euclidean GCD (non-CT). No negative exponent for now. */\n// prettier-ignore\nconst invert = (num, md) => {\n if (num === 0n || md <= 0n)\n err('no inverse n=' + num + ' mod=' + md);\n let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;\n while (a !== 0n) {\n const q = b / a, r = b % a;\n const m = x - u * q, n = y - v * q;\n b = a, a = r, x = u, y = v, u = m, v = n;\n }\n return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point\n};\nconst callHash = (name) => {\n // @ts-ignore\n const fn = hashes[name];\n if (typeof fn !== 'function')\n err('hashes.' + name + ' not set');\n return fn;\n};\nconst hash = (msg) => callHash('sha512')(msg);\nconst apoint = (p) => (p instanceof Point ? p : err('Point expected'));\n// ## End of Helpers\n// -----------------\nconst B256 = 2n ** 256n;\n/** Point in XYZT extended coordinates. */\nclass Point {\n static BASE;\n static ZERO;\n X;\n Y;\n Z;\n T;\n constructor(X, Y, Z, T) {\n const max = B256;\n this.X = assertRange(X, 0n, max);\n this.Y = assertRange(Y, 0n, max);\n this.Z = assertRange(Z, 1n, max);\n this.T = assertRange(T, 0n, max);\n Object.freeze(this);\n }\n static CURVE() {\n return ed25519_CURVE;\n }\n static fromAffine(p) {\n return new Point(p.x, p.y, 1n, M(p.x * p.y));\n }\n /** RFC8032 5.1.3: Uint8Array to Point. */\n static fromBytes(hex, zip215 = false) {\n const d = _d;\n // Copy array to not mess it up.\n const normed = u8fr(abytes(hex, L));\n // adjust first LE byte = last BE byte\n const lastByte = hex[31];\n normed[31] = lastByte & ~0x80;\n const y = bytesToNumLE(normed);\n // zip215=true: 0 <= y < 2^256\n // zip215=false, RFC8032: 0 <= y < 2^255-19\n const max = zip215 ? B256 : P;\n assertRange(y, 0n, max);\n const y2 = M(y * y); // y\u00B2\n const u = M(y2 - 1n); // u=y\u00B2-1\n const v = M(d * y2 + 1n); // v=dy\u00B2+1\n let { isValid, value: x } = uvRatio(u, v); // (uv\u00B3)(uv\u2077)^(p-5)/8; square root\n if (!isValid)\n err('bad point: y not sqrt'); // not square root: bad point\n const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate\n const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit\n if (!zip215 && x === 0n && isLastByteOdd)\n err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1\n if (isLastByteOdd !== isXOdd)\n x = M(-x);\n return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy\n }\n static fromHex(hex, zip215) {\n return Point.fromBytes(hexToBytes(hex), zip215);\n }\n get x() {\n return this.toAffine().x;\n }\n get y() {\n return this.toAffine().y;\n }\n /** Checks if the point is valid and on-curve. */\n assertValidity() {\n const a = _a;\n const d = _d;\n const p = this;\n if (p.is0())\n return err('bad point: ZERO'); // TODO: optimize, with vars below?\n // Equation in affine coordinates: ax\u00B2 + y\u00B2 = 1 + dx\u00B2y\u00B2\n // Equation in projective coordinates (X/Z, Y/Z, Z): (aX\u00B2 + Y\u00B2)Z\u00B2 = Z\u2074 + dX\u00B2Y\u00B2\n const { X, Y, Z, T } = p;\n const X2 = M(X * X); // X\u00B2\n const Y2 = M(Y * Y); // Y\u00B2\n const Z2 = M(Z * Z); // Z\u00B2\n const Z4 = M(Z2 * Z2); // Z\u2074\n const aX2 = M(X2 * a); // aX\u00B2\n const left = M(Z2 * M(aX2 + Y2)); // (aX\u00B2 + Y\u00B2)Z\u00B2\n const right = M(Z4 + M(d * M(X2 * Y2))); // Z\u2074 + dX\u00B2Y\u00B2\n if (left !== right)\n return err('bad point: equation left != right (1)');\n // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T\n const XY = M(X * Y);\n const ZT = M(Z * T);\n if (XY !== ZT)\n return err('bad point: equation left != right (2)');\n return this;\n }\n /** Equality check: compare points P&Q. */\n equals(other) {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality\n const X1Z2 = M(X1 * Z2);\n const X2Z1 = M(X2 * Z1);\n const Y1Z2 = M(Y1 * Z2);\n const Y2Z1 = M(Y2 * Z1);\n return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;\n }\n is0() {\n return this.equals(I);\n }\n /** Flip point over y coordinate. */\n negate() {\n return new Point(M(-this.X), this.Y, this.Z, M(-this.T));\n }\n /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */\n double() {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const a = _a;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd\n const A = M(X1 * X1);\n const B = M(Y1 * Y1);\n const C = M(2n * M(Z1 * Z1));\n const D = M(a * A);\n const x1y1 = X1 + Y1;\n const E = M(M(x1y1 * x1y1) - A - B);\n const G = D + B;\n const F = G - C;\n const H = D - B;\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */\n add(other) {\n const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;\n const { X: X2, Y: Y2, Z: Z2, T: T2 } = apoint(other); // doesn't check if other on-curve\n const a = _a;\n const d = _d;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3\n const A = M(X1 * X2);\n const B = M(Y1 * Y2);\n const C = M(T1 * d * T2);\n const D = M(Z1 * Z2);\n const E = M((X1 + Y1) * (X2 + Y2) - A - B);\n const F = M(D - C);\n const G = M(D + C);\n const H = M(B - a * A);\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n subtract(other) {\n return this.add(apoint(other).negate());\n }\n /**\n * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.\n * Uses {@link wNAF} for base point.\n * Uses fake point to mitigate side-channel leakage.\n * @param n scalar by which point is multiplied\n * @param safe safe mode guards against timing attacks; unsafe mode is faster\n */\n multiply(n, safe = true) {\n if (!safe && (n === 0n || this.is0()))\n return I;\n assertRange(n, 1n, N);\n if (n === 1n)\n return this;\n if (this.equals(G))\n return wNAF(n).p;\n // init result point & fake point\n let p = I;\n let f = G;\n for (let d = this; n > 0n; d = d.double(), n >>= 1n) {\n // if bit is present, add to point\n // if not present, add to fake, for timing safety\n if (n & 1n)\n p = p.add(d);\n else if (safe)\n f = f.add(d);\n }\n return p;\n }\n multiplyUnsafe(scalar) {\n return this.multiply(scalar, false);\n }\n /** Convert point to 2d xy affine point. (X, Y, Z) \u220B (x=X/Z, y=Y/Z) */\n toAffine() {\n const { X, Y, Z } = this;\n // fast-paths for ZERO point OR Z=1\n if (this.equals(I))\n return { x: 0n, y: 1n };\n const iz = invert(Z, P);\n // (Z * Z^-1) must be 1, otherwise bad math\n if (M(Z * iz) !== 1n)\n err('invalid inverse');\n // x = X*Z^-1; y = Y*Z^-1\n const x = M(X * iz);\n const y = M(Y * iz);\n return { x, y };\n }\n toBytes() {\n const { x, y } = this.assertValidity().toAffine();\n const b = numTo32bLE(y);\n // store sign in first LE byte\n b[31] |= x & 1n ? 0x80 : 0;\n return b;\n }\n toHex() {\n return bytesToHex(this.toBytes());\n }\n clearCofactor() {\n return this.multiply(big(h), false);\n }\n isSmallOrder() {\n return this.clearCofactor().is0();\n }\n isTorsionFree() {\n // Multiply by big number N. We can't `mul(N)` because of checks. Instead, we `mul(N/2)*2+1`\n let p = this.multiply(N / 2n, false).double();\n if (N % 2n)\n p = p.add(this);\n return p.is0();\n }\n}\n/** Generator / base point */\nconst G = new Point(Gx, Gy, 1n, M(Gx * Gy));\n/** Identity / zero point */\nconst I = new Point(0n, 1n, 1n, 0n);\n// Static aliases\nPoint.BASE = G;\nPoint.ZERO = I;\nconst numTo32bLE = (num) => hexToBytes(padh(assertRange(num, 0n, B256), L2)).reverse();\nconst bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));\nconst pow2 = (x, power) => {\n // pow2(x, 4) == x^(2^4)\n let r = x;\n while (power-- > 0n) {\n r *= r;\n r %= P;\n }\n return r;\n};\n// prettier-ignore\nconst pow_2_252_3 = (x) => {\n const x2 = (x * x) % P; // x^2, bits 1\n const b2 = (x2 * x) % P; // x^3, bits 11\n const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111\n const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111\n const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)\n const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)\n const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)\n const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)\n const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)\n const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)\n const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)\n const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.\n return { pow_p_5_8, b2 };\n};\nconst RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // \u221A-1\n// for sqrt comp\n// prettier-ignore\nconst uvRatio = (u, v) => {\n const v3 = M(v * v * v); // v\u00B3\n const v7 = M(v3 * v3 * v); // v\u2077\n const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv\u2077)^(p-5)/8\n let x = M(u * v3 * pow); // (uv\u00B3)(uv\u2077)^(p-5)/8\n const vx2 = M(v * x * x); // vx\u00B2\n const root1 = x; // First root candidate\n const root2 = M(x * RM1); // Second root candidate; RM1 is \u221A-1\n const useRoot1 = vx2 === u; // If vx\u00B2 = u (mod p), x is a square root\n const useRoot2 = vx2 === M(-u); // If vx\u00B2 = -u, set x <-- x * 2^((p-1)/4)\n const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx\u00B2 = -u\u221A-1\n if (useRoot1)\n x = root1;\n if (useRoot2 || noRoot)\n x = root2; // We return root2 anyway, for const-time\n if ((M(x) & 1n) === 1n)\n x = M(-x); // edIsNegative\n return { isValid: useRoot1 || useRoot2, value: x };\n};\n// N == L, just weird naming\nconst modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian\n/** hashes.sha512 should conform to the interface. */\n// TODO: rename\nconst sha512a = (...m) => hashes.sha512Async(concatBytes(...m)); // Async SHA512\nconst sha512s = (...m) => callHash('sha512')(concatBytes(...m));\n// RFC8032 5.1.5\nconst hash2extK = (hashed) => {\n // slice creates a copy, unlike subarray\n const head = hashed.slice(0, L);\n head[0] &= 248; // Clamp bits: 0b1111_1000\n head[31] &= 127; // 0b0111_1111\n head[31] |= 64; // 0b0100_0000\n const prefix = hashed.slice(L, L2); // secret key \"prefix\"\n const scalar = modL_LE(head); // modular division over curve order\n const point = G.multiply(scalar); // public key point\n const pointBytes = point.toBytes(); // point serialized to Uint8Array\n return { head, prefix, scalar, point, pointBytes };\n};\n// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.\nconst getExtendedPublicKeyAsync = (secretKey) => sha512a(abytes(secretKey, L)).then(hash2extK);\nconst getExtendedPublicKey = (secretKey) => hash2extK(sha512s(abytes(secretKey, L)));\n/** Creates 32-byte ed25519 public key from 32-byte secret key. Async. */\nconst getPublicKeyAsync = (secretKey) => getExtendedPublicKeyAsync(secretKey).then((p) => p.pointBytes);\n/** Creates 32-byte ed25519 public key from 32-byte secret key. To use, set `hashes.sha512` first. */\nconst getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;\nconst hashFinishA = (res) => sha512a(res.hashable).then(res.finish);\nconst hashFinishS = (res) => res.finish(sha512s(res.hashable));\n// Code, shared between sync & async sign\nconst _sign = (e, rBytes, msg) => {\n const { pointBytes: P, scalar: s } = e;\n const r = modL_LE(rBytes); // r was created outside, reduce it modulo L\n const R = G.multiply(r).toBytes(); // R = [r]B\n const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l\n return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)\n };\n return { hashable, finish };\n};\n/**\n * Signs message using secret key. Async.\n * Follows RFC8032 5.1.6.\n */\nconst signAsync = async (message, secretKey) => {\n const m = abytes(message);\n const e = await getExtendedPublicKeyAsync(secretKey);\n const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\n/**\n * Signs message using secret key. To use, set `hashes.sha512` first.\n * Follows RFC8032 5.1.6.\n */\nconst sign = (message, secretKey) => {\n const m = abytes(message);\n const e = getExtendedPublicKey(secretKey);\n const rBytes = sha512s(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishS(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\nconst defaultVerifyOpts = { zip215: true };\nconst _verify = (sig, msg, pub, opts = defaultVerifyOpts) => {\n sig = abytes(sig, L2); // Signature hex str/Bytes, must be 64 bytes\n msg = abytes(msg); // Message hex str/Bytes\n pub = abytes(pub, L);\n const { zip215 } = opts; // switch between zip215 and rfc8032 verif\n let A;\n let R;\n let s;\n let SB;\n let hashable = Uint8Array.of();\n try {\n A = Point.fromBytes(pub, zip215); // public key A decoded\n R = Point.fromBytes(sig.slice(0, L), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P\n s = bytesToNumLE(sig.slice(L, L2)); // Decode second half as an integer S\n SB = G.multiply(s, false); // in the range 0 <= s < L\n hashable = concatBytes(R.toBytes(), A.toBytes(), msg); // dom2(F, C) || R || A || PH(M)\n }\n catch (error) { }\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n if (SB == null)\n return false; // false if try-catch catched an error\n if (!zip215 && A.isSmallOrder())\n return false; // false for SBS: Strongly Binding Signature\n const k = modL_LE(hashed); // decode in little-endian, modulo L\n const RkA = R.add(A.multiply(k, false)); // [8]R + [8][k]A'\n return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'\n };\n return { hashable, finish };\n};\n/** Verifies signature on message and public key. Async. Follows RFC8032 5.1.7. */\nconst verifyAsync = async (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishA(_verify(signature, message, publicKey, opts));\n/** Verifies signature on message and public key. To use, set `hashes.sha512` first. Follows RFC8032 5.1.7. */\nconst verify = (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishS(_verify(signature, message, publicKey, opts));\n/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */\nconst etc = {\n bytesToHex: bytesToHex,\n hexToBytes: hexToBytes,\n concatBytes: concatBytes,\n mod: M,\n invert: invert,\n randomBytes: randomBytes,\n};\nconst hashes = {\n sha512Async: async (message) => {\n const s = subtle();\n const m = concatBytes(message);\n return u8n(await s.digest('SHA-512', m.buffer));\n },\n sha512: undefined,\n};\n// FIPS 186 B.4.1 compliant key generation produces private keys\n// with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1\nconst randomSecretKey = (seed = randomBytes(L)) => seed;\nconst keygen = (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = getPublicKey(secretKey);\n return { secretKey, publicKey };\n};\nconst keygenAsync = async (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = await getPublicKeyAsync(secretKey);\n return { secretKey, publicKey };\n};\n/** ed25519-specific key utilities. */\nconst utils = {\n getExtendedPublicKeyAsync: getExtendedPublicKeyAsync,\n getExtendedPublicKey: getExtendedPublicKey,\n randomSecretKey: randomSecretKey,\n};\n// ## Precomputes\n// --------------\nconst W = 8; // W is window size\nconst scalarBits = 256;\nconst pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop\nconst pwindowSize = 2 ** (W - 1); // 128 for W=8\nconst precompute = () => {\n const points = [];\n let p = G;\n let b = p;\n for (let w = 0; w < pwindows; w++) {\n b = p;\n points.push(b);\n for (let i = 1; i < pwindowSize; i++) {\n b = b.add(p);\n points.push(b);\n } // i=1, bc we skip 0\n p = b.double();\n }\n return points;\n};\nlet Gpows = undefined; // precomputes for base point G\n// const-time negate\nconst ctneg = (cnd, p) => {\n const n = p.negate();\n return cnd ? n : p;\n};\n/**\n * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by\n * caching multiples of G (base point). Cache is stored in 32MB of RAM.\n * Any time `G.multiply` is done, precomputes are used.\n * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.\n *\n * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,\n * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.\n *\n * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().\n */\nconst wNAF = (n) => {\n const comp = Gpows || (Gpows = precompute());\n let p = I;\n let f = G; // f must be G, or could become I in the end\n const pow_2_w = 2 ** W; // 256 for W=8\n const maxNum = pow_2_w; // 256 for W=8\n const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111\n const shiftBy = big(W); // 8 for W=8\n for (let w = 0; w < pwindows; w++) {\n let wbits = Number(n & mask); // extract W bits.\n n >>= shiftBy; // shift number by W bits.\n // We use negative indexes to reduce size of precomputed table by 2x.\n // Instead of needing precomputes 0..256, we only calculate them for 0..128.\n // If an index > 128 is found, we do (256-index) - where 256 is next window.\n // Naive: index +127 => 127, +224 => 224\n // Optimized: index +127 => 127, +224 => 256-32\n if (wbits > pwindowSize) {\n wbits -= maxNum;\n n += 1n;\n }\n const off = w * pwindowSize;\n const offF = off; // offsets, evaluate both\n const offP = off + Math.abs(wbits) - 1;\n const isEven = w % 2 !== 0; // conditions, evaluate both\n const isNeg = wbits < 0;\n if (wbits === 0) {\n // off == I: can't add it. Adding random offF instead.\n f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point\n }\n else {\n p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point\n }\n }\n if (n !== 0n)\n err('invalid wnaf');\n return { p, f }; // return both real and fake points for JIT\n};\n// !! Remove the export to easily use in REPL / browser console\nexport { etc, getPublicKey, getPublicKeyAsync, hash, hashes, keygen, keygenAsync, Point, sign, signAsync, utils, verify, verifyAsync, };\n", "import { Context } from 'hono';\nimport { Storage } from './storage/types.ts';\nimport { Config } from './config.ts';\nimport {\n validateTags,\n validatePublicKey,\n verifyEd25519Signature,\n buildSignatureMessage,\n} from './crypto.ts';\n\n// Constants (non-configurable)\nconst MAX_PAGE_SIZE = 100;\nconst REQUEST_RATE_WINDOW = 1000; // 1 second in milliseconds\n\n/**\n * Check JSON object depth to prevent stack overflow from deeply nested objects\n */\nfunction getJsonDepth(obj: any, maxDepth: number, currentDepth = 0): number {\n if (obj === null || typeof obj !== 'object') {\n return currentDepth;\n }\n\n if (currentDepth >= maxDepth) {\n return currentDepth + 1;\n }\n\n let maxChildDepth = currentDepth;\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const childDepth = getJsonDepth(obj[key], maxDepth, currentDepth + 1);\n maxChildDepth = Math.max(maxChildDepth, childDepth);\n\n if (maxChildDepth > maxDepth) {\n return maxChildDepth;\n }\n }\n }\n\n return maxChildDepth;\n}\n\n/**\n * Validate parameter is a non-empty string\n */\nfunction validateStringParam(value: any, paramName: string): void {\n if (typeof value !== 'string' || value.length === 0) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `${paramName} must be a non-empty string`);\n }\n}\n\n/**\n * Standard error codes for RPC responses\n */\nexport const ErrorCodes = {\n // Authentication errors\n AUTH_REQUIRED: 'AUTH_REQUIRED',\n INVALID_CREDENTIALS: 'INVALID_CREDENTIALS',\n\n // Validation errors\n INVALID_PUBLIC_KEY: 'INVALID_PUBLIC_KEY',\n INVALID_TAG: 'INVALID_TAG',\n INVALID_SDP: 'INVALID_SDP',\n INVALID_PARAMS: 'INVALID_PARAMS',\n MISSING_PARAMS: 'MISSING_PARAMS',\n\n // Resource errors\n OFFER_NOT_FOUND: 'OFFER_NOT_FOUND',\n OFFER_ALREADY_ANSWERED: 'OFFER_ALREADY_ANSWERED',\n OFFER_NOT_ANSWERED: 'OFFER_NOT_ANSWERED',\n NO_AVAILABLE_OFFERS: 'NO_AVAILABLE_OFFERS',\n\n // Authorization errors\n NOT_AUTHORIZED: 'NOT_AUTHORIZED',\n OWNERSHIP_MISMATCH: 'OWNERSHIP_MISMATCH',\n\n // Limit errors\n TOO_MANY_OFFERS: 'TOO_MANY_OFFERS',\n SDP_TOO_LARGE: 'SDP_TOO_LARGE',\n BATCH_TOO_LARGE: 'BATCH_TOO_LARGE',\n RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',\n TOO_MANY_OFFERS_PER_USER: 'TOO_MANY_OFFERS_PER_USER',\n STORAGE_FULL: 'STORAGE_FULL',\n TOO_MANY_ICE_CANDIDATES: 'TOO_MANY_ICE_CANDIDATES',\n\n // Generic errors\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n UNKNOWN_METHOD: 'UNKNOWN_METHOD',\n} as const;\n\n/**\n * Custom error class with error code support\n */\nexport class RpcError extends Error {\n constructor(\n public errorCode: string,\n message: string\n ) {\n super(message);\n this.name = 'RpcError';\n }\n}\n\n/**\n * RPC request format (body only - auth in headers)\n */\nexport interface RpcRequest {\n method: string;\n params?: any;\n clientIp?: string;\n}\n\n/**\n * RPC response format\n */\nexport interface RpcResponse {\n success: boolean;\n result?: any;\n error?: string;\n errorCode?: string;\n}\n\n/**\n * RPC Method Parameter Interfaces\n */\nexport interface DiscoverParams {\n tags: string[];\n limit?: number;\n offset?: number;\n}\n\nexport interface PublishOfferParams {\n tags: string[];\n offers: Array<{ sdp: string }>;\n ttl?: number;\n}\n\nexport interface DeleteOfferParams {\n offerId: string;\n}\n\nexport interface AnswerOfferParams {\n offerId: string;\n sdp: string;\n matchedTags?: string[];\n}\n\nexport interface GetOfferAnswerParams {\n offerId: string;\n}\n\nexport interface PollParams {\n since?: number;\n}\n\nexport interface AddIceCandidatesParams {\n offerId: string;\n candidates: any[];\n}\n\nexport interface GetIceCandidatesParams {\n offerId: string;\n since?: number;\n}\n\n/**\n * RPC method handler\n */\ntype RpcHandler<TParams = any> = (\n params: TParams,\n publicKey: string,\n timestamp: number,\n signature: string,\n storage: Storage,\n config: Config,\n request: RpcRequest\n) => Promise<any>;\n\n/**\n * Validate timestamp for replay attack prevention\n */\nfunction validateTimestamp(timestamp: number, config: Config): void {\n const now = Date.now();\n\n if (now - timestamp > config.timestampMaxAge) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'Timestamp too old');\n }\n\n if (timestamp - now > config.timestampMaxFuture) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'Timestamp too far in future');\n }\n}\n\n/**\n * Verify request signature using Ed25519\n * Stateless verification - no identity registration required\n */\nasync function verifyRequestSignature(\n publicKey: string,\n timestamp: number,\n nonce: string,\n signature: string,\n method: string,\n params: any,\n storage: Storage,\n config: Config\n): Promise<void> {\n // Validate timestamp first\n validateTimestamp(timestamp, config);\n\n // Validate public key format\n const pkValidation = validatePublicKey(publicKey);\n if (!pkValidation.valid) {\n throw new RpcError(ErrorCodes.INVALID_PUBLIC_KEY, pkValidation.error || 'Invalid public key');\n }\n\n // Build message and verify Ed25519 signature\n const message = buildSignatureMessage(timestamp, nonce, method, params);\n const isValid = await verifyEd25519Signature(publicKey, message, signature);\n\n if (!isValid) {\n throw new RpcError(ErrorCodes.INVALID_CREDENTIALS, 'Invalid signature');\n }\n\n // Check nonce uniqueness AFTER successful signature verification\n const nonceKey = `nonce:${publicKey}:${nonce}`;\n const nonceExpiresAt = timestamp + config.timestampMaxAge;\n const nonceIsNew = await storage.checkAndMarkNonce(nonceKey, nonceExpiresAt);\n\n if (!nonceIsNew) {\n throw new RpcError(ErrorCodes.INVALID_CREDENTIALS, 'Nonce already used (replay attack detected)');\n }\n}\n\n/**\n * RPC Method Handlers\n */\nconst handlers: Record<string, RpcHandler> = {\n /**\n * Discover offers by tags\n */\n async discover(params: DiscoverParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { tags, limit, offset } = params;\n\n const tagsValidation = validateTags(tags);\n if (!tagsValidation.valid) {\n throw new RpcError(ErrorCodes.INVALID_TAG, tagsValidation.error || 'Invalid tags');\n }\n\n // Mode 1: Paginated discovery\n if (limit !== undefined) {\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit < 0) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'limit must be a non-negative integer');\n }\n if (offset !== undefined && (typeof offset !== 'number' || !Number.isInteger(offset) || offset < 0)) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'offset must be a non-negative integer');\n }\n\n const pageLimit = Math.min(Math.max(1, limit), MAX_PAGE_SIZE);\n const pageOffset = Math.max(0, offset || 0);\n\n const excludePublicKey = publicKey || null;\n\n const offers = await storage.discoverOffers(\n tags,\n excludePublicKey,\n pageLimit,\n pageOffset\n );\n\n return {\n offers: offers.map(offer => ({\n offerId: offer.id,\n publicKey: offer.publicKey,\n tags: offer.tags,\n sdp: offer.sdp,\n createdAt: offer.createdAt,\n expiresAt: offer.expiresAt,\n })),\n count: offers.length,\n limit: pageLimit,\n offset: pageOffset,\n };\n }\n\n // Mode 2: Random discovery\n const excludePublicKey = publicKey || null;\n const offer = await storage.getRandomOffer(tags, excludePublicKey);\n\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'No offers found matching tags');\n }\n\n return {\n offerId: offer.id,\n publicKey: offer.publicKey,\n tags: offer.tags,\n sdp: offer.sdp,\n createdAt: offer.createdAt,\n expiresAt: offer.expiresAt,\n };\n },\n\n /**\n * Publish offers with tags\n */\n async publishOffer(params: PublishOfferParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { tags, offers, ttl } = params;\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required for offer publishing');\n }\n\n const tagsValidation = validateTags(tags);\n if (!tagsValidation.valid) {\n throw new RpcError(ErrorCodes.INVALID_TAG, tagsValidation.error || 'Invalid tags');\n }\n\n if (!offers || !Array.isArray(offers) || offers.length === 0) {\n throw new RpcError(ErrorCodes.MISSING_PARAMS, 'Must provide at least one offer');\n }\n\n if (offers.length > config.maxOffersPerRequest) {\n throw new RpcError(\n ErrorCodes.TOO_MANY_OFFERS,\n `Too many offers (max ${config.maxOffersPerRequest})`\n );\n }\n\n const userOfferCount = await storage.getOfferCountByPublicKey(publicKey);\n if (userOfferCount + offers.length > config.maxOffersPerUser) {\n throw new RpcError(\n ErrorCodes.TOO_MANY_OFFERS_PER_USER,\n `User offer limit exceeded. You have ${userOfferCount} offers, limit is ${config.maxOffersPerUser}.`\n );\n }\n\n const totalOfferCount = await storage.getOfferCount();\n if (totalOfferCount + offers.length > config.maxTotalOffers) {\n throw new RpcError(\n ErrorCodes.STORAGE_FULL,\n `Server offer limit reached (${config.maxTotalOffers}). Try again later.`\n );\n }\n\n offers.forEach((offer, index) => {\n if (!offer || typeof offer !== 'object') {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `Invalid offer at index ${index}: must be an object`);\n }\n if (!offer.sdp || typeof offer.sdp !== 'string') {\n throw new RpcError(ErrorCodes.INVALID_SDP, `Invalid offer at index ${index}: missing or invalid SDP`);\n }\n if (!offer.sdp.trim()) {\n throw new RpcError(ErrorCodes.INVALID_SDP, `Invalid offer at index ${index}: SDP cannot be empty`);\n }\n if (offer.sdp.length > config.maxSdpSize) {\n throw new RpcError(ErrorCodes.SDP_TOO_LARGE, `SDP too large at index ${index} (max ${config.maxSdpSize} bytes)`);\n }\n });\n\n if (ttl !== undefined) {\n if (typeof ttl !== 'number' || isNaN(ttl) || ttl < 0) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'TTL must be a non-negative number');\n }\n }\n\n const now = Date.now();\n const offerTtl =\n ttl !== undefined\n ? Math.min(Math.max(ttl, config.offerMinTtl), config.offerMaxTtl)\n : config.offerDefaultTtl;\n const expiresAt = now + offerTtl;\n\n const offerRequests = offers.map(offer => ({\n publicKey,\n tags,\n sdp: offer.sdp,\n expiresAt,\n }));\n\n const createdOffers = await storage.createOffers(offerRequests);\n\n return {\n publicKey,\n tags,\n offers: createdOffers.map(offer => ({\n offerId: offer.id,\n sdp: offer.sdp,\n createdAt: offer.createdAt,\n expiresAt: offer.expiresAt,\n })),\n createdAt: now,\n expiresAt,\n };\n },\n\n /**\n * Delete an offer by ID\n */\n async deleteOffer(params: DeleteOfferParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId } = params;\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n validateStringParam(offerId, 'offerId');\n\n const deleted = await storage.deleteOffer(offerId, publicKey);\n if (!deleted) {\n throw new RpcError(ErrorCodes.NOT_AUTHORIZED, 'Offer not found or not owned by this identity');\n }\n\n return { success: true };\n },\n\n /**\n * Answer an offer\n */\n async answerOffer(params: AnswerOfferParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId, sdp, matchedTags } = params;\n\n validateStringParam(offerId, 'offerId');\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n if (!sdp || typeof sdp !== 'string' || sdp.length === 0) {\n throw new RpcError(ErrorCodes.INVALID_SDP, 'Invalid SDP');\n }\n\n if (sdp.length > config.maxSdpSize) {\n throw new RpcError(ErrorCodes.SDP_TOO_LARGE, `SDP too large (max ${config.maxSdpSize} bytes)`);\n }\n\n if (matchedTags !== undefined && !Array.isArray(matchedTags)) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'matchedTags must be an array');\n }\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');\n }\n\n if (offer.answererPublicKey) {\n throw new RpcError(ErrorCodes.OFFER_ALREADY_ANSWERED, 'Offer already answered');\n }\n\n if (matchedTags && matchedTags.length > 0) {\n const offerTagSet = new Set(offer.tags);\n const invalidTags = matchedTags.filter(tag => !offerTagSet.has(tag));\n if (invalidTags.length > 0) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `matchedTags contains tags not on offer: ${invalidTags.join(', ')}`);\n }\n }\n\n await storage.answerOffer(offerId, publicKey, sdp, matchedTags);\n\n return { success: true, offerId };\n },\n\n /**\n * Get answer for an offer\n */\n async getOfferAnswer(params: GetOfferAnswerParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId } = params;\n\n validateStringParam(offerId, 'offerId');\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');\n }\n\n if (offer.publicKey !== publicKey) {\n throw new RpcError(ErrorCodes.NOT_AUTHORIZED, 'Not authorized to access this offer');\n }\n\n if (!offer.answererPublicKey || !offer.answerSdp) {\n throw new RpcError(ErrorCodes.OFFER_NOT_ANSWERED, 'Offer not yet answered');\n }\n\n return {\n sdp: offer.answerSdp,\n offerId: offer.id,\n answererPublicKey: offer.answererPublicKey,\n answeredAt: offer.answeredAt,\n };\n },\n\n /**\n * Combined polling for answers and ICE candidates\n */\n async poll(params: PollParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { since } = params;\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n if (since !== undefined && (typeof since !== 'number' || since < 0 || !Number.isFinite(since))) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'Invalid since parameter: must be a non-negative number');\n }\n const sinceTimestamp = since !== undefined ? since : 0;\n\n const answeredOffers = await storage.getAnsweredOffers(publicKey);\n const filteredAnswers = answeredOffers.filter(\n (offer) => offer.answeredAt && offer.answeredAt > sinceTimestamp\n );\n\n const ownedOffers = await storage.getOffersByPublicKey(publicKey);\n const answeredByUser = await storage.getOffersAnsweredBy(publicKey);\n\n const allOfferIds = [\n ...ownedOffers.map(offer => offer.id),\n ...answeredByUser.map(offer => offer.id),\n ];\n const offerIds = [...new Set(allOfferIds)];\n\n const iceCandidatesMap = await storage.getIceCandidatesForMultipleOffers(\n offerIds,\n publicKey,\n sinceTimestamp\n );\n\n const iceCandidatesByOffer: Record<string, any[]> = {};\n for (const [offerId, candidates] of iceCandidatesMap.entries()) {\n iceCandidatesByOffer[offerId] = candidates;\n }\n\n return {\n answers: filteredAnswers.map((offer) => ({\n offerId: offer.id,\n answererPublicKey: offer.answererPublicKey,\n sdp: offer.answerSdp,\n answeredAt: offer.answeredAt,\n })),\n iceCandidates: iceCandidatesByOffer,\n };\n },\n\n /**\n * Add ICE candidates\n */\n async addIceCandidates(params: AddIceCandidatesParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId, candidates } = params;\n\n validateStringParam(offerId, 'offerId');\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n if (!Array.isArray(candidates) || candidates.length === 0) {\n throw new RpcError(ErrorCodes.MISSING_PARAMS, 'Missing or invalid required parameter: candidates');\n }\n\n if (candidates.length > config.maxCandidatesPerRequest) {\n throw new RpcError(\n ErrorCodes.INVALID_PARAMS,\n `Too many candidates (max ${config.maxCandidatesPerRequest})`\n );\n }\n\n candidates.forEach((candidate, index) => {\n if (!candidate || typeof candidate !== 'object') {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `Invalid candidate at index ${index}: must be an object`);\n }\n\n const depth = getJsonDepth(candidate, config.maxCandidateDepth + 1);\n if (depth > config.maxCandidateDepth) {\n throw new RpcError(\n ErrorCodes.INVALID_PARAMS,\n `Candidate at index ${index} too deeply nested (max depth ${config.maxCandidateDepth})`\n );\n }\n\n let candidateJson: string;\n try {\n candidateJson = JSON.stringify(candidate);\n } catch (e) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, `Candidate at index ${index} is not serializable`);\n }\n\n if (candidateJson.length > config.maxCandidateSize) {\n throw new RpcError(\n ErrorCodes.INVALID_PARAMS,\n `Candidate at index ${index} too large (max ${config.maxCandidateSize} bytes)`\n );\n }\n });\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');\n }\n\n const currentCandidateCount = await storage.getIceCandidateCount(offerId);\n if (currentCandidateCount + candidates.length > config.maxIceCandidatesPerOffer) {\n throw new RpcError(\n ErrorCodes.TOO_MANY_ICE_CANDIDATES,\n `ICE candidate limit exceeded for offer. Current: ${currentCandidateCount}, limit: ${config.maxIceCandidatesPerOffer}.`\n );\n }\n\n const role = offer.publicKey === publicKey ? 'offerer' : 'answerer';\n const count = await storage.addIceCandidates(\n offerId,\n publicKey,\n role,\n candidates\n );\n\n return { count, offerId };\n },\n\n /**\n * Get ICE candidates\n */\n async getIceCandidates(params: GetIceCandidatesParams, publicKey, timestamp, signature, storage, config, request: RpcRequest) {\n const { offerId, since } = params;\n\n validateStringParam(offerId, 'offerId');\n\n if (!publicKey) {\n throw new RpcError(ErrorCodes.AUTH_REQUIRED, 'Authentication required');\n }\n\n if (since !== undefined && (typeof since !== 'number' || since < 0 || !Number.isFinite(since))) {\n throw new RpcError(ErrorCodes.INVALID_PARAMS, 'Invalid since parameter: must be a non-negative number');\n }\n const sinceTimestamp = since !== undefined ? since : 0;\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new RpcError(ErrorCodes.OFFER_NOT_FOUND, 'Offer not found');\n }\n\n const isOfferer = offer.publicKey === publicKey;\n const isAnswerer = offer.answererPublicKey === publicKey;\n\n if (!isOfferer && !isAnswerer) {\n throw new RpcError(ErrorCodes.NOT_AUTHORIZED, 'Not authorized to access ICE candidates for this offer');\n }\n\n const role = isOfferer ? 'answerer' : 'offerer';\n\n const candidates = await storage.getIceCandidates(\n offerId,\n role,\n sinceTimestamp\n );\n\n return {\n candidates: candidates.map((c: any) => ({\n candidate: c.candidate,\n createdAt: c.createdAt,\n })),\n offerId,\n };\n },\n};\n\n// Methods that don't require authentication\nconst UNAUTHENTICATED_METHODS = new Set(['discover']);\n\n/**\n * Handle RPC batch request with header-based authentication\n */\nexport async function handleRpc(\n requests: RpcRequest[],\n ctx: Context,\n storage: Storage,\n config: Config\n): Promise<RpcResponse[]> {\n const responses: RpcResponse[] = [];\n\n const clientIp =\n ctx.req.header('cf-connecting-ip') ||\n ctx.req.header('x-real-ip') ||\n ctx.req.header('x-forwarded-for')?.split(',')[0].trim() ||\n undefined;\n\n // General request rate limiting (per IP per second)\n if (clientIp) {\n const rateLimitKey = `req:${clientIp}`;\n const allowed = await storage.checkRateLimit(\n rateLimitKey,\n config.requestsPerIpPerSecond,\n REQUEST_RATE_WINDOW\n );\n\n if (!allowed) {\n return requests.map(() => ({\n success: false,\n error: `Rate limit exceeded. Maximum ${config.requestsPerIpPerSecond} requests per second per IP.`,\n errorCode: ErrorCodes.RATE_LIMIT_EXCEEDED,\n }));\n }\n }\n\n // Read auth headers (X-PublicKey instead of X-Name)\n const publicKey = ctx.req.header('X-PublicKey');\n const timestampHeader = ctx.req.header('X-Timestamp');\n const nonce = ctx.req.header('X-Nonce');\n const signature = ctx.req.header('X-Signature');\n\n const timestamp = timestampHeader ? parseInt(timestampHeader, 10) : 0;\n\n // Pre-calculate total operations\n let totalOperations = 0;\n for (const request of requests) {\n const { method, params } = request;\n if (method === 'publishOffer' && params?.offers && Array.isArray(params.offers)) {\n totalOperations += params.offers.length;\n } else if (method === 'addIceCandidates' && params?.candidates && Array.isArray(params.candidates)) {\n totalOperations += params.candidates.length;\n } else {\n totalOperations += 1;\n }\n }\n\n if (totalOperations > config.maxTotalOperations) {\n return requests.map(() => ({\n success: false,\n error: `Total operations across batch exceed limit: ${totalOperations} > ${config.maxTotalOperations}`,\n errorCode: ErrorCodes.BATCH_TOO_LARGE,\n }));\n }\n\n // Process all requests\n for (const request of requests) {\n try {\n const { method, params } = request;\n\n if (!method || typeof method !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid method',\n errorCode: ErrorCodes.INVALID_PARAMS,\n });\n continue;\n }\n\n const handler = handlers[method];\n if (!handler) {\n responses.push({\n success: false,\n error: `Unknown method: ${method}`,\n errorCode: ErrorCodes.UNKNOWN_METHOD,\n });\n continue;\n }\n\n const requiresAuth = !UNAUTHENTICATED_METHODS.has(method);\n\n if (requiresAuth) {\n if (!publicKey || typeof publicKey !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid X-PublicKey header',\n errorCode: ErrorCodes.AUTH_REQUIRED,\n });\n continue;\n }\n\n if (!timestampHeader || typeof timestampHeader !== 'string' || isNaN(timestamp)) {\n responses.push({\n success: false,\n error: 'Missing or invalid X-Timestamp header',\n errorCode: ErrorCodes.AUTH_REQUIRED,\n });\n continue;\n }\n\n if (!nonce || typeof nonce !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid X-Nonce header (use crypto.randomUUID())',\n errorCode: ErrorCodes.AUTH_REQUIRED,\n });\n continue;\n }\n\n if (!signature || typeof signature !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid X-Signature header',\n errorCode: ErrorCodes.AUTH_REQUIRED,\n });\n continue;\n }\n\n // Verify Ed25519 signature\n await verifyRequestSignature(\n publicKey,\n timestamp,\n nonce,\n signature,\n method,\n params,\n storage,\n config\n );\n\n const result = await handler(\n params || {},\n publicKey,\n timestamp,\n signature,\n storage,\n config,\n { ...request, clientIp }\n );\n\n responses.push({\n success: true,\n result,\n });\n } else {\n // Execute handler without strict auth requirement\n const result = await handler(\n params || {},\n publicKey || '',\n 0,\n '',\n storage,\n config,\n { ...request, clientIp }\n );\n\n responses.push({\n success: true,\n result,\n });\n }\n } catch (err) {\n if (err instanceof RpcError) {\n responses.push({\n success: false,\n error: err.message,\n errorCode: err.errorCode,\n });\n } else {\n console.error('Unexpected RPC error:', err);\n responses.push({\n success: false,\n error: 'Internal server error',\n errorCode: ErrorCodes.INTERNAL_ERROR,\n });\n }\n }\n }\n\n return responses;\n}\n", "import { Storage } from './storage/types.ts';\nimport { StorageType } from './storage/factory.ts';\n\n// Version is injected at build time via esbuild define\ndeclare const RONDEVU_VERSION: string;\nconst BUILD_VERSION = typeof RONDEVU_VERSION !== 'undefined' ? RONDEVU_VERSION : 'unknown';\n\n/**\n * Application configuration\n * Reads from environment variables with sensible defaults\n */\nexport interface Config {\n port: number;\n storageType: StorageType;\n storagePath: string;\n databaseUrl: string;\n dbPoolSize: number;\n corsOrigins: string[];\n version: string;\n offerDefaultTtl: number;\n offerMaxTtl: number;\n offerMinTtl: number;\n cleanupInterval: number;\n maxOffersPerRequest: number;\n maxBatchSize: number;\n maxSdpSize: number;\n maxCandidateSize: number;\n maxCandidateDepth: number;\n maxCandidatesPerRequest: number;\n maxTotalOperations: number;\n timestampMaxAge: number; // Max age for timestamps (replay protection)\n timestampMaxFuture: number; // Max future tolerance for timestamps (clock skew)\n // Resource limits (for abuse prevention)\n maxOffersPerUser: number; // Max concurrent offers per user\n maxTotalOffers: number; // Max total offers in storage\n maxIceCandidatesPerOffer: number; // Max ICE candidates per offer\n requestsPerIpPerSecond: number; // Rate limit: requests per IP per second\n}\n\n/**\n * Loads configuration from environment variables\n */\nexport function loadConfig(): Config {\n // Helper to safely parse and validate integer config values\n function parsePositiveInt(value: string | undefined, defaultValue: string, name: string, min = 1): number {\n const parsed = parseInt(value || defaultValue, 10);\n if (isNaN(parsed)) {\n throw new Error(`${name} must be a valid integer (got: ${value})`);\n }\n if (parsed < min) {\n throw new Error(`${name} must be >= ${min} (got: ${parsed})`);\n }\n return parsed;\n }\n\n const config = {\n port: parsePositiveInt(process.env.PORT, '3000', 'PORT', 1),\n storageType: (process.env.STORAGE_TYPE || 'memory') as StorageType,\n storagePath: process.env.STORAGE_PATH || ':memory:',\n databaseUrl: process.env.DATABASE_URL || '',\n dbPoolSize: parsePositiveInt(process.env.DB_POOL_SIZE, '10', 'DB_POOL_SIZE', 1),\n corsOrigins: process.env.CORS_ORIGINS\n ? process.env.CORS_ORIGINS.split(',').map(o => o.trim())\n : ['*'],\n version: process.env.VERSION || BUILD_VERSION,\n offerDefaultTtl: parsePositiveInt(process.env.OFFER_DEFAULT_TTL, '60000', 'OFFER_DEFAULT_TTL', 1000),\n offerMaxTtl: parsePositiveInt(process.env.OFFER_MAX_TTL, '86400000', 'OFFER_MAX_TTL', 1000),\n offerMinTtl: parsePositiveInt(process.env.OFFER_MIN_TTL, '60000', 'OFFER_MIN_TTL', 1000),\n cleanupInterval: parsePositiveInt(process.env.CLEANUP_INTERVAL, '60000', 'CLEANUP_INTERVAL', 1000),\n maxOffersPerRequest: parsePositiveInt(process.env.MAX_OFFERS_PER_REQUEST, '100', 'MAX_OFFERS_PER_REQUEST', 1),\n maxBatchSize: parsePositiveInt(process.env.MAX_BATCH_SIZE, '100', 'MAX_BATCH_SIZE', 1),\n maxSdpSize: parsePositiveInt(process.env.MAX_SDP_SIZE, String(64 * 1024), 'MAX_SDP_SIZE', 1024), // Min 1KB\n maxCandidateSize: parsePositiveInt(process.env.MAX_CANDIDATE_SIZE, String(4 * 1024), 'MAX_CANDIDATE_SIZE', 256), // Min 256 bytes\n maxCandidateDepth: parsePositiveInt(process.env.MAX_CANDIDATE_DEPTH, '10', 'MAX_CANDIDATE_DEPTH', 1),\n maxCandidatesPerRequest: parsePositiveInt(process.env.MAX_CANDIDATES_PER_REQUEST, '100', 'MAX_CANDIDATES_PER_REQUEST', 1),\n maxTotalOperations: parsePositiveInt(process.env.MAX_TOTAL_OPERATIONS, '1000', 'MAX_TOTAL_OPERATIONS', 1),\n timestampMaxAge: parsePositiveInt(process.env.TIMESTAMP_MAX_AGE, '60000', 'TIMESTAMP_MAX_AGE', 1000), // Min 1 second\n timestampMaxFuture: parsePositiveInt(process.env.TIMESTAMP_MAX_FUTURE, '60000', 'TIMESTAMP_MAX_FUTURE', 1000), // Min 1 second\n // Resource limits\n maxOffersPerUser: parsePositiveInt(process.env.MAX_OFFERS_PER_USER, '1000', 'MAX_OFFERS_PER_USER', 1),\n maxTotalOffers: parsePositiveInt(process.env.MAX_TOTAL_OFFERS, '100000', 'MAX_TOTAL_OFFERS', 1),\n maxIceCandidatesPerOffer: parsePositiveInt(process.env.MAX_ICE_CANDIDATES_PER_OFFER, '50', 'MAX_ICE_CANDIDATES_PER_OFFER', 1),\n requestsPerIpPerSecond: parsePositiveInt(process.env.REQUESTS_PER_IP_PER_SECOND, '50', 'REQUESTS_PER_IP_PER_SECOND', 1),\n };\n\n return config;\n}\n\n/**\n * Default config values (shared between Node and Workers)\n */\nexport const CONFIG_DEFAULTS = {\n offerDefaultTtl: 60000,\n offerMaxTtl: 86400000,\n offerMinTtl: 60000,\n cleanupInterval: 60000,\n maxOffersPerRequest: 100,\n maxBatchSize: 100,\n maxSdpSize: 64 * 1024,\n maxCandidateSize: 4 * 1024,\n maxCandidateDepth: 10,\n maxCandidatesPerRequest: 100,\n maxTotalOperations: 1000,\n timestampMaxAge: 60000,\n timestampMaxFuture: 60000,\n // Resource limits\n maxOffersPerUser: 1000,\n maxTotalOffers: 100000,\n maxIceCandidatesPerOffer: 50,\n requestsPerIpPerSecond: 50,\n} as const;\n\n/**\n * Build config for Cloudflare Workers from env vars\n */\nexport function buildWorkerConfig(env: {\n OFFER_DEFAULT_TTL?: string;\n OFFER_MAX_TTL?: string;\n OFFER_MIN_TTL?: string;\n MAX_OFFERS_PER_REQUEST?: string;\n MAX_BATCH_SIZE?: string;\n CORS_ORIGINS?: string;\n VERSION?: string;\n}): Config {\n return {\n port: 0, // Not used in Workers\n storageType: 'sqlite', // D1 is SQLite-compatible\n storagePath: '', // Not used with D1\n databaseUrl: '', // Not used with D1\n dbPoolSize: 10, // Not used with D1\n corsOrigins: env.CORS_ORIGINS?.split(',').map(o => o.trim()) ?? ['*'],\n version: env.VERSION ?? 'unknown',\n offerDefaultTtl: env.OFFER_DEFAULT_TTL ? parseInt(env.OFFER_DEFAULT_TTL, 10) : CONFIG_DEFAULTS.offerDefaultTtl,\n offerMaxTtl: env.OFFER_MAX_TTL ? parseInt(env.OFFER_MAX_TTL, 10) : CONFIG_DEFAULTS.offerMaxTtl,\n offerMinTtl: env.OFFER_MIN_TTL ? parseInt(env.OFFER_MIN_TTL, 10) : CONFIG_DEFAULTS.offerMinTtl,\n cleanupInterval: CONFIG_DEFAULTS.cleanupInterval,\n maxOffersPerRequest: env.MAX_OFFERS_PER_REQUEST ? parseInt(env.MAX_OFFERS_PER_REQUEST, 10) : CONFIG_DEFAULTS.maxOffersPerRequest,\n maxBatchSize: env.MAX_BATCH_SIZE ? parseInt(env.MAX_BATCH_SIZE, 10) : CONFIG_DEFAULTS.maxBatchSize,\n maxSdpSize: CONFIG_DEFAULTS.maxSdpSize,\n maxCandidateSize: CONFIG_DEFAULTS.maxCandidateSize,\n maxCandidateDepth: CONFIG_DEFAULTS.maxCandidateDepth,\n maxCandidatesPerRequest: CONFIG_DEFAULTS.maxCandidatesPerRequest,\n maxTotalOperations: CONFIG_DEFAULTS.maxTotalOperations,\n timestampMaxAge: CONFIG_DEFAULTS.timestampMaxAge,\n timestampMaxFuture: CONFIG_DEFAULTS.timestampMaxFuture,\n // Resource limits\n maxOffersPerUser: CONFIG_DEFAULTS.maxOffersPerUser,\n maxTotalOffers: CONFIG_DEFAULTS.maxTotalOffers,\n maxIceCandidatesPerOffer: CONFIG_DEFAULTS.maxIceCandidatesPerOffer,\n requestsPerIpPerSecond: CONFIG_DEFAULTS.requestsPerIpPerSecond,\n };\n}\n\n/**\n * Run cleanup of expired entries (shared between Node and Workers)\n * @returns Object with counts of deleted items\n */\nexport async function runCleanup(storage: Storage, now: number): Promise<{\n offers: number;\n rateLimits: number;\n nonces: number;\n}> {\n const offers = await storage.deleteExpiredOffers(now);\n const rateLimits = await storage.deleteExpiredRateLimits(now);\n const nonces = await storage.deleteExpiredNonces(now);\n\n return { offers, rateLimits, nonces };\n}\n", "import { Storage } from './types.ts';\n\n/**\n * Supported storage backend types\n */\nexport type StorageType = 'memory' | 'sqlite' | 'mysql' | 'postgres';\n\n/**\n * Configuration for creating a storage backend\n */\nexport interface StorageConfig {\n type: StorageType;\n /** SQLite database path (default: ':memory:') */\n sqlitePath?: string;\n /** Connection string for MySQL/PostgreSQL */\n connectionString?: string;\n /** Connection pool size for MySQL/PostgreSQL (default: 10) */\n poolSize?: number;\n}\n\n/**\n * Creates a storage backend based on configuration\n * Uses dynamic imports to avoid loading unused dependencies\n */\nexport async function createStorage(config: StorageConfig): Promise<Storage> {\n switch (config.type) {\n case 'memory': {\n const { MemoryStorage } = await import('./memory.ts');\n return new MemoryStorage();\n }\n\n case 'sqlite': {\n const { SQLiteStorage } = await import('./sqlite.ts');\n return new SQLiteStorage(config.sqlitePath || ':memory:');\n }\n\n case 'mysql': {\n if (!config.connectionString) {\n throw new Error('MySQL storage requires DATABASE_URL connection string');\n }\n const { MySQLStorage } = await import('./mysql.ts');\n return MySQLStorage.create(config.connectionString, config.poolSize || 10);\n }\n\n case 'postgres': {\n if (!config.connectionString) {\n throw new Error('PostgreSQL storage requires DATABASE_URL connection string');\n }\n const { PostgreSQLStorage } = await import('./postgres.ts');\n return PostgreSQLStorage.create(config.connectionString, config.poolSize || 10);\n }\n\n default:\n throw new Error(`Unsupported storage type: ${config.type}`);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["Database", "offersWithIds", "candidates", "mysql", "C", "G", "hexToBytes", "excludePublicKey", "offer", "err", "err", "MemoryStorage", "SQLiteStorage", "MySQLStorage", "PostgreSQLStorage", "err"]
|
|
7
7
|
}
|
package/package.json
CHANGED
package/src/app.ts
CHANGED
|
@@ -22,7 +22,7 @@ export function createApp(storage: Storage, config: Config) {
|
|
|
22
22
|
return config.corsOrigins[0];
|
|
23
23
|
},
|
|
24
24
|
allowMethods: ['GET', 'POST', 'OPTIONS'],
|
|
25
|
-
allowHeaders: ['Content-Type', 'Origin', 'X-
|
|
25
|
+
allowHeaders: ['Content-Type', 'Origin', 'X-PublicKey', 'X-Timestamp', 'X-Nonce', 'X-Signature'],
|
|
26
26
|
exposeHeaders: ['Content-Type'],
|
|
27
27
|
credentials: false,
|
|
28
28
|
maxAge: 86400,
|
|
@@ -33,7 +33,7 @@ export function createApp(storage: Storage, config: Config) {
|
|
|
33
33
|
return c.json({
|
|
34
34
|
version: config.version,
|
|
35
35
|
name: 'Rondevu',
|
|
36
|
-
description: 'WebRTC signaling with RPC interface and
|
|
36
|
+
description: 'WebRTC signaling with RPC interface and Ed25519 signature-based authentication',
|
|
37
37
|
}, 200);
|
|
38
38
|
});
|
|
39
39
|
|