Files
Lumotia/docs/architecture-map/02-tauri-runtime/capabilities-and-acl.md
Jake 26c7307607
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
agent: lumotia-rebrand — docs, scripts, root config, residuals
Phase 9 of the rebrand cascade. Sweep covers everything the Phase 8
frontend pass deliberately skipped: docs/, root markdown, scripts,
Cargo.toml descriptions, code comments that survived earlier
word-boundary sed, plus a handful of identifiers caught on the final
verify pass.

transcription-app changes:
- README.md, HANDOVER.md, KNOWN-ISSUES.md, run.sh — magnotia/Magnotia
  -> lumotia/Lumotia.
- docs/ — sweep across all subdirs except docs/handovers/ (preserved
  as immutable audit trail). Includes architecture-map references
  to magnotia_core::*, magnotia_storage::*, etc. now pointing at
  lumotia_*; dev-setup.md tracing output examples (lumotia_startup
  target); brief/ + superpowers/ + issues/ + whisper-ecosystem/ +
  audit/.
- Cargo.toml descriptions on 9 crates (core, audio, cloud-providers,
  hotkey, llm, mcp, plus referenced others).
- crates/core/src/{error,hardware,recommendation,paths}.rs +
  crates/audio/src/wav.rs + crates/llm/src/model_manager.rs +
  crates/cloud-providers/src/keystore.rs + crates/mcp/src/lib.rs —
  doc comments and a model-manager user-agent string.
- Caught on final pass: BroadcastChannel("magnotia_task_sync") -> ...
  ("lumotia_task_sync"); magnotia_locale i18n localStorage key
  renamed + migration shim added; CSS keyframe names
  magnotiaPulse / magnotiaBar / magnotiaFade renamed in the design-
  system kit; magnotia_viewer_item / magnotia_viewer_mode handoff
  keys renamed in HistoryPage + viewer/+page.svelte; src/assets/
  wordmark.svg text.
- src-tauri/src/lib.rs comment cleanup ("magnotia era" was sed'd
  to "lumotia era" earlier — restored).

Preserved (intentional):
- crates/core/src/paths.rs — keeps "magnotia" / "Magnotia" / ".magnotia"
  legacy detection strings in legacy_and_target_paths() so the
  migration shim can still find user data from the magnotia era.
- src/lib/stores/{page,focusTimer}.svelte.ts + src/lib/i18n/index.ts
  — migration call sites reference the legacy magnotia keys
  deliberately.
- docs/handovers/ — historical audit trail.

cargo build --workspace passes. npm run check: 0 errors / 0 warnings
(3958 files). cargo test --workspace: 339 pass / 0 fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:38:03 +01:00

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 mapTauri 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. Lumotia 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.toml and wired in src-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:default brings in the entire core:event set, 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-dragging is what the custom Titlebar uses to drag a frameless window.
  • opener:default lets the frontend open external URLs via tauri-plugin-opener. This is how the Settings → About links route out.
  • dialog:default enables the file/save dialogs used by the import-audio and save-diagnostic-report flows.
  • global-shortcut:allow-register and allow-unregister let the main window manage the dictation hotkey via the cross-platform plugin path. On Linux Lumotia uses the bespoke evdev backend (see commands::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-side commands::nudges::deliver_nudge adds 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. Only core: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, no opener, no global-shortcut, no autostart, no notification, no updater. 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:

  1. 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.
  2. Tauri-side guards. Several command handlers add their own ensure_main_window check 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_rule would 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 an ensure_main_window guard and an explicit ACL allowlist entry.
  • notification:allow-notify is on the main capability. Secondary windows cannot fire notifications. The frontend's nudge bus already uses the main-window guard via commands::nudges::deliver_nudge, but if you ever invoke the plugin's JS-side sendNotification directly 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 same cfg(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.
  • Testssecondary_windows_do_not_get_high_risk_plugin_permissions is the regression net.
  • Power assertions and securityensure_main_window is the defence-in-depth pair.