v0.9.6: InfoNet hashchain, Wormhole gate encryption, mesh reputation, 16 community contributors

Gate messages now propagate via the Infonet hashchain as encrypted blobs — every node syncs them
through normal chain sync while only Gate members with MLS keys can decrypt. Added mesh reputation
system, peer push workers, voluntary Wormhole opt-in for node participation, fork recovery,
killwormhole scripts, obfuscated terminology, and hardened the self-updater to protect encryption
keys and chain state during updates.

New features: Shodan search, train tracking, Sentinel Hub imagery, 8 new intelligence layers,
CCTV expansion to 11,000+ cameras across 6 countries, Mesh Terminal CLI, prediction markets,
desktop-shell scaffold, and comprehensive mesh test suite (215 frontend + backend tests passing).

Community contributors: @wa1id, @AlborzNazari, @adust09, @Xpirix, @imqdcr, @csysp, @suranyami,
@chr0n1x, @johan-martensson, @singularfailure, @smithbh, @OrfeoTerkuci, @deuza, @tm-const,
@Elhard1, @ttulttul
This commit is contained in:
anoracleofra-code
2026-03-26 05:58:04 -06:00
parent d363013742
commit 668ce16dc7
363 changed files with 170456 additions and 23229 deletions
+51
View File
@@ -0,0 +1,51 @@
# Desktop Shell Scaffold
This folder is the first native-side scaffold for the staged desktop boundary.
## Purpose
It gives the future Tauri/native shell a concrete shape for:
- command routing
- handler grouping
- runtime bridge installation
without forcing a packaging migration yet.
## Source of truth
The shared desktop control contract still lives in:
- `F:\Codebase\Oracle\live-risk-dashboard\frontend\src\lib\desktopControlContract.ts`
The native-side scaffold imports that contract rather than redefining it.
## First command scope
The initial native command set covers only:
- Wormhole lifecycle
- protected settings get/set
- update trigger
That is deliberate. The goal is to move the local privileged control plane first, not the entire
mesh data plane.
## Scaffold layout
- `F:\Codebase\Oracle\live-risk-dashboard\desktop-shell\src\types.ts`
- `F:\Codebase\Oracle\live-risk-dashboard\desktop-shell\src\handlers\wormholeHandlers.ts`
- `F:\Codebase\Oracle\live-risk-dashboard\desktop-shell\src\handlers\settingsHandlers.ts`
- `F:\Codebase\Oracle\live-risk-dashboard\desktop-shell\src\handlers\updateHandlers.ts`
- `F:\Codebase\Oracle\live-risk-dashboard\desktop-shell\src\nativeControlRouter.ts`
- `F:\Codebase\Oracle\live-risk-dashboard\desktop-shell\src\runtimeBridge.ts`
## How to use later
When the Tauri shell is introduced, its command layer should:
1. receive `invokeLocalControl(command, payload)`
2. dispatch through `createNativeControlRouter(...)`
3. return the handler result back to the frontend bridge
This keeps the frontend contract stable while shifting privileged ownership into the native shell.
@@ -0,0 +1,50 @@
import type { NativeControlHandlerMap } from '../types';
export function createSettingsHandlers(): Pick<
NativeControlHandlerMap,
| 'settings.wormhole.get'
| 'settings.wormhole.set'
| 'settings.privacy.get'
| 'settings.privacy.set'
| 'settings.api_keys.get'
| 'settings.api_keys.set'
| 'settings.news.get'
| 'settings.news.set'
| 'settings.news.reset'
> {
return {
'settings.wormhole.get': async (_payload, _ctx, exec) => exec('/api/settings/wormhole'),
'settings.wormhole.set': async (payload, _ctx, exec) =>
exec('/api/settings/wormhole', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'settings.privacy.get': async (_payload, _ctx, exec) =>
exec('/api/settings/privacy-profile'),
'settings.privacy.set': async (payload, _ctx, exec) =>
exec('/api/settings/privacy-profile', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'settings.api_keys.get': async (_payload, _ctx, exec) => exec('/api/settings/api-keys'),
'settings.api_keys.set': async (payload, _ctx, exec) =>
exec('/api/settings/api-keys', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'settings.news.get': async (_payload, _ctx, exec) => exec('/api/settings/news-feeds'),
'settings.news.set': async (payload, _ctx, exec) =>
exec('/api/settings/news-feeds', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'settings.news.reset': async (_payload, _ctx, exec) =>
exec('/api/settings/news-feeds/reset', {
method: 'POST',
}),
};
}
@@ -0,0 +1,10 @@
import type { NativeControlHandlerMap } from '../types';
export function createUpdateHandlers(): Pick<NativeControlHandlerMap, 'system.update'> {
return {
'system.update': async (_payload, _ctx, exec) =>
exec('/api/system/update', {
method: 'POST',
}),
};
}
@@ -0,0 +1,102 @@
import type { NativeControlHandlerMap } from '../types';
export function createWormholeHandlers(): Pick<
NativeControlHandlerMap,
| 'wormhole.status'
| 'wormhole.connect'
| 'wormhole.disconnect'
| 'wormhole.restart'
| 'wormhole.gate.enter'
| 'wormhole.gate.leave'
| 'wormhole.gate.proof'
| 'wormhole.gate.personas.get'
| 'wormhole.gate.persona.create'
| 'wormhole.gate.persona.activate'
| 'wormhole.gate.persona.clear'
| 'wormhole.gate.key.get'
| 'wormhole.gate.key.rotate'
| 'wormhole.gate.message.compose'
| 'wormhole.gate.message.decrypt'
| 'wormhole.gate.message.post'
| 'wormhole.gate.messages.decrypt'
> {
return {
'wormhole.status': async (_payload, _ctx, exec) => exec('/api/wormhole/status'),
'wormhole.connect': async (_payload, _ctx, exec) =>
exec('/api/wormhole/connect', { method: 'POST' }),
'wormhole.disconnect': async (_payload, _ctx, exec) =>
exec('/api/wormhole/disconnect', { method: 'POST' }),
'wormhole.restart': async (_payload, _ctx, exec) =>
exec('/api/wormhole/restart', { method: 'POST' }),
'wormhole.gate.personas.get': async (payload, _ctx, exec) =>
exec(`/api/wormhole/gate/${encodeURIComponent(String(payload?.gate_id || ''))}/personas`),
'wormhole.gate.persona.create': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/persona/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.persona.activate': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/persona/activate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.persona.clear': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/persona/clear', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.key.get': async (payload, _ctx, exec) =>
exec(`/api/wormhole/gate/${encodeURIComponent(String(payload?.gate_id || ''))}/key`),
'wormhole.gate.key.rotate': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/key/rotate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.message.compose': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/message/compose', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.message.decrypt': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/message/decrypt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.enter': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/enter', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.leave': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/leave', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.proof': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/proof', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.message.post': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/message/post', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
'wormhole.gate.messages.decrypt': async (payload, _ctx, exec) =>
exec('/api/wormhole/gate/messages/decrypt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
};
}
+4
View File
@@ -0,0 +1,4 @@
export * from './nativeControlRouter';
export * from './runtimeBridge';
export * from './nativeControlAudit';
export * from './types';
+60
View File
@@ -0,0 +1,60 @@
import type {
DesktopControlAuditOutcome,
DesktopControlAuditRecord,
DesktopControlAuditReport,
} from '../../frontend/src/lib/desktopControlContract';
import type { NativeControlAuditEvent, NativeControlAuditTrail } from './types';
const DEFAULT_LIMIT = 100;
function incrementOutcome(
counts: Partial<Record<DesktopControlAuditOutcome, number>>,
outcome: DesktopControlAuditOutcome,
) {
counts[outcome] = (counts[outcome] || 0) + 1;
}
export function createNativeControlAuditTrail(maxEntries: number = DEFAULT_LIMIT): NativeControlAuditTrail {
const entries: DesktopControlAuditRecord[] = [];
let totalRecorded = 0;
return {
record(event: NativeControlAuditEvent) {
totalRecorded += 1;
entries.push({
...event,
recordedAt: Date.now(),
});
if (entries.length > maxEntries) {
entries.splice(0, entries.length - maxEntries);
}
},
snapshot(limit: number = 25): DesktopControlAuditReport {
const recent = entries.slice(-Math.max(1, limit)).reverse();
const byOutcome: Partial<Record<DesktopControlAuditOutcome, number>> = {};
let lastProfileMismatch: DesktopControlAuditRecord | undefined;
let lastDenied: DesktopControlAuditRecord | undefined;
for (const entry of entries) {
incrementOutcome(byOutcome, entry.outcome);
if (entry.outcome === 'profile_warn' || entry.outcome === 'profile_denied') {
lastProfileMismatch = entry;
}
if (entry.outcome === 'profile_denied' || entry.outcome === 'capability_denied') {
lastDenied = entry;
}
}
return {
totalEvents: entries.length,
totalRecorded,
recent,
byOutcome,
lastProfileMismatch,
lastDenied,
};
},
clear() {
totalRecorded = 0;
entries.splice(0, entries.length);
},
};
}
+105
View File
@@ -0,0 +1,105 @@
import type {
DesktopControlCommand,
DesktopControlPayloadMap,
} from '../../frontend/src/lib/desktopControlContract';
import {
controlCommandCapability as resolveCommandCapability,
extractGateTargetRef,
sessionProfileCapabilities as capabilitiesForProfile,
} from '../../frontend/src/lib/desktopControlContract';
import { createSettingsHandlers } from './handlers/settingsHandlers';
import { createUpdateHandlers } from './handlers/updateHandlers';
import { createWormholeHandlers } from './handlers/wormholeHandlers';
import type {
NativeControlExecutor,
NativeControlHandlerContext,
NativeControlHandlerMap,
NativeControlInvokeMeta,
} from './types';
function createHandlerMap(): NativeControlHandlerMap {
return {
...createWormholeHandlers(),
...createSettingsHandlers(),
...createUpdateHandlers(),
};
}
export function createNativeControlRouter(
ctx: NativeControlHandlerContext,
exec: NativeControlExecutor,
) {
const handlers = createHandlerMap();
return {
async invoke<C extends DesktopControlCommand>(
command: C,
payload: DesktopControlPayloadMap[C],
meta?: NativeControlInvokeMeta,
): Promise<unknown> {
const handler = handlers[command];
if (!handler) {
throw new Error(`native_control_handler_missing:${command}`);
}
const expectedCapability = resolveCommandCapability(command);
const profile = ctx.sessionProfile;
const profileCapabilities = profile ? capabilitiesForProfile(profile) : [];
const profileAllows =
!profile || profileCapabilities.length === 0 || profileCapabilities.includes(expectedCapability);
const profileEnforced = Boolean((ctx.enforceSessionProfile || meta?.enforceProfileHint) && profile);
const allowedCapabilitiesConfigured =
Array.isArray(ctx.allowedCapabilities) && ctx.allowedCapabilities.length > 0;
const capabilityDenied =
allowedCapabilitiesConfigured && !ctx.allowedCapabilities!.includes(expectedCapability);
const targetRef = extractGateTargetRef(command, payload);
const auditBase = {
command,
expectedCapability,
declaredCapability: meta?.capability,
...(targetRef ? { targetRef } : {}),
sessionProfile: profile,
sessionProfileHint: meta?.sessionProfileHint,
enforceProfileHint: meta?.enforceProfileHint,
profileAllows,
allowedCapabilitiesConfigured,
enforced: profileEnforced,
} as const;
if (meta?.capability && meta.capability !== expectedCapability) {
ctx.auditControlUse?.({
...auditBase,
outcome: 'capability_mismatch',
});
throw new Error(
`native_control_capability_mismatch:${meta.capability}:${expectedCapability}`,
);
}
if (capabilityDenied) {
ctx.auditControlUse?.({
...auditBase,
outcome: 'capability_denied',
});
throw new Error(`native_control_capability_denied:${expectedCapability}`);
}
if (!profileAllows) {
const profileMessage = `native_control_profile_mismatch:${profile}:${expectedCapability}`;
ctx.auditControlUse?.({
...auditBase,
outcome: profileEnforced ? 'profile_denied' : 'profile_warn',
});
if (profileEnforced) {
throw new Error(profileMessage);
}
console.warn(profileMessage, {
command,
sessionProfileHint: meta?.sessionProfileHint,
});
}
if (profileAllows) {
ctx.auditControlUse?.({
...auditBase,
outcome: 'allowed',
});
}
return handler(payload, ctx, exec);
},
};
}
+65
View File
@@ -0,0 +1,65 @@
import type {
DesktopControlCommand,
DesktopControlPayloadMap,
LocalControlInvokeMeta,
} from '../../frontend/src/lib/desktopControlContract';
import { createNativeControlAuditTrail } from './nativeControlAudit';
import { createNativeControlRouter } from './nativeControlRouter';
import type {
NativeControlAuditEvent,
NativeControlExecutor,
NativeControlHandlerContext,
} from './types';
async function defaultExecutor<T = unknown>(baseUrl: string, path: string, init: RequestInit = {}): Promise<T> {
const res = await fetch(`${baseUrl}${path}`, init);
const data = await res.json().catch(() => ({}));
if (!res.ok || data?.ok === false) {
throw new Error(data?.detail || data?.message || 'native_control_request_failed');
}
return data as T;
}
export function createRuntimeBridge(ctx: NativeControlHandlerContext) {
const auditTrail = ctx.auditTrail || createNativeControlAuditTrail();
const auditControlUse = (event: NativeControlAuditEvent) => {
auditTrail.record(event);
ctx.auditControlUse?.(event);
};
const exec: NativeControlExecutor = <T = unknown>(path: string, init: RequestInit = {}) => {
const headers = new Headers(init.headers);
if (ctx.adminKey && !headers.has('X-Admin-Key')) {
headers.set('X-Admin-Key', ctx.adminKey);
}
return defaultExecutor<T>(ctx.backendBaseUrl, path, { ...init, headers });
};
function invocationContext(meta?: LocalControlInvokeMeta): NativeControlHandlerContext {
const baseCtx: NativeControlHandlerContext = {
...ctx,
auditTrail,
auditControlUse,
};
if (ctx.sessionProfile || !meta?.sessionProfileHint) {
return baseCtx;
}
return {
...baseCtx,
sessionProfile: meta.sessionProfileHint,
};
}
return {
invokeLocalControl<C extends DesktopControlCommand>(
command: C,
payload: DesktopControlPayloadMap[C],
meta?: LocalControlInvokeMeta,
) {
return createNativeControlRouter(invocationContext(meta), exec).invoke(command, payload, meta);
},
getNativeControlAuditReport(limit?: number) {
return auditTrail.snapshot(limit);
},
clearNativeControlAuditReport() {
auditTrail.clear();
},
};
}
+43
View File
@@ -0,0 +1,43 @@
import type {
DesktopControlAuditEvent,
DesktopControlAuditReport,
DesktopControlCapability,
DesktopControlCommand,
DesktopControlPayloadMap,
DesktopControlSessionProfile,
LocalControlInvokeMeta,
} from '../../frontend/src/lib/desktopControlContract';
export type NativeControlHandlerContext = {
backendBaseUrl: string;
wormholeBaseUrl: string;
adminKey?: string;
allowedCapabilities?: DesktopControlCapability[];
sessionProfile?: DesktopControlSessionProfile;
enforceSessionProfile?: boolean;
auditControlUse?: (event: NativeControlAuditEvent) => void;
auditTrail?: NativeControlAuditTrail;
};
export type NativeControlExecutor = <T = unknown>(
path: string,
init?: RequestInit,
) => Promise<T>;
export type NativeControlHandlerMap = {
[K in DesktopControlCommand]: (
payload: DesktopControlPayloadMap[K],
ctx: NativeControlHandlerContext,
exec: NativeControlExecutor,
) => Promise<unknown>;
};
export type NativeControlInvokeMeta = LocalControlInvokeMeta;
export type NativeControlAuditEvent = DesktopControlAuditEvent;
export type NativeControlAuditTrail = {
record: (event: NativeControlAuditEvent) => void;
snapshot: (limit?: number) => DesktopControlAuditReport;
clear: () => void;
};
+33
View File
@@ -0,0 +1,33 @@
# Tauri Skeleton
This folder is the first concrete Tauri-side integration skeleton for the desktop boundary.
## Scope
It is intentionally limited to the first trusted local control-plane command set:
- Wormhole lifecycle
- protected settings reads/writes
- update trigger
It does **not** attempt to move DM/data-plane operations yet.
## What this scaffold demonstrates
1. a native `invoke_local_control` command entrypoint
2. a small Rust-side router for the first command set
3. backend HTTP delegation with native-side admin-key ownership
4. a simple webview runtime injection path for:
- `window.__SHADOWBROKER_DESKTOP__.invokeLocalControl(...)`
## Important note
This is a scaffold, not a fully integrated desktop app yet. It exists so the next Tauri pass has a
clear structure instead of starting from scratch.
## Shared contract
The command names this scaffold must track are defined in:
- `F:\Codebase\Oracle\live-risk-dashboard\frontend\src\lib\desktopControlContract.ts`
- `F:\Codebase\Oracle\live-risk-dashboard\frontend\src\lib\desktopControlRouting.ts`
@@ -0,0 +1,13 @@
[package]
name = "shadowbroker-tauri-shell"
version = "0.1.0"
edition = "2021"
[build-dependencies]
tauri-build = { version = "2" }
[dependencies]
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri = { version = "2", features = [] }
@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}
@@ -0,0 +1,19 @@
use serde_json::Value;
use tauri::State;
use crate::{handlers::dispatch_control_command, DesktopAppState};
#[tauri::command]
pub async fn invoke_local_control(
command: String,
payload: Option<Value>,
state: State<'_, DesktopAppState>,
) -> Result<Value, String> {
dispatch_control_command(
&state.backend_base_url,
state.admin_key.as_deref(),
&command,
payload,
)
.await
}
@@ -0,0 +1,57 @@
use reqwest::Method;
use serde_json::Value;
use crate::http_client::call_backend_json;
pub async fn dispatch_control_command(
backend_base_url: &str,
admin_key: Option<&str>,
command: &str,
payload: Option<Value>,
) -> Result<Value, String> {
match command {
"wormhole.status" => {
call_backend_json(backend_base_url, admin_key, "/api/wormhole/status", Method::GET, None).await
}
"wormhole.connect" => {
call_backend_json(backend_base_url, admin_key, "/api/wormhole/connect", Method::POST, None).await
}
"wormhole.disconnect" => {
call_backend_json(backend_base_url, admin_key, "/api/wormhole/disconnect", Method::POST, None).await
}
"wormhole.restart" => {
call_backend_json(backend_base_url, admin_key, "/api/wormhole/restart", Method::POST, None).await
}
"settings.wormhole.get" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/wormhole", Method::GET, None).await
}
"settings.wormhole.set" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/wormhole", Method::PUT, payload).await
}
"settings.privacy.get" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/privacy-profile", Method::GET, None).await
}
"settings.privacy.set" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/privacy-profile", Method::PUT, payload).await
}
"settings.api_keys.get" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/api-keys", Method::GET, None).await
}
"settings.api_keys.set" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/api-keys", Method::PUT, payload).await
}
"settings.news.get" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/news-feeds", Method::GET, None).await
}
"settings.news.set" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/news-feeds", Method::PUT, payload).await
}
"settings.news.reset" => {
call_backend_json(backend_base_url, admin_key, "/api/settings/news-feeds/reset", Method::POST, None).await
}
"system.update" => {
call_backend_json(backend_base_url, admin_key, "/api/system/update", Method::POST, None).await
}
_ => Err(format!("unsupported_control_command:{command}")),
}
}
@@ -0,0 +1,40 @@
use reqwest::Method;
use serde_json::Value;
pub async fn call_backend_json(
base_url: &str,
admin_key: Option<&str>,
path: &str,
method: Method,
payload: Option<Value>,
) -> Result<Value, String> {
let client = reqwest::Client::new();
let mut request = client.request(method, format!("{base_url}{path}"));
if let Some(key) = admin_key {
if !key.trim().is_empty() {
request = request.header("X-Admin-Key", key);
}
}
if let Some(value) = payload {
request = request.json(&value);
}
let response = request
.send()
.await
.map_err(|e| format!("backend_request_failed:{e}"))?;
let status = response.status();
let text = response
.text()
.await
.map_err(|e| format!("backend_response_failed:{e}"))?;
let value: Value = serde_json::from_str(&text).unwrap_or_else(|_| serde_json::json!({}));
if !status.is_success() || value.get("ok") == Some(&Value::Bool(false)) {
let detail = value
.get("detail")
.and_then(|v| v.as_str())
.or_else(|| value.get("message").and_then(|v| v.as_str()))
.unwrap_or("native_control_request_failed");
return Err(detail.to_string());
}
Ok(value)
}
@@ -0,0 +1,37 @@
mod bridge;
mod handlers;
mod http_client;
use bridge::invoke_local_control;
pub struct DesktopAppState {
pub backend_base_url: String,
pub admin_key: Option<String>,
}
fn main() {
let backend_base_url =
std::env::var("SHADOWBROKER_BACKEND_URL").unwrap_or_else(|_| "http://127.0.0.1:8000".to_string());
let admin_key = std::env::var("SHADOWBROKER_ADMIN_KEY").ok();
tauri::Builder::default()
.manage(DesktopAppState {
backend_base_url,
admin_key,
})
.invoke_handler(tauri::generate_handler![invoke_local_control])
.setup(|app| {
if let Some(window) = app.get_webview_window("main") {
let script = r#"
window.__SHADOWBROKER_DESKTOP__ = {
invokeLocalControl: (command, payload) =>
window.__TAURI__.core.invoke('invoke_local_control', { command, payload })
};
"#;
let _ = window.eval(script);
}
Ok(())
})
.run(tauri::generate_context!())
.expect("failed to run shadowbroker tauri shell");
}
@@ -0,0 +1,21 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "ShadowBroker Desktop Shell",
"version": "0.1.0",
"identifier": "com.shadowbroker.desktop",
"build": {
"frontendDist": "../../frontend/.next",
"devUrl": "http://127.0.0.1:3000"
},
"app": {
"windows": [
{
"label": "main",
"title": "ShadowBroker",
"width": 1600,
"height": 1000,
"resizable": true
}
]
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"lib": ["ES2022", "DOM"]
},
"include": [
"src/**/*.ts",
"../frontend/src/lib/desktopControlContract.ts"
]
}