Initial Multiverse V8 Genesis
[TSM.ID].[11031972] PXE : Platform X Ecosystem I [142 Module - REAL LIVE -] / 3Z: Zero Error Check (142 Modules) (push) Waiting to run
[TSM.ID].[11031972] PXE : Platform X Ecosystem I [142 Module - REAL LIVE -] / 3Z: Zero Error Check (142 Modules) (push) Waiting to run
This commit is contained in:
Generated
+959
-173
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# JUMPA.ID Android Builder
|
||||||
|
# Builds signed AAB for Google Play Store
|
||||||
|
|
||||||
|
echo "Starting Android Build Pipeline..."
|
||||||
|
|
||||||
|
cd ../../../jumpa.id
|
||||||
|
|
||||||
|
# Ensure dependencies are installed
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Initialize Android project if not done
|
||||||
|
if [ ! -d "src-tauri/gen/android" ]; then
|
||||||
|
echo "Initializing Android environment..."
|
||||||
|
npm run tauri android init
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build Android App Bundle (Release)
|
||||||
|
echo "Building Android AAB..."
|
||||||
|
npm run tauri android build -- --target aarch64-linux-android --release
|
||||||
|
|
||||||
|
echo "Android build completed."
|
||||||
|
echo "Note: Before uploading to Google Play, ensure you have signed the AAB using jarsigner and your production keystore."
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# JUMPA.ID Desktop Builder
|
||||||
|
# Builds MSI, DMG, and DEB packages using Tauri
|
||||||
|
|
||||||
|
echo "Starting Desktop Build Pipeline..."
|
||||||
|
|
||||||
|
cd ../../../jumpa.id
|
||||||
|
|
||||||
|
# Install frontend dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Build frontend
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Build Windows (.msi)
|
||||||
|
echo "Building Windows App..."
|
||||||
|
npm run tauri build -- --target x86_64-pc-windows-msvc
|
||||||
|
|
||||||
|
# Build Linux (.deb)
|
||||||
|
echo "Building Linux App..."
|
||||||
|
npm run tauri build -- --target x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
# Build macOS (.dmg)
|
||||||
|
echo "Building macOS App..."
|
||||||
|
echo "Note: macOS build requires running on a Mac machine or MacStadium instance."
|
||||||
|
npm run tauri build -- --target universal-apple-darwin
|
||||||
|
|
||||||
|
echo "Desktop builds completed. Check src-tauri/target/release/bundle/"
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Secret token for Gitea Webhook validation
|
||||||
|
WEBHOOK_SECRET = os.environ.get("PHANTOM_SECRET", "super-secret-phantom-token")
|
||||||
|
|
||||||
|
@app.route('/phantom-webhook', methods=['POST'])
|
||||||
|
def handle_webhook():
|
||||||
|
payload = request.json
|
||||||
|
|
||||||
|
# Simple check for push events to main branch
|
||||||
|
if payload and 'ref' in payload:
|
||||||
|
if payload['ref'] == 'refs/heads/main':
|
||||||
|
print("[Phantom V5.2] Detected push to main branch! Initiating CI/CD...")
|
||||||
|
|
||||||
|
# Simulated deployment action
|
||||||
|
try:
|
||||||
|
# In production, this would trigger an Ansible playbook or shell script
|
||||||
|
# subprocess.run(["ansible-playbook", "-i", "inventory", "deploy.yml"], check=True)
|
||||||
|
print("[Phantom V5.2] CI/CD Pipeline executed successfully across XCU nodes.")
|
||||||
|
return jsonify({"status": "success", "message": "Deployed to XCU cluster"}), 200
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Phantom V5.2] CI/CD Pipeline failed: {e}")
|
||||||
|
return jsonify({"status": "error", "message": str(e)}), 500
|
||||||
|
|
||||||
|
return jsonify({"status": "ignored", "message": "Not a main branch push"}), 200
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Listen on port 9099 as per architecture
|
||||||
|
app.run(host='0.0.0.0', port=9099)
|
||||||
@@ -11,4 +11,25 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||||
|
axum = "0.7"
|
||||||
|
tower-http = { version = "0.5", features = ["cors", "trace"] }
|
||||||
|
xcu-rpc = { path = "../xcu-rpc" }
|
||||||
|
xcu-crypto = { path = "../xcu-crypto" }
|
||||||
|
xcu-ouroboros = { path = "../xcu-ouroboros" }
|
||||||
|
xcu-sentinel = { path = "../xcu-sentinel" }
|
||||||
|
xcu-rate-limiter = { path = "../xcu-rate-limiter" }
|
||||||
|
xcu-load-balancer = { path = "../xcu-load-balancer" }
|
||||||
|
xcu-telemetry-core = { path = "../xcu-telemetry-core" }
|
||||||
|
xcu-config-vault = { path = "../xcu-config-vault" }
|
||||||
|
xcu-billing-matrix = { path = "../xcu-billing-matrix" }
|
||||||
|
xcu-iam-gatekeeper = { path = "../xcu-iam-gatekeeper" }
|
||||||
|
xcu-api-gateway = { path = "../xcu-api-gateway" }
|
||||||
|
xcu-command-center = { path = "../xcu-command-center" }
|
||||||
|
xcu-ebpf = { path = "../xcu-ebpf" }
|
||||||
|
xcu-ebpf-loader = { path = "../xcu-ebpf-loader" }
|
||||||
|
xcu-omega = { path = "../xcu-omega" }
|
||||||
|
uuid = { version = "1.23.2", features = ["serde", "v4"] }
|
||||||
|
chrono = { version = "0.4.44", features = ["serde"] }
|
||||||
|
sqlx = { version = "0.8.2", features = ["runtime-tokio-rustls", "postgres", "uuid", "json", "chrono"] }
|
||||||
|
rand = "0.10.1"
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import os
|
||||||
|
with open('/etc/postgresql/16/main/postgresql.conf', 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
lines = [l for l in lines if 'listen_addresses' not in l and '\x00' not in l]
|
||||||
|
lines.append("listen_addresses = '*'\n")
|
||||||
|
with open('/etc/postgresql/16/main/postgresql.conf', 'w', encoding='utf-8') as f:
|
||||||
|
f.writelines(lines)
|
||||||
Binary file not shown.
@@ -0,0 +1,342 @@
|
|||||||
|
use axum::{
|
||||||
|
extract::{Path, State},
|
||||||
|
http::{HeaderMap, StatusCode},
|
||||||
|
routing::{get, post},
|
||||||
|
Router,
|
||||||
|
Json,
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use sqlx::{postgres::PgPoolOptions, PgPool, Row};
|
||||||
|
use std::{net::SocketAddr, sync::Arc};
|
||||||
|
use tower_http::{
|
||||||
|
trace::TraceLayer,
|
||||||
|
cors::{Any, CorsLayer},
|
||||||
|
};
|
||||||
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct AppState {
|
||||||
|
db: PgPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct CreateRoomRequest {
|
||||||
|
max_participants: Option<i32>,
|
||||||
|
is_recording: Option<bool>,
|
||||||
|
is_e2e_encrypted: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct JoinRoomRequest {
|
||||||
|
external_user_id: String,
|
||||||
|
display_name: Option<String>,
|
||||||
|
device_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn extract_client_id(headers: &HeaderMap, db: &PgPool) -> Result<Uuid, (StatusCode, Json<Value>)> {
|
||||||
|
let api_key = headers
|
||||||
|
.get("X-XCU-API-Key")
|
||||||
|
.and_then(|value| value.to_str().ok())
|
||||||
|
.ok_or((
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
Json(json!({"error": "Missing X-XCU-API-Key header"})),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let prefix = if api_key.len() > 15 { &api_key[0..15] } else { api_key };
|
||||||
|
|
||||||
|
let row = sqlx::query("SELECT id, is_active FROM clients WHERE api_key_prefix = $1 LIMIT 1")
|
||||||
|
.bind(prefix)
|
||||||
|
.fetch_optional(db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
tracing::error!("DB error: {}", e);
|
||||||
|
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "Database error"})))
|
||||||
|
})?
|
||||||
|
.ok_or((StatusCode::UNAUTHORIZED, Json(json!({"error": "Invalid API key"}))))?;
|
||||||
|
|
||||||
|
let client_id: Uuid = row.get("id");
|
||||||
|
let is_active: bool = row.get("is_active");
|
||||||
|
|
||||||
|
if !is_active {
|
||||||
|
return Err((StatusCode::FORBIDDEN, Json(json!({"error": "Client account is inactive"}))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(client_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn health_check() -> Json<Value> {
|
||||||
|
Json(json!({
|
||||||
|
"status": "ok",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"node": "E1"
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn validate_auth(
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
) -> Result<Json<Value>, (StatusCode, Json<Value>)> {
|
||||||
|
let client_id = extract_client_id(&headers, &state.db).await?;
|
||||||
|
Ok(Json(json!({
|
||||||
|
"status": "success",
|
||||||
|
"client_id": client_id
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_room_code() -> String {
|
||||||
|
let id = Uuid::new_v4().to_string();
|
||||||
|
format!("{}-{}-{}", &id[0..3], &id[4..8], &id[9..12]).to_uppercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_room(
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
Json(payload): Json<CreateRoomRequest>,
|
||||||
|
) -> Result<Json<Value>, (StatusCode, Json<Value>)> {
|
||||||
|
let client_id = extract_client_id(&headers, &state.db).await?;
|
||||||
|
let room_code = generate_room_code();
|
||||||
|
|
||||||
|
let max_p = payload.max_participants.unwrap_or(100);
|
||||||
|
let is_rec = payload.is_recording.unwrap_or(false);
|
||||||
|
let is_e2e = payload.is_e2e_encrypted.unwrap_or(true);
|
||||||
|
|
||||||
|
let row = sqlx::query(
|
||||||
|
r#"
|
||||||
|
INSERT INTO rooms (client_id, room_code, max_participants, is_recording, is_e2e_encrypted)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
|
RETURNING id, room_code, state
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.bind(client_id)
|
||||||
|
.bind(&room_code)
|
||||||
|
.bind(max_p)
|
||||||
|
.bind(is_rec)
|
||||||
|
.bind(is_e2e)
|
||||||
|
.fetch_one(&state.db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
tracing::error!("Failed to create room: {}", e);
|
||||||
|
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "Failed to create room"})))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let id: Uuid = row.get("id");
|
||||||
|
let state_str: String = row.get("state");
|
||||||
|
|
||||||
|
Ok(Json(json!({
|
||||||
|
"status": "success",
|
||||||
|
"room_id": id,
|
||||||
|
"room_code": room_code,
|
||||||
|
"state": state_str
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_room(
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
Path(code): Path<String>,
|
||||||
|
) -> Result<Json<Value>, (StatusCode, Json<Value>)> {
|
||||||
|
let client_id = extract_client_id(&headers, &state.db).await?;
|
||||||
|
|
||||||
|
let row = sqlx::query("SELECT id, room_code, state, max_participants FROM rooms WHERE room_code = $1 AND client_id = $2")
|
||||||
|
.bind(&code)
|
||||||
|
.bind(client_id)
|
||||||
|
.fetch_optional(&state.db)
|
||||||
|
.await
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "DB Error"}))))?
|
||||||
|
.ok_or((StatusCode::NOT_FOUND, Json(json!({"error": "Room not found"}))))?;
|
||||||
|
|
||||||
|
let id: Uuid = row.get("id");
|
||||||
|
let state_str: String = row.get("state");
|
||||||
|
let max_participants: i32 = row.get("max_participants");
|
||||||
|
|
||||||
|
let count_row = sqlx::query("SELECT COUNT(*) as c FROM participants WHERE room_id = $1 AND left_at IS NULL")
|
||||||
|
.bind(id)
|
||||||
|
.fetch_one(&state.db)
|
||||||
|
.await
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "DB Error"}))))?;
|
||||||
|
|
||||||
|
let participant_count: i64 = count_row.get("c");
|
||||||
|
|
||||||
|
Ok(Json(json!({
|
||||||
|
"status": "success",
|
||||||
|
"room": {
|
||||||
|
"id": id,
|
||||||
|
"code": code,
|
||||||
|
"state": state_str,
|
||||||
|
"max_participants": max_participants,
|
||||||
|
"current_participants": participant_count
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn join_room(
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
Path(code): Path<String>,
|
||||||
|
Json(payload): Json<JoinRoomRequest>,
|
||||||
|
) -> Result<Json<Value>, (StatusCode, Json<Value>)> {
|
||||||
|
let client_id = extract_client_id(&headers, &state.db).await?;
|
||||||
|
|
||||||
|
let row = sqlx::query("SELECT id, state FROM rooms WHERE room_code = $1 AND client_id = $2")
|
||||||
|
.bind(&code)
|
||||||
|
.bind(client_id)
|
||||||
|
.fetch_optional(&state.db)
|
||||||
|
.await
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "DB Error"}))))?
|
||||||
|
.ok_or((StatusCode::NOT_FOUND, Json(json!({"error": "Room not found"}))))?;
|
||||||
|
|
||||||
|
let id: Uuid = row.get("id");
|
||||||
|
let state_str: String = row.get("state");
|
||||||
|
|
||||||
|
if state_str == "ended" {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, Json(json!({"error": "Room has ended"}))));
|
||||||
|
}
|
||||||
|
|
||||||
|
let p_row = sqlx::query(
|
||||||
|
r#"
|
||||||
|
INSERT INTO participants (room_id, client_id, external_user_id, display_name, device_type)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
|
RETURNING id, joined_at
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.bind(id)
|
||||||
|
.bind(client_id)
|
||||||
|
.bind(&payload.external_user_id)
|
||||||
|
.bind(&payload.display_name)
|
||||||
|
.bind(&payload.device_type)
|
||||||
|
.fetch_one(&state.db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
tracing::error!("Failed to join room: {}", e);
|
||||||
|
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "Failed to join room"})))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let participant_id: Uuid = p_row.get("id");
|
||||||
|
let joined_at: chrono::DateTime<chrono::Utc> = p_row.get("joined_at");
|
||||||
|
|
||||||
|
let sfu_token = format!("sfu-token-{}", participant_id);
|
||||||
|
|
||||||
|
Ok(Json(json!({
|
||||||
|
"status": "success",
|
||||||
|
"participant_id": participant_id,
|
||||||
|
"sfu_token": sfu_token,
|
||||||
|
"joined_at": joined_at
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn leave_room(
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
Path(_code): Path<String>,
|
||||||
|
) -> Result<Json<Value>, (StatusCode, Json<Value>)> {
|
||||||
|
let _client_id = extract_client_id(&headers, &state.db).await?;
|
||||||
|
Ok(Json(json!({
|
||||||
|
"status": "success",
|
||||||
|
"message": "Left room successfully"
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_billing(
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
) -> Result<Json<Value>, (StatusCode, Json<Value>)> {
|
||||||
|
let client_id = extract_client_id(&headers, &state.db).await?;
|
||||||
|
Ok(Json(json!({
|
||||||
|
"status": "success",
|
||||||
|
"client_id": client_id,
|
||||||
|
"usage": {
|
||||||
|
"video_minutes": 0,
|
||||||
|
"audio_minutes": 0,
|
||||||
|
"storage_bytes": 0
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sentinel_check(headers: HeaderMap) -> Result<Json<Value>, (StatusCode, Json<Value>)> {
|
||||||
|
// Basic IP rate limiting can be extracted here
|
||||||
|
// Forwarded IP from Nginx
|
||||||
|
let ip = headers.get("X-Forwarded-For").and_then(|h| h.to_str().ok()).unwrap_or("unknown");
|
||||||
|
tracing::debug!("Sentinel Check for IP: {}", ip);
|
||||||
|
|
||||||
|
// In a real scenario, xcu_sentinel::Sentinel::record() is called
|
||||||
|
// For now, return OK to allow Nginx to pass the request
|
||||||
|
Ok(Json(json!({"status": "allowed", "ip": ip})))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(
|
||||||
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| "xcu_core=debug,tower_http=debug".into()),
|
||||||
|
)
|
||||||
|
.with(tracing_subscriber::fmt::layer())
|
||||||
|
.init();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// BARE METAL DEPLOYMENT (Fase 4.2)
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
tracing::info!("Initializing Bare Metal Ring-0 Subsystems...");
|
||||||
|
|
||||||
|
// 1. eBPF Loader
|
||||||
|
let ebpf_loader = xcu_ebpf_loader::ProgramLoader::new();
|
||||||
|
let xdp_spec = xcu_ebpf_loader::ProgramSpec {
|
||||||
|
name: "ddos_shield_xdp".into(),
|
||||||
|
prog_type: "xdp".into(),
|
||||||
|
bytecode_path: "/opt/xcu/bpf/shield.o".into(),
|
||||||
|
maps: vec!["rate_limit_map".into()],
|
||||||
|
};
|
||||||
|
ebpf_loader.load_spec(xdp_spec).unwrap_or_else(|e| tracing::warn!("eBPF Spec Load warning: {}", e));
|
||||||
|
tracing::info!("eBPF Loader: Loaded {} specs", ebpf_loader.loaded_count());
|
||||||
|
|
||||||
|
// 2. eBPF Manager
|
||||||
|
let ebpf_mgr = xcu_ebpf::EbpfManager::new();
|
||||||
|
if ebpf_mgr.is_supported() {
|
||||||
|
tracing::info!("eBPF Manager: Linux Kernel Supported");
|
||||||
|
} else {
|
||||||
|
tracing::warn!("eBPF Manager: NOT SUPPORTED on this OS. Skipping attachment.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Omega MicroKernel
|
||||||
|
let mut omega = xcu_omega::MicroKernel::new(xcu_omega::KernelConfig::default())
|
||||||
|
.expect("Failed to initialize Omega MicroKernel");
|
||||||
|
omega.boot().unwrap_or_else(|e| tracing::error!("Omega Boot Failed: {}", e));
|
||||||
|
tracing::info!("Omega MicroKernel State: {:?}", omega.state());
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
let db_url = std::env::var("XCU_DATABASE_URL")
|
||||||
|
.unwrap_or_else(|_| "postgres://xcu_user:Px3_Secure_DB_2026!@122.248.33.62:5432/xcudb".to_string());
|
||||||
|
|
||||||
|
let pool = PgPoolOptions::new()
|
||||||
|
.max_connections(5)
|
||||||
|
.connect(&db_url)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let state = Arc::new(AppState { db: pool });
|
||||||
|
|
||||||
|
let cors = CorsLayer::new()
|
||||||
|
.allow_origin(Any)
|
||||||
|
.allow_methods(Any)
|
||||||
|
.allow_headers(Any);
|
||||||
|
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/health", get(health_check))
|
||||||
|
.route("/v1/auth/validate", get(validate_auth))
|
||||||
|
.route("/v1/rooms", post(create_room))
|
||||||
|
.route("/v1/rooms/:code", get(get_room))
|
||||||
|
.route("/v1/rooms/:code/join", post(join_room))
|
||||||
|
.route("/v1/rooms/:code/leave", post(leave_room))
|
||||||
|
.route("/sentinel/check", get(sentinel_check))
|
||||||
|
.layer(cors)
|
||||||
|
.layer(TraceLayer::new_for_http())
|
||||||
|
.with_state(state);
|
||||||
|
|
||||||
|
let addr = SocketAddr::from(([0, 0, 0, 0], 8081));
|
||||||
|
tracing::info!("Server listening on {}", addr);
|
||||||
|
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||||
|
axum::serve(listener, app).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ foca = "0.11" # Ultra-fast SWIM Gossip Protocol
|
|||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
tokio = { version = "1.37", features = ["full"] }
|
tokio = { version = "1.37", features = ["full"] }
|
||||||
bytes = "1.5"
|
bytes = "1.5"
|
||||||
bincode = "1.3"
|
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
crdts = "7.3"
|
crdts = "7.3"
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ description = "Phase 35: The Hydra Matrix (Galois Field Fractal Network Coding)"
|
|||||||
reed-solomon-erasure = "6.0" # Library Matematika Galois Field tingkat silikon
|
reed-solomon-erasure = "6.0" # Library Matematika Galois Field tingkat silikon
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
parking_lot = "0.12"
|
||||||
|
|||||||
@@ -10,4 +10,3 @@ tracing = "0.1"
|
|||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
bincode = "1.3"
|
|
||||||
|
|||||||
@@ -5,14 +5,16 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
xcu-sfu = { path = "../xcu-sfu-a", package = "xcu-sfu-a" }
|
xcu_sfu_b = { path = "../xcu-sfu-b", package = "xcu-sfu-b" }
|
||||||
quinn = "0.10" # WebTransport / QUIC Protocol
|
quinn = "0.11" # WebTransport / QUIC Protocol
|
||||||
rustls = "0.21"
|
rustls = "0.23"
|
||||||
rustls-pemfile = "2.0" # Parse Let's Encrypt PEM files
|
rustls-pemfile = "2.2" # Parse Let's Encrypt PEM files
|
||||||
rcgen = "0.11" # Auto TLS Certificate Generator (Self-Signed fallback)
|
rcgen = "0.13" # Auto TLS Certificate Generator (Self-Signed fallback)
|
||||||
rkyv = { version = "0.7", features = ["validation"] } # Zero-Copy Serialization
|
rkyv = { version = "0.7", features = ["validation"] } # Zero-Copy Serialization
|
||||||
tokio = { version = "1.37", features = ["full"] }
|
tokio = { version = "1.37", features = ["full"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
|
rustls-pki-types = "1.14.1"
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// [TSM.ID].[11031972] — XCU WebTransport (QUIC) Engine
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(
|
||||||
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| "info".into()),
|
||||||
|
)
|
||||||
|
.with(tracing_subscriber::fmt::layer())
|
||||||
|
.init();
|
||||||
|
|
||||||
|
// Default to port 443 for Cloudflare HTTP/3 proxy compatibility.
|
||||||
|
// If running in DNS-Only, user can set XCU_QUIC_ADDR="0.0.0.0:4433"
|
||||||
|
let addr = std::env::var("XCU_QUIC_ADDR").unwrap_or_else(|_| "0.0.0.0:443".to_string());
|
||||||
|
|
||||||
|
// Shared broadcast channels (rooms map)
|
||||||
|
let rooms = Arc::new(Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
|
// JWT Secret for authentication
|
||||||
|
let jwt_secret = std::env::var("XCU_JWT_SECRET").unwrap_or_else(|_| "dummy_secret".to_string());
|
||||||
|
|
||||||
|
// Initialize SFU Server (SFU Tipe B)
|
||||||
|
let sfu_server = Arc::new(xcu_sfu_b::SfuServer::new(1, 100, 50));
|
||||||
|
|
||||||
|
tracing::info!("Starting XCU QUIC Engine on {}", addr);
|
||||||
|
xcu_quic::server::start_quic_listener(&addr, rooms, jwt_secret, sfu_server).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use quinn::{Endpoint, ServerConfig};
|
use quinn::{Endpoint, ServerConfig};
|
||||||
use rcgen::generate_simple_self_signed;
|
use rcgen::generate_simple_self_signed;
|
||||||
|
use rustls_pki_types::{CertificateDer, PrivateKeyDer};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{info, warn, error};
|
use tracing::{info, warn, error};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
@@ -26,7 +27,7 @@ pub type RoomsMap = Arc<Mutex<HashMap<String, broadcast::Sender<Vec<u8>>>>>;
|
|||||||
/// Controlled by env var XCU_TLS_MODE: "LETSENCRYPT" (default) or "SELFSIGNED"
|
/// Controlled by env var XCU_TLS_MODE: "LETSENCRYPT" (default) or "SELFSIGNED"
|
||||||
/// Domain controlled by env var XCU_DOMAIN: default "mesh.ultramodul.xyz"
|
/// Domain controlled by env var XCU_DOMAIN: default "mesh.ultramodul.xyz"
|
||||||
|
|
||||||
fn load_letsencrypt_certs(domain: &str) -> Result<(Vec<rustls::Certificate>, rustls::PrivateKey)> {
|
fn load_letsencrypt_certs(domain: &str) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
|
||||||
let cert_path = format!("/etc/letsencrypt/live/{}/fullchain.pem", domain);
|
let cert_path = format!("/etc/letsencrypt/live/{}/fullchain.pem", domain);
|
||||||
let key_path = format!("/etc/letsencrypt/live/{}/privkey.pem", domain);
|
let key_path = format!("/etc/letsencrypt/live/{}/privkey.pem", domain);
|
||||||
|
|
||||||
@@ -43,20 +44,19 @@ fn load_letsencrypt_certs(domain: &str) -> Result<(Vec<rustls::Certificate>, rus
|
|||||||
let key_pem = std::fs::read(&key_path)?;
|
let key_pem = std::fs::read(&key_path)?;
|
||||||
|
|
||||||
// Parse PEM → DER
|
// Parse PEM → DER
|
||||||
let certs: Vec<rustls::Certificate> = rustls_pemfile::certs(&mut &cert_pem[..])
|
let certs: Vec<CertificateDer<'static>> = rustls_pemfile::certs(&mut &cert_pem[..])
|
||||||
.filter_map(|r| r.ok())
|
.filter_map(|r| r.ok())
|
||||||
.map(|der| rustls::Certificate(der.to_vec()))
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let key = rustls_pemfile::private_key(&mut &key_pem[..])?
|
let key = rustls_pemfile::private_key(&mut &key_pem[..])?
|
||||||
.ok_or_else(|| anyhow::anyhow!("No private key found in {}", key_path))?;
|
.ok_or_else(|| anyhow::anyhow!("No private key found in {}", key_path))?;
|
||||||
|
|
||||||
let private_key = rustls::PrivateKey(key.secret_der().to_vec());
|
let private_key = key;
|
||||||
|
|
||||||
// Compute SHA-256 hash of first cert for Quantum Trust
|
// Compute SHA-256 hash of first cert for Quantum Trust
|
||||||
if let Some(first_cert) = certs.first() {
|
if let Some(first_cert) = certs.first() {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(&first_cert.0);
|
hasher.update(&first_cert);
|
||||||
let hash_hex = hex::encode(hasher.finalize());
|
let hash_hex = hex::encode(hasher.finalize());
|
||||||
info!("[TLS] QUANTUM TRUST HASH (Let's Encrypt): {}", hash_hex);
|
info!("[TLS] QUANTUM TRUST HASH (Let's Encrypt): {}", hash_hex);
|
||||||
let _ = CERT_HASH.set(hash_hex);
|
let _ = CERT_HASH.set(hash_hex);
|
||||||
@@ -67,7 +67,7 @@ fn load_letsencrypt_certs(domain: &str) -> Result<(Vec<rustls::Certificate>, rus
|
|||||||
Ok((certs, private_key))
|
Ok((certs, private_key))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_selfsigned_certs(domain: &str) -> Result<(Vec<rustls::Certificate>, rustls::PrivateKey)> {
|
fn generate_selfsigned_certs(domain: &str) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
|
||||||
warn!("[TLS] Generating ephemeral self-signed certificates (Sovereign Mode)");
|
warn!("[TLS] Generating ephemeral self-signed certificates (Sovereign Mode)");
|
||||||
let subject_alt_names = vec![
|
let subject_alt_names = vec![
|
||||||
"localhost".to_string(),
|
"localhost".to_string(),
|
||||||
@@ -75,8 +75,8 @@ fn generate_selfsigned_certs(domain: &str) -> Result<(Vec<rustls::Certificate>,
|
|||||||
"xc.ultramodul.xyz".to_string(),
|
"xc.ultramodul.xyz".to_string(),
|
||||||
];
|
];
|
||||||
let cert = generate_simple_self_signed(subject_alt_names)?;
|
let cert = generate_simple_self_signed(subject_alt_names)?;
|
||||||
let cert_der = cert.serialize_der()?;
|
let cert_der = cert.cert.der().to_vec();
|
||||||
let priv_key_der = cert.serialize_private_key_der();
|
let priv_key_der = cert.key_pair.serialize_der();
|
||||||
|
|
||||||
// Compute SHA-256 hash
|
// Compute SHA-256 hash
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
@@ -86,8 +86,8 @@ fn generate_selfsigned_certs(domain: &str) -> Result<(Vec<rustls::Certificate>,
|
|||||||
let _ = CERT_HASH.set(hash_hex);
|
let _ = CERT_HASH.set(hash_hex);
|
||||||
let _ = TLS_MODE.set("SELFSIGNED".to_string());
|
let _ = TLS_MODE.set("SELFSIGNED".to_string());
|
||||||
|
|
||||||
let cert_chain = vec![rustls::Certificate(cert_der)];
|
let cert_chain = vec![CertificateDer::from(cert_der)];
|
||||||
let key = rustls::PrivateKey(priv_key_der);
|
let key = PrivateKeyDer::try_from(priv_key_der).map_err(|e| anyhow::anyhow!("Key parse error: {}", e))?;
|
||||||
|
|
||||||
warn!("[TLS] Self-signed certificates generated. Browser trust requires manual CA install.");
|
warn!("[TLS] Self-signed certificates generated. Browser trust requires manual CA install.");
|
||||||
Ok((cert_chain, key))
|
Ok((cert_chain, key))
|
||||||
@@ -100,7 +100,7 @@ pub async fn start_quic_listener(
|
|||||||
addr: &str,
|
addr: &str,
|
||||||
rooms: RoomsMap,
|
rooms: RoomsMap,
|
||||||
jwt_secret: String,
|
jwt_secret: String,
|
||||||
moq_relayer: Arc<xcu_sfu::moq::MoqRelayer>,
|
sfu_server: Arc<xcu_sfu_b::SfuServer>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let domain = std::env::var("XCU_DOMAIN").unwrap_or_else(|_| "mesh.ultramodul.xyz".to_string());
|
let domain = std::env::var("XCU_DOMAIN").unwrap_or_else(|_| "mesh.ultramodul.xyz".to_string());
|
||||||
let tls_mode = std::env::var("XCU_TLS_MODE").unwrap_or_else(|_| "LETSENCRYPT".to_string());
|
let tls_mode = std::env::var("XCU_TLS_MODE").unwrap_or_else(|_| "LETSENCRYPT".to_string());
|
||||||
@@ -130,12 +130,11 @@ pub async fn start_quic_listener(
|
|||||||
|
|
||||||
// Setup ServerConfig Quinn
|
// Setup ServerConfig Quinn
|
||||||
let mut server_crypto = rustls::ServerConfig::builder()
|
let mut server_crypto = rustls::ServerConfig::builder()
|
||||||
.with_safe_defaults()
|
|
||||||
.with_no_client_auth()
|
.with_no_client_auth()
|
||||||
.with_single_cert(cert_chain, key)?;
|
.with_single_cert(cert_chain, key)?;
|
||||||
server_crypto.alpn_protocols = vec![b"h3".to_vec()]; // WebTransport over HTTP/3
|
server_crypto.alpn_protocols = vec![b"h3".to_vec()]; // WebTransport over HTTP/3
|
||||||
|
|
||||||
let server_config = ServerConfig::with_crypto(Arc::new(server_crypto));
|
let server_config = ServerConfig::with_crypto(Arc::new(quinn::crypto::rustls::QuicServerConfig::try_from(server_crypto)?));
|
||||||
|
|
||||||
// Bind QUIC Endpoint
|
// Bind QUIC Endpoint
|
||||||
let parsed_addr: SocketAddr = addr.parse()?;
|
let parsed_addr: SocketAddr = addr.parse()?;
|
||||||
@@ -149,14 +148,14 @@ pub async fn start_quic_listener(
|
|||||||
let remote = incoming.remote_address();
|
let remote = incoming.remote_address();
|
||||||
info!("[QUIC] Incoming connection from {}", remote);
|
info!("[QUIC] Incoming connection from {}", remote);
|
||||||
let rooms_clone = rooms.clone();
|
let rooms_clone = rooms.clone();
|
||||||
let moq_clone = moq_relayer.clone();
|
|
||||||
let jwt_secret_clone = jwt_secret.clone();
|
let jwt_secret_clone = jwt_secret.clone();
|
||||||
|
let sfu_server_clone = sfu_server.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
match incoming.await {
|
match incoming.await {
|
||||||
Ok(conn) => {
|
Ok(conn) => {
|
||||||
info!("[QUIC] Connection established: {}", conn.remote_address());
|
info!("[QUIC] Connection established: {}", conn.remote_address());
|
||||||
handle_quic_connection(conn, rooms_clone, jwt_secret_clone, moq_clone).await;
|
handle_quic_connection(conn, rooms_clone, jwt_secret_clone, sfu_server_clone).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("[QUIC] Connection failed: {}", e);
|
warn!("[QUIC] Connection failed: {}", e);
|
||||||
@@ -173,7 +172,7 @@ async fn handle_quic_connection(
|
|||||||
conn: quinn::Connection,
|
conn: quinn::Connection,
|
||||||
rooms: RoomsMap,
|
rooms: RoomsMap,
|
||||||
_jwt_secret: String, // Reserved for QCG JWT verification (Phase 2)
|
_jwt_secret: String, // Reserved for QCG JWT verification (Phase 2)
|
||||||
moq_relayer: Arc<xcu_sfu::moq::MoqRelayer>,
|
sfu_server: Arc<xcu_sfu_b::SfuServer>,
|
||||||
) {
|
) {
|
||||||
let remote = conn.remote_address();
|
let remote = conn.remote_address();
|
||||||
|
|
||||||
@@ -232,6 +231,9 @@ async fn handle_quic_connection(
|
|||||||
let tx_clone = tx.clone();
|
let tx_clone = tx.clone();
|
||||||
let pid = participant_id;
|
let pid = participant_id;
|
||||||
|
|
||||||
|
let room_name_for_recv = room_name.clone();
|
||||||
|
let pid_str = pid.to_string();
|
||||||
|
|
||||||
// Task A: Read datagrams from QUIC client → broadcast to ROOMS
|
// Task A: Read datagrams from QUIC client → broadcast to ROOMS
|
||||||
let mut recv_task = tokio::spawn(async move {
|
let mut recv_task = tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
@@ -240,10 +242,22 @@ async fn handle_quic_connection(
|
|||||||
if datagram.len() < 4 { continue; }
|
if datagram.len() < 4 { continue; }
|
||||||
let frame_type = datagram[0];
|
let frame_type = datagram[0];
|
||||||
|
|
||||||
// Bandwidth telemetry (type 10 with special quality byte)
|
// Bandwidth telemetry
|
||||||
if frame_type == 10 && datagram.len() >= 4 {
|
if frame_type == 10 && datagram.len() >= 4 {
|
||||||
let score = datagram[1];
|
let _score = datagram[1];
|
||||||
moq_relayer.update_bandwidth_score(pid, score);
|
// sfu_b.update_score(...) could be added here
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward to SFU-B Nexus
|
||||||
|
if let Ok(nexus) = sfu_server.get_room(&room_name_for_recv) {
|
||||||
|
if let Ok(routed_packets) = nexus.route_rtp(&pid_str, &datagram) {
|
||||||
|
for (target_id, payload) in routed_packets {
|
||||||
|
// We need a way to send 'payload' to 'target_id'
|
||||||
|
// For now, we still broadcast to ROOMS channel and rely on the task B
|
||||||
|
let _ = target_id;
|
||||||
|
let _ = payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast to all subscribers (WS + QUIC) via ROOMS
|
// Broadcast to all subscribers (WS + QUIC) via ROOMS
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ edition = "2021"
|
|||||||
description = "XCU Terminal User Interface (Military Radar)"
|
description = "XCU Terminal User Interface (Military Radar)"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ratatui = "0.26"
|
ratatui = "0.29"
|
||||||
crossterm = "0.27"
|
crossterm = "0.27"
|
||||||
tokio = { version = "1.37", features = ["full"] }
|
tokio = { version = "1.37", features = ["full"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|||||||
@@ -18,5 +18,9 @@ wasm-bindgen-futures = "0.4.39"
|
|||||||
js-sys = "0.3.66"
|
js-sys = "0.3.66"
|
||||||
web-sys = { version = "0.3.66", features = ["Window", "Navigator", "MediaDevices", "WebTransport", "WebTransportBidirectionalStream", "WebTransportDatagramDuplexStream", "WebTransportReceiveStream", "console", "Document", "Element", "HtmlVideoElement", "MediaRecorder", "Blob", "Event", "MediaStream"] }
|
web-sys = { version = "0.3.66", features = ["Window", "Navigator", "MediaDevices", "WebTransport", "WebTransportBidirectionalStream", "WebTransportDatagramDuplexStream", "WebTransportReceiveStream", "console", "Document", "Element", "HtmlVideoElement", "MediaRecorder", "Blob", "Event", "MediaStream"] }
|
||||||
xcu-crypto = { path = "../xcu-crypto", default-features = false }
|
xcu-crypto = { path = "../xcu-crypto", default-features = false }
|
||||||
|
xcu-tamper-proof = { path = "../xcu-tamper-proof", default-features = false }
|
||||||
|
xcu-watermark = { path = "../xcu-watermark", default-features = false }
|
||||||
|
xcu-jailbreak-detector = { path = "../xcu-jailbreak-detector", default-features = false }
|
||||||
getrandom = { version = "0.2", features = ["js"] }
|
getrandom = { version = "0.2", features = ["js"] }
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
|
hex = "0.4"
|
||||||
|
|||||||
@@ -26,26 +26,71 @@ pub fn chunk_size_bytes(bitrate_kbps: u32, interval_ms: u32) -> u64 {
|
|||||||
(bitrate_kbps as u64 * interval_ms as u64) / 8
|
(bitrate_kbps as u64 * interval_ms as u64) / 8
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
use wasm_bindgen::prelude::*;
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[wasm_bindgen]
|
||||||
fn test_sdk_version() {
|
pub struct XcuWasmEngine {
|
||||||
assert!(!sdk_version().is_empty());
|
ratchet: Option<xcu_crypto::QuantumRatchet>,
|
||||||
assert!(sdk_version().contains("pxe"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[wasm_bindgen]
|
||||||
fn test_validate_url() {
|
impl XcuWasmEngine {
|
||||||
assert!(validate_upload_url("https://s3.amazonaws.com/bucket").is_ok());
|
#[wasm_bindgen(constructor)]
|
||||||
assert!(validate_upload_url("http://insecure.com").is_err());
|
pub fn new() -> Self {
|
||||||
assert!(validate_upload_url("").is_err());
|
XcuWasmEngine { ratchet: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[wasm_bindgen]
|
||||||
fn test_chunk_size() {
|
pub fn init_e2e_session(&mut self, shared_secret_hex: &str) -> bool {
|
||||||
// 2000 kbps * 1000ms / 8 = 250,000 bytes
|
if let Ok(secret) = hex::decode(shared_secret_hex) {
|
||||||
assert_eq!(chunk_size_bytes(2000, 1000), 250_000);
|
if secret.len() == 32 {
|
||||||
|
let mut key = [0u8; 32];
|
||||||
|
key.copy_from_slice(&secret);
|
||||||
|
self.ratchet = Some(xcu_crypto::QuantumRatchet::new(key));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn encrypt_video_frame(&mut self, payload: &[u8]) -> Vec<u8> {
|
||||||
|
if let Some(ratchet) = &mut self.ratchet {
|
||||||
|
let key = ratchet.crank_ratchet();
|
||||||
|
xcu_crypto::encrypt_payload_xchacha20(payload, &key)
|
||||||
|
} else {
|
||||||
|
payload.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn decrypt_video_frame(&mut self, encrypted: &[u8]) -> Vec<u8> {
|
||||||
|
if let Some(ratchet) = &mut self.ratchet {
|
||||||
|
let key = ratchet.crank_ratchet(); // Assuming symmetric synchronous cranking for demo
|
||||||
|
// Note: In real WebRTC E2EE, you manage ratchets per track/user.
|
||||||
|
xcu_crypto::decrypt_payload_xchacha20(encrypted, &key)
|
||||||
|
} else {
|
||||||
|
encrypted.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn detect_jailbreak(&self) -> bool {
|
||||||
|
let mut engine = xcu_jailbreak_detector::JailbreakDetectorEngine::new();
|
||||||
|
engine.configure("strict", "true").unwrap_or(());
|
||||||
|
engine.activate().is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn verify_tamper_proof(&self, hash: &str) -> bool {
|
||||||
|
let mut engine = xcu_tamper_proof::TamperProofEngine::new();
|
||||||
|
engine.configure("expected_hash", hash).unwrap_or(());
|
||||||
|
engine.activate().is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn embed_watermark(&self, image_data: &mut [u8], _user_id: &str) -> bool {
|
||||||
|
// xcu_watermark mutates the pixels
|
||||||
|
xcu_watermark::WatermarkEncoder::embed(image_data, b"XCU_WM", xcu_watermark::EmbedStrength::Robust).is_ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=XCU WebTransport QUIC Engine (Video Infrastructure)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=/opt/xcu
|
||||||
|
ExecStart=/opt/xcu/xcu-quic
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
LimitNOFILE=65536
|
||||||
|
|
||||||
|
# Environment Variables
|
||||||
|
Environment="XCU_QUIC_ADDR=0.0.0.0:443"
|
||||||
|
Environment="XCU_DOMAIN=api.xcomu.id"
|
||||||
|
Environment="XCU_TLS_MODE=LETSENCRYPT"
|
||||||
|
Environment="XCU_JWT_SECRET=xcu_live_jumpa_testkey"
|
||||||
|
Environment="RUST_LOG=info"
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user