@chittyos/core
Version:
ChittyOS Core - Essential package with ID, auth, verification, beacon tracking, and brand components for all ChittyOS applications
1 lines • 95 kB
Source Map (JSON)
{"version":3,"sources":["../src/beacon/index.ts","../src/id/index.ts","../src/auth/index.ts","../src/verify/index.ts","../src/brand/index.ts","../src/canon/index.ts","../src/registry/index.ts","../src/chittychat/index.ts","../src/index.ts"],"sourcesContent":["/**\n * ChittyOS Beacon - Application tracking and monitoring\n */\n\nimport { nanoid } from 'nanoid'\nimport * as os from 'os'\nimport * as fs from 'fs'\nimport { execSync } from 'child_process'\n\nexport interface BeaconConfig {\n endpoint?: string\n interval?: number\n enabled?: boolean\n silent?: boolean\n appId?: string\n appName?: string\n}\n\nexport interface AppInfo {\n id: string\n name: string\n version: string\n platform: string\n environment: string\n hostname: string\n nodeVersion: string\n os: string\n hasClaudeCode: boolean\n hasGit: boolean\n startedAt: string\n pid: number\n git?: {\n branch: string\n commit: string\n remote: string\n }\n chittyos?: {\n core: string\n modules: string[]\n }\n}\n\nconst DEFAULT_CONFIG: BeaconConfig = {\n endpoint: process.env.CHITTY_BEACON_ENDPOINT || 'https://beacon.chitty.cc',\n interval: parseInt(process.env.CHITTY_BEACON_INTERVAL || '') || 300000,\n enabled: process.env.CHITTY_BEACON_DISABLED !== 'true',\n silent: process.env.CHITTY_BEACON_VERBOSE !== 'true'\n}\n\nlet config = { ...DEFAULT_CONFIG }\nlet appInfo: AppInfo | null = null\nlet heartbeatInterval: NodeJS.Timeout | null = null\n\nexport function configure(customConfig: BeaconConfig): void {\n config = { ...config, ...customConfig }\n}\n\nexport function detectApp(): AppInfo {\n const app: AppInfo = {\n id: generateAppId(),\n name: detectAppName(),\n version: detectVersion(),\n platform: detectPlatform(),\n environment: process.env.NODE_ENV || 'production',\n hostname: os.hostname(),\n nodeVersion: process.version,\n os: `${os.type()} ${os.release()}`,\n hasClaudeCode: detectClaudeCode(),\n hasGit: fs.existsSync('.git'),\n startedAt: new Date().toISOString(),\n pid: process.pid,\n chittyos: {\n core: '1.0.0',\n modules: detectChittyModules()\n }\n }\n\n // Add git info if available\n if (app.hasGit) {\n try {\n app.git = {\n branch: execSync('git branch --show-current', { encoding: 'utf8' }).trim(),\n commit: execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim(),\n remote: execSync('git remote get-url origin', { encoding: 'utf8' }).trim()\n }\n } catch (e) {\n // Ignore git errors\n }\n }\n\n return app\n}\n\nfunction generateAppId(): string {\n if (config.appId) return config.appId\n\n // Platform-specific IDs\n if (process.env.REPL_ID) return `replit-${process.env.REPL_ID}`\n if (process.env.GITHUB_REPOSITORY) return `github-${process.env.GITHUB_REPOSITORY.replace('/', '-')}`\n if (process.env.VERCEL_URL) return `vercel-${process.env.VERCEL_URL}`\n if (process.env.HEROKU_APP_NAME) return `heroku-${process.env.HEROKU_APP_NAME}`\n\n // Generate from package.json or create unique ID\n try {\n const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'))\n return `npm-${pkg.name}-${nanoid(8)}`\n } catch (e) {\n return `chitty-${nanoid()}`\n }\n}\n\nfunction detectAppName(): string {\n if (config.appName) return config.appName\n\n return process.env.CHITTY_APP_NAME ||\n process.env.REPL_SLUG ||\n process.env.GITHUB_REPOSITORY ||\n process.env.VERCEL_URL ||\n process.env.HEROKU_APP_NAME ||\n process.env.npm_package_name ||\n (() => {\n try {\n const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'))\n return pkg.name\n } catch (e) {\n return 'chittyos-app'\n }\n })()\n}\n\nfunction detectVersion(): string {\n try {\n const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'))\n return pkg.version\n } catch (e) {\n return '0.0.0'\n }\n}\n\nfunction detectPlatform(): string {\n if (process.env.REPL_ID) return 'replit'\n if (process.env.GITHUB_ACTIONS) return 'github-actions'\n if (process.env.VERCEL) return 'vercel'\n if (process.env.NETLIFY) return 'netlify'\n if (process.env.RENDER) return 'render'\n if (process.env.HEROKU_APP_NAME) return 'heroku'\n if (process.env.AWS_LAMBDA_FUNCTION_NAME) return 'aws-lambda'\n if (process.env.GOOGLE_CLOUD_PROJECT) return 'google-cloud'\n if (process.env.WEBSITE_INSTANCE_ID) return 'azure'\n if (process.env.CF_PAGES) return 'cloudflare-pages'\n if (process.env.CLOUDFLARE_ACCOUNT_ID) return 'cloudflare-workers'\n return 'unknown'\n}\n\nfunction detectClaudeCode(): boolean {\n return process.env.CLAUDE_CODE === 'true' ||\n fs.existsSync('.claude') ||\n fs.existsSync('CLAUDE.md') ||\n fs.existsSync('claude.json')\n}\n\nfunction detectChittyModules(): string[] {\n const modules: string[] = []\n\n try {\n const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'))\n const deps = { ...pkg.dependencies, ...pkg.devDependencies }\n\n for (const dep of Object.keys(deps)) {\n if (dep.startsWith('@chittyos/') || dep.startsWith('@chittycorp/')) {\n modules.push(dep)\n }\n }\n } catch (e) {\n // Ignore errors\n }\n\n return modules\n}\n\nexport async function sendBeacon(event: string, data?: any): Promise<void> {\n if (!config.enabled) return\n\n const payload = {\n ...appInfo,\n event,\n timestamp: new Date().toISOString(),\n uptime: process.uptime(),\n ...data\n }\n\n try {\n const response = await fetch(`${config.endpoint}/track`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': '@chittyos/core/1.0.0'\n },\n body: JSON.stringify(payload)\n })\n\n if (!config.silent && !response.ok) {\n console.log(`[ChittyBeacon] Response: ${response.status}`)\n }\n } catch (error) {\n if (!config.silent) {\n console.log(`[ChittyBeacon] Error: ${(error as Error).message}`)\n }\n }\n}\n\nexport function init(customConfig?: BeaconConfig): void {\n if (customConfig) {\n configure(customConfig)\n }\n\n if (!config.enabled) {\n if (!config.silent) {\n console.log('[ChittyBeacon] Disabled')\n }\n return\n }\n\n appInfo = detectApp()\n\n // Send startup beacon\n sendBeacon('startup')\n\n // Send periodic heartbeats\n heartbeatInterval = setInterval(() => {\n sendBeacon('heartbeat')\n }, config.interval!)\n\n // Don't keep process alive just for beacon\n heartbeatInterval.unref()\n\n // Send shutdown beacon\n const shutdown = () => {\n sendBeacon('shutdown')\n }\n\n process.once('exit', shutdown)\n process.once('SIGINT', shutdown)\n process.once('SIGTERM', shutdown)\n\n if (!config.silent) {\n console.log(`[ChittyBeacon] Tracking ${appInfo.name} on ${appInfo.platform}`)\n }\n}\n\nexport function stop(): void {\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval)\n heartbeatInterval = null\n }\n sendBeacon('stopped')\n}\n\n// Auto-init if not in test environment\nif (process.env.NODE_ENV !== 'test') {\n init()\n}","/**\n * ChittyOS ID - Identity generation and management\n */\n\nimport { nanoid } from 'nanoid'\nimport * as crypto from 'crypto'\n\nexport interface ChittyID {\n id: string\n publicKey?: string\n privateKey?: string\n trustLevel: number\n createdAt: string\n metadata?: Record<string, any>\n}\n\nexport interface ChittyIDConfig {\n endpoint?: string\n apiKey?: string\n generateKeys?: boolean\n}\n\n// Extend globalThis to include session context\ndeclare global {\n var chittySessionId: string | undefined\n}\n\nconst DEFAULT_CONFIG: ChittyIDConfig = {\n endpoint: process.env.CHITTY_ID_ENDPOINT || 'https://id.chitty.cc',\n apiKey: process.env.CHITTY_ID_API_KEY,\n generateKeys: true\n}\n\nlet config = { ...DEFAULT_CONFIG }\n\nexport function configure(customConfig: ChittyIDConfig): void {\n config = { ...config, ...customConfig }\n}\n\n/**\n * REMOVED: ChittyCore does not generate ChittyIDs locally\n * All ChittyIDs must be requested from id.chitty.cc service\n * Use generate() or requestChittyID() instead\n */\n\n/**\n * Request a ChittyID from the ChittyID service at id.chitty.cc\n * ChittyCore does not generate IDs - it requests them from the service\n */\nexport async function requestChittyID(metadata?: Record<string, any>): Promise<ChittyID> {\n if (!config.apiKey) {\n throw new Error('ChittyID request requires API key authentication to access id.chitty.cc service')\n }\n\n try {\n // Request ChittyID from the id.chitty.cc service\n const response = await fetch(`${config.endpoint}/api/pipeline/generate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`\n },\n body: JSON.stringify({\n metadata,\n // Include session context for distributed sync\n sessionId: globalThis.chittySessionId || undefined\n })\n })\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error('ChittyID request failed: Invalid or expired API key for id.chitty.cc')\n }\n if (response.status === 403) {\n throw new Error('ChittyID request failed: Insufficient permissions for id.chitty.cc service')\n }\n throw new Error(`ChittyID service error: ${response.status} - ${response.statusText}`)\n }\n\n const result = await response.json() as ChittyID\n\n // Store session ID for cross-service sync if provided\n if (result.metadata?.sessionId) {\n globalThis.chittySessionId = result.metadata.sessionId\n }\n\n return result\n } catch (error) {\n // Re-throw all errors - no fallback in pipeline-only architecture\n throw error\n }\n}\n\n/**\n * Request a ChittyID from the id.chitty.cc service\n * ChittyCore does not generate IDs - it requests them from the service\n */\nexport async function generate(metadata?: Record<string, any>): Promise<ChittyID> {\n if (!config.endpoint) {\n throw new Error('ChittyID service endpoint not configured - set CHITTY_ID_ENDPOINT to id.chitty.cc')\n }\n\n // Request ChittyID from id.chitty.cc service\n return await requestChittyID(metadata)\n}\n\n/**\n * Verify a ChittyID signature\n */\nexport function verifySignature(\n chittyId: string,\n signature: string,\n data: string,\n publicKey: string\n): boolean {\n try {\n const verify = crypto.createVerify('SHA256')\n verify.update(data)\n return verify.verify(publicKey, signature, 'base64')\n } catch (error) {\n return false\n }\n}\n\n/**\n * Sign data with a ChittyID private key\n */\nexport function signData(data: string, privateKey: string): string {\n const sign = crypto.createSign('SHA256')\n sign.update(data)\n return sign.sign(privateKey, 'base64')\n}\n\n/**\n * Validate ChittyID format\n */\nexport function isValidChittyID(id: string): boolean {\n return /^CID_[A-Za-z0-9_-]{21}$/.test(id)\n}\n\n/**\n * Parse ChittyID from various formats\n */\nexport function parseChittyID(input: string): string | null {\n // Direct ChittyID\n if (isValidChittyID(input)) return input\n\n // Extract from JWT or other tokens\n const cidMatch = input.match(/CID_[A-Za-z0-9_-]{21}/)\n if (cidMatch) return cidMatch[0]\n\n return null\n}\n\n/**\n * Validate a ChittyID via the pipeline validation endpoint\n */\nexport async function validateRemote(chittyId: string): Promise<boolean> {\n try {\n const response = await fetch(`${config.endpoint}/api/validate/${chittyId}`, {\n method: 'GET',\n headers: {\n ...(config.apiKey && { 'Authorization': `Bearer ${config.apiKey}` })\n }\n })\n\n if (response.ok) {\n const result = await response.json() as { valid: boolean }\n return result.valid === true\n }\n\n return false\n } catch (error) {\n console.warn('[ChittyID] Validation service unavailable, using local validation')\n return isValidChittyID(chittyId)\n }\n}\n\n/**\n * Get session context for distributed sync\n */\nexport function getSessionContext(): string | undefined {\n return globalThis.chittySessionId\n}\n\n/**\n * Set session context for distributed sync\n */\nexport function setSessionContext(sessionId: string): void {\n globalThis.chittySessionId = sessionId\n}\n\nexport default {\n configure,\n generate,\n requestChittyID,\n validateRemote,\n verifySignature,\n signData,\n isValidChittyID,\n parseChittyID,\n getSessionContext,\n setSessionContext\n}","/**\n * ChittyOS Auth - Authentication and authorization\n */\n\nimport { SignJWT, jwtVerify, type JWTPayload } from 'jose'\nimport { nanoid } from 'nanoid'\nimport * as crypto from 'crypto'\n\nexport interface AuthConfig {\n jwtSecret?: string\n issuer?: string\n audience?: string\n expiresIn?: string\n}\n\nexport interface AuthToken {\n token: string\n expiresAt: Date\n refreshToken?: string\n}\n\nexport interface AuthUser {\n id: string\n chittyId?: string\n email?: string\n roles?: string[]\n permissions?: string[]\n metadata?: Record<string, any>\n}\n\nexport interface AuthSession {\n id: string\n userId: string\n token: string\n refreshToken: string\n expiresAt: Date\n createdAt: Date\n}\n\nconst DEFAULT_CONFIG: AuthConfig = {\n jwtSecret: process.env.CHITTY_JWT_SECRET || crypto.randomBytes(32).toString('base64'),\n issuer: 'chittyos',\n audience: 'chittyos-apps',\n expiresIn: '24h'\n}\n\nlet config = { ...DEFAULT_CONFIG }\nconst sessions = new Map<string, AuthSession>()\n\nexport function configure(customConfig: AuthConfig): void {\n config = { ...config, ...customConfig }\n}\n\n/**\n * Create a JWT token for a user\n */\nexport async function createToken(user: AuthUser): Promise<AuthToken> {\n const secret = new TextEncoder().encode(config.jwtSecret!)\n\n const expiresAt = new Date()\n const hours = parseInt(config.expiresIn?.replace('h', '') || '24')\n expiresAt.setHours(expiresAt.getHours() + hours)\n\n const token = await new SignJWT({\n sub: user.id,\n chittyId: user.chittyId,\n email: user.email,\n roles: user.roles || [],\n permissions: user.permissions || [],\n metadata: user.metadata || {}\n })\n .setProtectedHeader({ alg: 'HS256' })\n .setIssuedAt()\n .setIssuer(config.issuer!)\n .setAudience(config.audience!)\n .setExpirationTime(config.expiresIn!)\n .setJti(nanoid())\n .sign(secret)\n\n const refreshToken = nanoid(32)\n\n // Store session\n const session: AuthSession = {\n id: nanoid(),\n userId: user.id,\n token,\n refreshToken,\n expiresAt,\n createdAt: new Date()\n }\n sessions.set(session.id, session)\n sessions.set(refreshToken, session)\n\n return {\n token,\n expiresAt,\n refreshToken\n }\n}\n\n/**\n * Verify and decode a JWT token\n */\nexport async function verifyToken(token: string): Promise<JWTPayload & AuthUser> {\n try {\n const secret = new TextEncoder().encode(config.jwtSecret!)\n\n const { payload } = await jwtVerify(token, secret, {\n issuer: config.issuer!,\n audience: config.audience!\n })\n\n return {\n ...payload,\n id: payload.sub!,\n chittyId: payload.chittyId as string | undefined,\n email: payload.email as string | undefined,\n roles: payload.roles as string[] | undefined,\n permissions: payload.permissions as string[] | undefined,\n metadata: payload.metadata as Record<string, any> | undefined\n }\n } catch (error) {\n throw new Error('Invalid or expired token')\n }\n}\n\n/**\n * Refresh an auth token\n */\nexport async function refreshToken(refreshToken: string): Promise<AuthToken> {\n const session = sessions.get(refreshToken)\n\n if (!session) {\n throw new Error('Invalid refresh token')\n }\n\n if (session.expiresAt < new Date()) {\n sessions.delete(session.id)\n sessions.delete(session.refreshToken)\n throw new Error('Session expired')\n }\n\n // Decode the old token to get user info\n const user = await verifyToken(session.token).catch(() => null)\n\n if (!user) {\n throw new Error('Cannot refresh token')\n }\n\n // Create new token\n return createToken({\n id: user.id,\n chittyId: user.chittyId,\n email: user.email,\n roles: user.roles,\n permissions: user.permissions,\n metadata: user.metadata\n })\n}\n\n/**\n * Revoke a session\n */\nexport function revokeSession(sessionId: string): boolean {\n const session = sessions.get(sessionId)\n\n if (session) {\n sessions.delete(session.id)\n sessions.delete(session.refreshToken)\n return true\n }\n\n return false\n}\n\n/**\n * Check if user has required roles\n */\nexport function hasRoles(user: AuthUser, requiredRoles: string[]): boolean {\n if (!user.roles) return false\n return requiredRoles.every(role => user.roles!.includes(role))\n}\n\n/**\n * Check if user has required permissions\n */\nexport function hasPermissions(user: AuthUser, requiredPermissions: string[]): boolean {\n if (!user.permissions) return false\n return requiredPermissions.every(perm => user.permissions!.includes(perm))\n}\n\n/**\n * Generate a secure password hash\n */\nexport async function hashPassword(password: string): Promise<string> {\n const salt = crypto.randomBytes(16).toString('hex')\n const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex')\n return `${salt}:${hash}`\n}\n\n/**\n * Verify a password against its hash\n */\nexport async function verifyPassword(password: string, hashedPassword: string): Promise<boolean> {\n const [salt, hash] = hashedPassword.split(':')\n const verifyHash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex')\n return hash === verifyHash\n}\n\n/**\n * Clean up expired sessions\n */\nexport function cleanupSessions(): void {\n const now = new Date()\n\n for (const [key, session] of sessions) {\n if (session.expiresAt < now) {\n sessions.delete(key)\n }\n }\n}\n\n// Run cleanup every hour\nsetInterval(cleanupSessions, 3600000).unref()\n\nexport default {\n configure,\n createToken,\n verifyToken,\n refreshToken,\n revokeSession,\n hasRoles,\n hasPermissions,\n hashPassword,\n verifyPassword,\n cleanupSessions\n}","/**\n * ChittyOS Verify - Data verification and validation\n */\n\nimport { z, ZodSchema } from 'zod'\nimport * as crypto from 'crypto'\n\nexport interface VerifyConfig {\n strictMode?: boolean\n hashAlgorithm?: string\n}\n\nexport interface VerificationResult {\n valid: boolean\n errors?: string[]\n warnings?: string[]\n}\n\nexport interface SignedData {\n data: any\n signature: string\n timestamp: string\n chittyId?: string\n}\n\nconst DEFAULT_CONFIG: VerifyConfig = {\n strictMode: false,\n hashAlgorithm: 'sha256'\n}\n\nlet config = { ...DEFAULT_CONFIG }\n\nexport function configure(customConfig: VerifyConfig): void {\n config = { ...config, ...customConfig }\n}\n\n/**\n * Validate data against a Zod schema\n */\nexport function validateSchema<T>(\n data: unknown,\n schema: ZodSchema<T>\n): VerificationResult & { data?: T } {\n try {\n const validated = schema.parse(data)\n return {\n valid: true,\n data: validated\n }\n } catch (error) {\n if (error instanceof z.ZodError) {\n return {\n valid: false,\n errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`)\n }\n }\n return {\n valid: false,\n errors: ['Validation failed']\n }\n }\n}\n\n/**\n * Common validation schemas\n */\nexport const schemas = {\n email: z.string().email(),\n\n chittyId: z.string().regex(/^CID_[A-Za-z0-9_-]{21}$/),\n\n uuid: z.string().uuid(),\n\n url: z.string().url(),\n\n phoneNumber: z.string().regex(/^\\+?[1-9]\\d{1,14}$/),\n\n dateTime: z.string().datetime(),\n\n ipAddress: z.string().ip(),\n\n semver: z.string().regex(/^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9]+)?$/),\n\n strongPassword: z.string()\n .min(8)\n .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]/),\n\n jwt: z.string().regex(/^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$/),\n\n base64: z.string().regex(/^[A-Za-z0-9+/]*={0,2}$/),\n\n hexColor: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/),\n\n creditCard: z.string().regex(/^\\d{13,19}$/).refine(luhnCheck, {\n message: 'Invalid credit card number'\n })\n}\n\n/**\n * Luhn algorithm for credit card validation\n */\nfunction luhnCheck(cardNumber: string): boolean {\n const digits = cardNumber.replace(/\\D/g, '')\n let sum = 0\n let isEven = false\n\n for (let i = digits.length - 1; i >= 0; i--) {\n let digit = parseInt(digits[i], 10)\n\n if (isEven) {\n digit *= 2\n if (digit > 9) {\n digit -= 9\n }\n }\n\n sum += digit\n isEven = !isEven\n }\n\n return sum % 10 === 0\n}\n\n/**\n * Calculate hash of data\n */\nexport function hashData(data: any, algorithm = config.hashAlgorithm!): string {\n const stringData = typeof data === 'string' ? data : JSON.stringify(data)\n return crypto.createHash(algorithm).update(stringData).digest('hex')\n}\n\n/**\n * Verify data integrity using hash\n */\nexport function verifyIntegrity(data: any, expectedHash: string, algorithm = config.hashAlgorithm!): boolean {\n const actualHash = hashData(data, algorithm)\n return actualHash === expectedHash\n}\n\n/**\n * Create a signed data object\n */\nexport function signData(data: any, privateKey: string, chittyId?: string): SignedData {\n const timestamp = new Date().toISOString()\n const dataToSign = JSON.stringify({ data, timestamp })\n\n const sign = crypto.createSign('SHA256')\n sign.update(dataToSign)\n const signature = sign.sign(privateKey, 'base64')\n\n return {\n data,\n signature,\n timestamp,\n chittyId\n }\n}\n\n/**\n * Verify a signed data object\n */\nexport function verifySignedData(signedData: SignedData, publicKey: string): VerificationResult {\n try {\n const dataToVerify = JSON.stringify({\n data: signedData.data,\n timestamp: signedData.timestamp\n })\n\n const verify = crypto.createVerify('SHA256')\n verify.update(dataToVerify)\n const isValid = verify.verify(publicKey, signedData.signature, 'base64')\n\n return {\n valid: isValid,\n errors: isValid ? undefined : ['Invalid signature']\n }\n } catch (error) {\n return {\n valid: false,\n errors: ['Signature verification failed']\n }\n }\n}\n\n/**\n * Validate file checksum\n */\nexport async function validateChecksum(\n filePath: string,\n expectedChecksum: string,\n algorithm = 'sha256'\n): Promise<boolean> {\n const fs = await import('fs')\n const stream = fs.createReadStream(filePath)\n const hash = crypto.createHash(algorithm)\n\n return new Promise((resolve, reject) => {\n stream.on('data', (data) => hash.update(data))\n stream.on('end', () => {\n const actualChecksum = hash.digest('hex')\n resolve(actualChecksum === expectedChecksum)\n })\n stream.on('error', reject)\n })\n}\n\n/**\n * Sanitize user input\n */\nexport function sanitizeInput(input: string): string {\n // Remove control characters\n let sanitized = input.replace(/[\\x00-\\x1F\\x7F]/g, '')\n\n // Escape HTML entities\n sanitized = sanitized\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(/\\//g, '/')\n\n // Trim whitespace\n return sanitized.trim()\n}\n\n/**\n * Validate and sanitize JSON\n */\nexport function validateJSON(jsonString: string): VerificationResult & { data?: any } {\n try {\n const parsed = JSON.parse(jsonString)\n return {\n valid: true,\n data: parsed\n }\n } catch (error) {\n return {\n valid: false,\n errors: ['Invalid JSON: ' + (error as Error).message]\n }\n }\n}\n\nexport default {\n configure,\n validateSchema,\n schemas,\n hashData,\n verifyIntegrity,\n signData,\n verifySignedData,\n validateChecksum,\n sanitizeInput,\n validateJSON\n}","/**\n * ChittyOS Brand - Branding and theming utilities\n */\n\nexport interface BrandColors {\n primary: string\n secondary: string\n accent: string\n background: string\n foreground: string\n muted: string\n mutedForeground: string\n border: string\n destructive: string\n success: string\n warning: string\n info: string\n}\n\nexport interface BrandTheme {\n name: string\n colors: {\n light: BrandColors\n dark: BrandColors\n }\n fonts: {\n sans: string\n serif: string\n mono: string\n }\n radius: string\n spacing: {\n xs: string\n sm: string\n md: string\n lg: string\n xl: string\n }\n}\n\nexport const CHITTY_COLORS = {\n light: {\n primary: '#0F172A',\n secondary: '#64748B',\n accent: '#3B82F6',\n background: '#FFFFFF',\n foreground: '#0F172A',\n muted: '#F1F5F9',\n mutedForeground: '#64748B',\n border: '#E2E8F0',\n destructive: '#EF4444',\n success: '#10B981',\n warning: '#F59E0B',\n info: '#3B82F6'\n },\n dark: {\n primary: '#F8FAFC',\n secondary: '#94A3B8',\n accent: '#60A5FA',\n background: '#0F172A',\n foreground: '#F8FAFC',\n muted: '#1E293B',\n mutedForeground: '#94A3B8',\n border: '#334155',\n destructive: '#F87171',\n success: '#34D399',\n warning: '#FBBF24',\n info: '#60A5FA'\n }\n} as const\n\nexport const CHITTY_THEME: BrandTheme = {\n name: 'ChittyOS Default',\n colors: CHITTY_COLORS,\n fonts: {\n sans: 'system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif',\n serif: 'Georgia, Cambria, \"Times New Roman\", Times, serif',\n mono: 'ui-monospace, \"SF Mono\", \"Cascadia Mono\", \"Roboto Mono\", monospace'\n },\n radius: '0.5rem',\n spacing: {\n xs: '0.25rem',\n sm: '0.5rem',\n md: '1rem',\n lg: '1.5rem',\n xl: '2rem'\n }\n}\n\nexport const BRAND_CONFIG = {\n name: 'ChittyOS',\n tagline: 'Intelligent Business Operating System',\n description: 'Enterprise-grade distributed system for legal technology and business automation',\n logo: {\n text: 'ChittyOS',\n icon: '⚡',\n svg: null as string | null\n },\n copyright: `© ${new Date().getFullYear()} ChittyOS. All rights reserved.`,\n social: {\n github: 'https://github.com/chittyos',\n twitter: null as string | null,\n linkedin: null as string | null,\n discord: null as string | null\n },\n support: {\n email: 'support@chitty.cc',\n docs: 'https://docs.chitty.cc',\n status: 'https://status.chitty.cc'\n }\n} as const\n\n/**\n * Get CSS variables for theme colors\n */\nexport function getCSSVariables(theme: 'light' | 'dark' = 'light'): string {\n const colors = CHITTY_COLORS[theme]\n const vars: string[] = []\n\n for (const [key, value] of Object.entries(colors)) {\n const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase()\n vars.push(`--chitty-${cssKey}: ${value};`)\n }\n\n // Add font variables\n vars.push(`--chitty-font-sans: ${CHITTY_THEME.fonts.sans};`)\n vars.push(`--chitty-font-serif: ${CHITTY_THEME.fonts.serif};`)\n vars.push(`--chitty-font-mono: ${CHITTY_THEME.fonts.mono};`)\n\n // Add spacing variables\n for (const [key, value] of Object.entries(CHITTY_THEME.spacing)) {\n vars.push(`--chitty-spacing-${key}: ${value};`)\n }\n\n // Add radius\n vars.push(`--chitty-radius: ${CHITTY_THEME.radius};`)\n\n return vars.join('\\n ')\n}\n\n/**\n * Generate a style tag with ChittyOS theme\n */\nexport function generateStyleTag(theme: 'light' | 'dark' = 'light'): string {\n return `\n<style id=\"chittyos-theme\">\n :root {\n ${getCSSVariables(theme)}\n }\n\n .chitty-theme {\n color: var(--chitty-foreground);\n background-color: var(--chitty-background);\n font-family: var(--chitty-font-sans);\n }\n\n .chitty-primary {\n color: var(--chitty-primary);\n }\n\n .chitty-secondary {\n color: var(--chitty-secondary);\n }\n\n .chitty-accent {\n color: var(--chitty-accent);\n }\n\n .chitty-muted {\n color: var(--chitty-muted-foreground);\n background-color: var(--chitty-muted);\n }\n\n .chitty-border {\n border-color: var(--chitty-border);\n }\n\n .chitty-btn {\n padding: var(--chitty-spacing-sm) var(--chitty-spacing-md);\n border-radius: var(--chitty-radius);\n background-color: var(--chitty-primary);\n color: var(--chitty-background);\n border: none;\n cursor: pointer;\n font-family: var(--chitty-font-sans);\n }\n\n .chitty-btn:hover {\n opacity: 0.9;\n }\n\n .chitty-card {\n padding: var(--chitty-spacing-md);\n border-radius: var(--chitty-radius);\n background-color: var(--chitty-background);\n border: 1px solid var(--chitty-border);\n box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);\n }\n\n .chitty-input {\n padding: var(--chitty-spacing-sm);\n border-radius: var(--chitty-radius);\n background-color: var(--chitty-background);\n color: var(--chitty-foreground);\n border: 1px solid var(--chitty-border);\n font-family: var(--chitty-font-sans);\n }\n\n .chitty-input:focus {\n outline: none;\n border-color: var(--chitty-accent);\n box-shadow: 0 0 0 2px rgb(59 130 246 / 0.1);\n }\n\n .chitty-badge {\n padding: var(--chitty-spacing-xs) var(--chitty-spacing-sm);\n border-radius: calc(var(--chitty-radius) * 0.5);\n background-color: var(--chitty-muted);\n color: var(--chitty-muted-foreground);\n font-size: 0.875rem;\n font-weight: 500;\n }\n\n .chitty-code {\n padding: var(--chitty-spacing-xs);\n border-radius: calc(var(--chitty-radius) * 0.5);\n background-color: var(--chitty-muted);\n color: var(--chitty-foreground);\n font-family: var(--chitty-font-mono);\n font-size: 0.875rem;\n }\n</style>`\n}\n\n/**\n * Generate a React/JSX theme provider\n */\nexport function generateThemeProvider(): string {\n return `\nimport React, { createContext, useContext, useState } from 'react'\n\nconst ThemeContext = createContext({\n theme: 'light',\n toggleTheme: () => {},\n colors: ${JSON.stringify(CHITTY_COLORS.light, null, 2)}\n})\n\nexport function ChittyThemeProvider({ children }) {\n const [theme, setTheme] = useState('light')\n\n const toggleTheme = () => {\n setTheme(prev => prev === 'light' ? 'dark' : 'light')\n }\n\n const colors = theme === 'light'\n ? ${JSON.stringify(CHITTY_COLORS.light, null, 2)}\n : ${JSON.stringify(CHITTY_COLORS.dark, null, 2)}\n\n return (\n <ThemeContext.Provider value={{ theme, toggleTheme, colors }}>\n {children}\n </ThemeContext.Provider>\n )\n}\n\nexport const useChittyTheme = () => useContext(ThemeContext)\n`\n}\n\n/**\n * Generate Tailwind config for ChittyOS theme\n */\nexport function generateTailwindConfig(): object {\n return {\n theme: {\n extend: {\n colors: {\n chitty: {\n primary: 'var(--chitty-primary)',\n secondary: 'var(--chitty-secondary)',\n accent: 'var(--chitty-accent)',\n background: 'var(--chitty-background)',\n foreground: 'var(--chitty-foreground)',\n muted: 'var(--chitty-muted)',\n 'muted-foreground': 'var(--chitty-muted-foreground)',\n border: 'var(--chitty-border)',\n destructive: 'var(--chitty-destructive)',\n success: 'var(--chitty-success)',\n warning: 'var(--chitty-warning)',\n info: 'var(--chitty-info)'\n }\n },\n fontFamily: {\n 'chitty-sans': CHITTY_THEME.fonts.sans.split(','),\n 'chitty-serif': CHITTY_THEME.fonts.serif.split(','),\n 'chitty-mono': CHITTY_THEME.fonts.mono.split(',')\n },\n borderRadius: {\n 'chitty': CHITTY_THEME.radius\n },\n spacing: {\n 'chitty-xs': CHITTY_THEME.spacing.xs,\n 'chitty-sm': CHITTY_THEME.spacing.sm,\n 'chitty-md': CHITTY_THEME.spacing.md,\n 'chitty-lg': CHITTY_THEME.spacing.lg,\n 'chitty-xl': CHITTY_THEME.spacing.xl\n }\n }\n }\n }\n}\n\n/**\n * ASCII art logo\n */\nexport const ASCII_LOGO = `\n _____ _ _ _ _ ____ _____\n / ____| | (_) | | | / __ \\\\ / ____|\n | | | |__ _| |_| |_ _ _| | | | (___\n | | | '_ \\\\| | __| __| | | | | | |\\\\___ \\\\\n | |____| | | | | |_| |_| |_| | |__| |____) |\n \\\\_____|_| |_|_|\\\\__|\\\\__|\\\\__, |\\\\____/|_____/\n __/ |\n |___/\n`\n\nexport default {\n CHITTY_COLORS,\n CHITTY_THEME,\n BRAND_CONFIG,\n getCSSVariables,\n generateStyleTag,\n generateThemeProvider,\n generateTailwindConfig,\n ASCII_LOGO\n}","/**\n * ChittyOS Canon - Source of truth and canonical data management\n * Manages the authoritative state and version control for distributed data\n */\n\nimport { nanoid } from 'nanoid'\nimport * as crypto from 'crypto'\nimport { ChittyID } from '../id'\n\nexport interface CanonicalRecord {\n id: string\n canonId: string\n version: number\n data: any\n schema?: string\n hash: string\n signature?: string\n chittyId: string\n timestamp: string\n previousHash?: string\n metadata?: {\n source: string\n tags: string[]\n ttl?: number\n immutable?: boolean\n }\n}\n\nexport interface CanonConfig {\n endpoint?: string\n enableChain?: boolean\n enableSignatures?: boolean\n storageAdapter?: CanonStorageAdapter\n}\n\nexport interface CanonStorageAdapter {\n get(canonId: string): Promise<CanonicalRecord | null>\n set(record: CanonicalRecord): Promise<void>\n delete(canonId: string): Promise<boolean>\n list(filter?: any): Promise<CanonicalRecord[]>\n}\n\nexport interface CanonValidation {\n valid: boolean\n errors?: string[]\n warnings?: string[]\n}\n\nconst DEFAULT_CONFIG: CanonConfig = {\n endpoint: process.env.CHITTY_CANON_ENDPOINT || 'https://canon.chitty.cc',\n enableChain: true,\n enableSignatures: true\n}\n\nlet config = { ...DEFAULT_CONFIG }\nconst canonCache = new Map<string, CanonicalRecord>()\nconst chainIndex = new Map<string, CanonicalRecord[]>()\n\nexport function configure(customConfig: CanonConfig): void {\n config = { ...config, ...customConfig }\n}\n\n/**\n * Create a new canonical record\n */\nexport function createCanonical(\n data: any,\n chittyId: string,\n metadata?: CanonicalRecord['metadata']\n): CanonicalRecord {\n const canonId = `CANON_${nanoid(21)}`\n const timestamp = new Date().toISOString()\n\n // Calculate data hash\n const hash = crypto\n .createHash('sha256')\n .update(JSON.stringify({ data, chittyId, timestamp }))\n .digest('hex')\n\n const record: CanonicalRecord = {\n id: nanoid(),\n canonId,\n version: 1,\n data,\n hash,\n chittyId,\n timestamp,\n metadata: {\n source: metadata?.source || 'chittyos-core',\n tags: metadata?.tags || [],\n ttl: metadata?.ttl,\n immutable: metadata?.immutable || false\n }\n }\n\n // Store in cache\n canonCache.set(canonId, record)\n\n // Initialize chain if enabled\n if (config.enableChain) {\n chainIndex.set(canonId, [record])\n }\n\n return record\n}\n\n/**\n * Update a canonical record (creates new version)\n */\nexport function updateCanonical(\n canonId: string,\n data: any,\n chittyId: string\n): CanonicalRecord | null {\n const existing = canonCache.get(canonId)\n\n if (!existing) {\n return null\n }\n\n if (existing.metadata?.immutable) {\n throw new Error('Cannot update immutable canonical record')\n }\n\n const timestamp = new Date().toISOString()\n const previousHash = existing.hash\n\n // Calculate new hash including previous hash for chain integrity\n const hash = crypto\n .createHash('sha256')\n .update(JSON.stringify({ data, chittyId, timestamp, previousHash }))\n .digest('hex')\n\n const record: CanonicalRecord = {\n ...existing,\n id: nanoid(),\n version: existing.version + 1,\n data,\n hash,\n previousHash,\n chittyId,\n timestamp\n }\n\n // Update cache\n canonCache.set(canonId, record)\n\n // Update chain if enabled\n if (config.enableChain) {\n const chain = chainIndex.get(canonId) || []\n chain.push(record)\n chainIndex.set(canonId, chain)\n }\n\n return record\n}\n\n/**\n * Get canonical record by ID\n */\nexport async function getCanonical(canonId: string): Promise<CanonicalRecord | null> {\n // Check cache first\n if (canonCache.has(canonId)) {\n return canonCache.get(canonId)!\n }\n\n // Try storage adapter\n if (config.storageAdapter) {\n const record = await config.storageAdapter.get(canonId)\n if (record) {\n canonCache.set(canonId, record)\n return record\n }\n }\n\n // Try remote endpoint\n if (config.endpoint) {\n try {\n const response = await fetch(`${config.endpoint}/canon/${canonId}`)\n if (response.ok) {\n const record = await response.json() as CanonicalRecord\n canonCache.set(canonId, record)\n return record\n }\n } catch (error) {\n console.error('[Canon] Failed to fetch from endpoint:', error)\n }\n }\n\n return null\n}\n\n/**\n * Get version history for a canonical record\n */\nexport function getCanonicalHistory(canonId: string): CanonicalRecord[] {\n return chainIndex.get(canonId) || []\n}\n\n/**\n * Validate canonical record integrity\n */\nexport function validateCanonical(record: CanonicalRecord): CanonValidation {\n const errors: string[] = []\n const warnings: string[] = []\n\n // Validate structure\n if (!record.canonId || !record.canonId.startsWith('CANON_')) {\n errors.push('Invalid canon ID format')\n }\n\n if (!record.hash) {\n errors.push('Missing hash')\n }\n\n if (!record.chittyId) {\n errors.push('Missing ChittyID')\n }\n\n // Validate hash integrity\n const expectedHash = crypto\n .createHash('sha256')\n .update(JSON.stringify({\n data: record.data,\n chittyId: record.chittyId,\n timestamp: record.timestamp,\n ...(record.previousHash && { previousHash: record.previousHash })\n }))\n .digest('hex')\n\n if (record.hash !== expectedHash) {\n errors.push('Hash mismatch - data integrity compromised')\n }\n\n // Validate chain integrity if previous hash exists\n if (record.previousHash && record.version > 1) {\n const history = getCanonicalHistory(record.canonId)\n const previousRecord = history[record.version - 2]\n\n if (previousRecord && previousRecord.hash !== record.previousHash) {\n errors.push('Chain integrity violation - previous hash mismatch')\n }\n }\n\n // Check TTL\n if (record.metadata?.ttl) {\n const age = Date.now() - new Date(record.timestamp).getTime()\n const ttlMs = record.metadata.ttl * 1000\n\n if (age > ttlMs) {\n warnings.push('Record has exceeded TTL')\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n warnings: warnings.length > 0 ? warnings : undefined\n }\n}\n\n/**\n * Sign a canonical record\n */\nexport function signCanonical(record: CanonicalRecord, privateKey: string): CanonicalRecord {\n if (!config.enableSignatures) {\n return record\n }\n\n const sign = crypto.createSign('SHA256')\n sign.update(record.hash)\n const signature = sign.sign(privateKey, 'base64')\n\n return {\n ...record,\n signature\n }\n}\n\n/**\n * Verify canonical record signature\n */\nexport function verifyCanonicalSignature(\n record: CanonicalRecord,\n publicKey: string\n): boolean {\n if (!record.signature) {\n return false\n }\n\n try {\n const verify = crypto.createVerify('SHA256')\n verify.update(record.hash)\n return verify.verify(publicKey, record.signature, 'base64')\n } catch (error) {\n return false\n }\n}\n\n/**\n * Merge conflicting canonical records\n */\nexport function mergeCanonical(\n records: CanonicalRecord[],\n strategy: 'latest' | 'highest-version' | 'custom' = 'latest',\n customMerge?: (records: CanonicalRecord[]) => any\n): CanonicalRecord {\n if (records.length === 0) {\n throw new Error('No records to merge')\n }\n\n if (records.length === 1) {\n return records[0]\n }\n\n let winner: CanonicalRecord\n\n switch (strategy) {\n case 'latest':\n winner = records.reduce((latest, record) =>\n new Date(record.timestamp) > new Date(latest.timestamp) ? record : latest\n )\n break\n\n case 'highest-version':\n winner = records.reduce((highest, record) =>\n record.version > highest.version ? record : highest\n )\n break\n\n case 'custom':\n if (!customMerge) {\n throw new Error('Custom merge function required')\n }\n const mergedData = customMerge(records)\n winner = createCanonical(\n mergedData,\n records[0].chittyId,\n { source: 'merge', tags: ['merged'] }\n )\n break\n\n default:\n winner = records[0]\n }\n\n // Add merge metadata\n return {\n ...winner,\n metadata: {\n ...winner.metadata,\n source: 'merge',\n tags: [...(winner.metadata?.tags || []), 'merged', `from-${records.length}-records`]\n }\n }\n}\n\n/**\n * Query canonical records\n */\nexport async function queryCanonical(filter: {\n chittyId?: string\n tags?: string[]\n source?: string\n afterTimestamp?: string\n beforeTimestamp?: string\n}): Promise<CanonicalRecord[]> {\n let results: CanonicalRecord[] = []\n\n // Query from cache\n for (const record of canonCache.values()) {\n let matches = true\n\n if (filter.chittyId && record.chittyId !== filter.chittyId) {\n matches = false\n }\n\n if (filter.tags && filter.tags.length > 0) {\n const recordTags = record.metadata?.tags || []\n if (!filter.tags.every(tag => recordTags.includes(tag))) {\n matches = false\n }\n }\n\n if (filter.source && record.metadata?.source !== filter.source) {\n matches = false\n }\n\n if (filter.afterTimestamp && record.timestamp <= filter.afterTimestamp) {\n matches = false\n }\n\n if (filter.beforeTimestamp && record.timestamp >= filter.beforeTimestamp) {\n matches = false\n }\n\n if (matches) {\n results.push(record)\n }\n }\n\n // Query from storage adapter\n if (config.storageAdapter) {\n const storedRecords = await config.storageAdapter.list(filter)\n results = [...results, ...storedRecords]\n }\n\n // Remove duplicates\n const seen = new Set<string>()\n results = results.filter(record => {\n if (seen.has(record.canonId)) {\n return false\n }\n seen.add(record.canonId)\n return true\n })\n\n return results\n}\n\n/**\n * Clear canon cache\n */\nexport function clearCanonCache(): void {\n canonCache.clear()\n chainIndex.clear()\n}\n\n/**\n * Get canon statistics\n */\nexport function getCanonStats(): {\n totalRecords: number\n totalChains: number\n cacheSize: number\n oldestRecord?: string\n newestRecord?: string\n} {\n let oldest: string | undefined\n let newest: string | undefined\n\n for (const record of canonCache.values()) {\n if (!oldest || record.timestamp < oldest) {\n oldest = record.timestamp\n }\n if (!newest || record.timestamp > newest) {\n newest = record.timestamp\n }\n }\n\n return {\n totalRecords: canonCache.size,\n totalChains: chainIndex.size,\n cacheSize: JSON.stringify([...canonCache.values()]).length,\n oldestRecord: oldest,\n newestRecord: newest\n }\n}\n\nexport default {\n configure,\n createCanonical,\n updateCanonical,\n getCanonical,\n getCanonicalHistory,\n validateCanonical,\n signCanonical,\n verifyCanonicalSignature,\n mergeCanonical,\n queryCanonical,\n clearCanonCache,\n getCanonStats\n}","/**\n * ChittyOS Registry - Service discovery and connection management\n * Manages service endpoints, connections, and health status\n */\n\nimport { EventEmitter } from 'eventemitter3'\nimport { nanoid } from 'nanoid'\nimport { sendBeacon } from '../beacon'\n\nexport interface ServiceEndpoint {\n id: string\n name: string\n type: 'api' | 'websocket' | 'grpc' | 'database' | 'storage' | 'custom'\n url: string\n protocol: string\n host: string\n port?: number\n path?: string\n metadata?: Record<string, any>\n}\n\nexport interface ServiceConnection {\n id: string\n serviceId: string\n chittyId: string\n status: 'connecting' | 'connected' | 'disconnected' | 'error'\n establishedAt?: string\n lastPingAt?: string\n latency?: number\n error?: string\n metadata?: Record<string, any>\n}\n\nexport interface ServiceHealth {\n serviceId: string\n status: 'healthy' | 'degraded' | 'unhealthy' | 'unknown'\n lastCheckAt: string\n uptime?: number\n responseTime?: number\n errorRate?: number\n checks: {\n name: string\n status: 'pass' | 'fail' | 'warn'\n message?: string\n }[]\n}\n\nexport interface RegistryConfig {\n discoveryEndpoint?: string\n healthCheckInterval?: number\n connectionTimeout?: number\n maxRetries?: number\n enableAutoDiscovery?: boolean\n}\n\nexport type RegistryEvents = {\n 'service:registered': (service: ServiceEndpoint) => void\n 'service:unregistered': (serviceId: string) => void\n 'service:healthy': (serviceId: string) => void\n 'service:unhealthy': (serviceId: string, error: string) => void\n 'connection:established': (connection: ServiceConnection) => void\n 'connection:lost': (connection: ServiceConnection) => void\n 'discovery:update': (services: ServiceEndpoint[]) => void\n}\n\nclass ServiceRegistry extends EventEmitter<RegistryEvents> {\n private config: Required<RegistryConfig>\n private services = new Map<string, ServiceEndpoint>()\n private connections = new Map<string, ServiceConnection>()\n private healthStatus = new Map<string, ServiceHealth>()\n private healthCheckTimers = new Map<string, NodeJS.Timeout>()\n private discoveryTimer: NodeJS.Timeout | null = null\n\n constructor(config: RegistryConfig = {}) {\n super()\n\n this.config = {\n discoveryEndpoint: config.discoveryEndpoint || process.env.CHITTY_REGISTRY_ENDPOINT || 'https://registry.chitty.cc',\n healthCheckInterval: config.healthCheckInterval || 30000,\n connectionTimeout: config.connectionTimeout || 10000,\n maxRetries: config.maxRetries || 3,\n enableAutoDiscovery: config.enableAutoDiscovery ?? true\n }\n\n if (this.config.enableAutoDiscovery) {\n this.startAutoDiscovery()\n }\n }\n\n /**\n * Register a service endpoint\n */\n registerService(service: Omit<ServiceEndpoint, 'id'>): ServiceEndpoint {\n const registeredService: ServiceEndpoint = {\n ...service,\n id: service.name + '_' + nanoid(8)\n }\n\n // Parse URL for components\n try {\n const url = new URL(service.url)\n registeredService.protocol = url.protocol.replace(':', '')\n registeredService.host = url.hostname\n registeredService.port = url.port ? parseInt(url.port) : undefined\n registeredService.path = url.pathname !== '/' ? url.pathname : undefined\n } catch (error) {\n console.warn('[Registry] Invalid URL for service:', service.name)\n }\n\n this.services.set(registeredService.id, registeredService)\n this.emit('service:registered', registeredService)\n\n // Start health checks\n this.startHealthCheck(registeredService.id)\n\n // Track registration\n sendBeacon('registry_service_registered', {\n serviceId: registeredService.id,\n name: registeredService.name,\n type: registeredService.type\n })\n\n return registeredService\n }\n\n /**\n * Unregister a service\n */\n unregisterService(serviceId: string): boolean {\n const service = this.services.get(serviceId)\n if (!service) {\n return false\n }\n\n // Stop health checks\n this.stopHealthCheck(serviceId)\n\n // Close connections\n for (const [connId, conn] of this.connections) {\n if (conn.serviceId === serviceId) {\n this.disconnectConnection(connId)\n }\n }\n\n this.services.delete(serviceId)\n this.healthStatus.delete(serviceId)\n this.emit('service:unregistered', serviceId)\n\n return true\n }\n\n /**\n * Get service by ID or name\n */\n getService(idOrName: string): ServiceEndpoint | undefined {\n // Try by ID first\n if (this.services.has(idOrName)) {\n return this.services.get(idOrName)\n }\n\n // Try by name\n for (const service of this.services.values()) {\n if (service.name === idOrName) {\n return service\n }\n }\n\n return undefined\n }\n\n /**\n * Get all services of a specific type\n */\n getServicesByType(type: ServiceEndpoint['type']): ServiceEndpoint[] {\n return Array.from(this.services.values()).filter(s => s.type === type)\n }\n\n /**\n * Establish connection to a service\n */\n async connectToService(\n serviceId: string,\n chittyId: string,\n metadata?: Record<string, any>\n ): Promise<ServiceConnection> {\n const service = this.services.get(serviceId)\n if (!service) {\n throw new Error(`Service ${serviceId} not found`)\n }\n\n const connection: ServiceConnection = {\n id: `conn_${nanoid()}`,\n serviceId,\n chittyId,\n status: 'connecting',\n metadata\n }\n\n this.connections.set(connection.id, connection)\n\n try {\n // Test connection based on service type\n const startTime = Date.now()\n await this.testConnection(service)\n const latency = Date.now() - startTime\n\n // Update connection status\n connection.status = 'connected'\n connection.establishedAt = new Date().toISOString()\n connection.latency = latency\n this.connections.set(connection.id, connection)\n\n this.emit('connection:esta