CodeVetter · Repo Unpack · Behavior traces
tauri-apps/tauri
Three flows were traced end-to-end. (1) App startup: user main → Builder → build(Context) → runtime event loop → Ready → setup hook → windows/webviews created. (2) IPC invoke: JS invoke() → injected ipc.js → postMessage or ipc:// POST → parse → invoke_key check → ACL resolve → plugin or app handler → eval/Channel response. (3) CLI dev: cargo-tauri → Commands::Dev → config load → beforeDevCommand spawn → dev-server wait → cargo run under a file watcher that rebuilds on change.
- Startup trace: `main()` calls `api_lib::run()` → `run_app(tauri::Builder::default(), ...)` chains `.plugin(tauri_plugin_log...)`, `.plugin(tauri_plugin_sample::init())`, `.setup(...)` → `.run(tauri::generate_context!())`.
- Startup trace (cont.): `App::run` sets the event-loop main_thread_id and calls `runtime.run(make_run_event_loop_callback)`; on `RuntimeRunEvent::Ready` the stored `setup(&mut app)` hook runs (panic on error), then `on_event_loop_event` translates the runtime event into a public `RunEvent` delivered to the user callback.
- Startup trace (cont.): during build, `Builder` fields are seeded — `invoke_key` from `crate::generate_invoke_key()`, `invoke_initialization_script` rendered from the `scripts/ipc-protocol.js` template with process_ipc_message_fn, os_name and FETCH_CHANNEL_DATA_COMMAND.
- Context generation trace: build.rs `tauri_build::build()` → codegen `context_codegen(ContextData)` reads acl-manifests.json/capabilities.json from OUT_DIR, calls `Resolved::resolve(&acl, capabilities, target)`, embeds assets/icons, and the app includes the result via `tauri::generate_context!` or `tauri_build_context!` (OUT_DIR/tauri-build-context.rs).
- Invoke trace (JS side): `invoke(cmd, args)` → `window.__TAURI_INTERNALS__.invoke` (injected by scripts/ipc.js + ipc-protocol.js) → `process_ipc_message_fn` serializes args to JSON or octet-stream → postMessage or `ipc://` fetch with Tauri-* headers.
- Invoke trace (Rust side): `message_handler` → `handle_ipc_message` → deserialize `Message` → `webview.on_message(request, responder)` → verify `invoke_key` → build `InvokeResolver`/`InvokeMessage` → `runtime_authority.resolve_access` → if `plugin:` command, `run_plugin_invoke_handler`; else the app `invoke_handler` → responder callback → `format_callback` → `webview.eval` or `Channel::send`.
- ACL denial trace: when the command is a plugin command, the app has an ACL manifest, or the origin is remote, and `invoke.acl` is none, the resolver rejects — debug builds use `resolve_access_message` for a detailed reason, release builds send "Command <cmd> not allowed by ACL".
- Asset request trace: `asset://localhost/<encoded>` → `get_response` → percent-decode path → `SafePathBuf::new` validation (403 on failure) → `scope.is_allowed` (403) → `tokio::fs::File::open` → mime sniff of first 8KiB → full body, HEAD, or range response.
- Dev flow trace: `main` → `tauri_cli::run` → `Commands::Dev` → `dev::command` → `command_internal` resolves Target + `get_config` → `AppInterface::new` → `dev::setup` spawns beforeDevCommand as a SharedChild, optionally starts the builtin dev server, and polls the devUrl TCP socket (90 attempts × 2s) → `interface.dev` → rust.rs `run_dev`/`run_dev_watcher`.
- Dev teardown trace: `on_app_exit` decides whether to exit or keep watching; `kill_before_dev_process` writes kill-children.sh to the temp dir on Unix (or runs a PowerShell Kill-Tree on Windows) then kills the beforeDevCommand child.
- Build/bundle trace: `Commands::Build` → `build::command` → `get_config` + AppInterface → cargo release build via the interface → `get_bundler_settings` → `tauri_bundler::bundle_project` → updater-enabled bundles post-processed for signatures.
- Exit/restart trace: `AppHandle::restart`/`request_restart` set `manager.restart_on_exit`; on `RuntimeRunEvent::Exit` the callback runs user handler → `cleanup_before_exit()` → `crate::process::restart(&self.env())` when the flag is set; ExitRequested can be vetoed via `ExitRequestApi::prevent_exit` unless code == RESTART_EXIT_CODE (i32::MAX).
- Plugin lifecycle trace: `Builder::plugin` stores into `PluginStore`; during setup each plugin's `initialize(app, config)` runs; hooks `window_created`, `webview_created`, `on_navigation`, `on_page_load`, `on_event` fire at their lifecycle points, and `cleanup_before_exit` hooks run before process exit.