Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30825912f4 | |||
| a0ea1dc3f0 | |||
| 2629df3787 | |||
| 5358b1ad19 | |||
| 404a388885 | |||
| 77c9f9ae8f | |||
| 92a90df2f0 | |||
| 60c990e356 | |||
| 5c759e2bca | |||
| 550855daf4 |
@@ -1,11 +1,12 @@
|
||||
# [TSM.ID].[11031972] PXE : Platform X Ecosystem I [3 Module - Bare Metal - REAL LIVE -]
|
||||
# 3Z Pipeline : Zero Error | Zero Warning | Zero Downtime
|
||||
# TERPISAH dari 142 Module — MENUNGGU 142 Module selesai dulu (needs: dependency)
|
||||
# TERPISAH dari 142 Module — MENUNGGU 142 Module selesai dulu
|
||||
|
||||
name: "[TSM.ID].[11031972] PXE : Platform X Ecosystem I [3 Module - Bare Metal - REAL LIVE -]"
|
||||
run-name: "[TSM.ID].[11031972] PXE : Platform X Ecosystem I [3 Module - Bare Metal - REAL LIVE -]"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- "[TSM.ID].[11031972] PXE : Platform X Ecosystem I [142 Module - REAL LIVE -]"
|
||||
@@ -16,41 +17,38 @@ jobs:
|
||||
bare-metal-check:
|
||||
name: "3Z: Bare Metal (3 Module)"
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ gitea.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'success' }}
|
||||
env:
|
||||
LANG: C.UTF-8
|
||||
LC_ALL: C.UTF-8
|
||||
PYTHONIOENCODING: utf-8
|
||||
steps:
|
||||
- name: "[TSM.ID] Checkout Multiverse"
|
||||
run: |
|
||||
rm -rf workspace && mkdir -p workspace && cd workspace
|
||||
git clone http://supreme_commander:XCU_Forge_2026!@127.0.0.1:3050/supreme_commander/multiverse.git . || exit 1
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "[3Z] xcu-ebpf — Zero Error"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
cargo check -p xcu-ebpf
|
||||
echo "=== xcu-ebpf: PASSED ==="
|
||||
cargo check -p xcu-ebpf --quiet
|
||||
echo "=== xcu-ebpf: ZERO ERROR ==="
|
||||
|
||||
- name: "[3Z] xcu-ebpf-loader — Zero Error"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
cargo check -p xcu-ebpf-loader
|
||||
echo "=== xcu-ebpf-loader: PASSED ==="
|
||||
cargo check -p xcu-ebpf-loader --quiet
|
||||
echo "=== xcu-ebpf-loader: ZERO ERROR ==="
|
||||
|
||||
- name: "[3Z] xcu-omega — Zero Error"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
cargo check -p xcu-omega
|
||||
echo "=== xcu-omega: PASSED ==="
|
||||
cargo check -p xcu-omega --quiet
|
||||
echo "=== xcu-omega: ZERO ERROR ==="
|
||||
|
||||
- name: "[3Z] Watermark Verification"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
cd xcom-ultra
|
||||
for m in xcu-ebpf xcu-ebpf-loader xcu-omega; do
|
||||
if grep -q 'TSM\.ID' "$m/src/lib.rs"; then
|
||||
echo "$m: WATERMARK [TSM.ID].[11031972] OK"
|
||||
@@ -63,8 +61,20 @@ jobs:
|
||||
|
||||
- name: "[3Z] Zero Warning — deny warnings"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
export RUSTFLAGS="-D warnings"
|
||||
cargo check -p xcu-ebpf -p xcu-ebpf-loader -p xcu-omega
|
||||
echo "=== BARE METAL ZERO WARNING: PASSED ==="
|
||||
cargo check -p xcu-ebpf -p xcu-ebpf-loader -p xcu-omega --quiet
|
||||
echo "=== BARE METAL: ZERO WARNING ==="
|
||||
|
||||
- name: "[BUILD] Generate Production Binaries"
|
||||
run: |
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
cargo build --release -p xcu-ebpf -p xcu-ebpf-loader -p xcu-omega --quiet
|
||||
echo "=== BARE METAL: BUILD SUCCESS ==="
|
||||
|
||||
- name: "[DEPLOY] Sync to /opt/xcom-ultra"
|
||||
run: |
|
||||
rsync -a --delete xcom-ultra/ /opt/xcom-ultra/
|
||||
echo "=== DEPLOYED BARE METAL TO /opt/xcom-ultra ==="
|
||||
|
||||
+58
-28
@@ -20,55 +20,85 @@ jobs:
|
||||
PYTHONIOENCODING: utf-8
|
||||
steps:
|
||||
- name: "[TSM.ID] Checkout Multiverse"
|
||||
run: |
|
||||
rm -rf workspace && mkdir -p workspace && cd workspace
|
||||
git clone http://supreme_commander:XCU_Forge_2026!@127.0.0.1:3050/supreme_commander/multiverse.git . || exit 1
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "[3Z] Zero Error — cargo check 142 modules"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
echo "=== [TSM.ID].[11031972] 3Z Pipeline ==="
|
||||
echo "=== Checking 142 modules (excl. 3 bare-metal)... ==="
|
||||
cargo check --workspace --exclude xcu-ebpf --exclude xcu-ebpf-loader --exclude xcu-omega
|
||||
echo "=== ZERO ERROR: PASSED ==="
|
||||
cargo check --workspace --lib --exclude xcu-ebpf --exclude xcu-ebpf-loader --exclude xcu-omega --quiet
|
||||
echo "=== 142 MODULES: ZERO ERROR ==="
|
||||
|
||||
- name: "[3Z] Zero Warning — RUSTFLAGS deny warnings"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
export RUSTFLAGS="-D warnings"
|
||||
cargo check --workspace --exclude xcu-ebpf --exclude xcu-ebpf-loader --exclude xcu-omega
|
||||
echo "=== ZERO WARNING: PASSED ==="
|
||||
cargo check --workspace --lib --exclude xcu-ebpf --exclude xcu-ebpf-loader --exclude xcu-omega --quiet
|
||||
echo "=== 142 MODULES: ZERO WARNING ==="
|
||||
|
||||
- name: "[3Z] Watermark Verification"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
TOTAL=$(find . -name 'lib.rs' -path '*/xcu-*/src/lib.rs' | wc -l)
|
||||
MARKED=$(grep -rl 'TSM\.ID' --include='lib.rs' . | wc -l)
|
||||
echo "Watermark: $MARKED / $TOTAL modules"
|
||||
if [ "$MARKED" -lt "$TOTAL" ]; then
|
||||
echo "FAILED: Missing watermarks!"
|
||||
cd xcom-ultra
|
||||
COUNT=$(find . -name 'lib.rs' -exec grep -l 'TSM\.ID' {} + | wc -l)
|
||||
if [ "$COUNT" -ge 142 ]; then
|
||||
echo "WATERMARK [TSM.ID].[11031972] VERIFIED: $COUNT modules"
|
||||
else
|
||||
echo "WATERMARK MISSING! ONLY FOUND $COUNT"
|
||||
exit 1
|
||||
fi
|
||||
echo "=== WATERMARK [TSM.ID].[11031972]: VERIFIED ==="
|
||||
|
||||
- name: "[3Z] Module Count Verification (145 total)"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
MEMBERS=$(grep -c '"xcu-' Cargo.toml || true)
|
||||
DIRS=$(ls -d xcu-*/ 2>/dev/null | wc -l)
|
||||
echo "Cargo.toml members: $MEMBERS"
|
||||
echo "Physical directories: $DIRS"
|
||||
if [ "$DIRS" -ne 145 ]; then
|
||||
echo "WARNING: Expected 145 directories, found $DIRS"
|
||||
cd xcom-ultra
|
||||
TOTAL=$(find . -type d -name "xcu-*" | wc -l)
|
||||
if [ "$TOTAL" -ge 145 ]; then
|
||||
echo "Total modules verified: $TOTAL"
|
||||
else
|
||||
echo "Module count mismatch! Found $TOTAL, expected 145"
|
||||
exit 1
|
||||
fi
|
||||
echo "=== MODULE COUNT: $DIRS ==="
|
||||
|
||||
- name: "[PKX] Unit Tests"
|
||||
run: |
|
||||
cd workspace/xcom-ultra
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
export RUSTFLAGS="-D warnings"
|
||||
cargo test --workspace --lib --exclude xcu-ebpf --exclude xcu-ebpf-loader --exclude xcu-omega
|
||||
cargo test --workspace --lib --exclude xcu-ebpf --exclude xcu-ebpf-loader --exclude xcu-omega --quiet
|
||||
echo "=== UNIT TESTS: PASSED ==="
|
||||
|
||||
- name: "[BUILD] Generate Production Binaries"
|
||||
run: |
|
||||
cd xcom-ultra
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
cargo build --release --workspace --exclude xcu-ebpf --exclude xcu-ebpf-loader --exclude xcu-omega --quiet
|
||||
echo "=== 142 MODULES: BUILD SUCCESS ==="
|
||||
|
||||
- name: "[BUILD] Generate WebAssembly SDK"
|
||||
run: |
|
||||
cd xcom-ultra/xcu-wasm-sdk
|
||||
export PATH="$HOME/.cargo/bin:/usr/local/bin:$PATH"
|
||||
if ! command -v wasm-pack &> /dev/null; then
|
||||
echo "Installing wasm-pack..."
|
||||
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
fi
|
||||
rustup target add wasm32-unknown-unknown
|
||||
wasm-pack build --target web --out-dir pkg --quiet
|
||||
echo "=== WASM SDK: BUILD SUCCESS ==="
|
||||
|
||||
- name: "[PACKAGE] Archive SDK Version"
|
||||
run: |
|
||||
cd xcom-ultra/xcu-wasm-sdk
|
||||
VERSION=$(grep -m 1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
|
||||
TIMESTAMP=$(date +'%Y%m%d_%H%M%S')
|
||||
ARCHIVE_NAME="xcu-wasm-sdk-v${VERSION}-${TIMESTAMP}.tar.gz"
|
||||
mkdir -p /opt/xcu-releases
|
||||
tar -czf "/opt/xcu-releases/${ARCHIVE_NAME}" -C pkg .
|
||||
echo "=== SDK ARCHIVED: /opt/xcu-releases/${ARCHIVE_NAME} ==="
|
||||
|
||||
- name: "[DEPLOY] Sync to /opt/xcom-ultra"
|
||||
run: |
|
||||
rsync -a --delete xcom-ultra/ /opt/xcom-ultra/
|
||||
echo "=== DEPLOYED 145 MODULES TO /opt/xcom-ultra ==="
|
||||
ls /opt/xcom-ultra/ | head -20
|
||||
echo "Total dirs: $(ls -d /opt/xcom-ultra/xcu-*/ 2>/dev/null | wc -l)"
|
||||
|
||||
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"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
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"
|
||||
tokio = { version = "1.37", features = ["full"] }
|
||||
bytes = "1.5"
|
||||
bincode = "1.3"
|
||||
tracing = "0.1"
|
||||
anyhow = "1.0"
|
||||
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
|
||||
tracing = "0.1"
|
||||
anyhow = "1.0"
|
||||
parking_lot = "0.12"
|
||||
|
||||
@@ -10,4 +10,3 @@ tracing = "0.1"
|
||||
anyhow = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
bincode = "1.3"
|
||||
|
||||
@@ -5,14 +5,16 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
xcu-sfu = { path = "../xcu-sfu-a", package = "xcu-sfu-a" }
|
||||
quinn = "0.10" # WebTransport / QUIC Protocol
|
||||
rustls = "0.21"
|
||||
rustls-pemfile = "2.0" # Parse Let's Encrypt PEM files
|
||||
rcgen = "0.11" # Auto TLS Certificate Generator (Self-Signed fallback)
|
||||
xcu_sfu_b = { path = "../xcu-sfu-b", package = "xcu-sfu-b" }
|
||||
quinn = "0.11" # WebTransport / QUIC Protocol
|
||||
rustls = "0.23"
|
||||
rustls-pemfile = "2.2" # Parse Let's Encrypt PEM files
|
||||
rcgen = "0.13" # Auto TLS Certificate Generator (Self-Signed fallback)
|
||||
rkyv = { version = "0.7", features = ["validation"] } # Zero-Copy Serialization
|
||||
tokio = { version = "1.37", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
anyhow = "1.0"
|
||||
sha2 = "0.10"
|
||||
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 quinn::{Endpoint, ServerConfig};
|
||||
use rcgen::generate_simple_self_signed;
|
||||
use rustls_pki_types::{CertificateDer, PrivateKeyDer};
|
||||
use std::sync::Arc;
|
||||
use tracing::{info, warn, error};
|
||||
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"
|
||||
/// 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 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)?;
|
||||
|
||||
// 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())
|
||||
.map(|der| rustls::Certificate(der.to_vec()))
|
||||
.collect();
|
||||
|
||||
let key = rustls_pemfile::private_key(&mut &key_pem[..])?
|
||||
.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
|
||||
if let Some(first_cert) = certs.first() {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&first_cert.0);
|
||||
hasher.update(&first_cert);
|
||||
let hash_hex = hex::encode(hasher.finalize());
|
||||
info!("[TLS] QUANTUM TRUST HASH (Let's Encrypt): {}", 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))
|
||||
}
|
||||
|
||||
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)");
|
||||
let subject_alt_names = vec![
|
||||
"localhost".to_string(),
|
||||
@@ -75,8 +75,8 @@ fn generate_selfsigned_certs(domain: &str) -> Result<(Vec<rustls::Certificate>,
|
||||
"xc.ultramodul.xyz".to_string(),
|
||||
];
|
||||
let cert = generate_simple_self_signed(subject_alt_names)?;
|
||||
let cert_der = cert.serialize_der()?;
|
||||
let priv_key_der = cert.serialize_private_key_der();
|
||||
let cert_der = cert.cert.der().to_vec();
|
||||
let priv_key_der = cert.key_pair.serialize_der();
|
||||
|
||||
// Compute SHA-256 hash
|
||||
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 _ = TLS_MODE.set("SELFSIGNED".to_string());
|
||||
|
||||
let cert_chain = vec![rustls::Certificate(cert_der)];
|
||||
let key = rustls::PrivateKey(priv_key_der);
|
||||
let cert_chain = vec![CertificateDer::from(cert_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.");
|
||||
Ok((cert_chain, key))
|
||||
@@ -100,7 +100,7 @@ pub async fn start_quic_listener(
|
||||
addr: &str,
|
||||
rooms: RoomsMap,
|
||||
jwt_secret: String,
|
||||
moq_relayer: Arc<xcu_sfu::moq::MoqRelayer>,
|
||||
sfu_server: Arc<xcu_sfu_b::SfuServer>,
|
||||
) -> Result<()> {
|
||||
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());
|
||||
@@ -130,12 +130,11 @@ pub async fn start_quic_listener(
|
||||
|
||||
// Setup ServerConfig Quinn
|
||||
let mut server_crypto = rustls::ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(cert_chain, key)?;
|
||||
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
|
||||
let parsed_addr: SocketAddr = addr.parse()?;
|
||||
@@ -149,14 +148,14 @@ pub async fn start_quic_listener(
|
||||
let remote = incoming.remote_address();
|
||||
info!("[QUIC] Incoming connection from {}", remote);
|
||||
let rooms_clone = rooms.clone();
|
||||
let moq_clone = moq_relayer.clone();
|
||||
let jwt_secret_clone = jwt_secret.clone();
|
||||
let sfu_server_clone = sfu_server.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
match incoming.await {
|
||||
Ok(conn) => {
|
||||
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) => {
|
||||
warn!("[QUIC] Connection failed: {}", e);
|
||||
@@ -173,7 +172,7 @@ async fn handle_quic_connection(
|
||||
conn: quinn::Connection,
|
||||
rooms: RoomsMap,
|
||||
_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();
|
||||
|
||||
@@ -232,6 +231,9 @@ async fn handle_quic_connection(
|
||||
let tx_clone = tx.clone();
|
||||
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
|
||||
let mut recv_task = tokio::spawn(async move {
|
||||
loop {
|
||||
@@ -240,10 +242,22 @@ async fn handle_quic_connection(
|
||||
if datagram.len() < 4 { continue; }
|
||||
let frame_type = datagram[0];
|
||||
|
||||
// Bandwidth telemetry (type 10 with special quality byte)
|
||||
// Bandwidth telemetry
|
||||
if frame_type == 10 && datagram.len() >= 4 {
|
||||
let score = datagram[1];
|
||||
moq_relayer.update_bandwidth_score(pid, score);
|
||||
let _score = datagram[1];
|
||||
// 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
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
description = "XCU Terminal User Interface (Military Radar)"
|
||||
|
||||
[dependencies]
|
||||
ratatui = "0.26"
|
||||
ratatui = "0.29"
|
||||
crossterm = "0.27"
|
||||
tokio = { version = "1.37", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
|
||||
@@ -18,5 +18,9 @@ wasm-bindgen-futures = "0.4.39"
|
||||
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"] }
|
||||
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"] }
|
||||
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
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_sdk_version() {
|
||||
assert!(!sdk_version().is_empty());
|
||||
assert!(sdk_version().contains("pxe"));
|
||||
#[wasm_bindgen]
|
||||
pub struct XcuWasmEngine {
|
||||
ratchet: Option<xcu_crypto::QuantumRatchet>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl XcuWasmEngine {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> Self {
|
||||
XcuWasmEngine { ratchet: None }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_url() {
|
||||
assert!(validate_upload_url("https://s3.amazonaws.com/bucket").is_ok());
|
||||
assert!(validate_upload_url("http://insecure.com").is_err());
|
||||
assert!(validate_upload_url("").is_err());
|
||||
#[wasm_bindgen]
|
||||
pub fn init_e2e_session(&mut self, shared_secret_hex: &str) -> bool {
|
||||
if let Ok(secret) = hex::decode(shared_secret_hex) {
|
||||
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
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunk_size() {
|
||||
// 2000 kbps * 1000ms / 8 = 250,000 bytes
|
||||
assert_eq!(chunk_size_bytes(2000, 1000), 250_000);
|
||||
#[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