Download src/server.mjs from Snapkitty/bob-voyager: direct link, hf CLI and curl.
- Browser
- Download file 11.1 kB
-
https://huggingface.co/Snapkitty/bob-voyager/resolve/main/src/server.mjs
- Command line
-
hf download hf://Snapkitty/bob-voyager/src/server.mjs
-
curl -L -o server.mjs https://huggingface.co/Snapkitty/bob-voyager/resolve/main/src/server.mjs
11.1 kB
| // BOB VOYAGER β Aerospace Backend | |
| // Apache License 2.0 β SnapKitty Collective 2026 | |
| // NORAD 25544 Β· ISS ZARYA Β· Live Telemetry Proxy + WORM Engine | |
| import http from 'http'; | |
| import https from 'https'; | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| import crypto from 'crypto'; | |
| import { fileURLToPath } from 'url'; | |
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); | |
| const ROOT = path.join(__dirname, '..'); | |
| const DATA_DIR = path.join(ROOT, 'data'); | |
| const WORM_LOG = path.join(DATA_DIR, 'worm_chain.jsonl'); | |
| const TELEM_LOG = path.join(DATA_DIR, 'telemetry.jsonl'); | |
| const PORT = process.env.PORT || 4299; | |
| // ββ WORM Chain ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| let wormPrev = 'GENESIS_BOB_VOYAGER_SERVER'; | |
| let wormCount = 0; | |
| function wormSeal(event, payload = {}) { | |
| const msg = `${wormPrev}|${event}|${Date.now()}`; | |
| const hash = crypto.createHash('sha256').update(msg).digest('hex'); | |
| const entry = { | |
| seq: wormCount++, | |
| hash, | |
| prev: wormPrev, | |
| event, | |
| payload, | |
| timestamp: new Date().toISOString(), | |
| }; | |
| wormPrev = hash; | |
| fs.appendFileSync(WORM_LOG, JSON.stringify(entry) + '\n'); | |
| return entry; | |
| } | |
| // ββ ISS telemetry cache βββββββββββββββββββββββββββββββββββββββ | |
| let cache = null; | |
| let cacheTime = 0; | |
| const CACHE_TTL = 4500; // ms β refresh slightly faster than 5s client poll | |
| function fetchISS() { | |
| return new Promise((resolve, reject) => { | |
| https.get('https://api.wheretheiss.at/v1/satellites/25544', res => { | |
| let body = ''; | |
| res.on('data', d => body += d); | |
| res.on('end', () => { | |
| try { | |
| const d = JSON.parse(body); | |
| resolve(d); | |
| } catch (e) { reject(e); } | |
| }); | |
| }).on('error', reject); | |
| }); | |
| } | |
| async function refreshTelemetry() { | |
| try { | |
| const d = await fetchISS(); | |
| const orbital = computeOrbital(d.altitude); | |
| cache = { ...d, ...orbital, fetched_at: new Date().toISOString() }; | |
| cacheTime = Date.now(); | |
| // WORM seal every telemetry update | |
| const seal = wormSeal('TELEMETRY', { | |
| lat: d.latitude.toFixed(4), | |
| lon: d.longitude.toFixed(4), | |
| alt: d.altitude.toFixed(2), | |
| vel: d.velocity.toFixed(0), | |
| }); | |
| // Append to telemetry log | |
| const logEntry = { ...cache, worm: seal.hash.slice(0, 16) }; | |
| fs.appendFileSync(TELEM_LOG, JSON.stringify(logEntry) + '\n'); | |
| console.log(`[TELEM] ${cache.fetched_at} | LAT ${d.latitude.toFixed(2)} LON ${d.longitude.toFixed(2)} ALT ${d.altitude.toFixed(1)} | WORM ${seal.hash.slice(0,10)}`); | |
| } catch (e) { | |
| console.error('[TELEM] fetch error:', e.message); | |
| } | |
| } | |
| // ββ Orbital mechanics βββββββββββββββββββββββββββββββββββββββββ | |
| const MU = 398600.4418; | |
| const R_EARTH = 6371; | |
| const ISS_ECC = 0.0001698; | |
| function computeOrbital(alt) { | |
| const r = R_EARTH + alt; | |
| const T = 2 * Math.PI * Math.sqrt(r ** 3 / MU); // seconds | |
| const n = 86400 / T; // rev/day | |
| const vv = Math.sqrt(MU / r); // km/s vis-viva | |
| const fp = R_EARTH * Math.acos(R_EARTH / r); // footprint km | |
| const apg = alt * (1 + ISS_ECC); | |
| const per = alt * (1 - ISS_ECC); | |
| return { | |
| orbital_period_min: parseFloat((T / 60).toFixed(4)), | |
| mean_motion_rev_day: parseFloat(n.toFixed(6)), | |
| vis_viva_kms: parseFloat(vv.toFixed(6)), | |
| semi_major_km: parseFloat(r.toFixed(2)), | |
| footprint_km: parseFloat(fp.toFixed(1)), | |
| apogee_km: parseFloat(apg.toFixed(2)), | |
| perigee_km: parseFloat(per.toFixed(2)), | |
| inclination_deg: 51.6, | |
| norad_id: 25544, | |
| }; | |
| } | |
| function haversine(lat1, lon1, lat2, lon2) { | |
| const R = 6371; | |
| const dLat = (lat2 - lat1) * Math.PI / 180; | |
| const dLon = (lon2 - lon1) * Math.PI / 180; | |
| const a = Math.sin(dLat / 2) ** 2 | |
| + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) ** 2; | |
| return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); | |
| } | |
| const GROUND_STATIONS = { | |
| SF: { name: 'San Francisco', lat: 37.7749, lon: -122.4194 }, | |
| JSC: { name: 'Johnson Space Center', lat: 29.5583, lon: -95.0894 }, | |
| TSUP: { name: 'TsUP Moscow', lat: 55.7558, lon: 37.6173 }, | |
| JAXA: { name: 'JAXA Tsukuba', lat: 36.0466, lon: 140.1229 }, | |
| ESA: { name: 'ESA ESOC Darmstadt', lat: 49.8715, lon: 8.6221 }, | |
| BAKO: { name: 'Baikonur Cosmodrome', lat: 45.9200, lon: 63.3420 }, | |
| }; | |
| function enrichWithGroundStations(telemetry) { | |
| if (!telemetry) return null; | |
| const gs = {}; | |
| for (const [code, station] of Object.entries(GROUND_STATIONS)) { | |
| const dist = haversine(telemetry.latitude, telemetry.longitude, station.lat, station.lon); | |
| const r = R_EARTH + telemetry.altitude; | |
| const centralAngle = dist / R_EARTH; | |
| const elev = Math.atan( | |
| (Math.cos(centralAngle) - R_EARTH / r) / Math.sin(centralAngle) | |
| ) * 180 / Math.PI; | |
| gs[code] = { | |
| name: station.name, | |
| dist_km: parseFloat(dist.toFixed(1)), | |
| elevation_deg: parseFloat(elev.toFixed(2)), | |
| in_contact: elev > 0, | |
| }; | |
| } | |
| return gs; | |
| } | |
| // ββ HTTP Router βββββββββββββββββββββββββββββββββββββββββββββββ | |
| function cors(res) { | |
| res.setHeader('Access-Control-Allow-Origin', '*'); | |
| res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); | |
| res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); | |
| } | |
| function json(res, data, status = 200) { | |
| cors(res); | |
| res.writeHead(status, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify(data, null, 2)); | |
| } | |
| function serveFile(res, filePath, contentType) { | |
| try { | |
| const data = fs.readFileSync(filePath); | |
| cors(res); | |
| res.writeHead(200, { 'Content-Type': contentType }); | |
| res.end(data); | |
| } catch { | |
| res.writeHead(404); res.end('Not found'); | |
| } | |
| } | |
| const server = http.createServer(async (req, res) => { | |
| if (req.method === 'OPTIONS') { cors(res); res.writeHead(204); res.end(); return; } | |
| const url = req.url.split('?')[0]; | |
| // ββ /api/telemetry β live ISS position + orbital elements ββ | |
| if (url === '/api/telemetry') { | |
| if (!cache || Date.now() - cacheTime > CACHE_TTL) await refreshTelemetry(); | |
| const gs = enrichWithGroundStations(cache); | |
| json(res, { ok: true, telemetry: cache, ground_stations: gs, worm_head: wormPrev.slice(0, 16) }); | |
| return; | |
| } | |
| // ββ /api/worm β last N WORM entries βββββββββββββββββββββββββ | |
| if (url === '/api/worm') { | |
| try { | |
| const lines = fs.readFileSync(WORM_LOG, 'utf8').trim().split('\n').slice(-50); | |
| const entries = lines.map(l => JSON.parse(l)); | |
| json(res, { ok: true, count: wormCount, head: wormPrev, entries }); | |
| } catch { | |
| json(res, { ok: true, count: wormCount, head: wormPrev, entries: [] }); | |
| } | |
| return; | |
| } | |
| // ββ /api/track β last N telemetry positions βββββββββββββββββ | |
| if (url === '/api/track') { | |
| try { | |
| const lines = fs.readFileSync(TELEM_LOG, 'utf8').trim().split('\n').slice(-200); | |
| const track = lines.map(l => { | |
| const d = JSON.parse(l); | |
| return { lat: d.latitude, lon: d.longitude, alt: d.altitude, ts: d.fetched_at }; | |
| }); | |
| json(res, { ok: true, count: track.length, track }); | |
| } catch { | |
| json(res, { ok: true, count: 0, track: [] }); | |
| } | |
| return; | |
| } | |
| // ββ /api/groundstations ββββββββββββββββββββββββββββββββββββββ | |
| if (url === '/api/groundstations') { | |
| if (!cache || Date.now() - cacheTime > CACHE_TTL) await refreshTelemetry(); | |
| json(res, { ok: true, stations: enrichWithGroundStations(cache) }); | |
| return; | |
| } | |
| // ββ /api/health ββββββββββββββββββββββββββββββββββββββββββββββ | |
| if (url === '/api/health') { | |
| json(res, { | |
| ok: true, | |
| service: 'bob-voyager', | |
| version: '2.0.0', | |
| norad: 25544, | |
| worm_count: wormCount, | |
| worm_head: wormPrev.slice(0, 16), | |
| cache_age_ms: Date.now() - cacheTime, | |
| uptime_s: process.uptime().toFixed(0), | |
| license: 'Apache-2.0', | |
| }); | |
| return; | |
| } | |
| // ββ /api/orbital β pure orbital mechanics (no API call) βββββ | |
| if (url === '/api/orbital') { | |
| const alt = cache?.altitude ?? 408; | |
| json(res, { ok: true, altitude_km: alt, ...computeOrbital(alt) }); | |
| return; | |
| } | |
| // ββ Static: serve public/ βββββββββββββββββββββββββββββββββββββ | |
| const staticBase = path.join(ROOT, 'public'); | |
| let filePath = url === '/' ? path.join(staticBase, 'index.html') : path.join(staticBase, url); | |
| const ext = path.extname(filePath); | |
| const types = { '.html':'text/html', '.js':'text/javascript', '.css':'text/css', | |
| '.json':'application/json', '.ico':'image/x-icon' }; | |
| if (fs.existsSync(filePath)) { | |
| serveFile(res, filePath, types[ext] || 'text/plain'); | |
| return; | |
| } | |
| res.writeHead(404); res.end('Not found'); | |
| }); | |
| // ββ Boot ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true }); | |
| wormSeal('SERVER_BOOT', { port: PORT, version: '2.0.0', norad: 25544 }); | |
| await refreshTelemetry(); | |
| setInterval(refreshTelemetry, CACHE_TTL); | |
| server.listen(PORT, () => { | |
| console.log(` | |
| ββββββββββββββββββββββββββββββββββββββββββββββ | |
| β BOB VOYAGER β Aerospace Backend v2.0 β | |
| β NORAD 25544 Β· ISS ZARYA Β· WORM Sealed β | |
| β http://localhost:${PORT} β | |
| β Apache 2.0 Β· SnapKitty Collective 2026 β | |
| ββββββββββββββββββββββββββββββββββββββββββββββ | |
| API endpoints: | |
| GET /api/telemetry live ISS + orbital elements + ground stations | |
| GET /api/worm last 50 WORM chain entries | |
| GET /api/track last 200 telemetry positions | |
| GET /api/groundstations SF Β· JSC Β· TsUP Β· JAXA Β· ESA Β· Baikonur | |
| GET /api/orbital Keplerian elements (no external call) | |
| GET /api/health service status | |
| `); | |
| }); | |