Skip to content

Commit e24920e

Browse files
committed
🔒 Add Tailscale remote access + enhanced telemetry
- Smart BRIDGE_URL routing (Tailscale proxy vs localhost) - Allow Tailscale IPs (100.x.x.x) without Origin headers - Enhanced telemetry tracking (health checks, command execution, errors) - Connection type detection (tailnet vs direct) - Secure: Browser requests work, raw API calls blocked - Tested working from iPhone via Tailnet
1 parent 1986929 commit e24920e

2 files changed

Lines changed: 46 additions & 8 deletions

File tree

docs/assets/js/interact.js

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ let isConnected = false;
66
let checkInterval = null;
77
const DEBUG = true;
88

9-
// 🎯 BUSINESS LOGIC: Only Tailscale domain gets interactive features
9+
// 🎯 BUSINESS LOGIC: Detect if we're on Tailnet or localhost
1010
const currentDomain = window.location.hostname;
1111
const IS_INTERACTIVE_DOMAIN = currentDomain === '1minds3t.echo-universe.ts.net' || currentDomain === 'localhost' || currentDomain === '127.0.0.1';
1212

13+
// Use Tailscale proxy when accessing remotely
14+
const BRIDGE_URL = currentDomain === '1minds3t.echo-universe.ts.net'
15+
? 'https://1minds3t.echo-universe.ts.net/omnipkg-api' // Tailscale proxy
16+
: `http://127.0.0.1:${PORT}`; // Direct localhost
17+
1318
const SAFE_TELEMETRY_KEYS = new Set([
1419
'command', 'path', 'title', 'port', 'package',
1520
'method', 'error', 'duration', 'success'
@@ -135,27 +140,34 @@ function createStatusBanner() {
135140
async function checkHealth() {
136141
if (!IS_INTERACTIVE_DOMAIN) return;
137142

138-
debug(`Health check: Direct connection to localhost:${PORT}`);
143+
debug(`Health check: ${BRIDGE_URL}`);
144+
145+
// 📊 TELEMETRY: Always notify Cloudflare of health checks
146+
sendTelemetry("health_check", { method: currentDomain === '1minds3t.echo-universe.ts.net' ? 'tailnet' : 'direct' });
139147

140148
try {
141-
// 🔥 DIRECT CONNECTION: No worker proxy needed
142-
const res = await fetch(`http://127.0.0.1:${PORT}/health`, {
149+
const res = await fetch(`${BRIDGE_URL}/health`, { // ← USE BRIDGE_URL
143150
method: 'GET',
144151
headers: { 'Content-Type': 'application/json' }
145152
});
146153

147154
if (res.ok) {
148155
const data = await res.json();
149156
updateStatus(true, `Connected to Local Bridge v${data.version || '2.1.0'} (Port ${PORT})`);
157+
// Track successful connection
158+
sendTelemetry("bridge_connected", { port: PORT, version: data.version });
150159
} else {
151160
updateStatus(false, 'Local bridge not running | Run: 8pkg web start');
161+
sendTelemetry("bridge_failed", { port: PORT, status: res.status });
152162
}
153163
} catch (e) {
154164
debug('Health check failed:', e);
155165
updateStatus(false, 'Local bridge not running | Run: 8pkg web start');
166+
sendTelemetry("bridge_offline", { port: PORT, error: e.message });
156167
}
157168
}
158169

170+
159171
function updateStatus(connected, message) {
160172
isConnected = connected;
161173
const dot = document.getElementById('status-dot');
@@ -271,9 +283,11 @@ function createStaticButton() {
271283
// ==========================================
272284
// Execute Command
273285
// ==========================================
286+
274287
async function runCommand(cmd, btnElement) {
275288
if (!isConnected) {
276289
alert('Local bridge not running. Start with: 8pkg web start');
290+
sendTelemetry("command_blocked", { reason: "bridge_offline" });
277291
return;
278292
}
279293

@@ -299,14 +313,16 @@ async function runCommand(cmd, btnElement) {
299313
// Send telemetry (fire and forget)
300314
sendTelemetry("command_exec", { command: cmd.split(' ')[0] });
301315

316+
const startTime = Date.now();
317+
302318
try {
303319
// Open modal immediately to show streaming output
304320
const modal = createStreamingModal(cmd);
305321
const outputPre = modal.querySelector('.omni-output');
306322
let fullOutput = '';
307323

308-
// 🔥 DIRECT CONNECTION with streaming
309-
const res = await fetch(`http://127.0.0.1:${PORT}/run`, {
324+
// 🔥 CONNECTION with streaming
325+
const res = await fetch(`${BRIDGE_URL}/run`, {
310326
method: 'POST',
311327
headers: {
312328
'Content-Type': 'application/json'
@@ -342,6 +358,14 @@ async function runCommand(cmd, btnElement) {
342358
}
343359
}
344360

361+
// Track successful completion
362+
const duration = Date.now() - startTime;
363+
sendTelemetry("command_complete", {
364+
command: cmd.split(' ')[0],
365+
duration_ms: duration,
366+
success: true
367+
});
368+
345369
// Success state
346370
btnElement.innerHTML = '';
347371
const checkIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
@@ -365,6 +389,15 @@ async function runCommand(cmd, btnElement) {
365389
}, 2000);
366390
} catch (e) {
367391
debug('Command error:', e);
392+
393+
// Track error
394+
const duration = Date.now() - startTime;
395+
sendTelemetry("command_error", {
396+
command: cmd.split(' ')[0],
397+
error: e.message,
398+
duration_ms: duration
399+
});
400+
368401
showOutput(cmd, `Error: ${e.message}`);
369402

370403
btnElement.innerHTML = '';

src/omnipkg/apis/local_bridge.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,14 +392,19 @@ def enforce_origin():
392392
logger.info("⚠️ DEV MODE: Allowing request without Origin header")
393393
return
394394

395+
# 🔓 TAILSCALE PROXY: Requests from Tailscale proxy don't have Origin
396+
if not origin and request.headers.get('X-Forwarded-For'):
397+
logger.info("🔒 Tailscale proxy request detected - allowing")
398+
return
399+
395400
# Block requests with NO Origin in production
396401
if not origin and not DEV_MODE:
397402
return jsonify({
398403
"error": "❌ Direct API access prohibited.",
399404
"message": "You must use the OmniPkg Web UI to interact with this bridge.",
400405
"url": PRIMARY_DASHBOARD
401406
}), 403
402-
407+
403408
# Validate origin if present
404409
if origin:
405410
clean_origin = origin.rstrip("/")
@@ -562,7 +567,7 @@ def run_bridge_server():
562567
print(f"Local Port: {port}", flush=True)
563568

564569
app = create_app(port)
565-
app.run(host="127.0.0.1", port=port, threaded=True, use_reloader=False)
570+
app.run(host="0.0.0.0", port=port, threaded=True, use_reloader=False) # ← CHANGE TO 0.0.0.0
566571

567572
# ==========================================
568573
# PART 2: Manager Logic (CLI Control)

0 commit comments

Comments
 (0)