Five-slice navigable map of the entire codebase under
docs/architecture-map/. Each slice is a self-contained
breadcrumbed sub-tree:
01-frontend (16) Svelte/SvelteKit UI
02-tauri-runtime (26) src-tauri commands + lifecycle
03-audio-transcription (16) audio + transcription crates
04-llm-formatting-mcp (19) llm, ai-formatting, mcp, cloud
05-core-storage-hotkey-build core, storage, hotkey, workspace,
(26) CI, dev glue
Plus master README.md and data-flow-end-to-end.md tracing
audio bytes from microphone to FTS5 search to MCP read.
Generated by 5 parallel subagents on 2026/05/09 against
HEAD 3c47000. Each page has YAML frontmatter, file:line code
refs, sibling cross-links, plain-English summaries.
Aggregated debt surfaced (full lists in master README):
RB-08 macOS power assertion, schema head drift v14 vs v15,
VAD blocked on ort version conflict, streaming primitives
not wired into live.rs, no prompt versioning, MCP has no
auth, cloud-providers in-memory keystore, SettingsPage
2 484 LOC, commands/live.rs 1 737 LOC, dual theme system,
brand rename to Lumenote pending across the codebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.9 KiB
name, type, slice, last_verified
| name | type | slice | last_verified |
|---|---|---|---|
| Capabilities and ACL | architecture-map-page | 02-tauri-runtime | 2026/05/09 |
Capabilities and ACL
Where you are: Architecture map → Tauri runtime → Capabilities and ACL
Plain English summary. Tauri 2 ships a permission-and-capability ACL: every plugin and core API ships permissions, capabilities bind permissions to specific window labels. Magnotia ships two capability files. The main window gets the broad set (dialog, opener, autostart, global shortcuts, notifications, full window control). The three secondary windows (task float, transcript viewer, transcription preview) get a narrow set with no plugin permissions at all and no destructive window APIs. The split is the firewall that prevents a compromised secondary window from launching an installer or registering a global hotkey.
At a glance
- Path:
src-tauri/capabilities/main.json,src-tauri/capabilities/secondary-windows.json. - Auto-generated schemas:
src-tauri/gen/schemas/(desktop-schema.json,linux-schema.json,macOS-schema.json,windows-schema.json,acl-manifests.json, plus per-plugin globals). Do not edit by hand — they regenerate from the installed plugin set. - Tauri commands exposed: none (declarative config).
- Events emitted: none.
- Depends on: the runtime plugin set declared in
src-tauri/Cargo.tomland wired insrc-tauri/src/lib.rs::run. If a capability references a permission whose plugin is not enabled, the build fails. - Called from frontend at: every
invoke()is run through this ACL — the capability for the calling window must include the permission for the command being called, otherwise Tauri rejects the IPC call before it reaches the Rust handler.
What's in here
main.json (src-tauri/capabilities/main.json:1)
identifier: "main"
description: "Main window capability for user-initiated app control."
windows: ["main"]
permissions:
- core:default
- core:window:allow-start-dragging
- core:window:allow-set-always-on-top
- core:window:allow-minimize
- core:window:allow-toggle-maximize
- core:window:allow-is-maximized
- core:window:allow-close
- core:window:allow-hide
- core:window:allow-show
- core:window:allow-set-focus
- opener:default
- dialog:default
- global-shortcut:allow-register
- global-shortcut:allow-unregister
- autostart:allow-enable
- autostart:allow-disable
- autostart:allow-is-enabled
- notification:allow-is-permission-granted
- notification:allow-request-permission
- notification:allow-notify
Notes:
core:defaultbrings in the entirecore:eventset, allowing the main window to listen and emit on all event channels.- The window-control set is granular: there is no
core:window:default, every action is opted in.allow-start-draggingis what the custom Titlebar uses to drag a frameless window. opener:defaultlets the frontend open external URLs viatauri-plugin-opener. This is how the Settings → About links route out.dialog:defaultenables the file/save dialogs used by the import-audio and save-diagnostic-report flows.global-shortcut:allow-registerandallow-unregisterlet the main window manage the dictation hotkey via the cross-platform plugin path. On Linux Magnotia uses the bespoke evdev backend (seecommands::hotkey), but Settings still talks to the global-shortcut plugin so the same UI works on macOS / Windows.autostart:*lets Settings toggle login-time autostart.notification:*is what the Phase 6 nudge bus uses; the Rust-sidecommands::nudges::deliver_nudgeadds the main-window-only firewall.
secondary-windows.json (src-tauri/capabilities/secondary-windows.json:1)
identifier: "secondary-windows"
description: "Narrow capability for passive secondary windows."
windows: ["tasks-float", "transcript-viewer", "transcription-preview"]
permissions:
- core:event:default
- core:window:allow-start-dragging
- core:window:allow-close
- core:window:allow-hide
- core:window:allow-show
- core:window:allow-set-focus
- core:window:allow-set-always-on-top
Notes:
- No
core:default. Onlycore:event:default. Secondary windows can listen to events emitted by the backend or other windows but can't reach the broader core surface (e.g. shell, fs). - No plugin permissions at all. No
dialog, noopener, noglobal-shortcut, noautostart, nonotification, noupdater. This is enforced as a regression test (see below). - The window-control set is the bare minimum to drag (titleless overlay), close, hide, show, focus, and pin always-on-top. No maximise, no minimise, no toggle-fullscreen.
Regression test
src-tauri/tests/config_hardening.rs::secondary_windows_do_not_get_high_risk_plugin_permissions (src-tauri/tests/config_hardening.rs:50) walks every JSON file in capabilities/, finds the ones whose windows array contains any of the three secondary labels, and asserts none of their permissions start with dialog:, autostart:, global-shortcut:, opener:, or updater:. The test fails the moment someone adds, for example, dialog:default to the secondary-windows file.
gen/schemas/
Auto-generated by tauri-build from the plugin set. Files:
acl-manifests.json
capabilities.json
desktop-schema.json
linux-schema.json
macOS-schema.json
windows-schema.json
plugin-global-scope-schema.json
plugin-default-permissions.json
These are committed to source control so a fresh checkout has a working ACL even before the first build. Do not edit by hand. If you add a plugin, cargo tauri build regenerates them. If a capability file references a permission absent from these schemas, the IDE's JSON-schema validation will flag it before the build even runs.
Data flow
The ACL is enforced in two places:
- Webview IPC layer. Every
invoke()call from a window is checked against the capability bound to that window's label. A permission miss returns an IPC error before the Rust handler runs. - Tauri-side guards. Several command handlers add their own
ensure_main_windowcheck on top of the ACL (see Power assertions and security). This is defence in depth: even if the ACL ever drifted, a secondary window calling, say,delete_implementation_rulewould still be rejected by the Rust guard.
Watch-outs
- Adding a permission to either capability is a security decision. The "secondary windows" set in particular has been pruned deliberately — the in-repo tests and the brief item #B.2 review trace the path that closed each gap.
- The Phase 9 LLM-content-tags command (
commands::llm::extract_content_tags_cmd) does NOT check the capability; the History page lives in the main window so the main capability already gates it. If you ever expose this command to a secondary window, add anensure_main_windowguard and an explicit ACL allowlist entry. notification:allow-notifyis on the main capability. Secondary windows cannot fire notifications. The frontend's nudge bus already uses the main-window guard viacommands::nudges::deliver_nudge, but if you ever invoke the plugin's JS-sidesendNotificationdirectly from a secondary window, it will fail with an ACL error.- The schema files in
gen/schemas/are updated when the dependency graph changes. If you bump a plugin version, expect a diff there too. Commit them in the same PR as the dependency bump.
See also
- App lifecycle — plugins are wired in
lib.rs::run, gated by the samecfg(not(target_os = "android"))block that gates the desktop-only permissions. - Cargo and features — the dependency graph that
gen/schemas/is generated from. - Tauri config — the CSP works alongside the ACL: CSP guards the webview, ACL guards the IPC.
- Tests —
secondary_windows_do_not_get_high_risk_plugin_permissionsis the regression net. - Power assertions and security —
ensure_main_windowis the defence-in-depth pair.