Skip to content

Architecture

Finnegan's Owner edited this page Mar 1, 2026 · 4 revisions

Architecture

Launcher is a Tauri v2 desktop application with a Rust backend and a Nuxt 4 / Vue 3 frontend.

Project Structure

launcher/
├── app/                          # Frontend (Nuxt 4)
│   ├── assets/css/main.css       # Tailwind CSS v4 design tokens
│   ├── components/               # Vue components
│   │   ├── BriefServerInfo.vue   # Server row in the list
│   │   ├── ConnectionInput.vue   # Reusable form input
│   │   ├── InsertableDropdown.vue # Dropdown with inline creation
│   │   └── TrustCertModal.vue    # Certificate trust dialog
│   ├── composables/              # Vue composables
│   │   ├── useConfirmRejectModal.ts # Imperative modal mounting
│   │   └── useZoom.ts            # Keyboard zoom (Cmd/Ctrl +/-/0)
│   ├── pages/                    # File-based routing
│   │   ├── index.vue             # Main server list
│   │   └── connections/[id].vue  # Connection editor
│   └── types.d.ts                # TypeScript interfaces
├── src-tauri/                    # Backend (Rust)
│   ├── src/
│   │   ├── main.rs               # Tauri commands and app setup
│   │   ├── connection.rs         # ConnectionStore, persistence, cert trust
│   │   ├── webstart.rs           # JNLP parsing, JAR downloading, Java process
│   │   ├── verify.rs             # JAR signature verification (CMS/PKCS#7)
│   │   └── errors.rs             # VerificationError types
│   ├── lib/java-console.jar      # Bundled Java console
│   ├── capabilities/default.json # Tauri permission declarations
│   ├── Cargo.toml                # Rust dependencies
│   └── tauri.conf.json           # Tauri app configuration
└── nuxt.config.ts                # Nuxt configuration

Backend (Rust)

Tauri Commands

Commands are the IPC bridge between frontend and backend. They use rename_all = "snake_case", so the JavaScript side must use snake_case parameter names.

Command Returns Description
launch Result<String, String> Downloads JARs, verifies signatures, launches Java process
save Result<String, String> Saves a connection entry
delete Result<String, String> Deletes a connection
import Result<String, String> Imports connections from a JSON file (with duplicate detection)
load_connections String Returns all connections as JSON array
load_single_connection Result<Value, String> Returns one connection by ID
get_default_connectionentry Result<Value, String> Returns a default connection template
get_all_groups Result<Value, String> Returns all group names
trust_cert Result<String, String> Adds a certificate to the trust store
get_launcher_info String Returns app version info

Launch Flow

  1. Frontend calls launch with connection ID and a progress Channel
  2. Backend downloads JNLP from server address
  3. Parses JNLP for JAR URLs and main class
  4. Downloads JARs (with progress updates via Channel)
  5. Optionally verifies JAR signatures (CMS/PKCS#7)
  6. If untrusted cert found, returns cert info to frontend for user approval
  7. Sanitizes JVM arguments (blocks dangerous flags like -javaagent:)
  8. Launches Java process with classpath; credentials passed via environment variables
  9. Optionally pipes stdout to a Java console process

Data Storage

  • ConnectionStore manages all connections in a Mutex<HashMap<String, Arc<ConnectionEntry>>>
  • Persisted to ~/.launcher/launcher-data.json
  • Trusted certs persisted to ~/.launcher/launcher-trusted-certs.json
  • JAR cache stored in ~/.launcher/cache/

Security Model

  • TLS certificate validation is intentionally disabled (danger_accept_invalid_certs) because integration engine servers commonly use self-signed certificates
  • JAR signature verification is the actual trust boundary — JARs are verified against CMS/PKCS#7 signatures before execution
  • Users explicitly approve untrusted certificates through the trust dialog
  • JNLP version attributes are sanitized to prevent path traversal
  • JVM arguments from JNLP are filtered to block code execution flags (-javaagent:, -agentpath:, etc.)
  • Credentials are passed to Java processes via environment variables (not command-line arguments)

Tauri Permissions

Capabilities are declared in src-tauri/capabilities/default.json:

Permission Purpose
core:default Core IPC and event system
dialog:default Native file picker and confirm dialogs
notification:default Desktop notifications
fs:default File system access
http:default HTTP requests (all URLs, for server connectivity checks)
shell:allow-open Open URLs in default browser (restricted to wiki URL only)
core:webview:allow-set-webview-zoom Keyboard zoom support

Frontend (Nuxt 4 / Vue 3)

Design System

Tailwind CSS v4 with @theme design tokens in app/assets/css/main.css:

Token Value Usage
surface-0 #24272e Page background
surface-1 #2c2f37 Cards, inputs
surface-2 #353840 Hover, selected states
surface-3 #3e414a Highest elevation
accent #4f7df7 Primary actions
text-primary #e2e5eb Main text
text-secondary #9ca3b0 Supporting text

Icons use Phosphor Icons via the ph: prefix with the @nuxt/icon module.

Key Patterns

  • Channel API: launch uses tauri::ipc::Channel for streaming progress updates from Rust to the frontend
  • Imperative modals: useConfirmRejectModal mounts Vue components programmatically for the cert trust dialog, returning a Promise
  • Module-level state: useZoom uses a module-scoped ref for app-wide singleton zoom state
  • CSS tooltips: Custom [data-tooltip] attribute with ::after pseudo-element (native title tooltips are unreliable in Tauri's WKWebView). Use data-tooltip-below for elements near the top of the window.
  • localStorage persistence: Sort mode, collapsed groups, and zoom level all persist to localStorage

Error Handling Conventions

  • Rust: Prefer ? operator and .map_err() over .unwrap(). Return errors to frontend via Result<T, String>. Use .expect("descriptive message") only for mutex locks (poisoning is unrecoverable).
  • Frontend: Wrap invoke calls in try/catch and display errors inline via the error bar at the bottom of the main screen.

Clone this wiki locally