C.W.K.
Stream
Lesson 02 of 07 · published

Adding a Plugin: The Three-Surface Rule

~13 min · tauri, plugins, three-surface, capabilities

Level 0Web Tourist
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"A plugin isn't installed until it exists in three places. Two out of three is the silent-failure trap."

Three Surfaces, Every Time

Wiring a Tauri plugin correctly always touches three files. Hold this as a checklist:

  1. Cargo.toml — the Rust crate dependency (tauri-plugin-store = "2").
  2. lib.rs — register it in the Builder chain (.plugin(tauri_plugin_store::Builder::new().build())).
  3. capabilities — grant its permission ("store:default").

And usually a fourth, lighter one: the JS package (@tauri-apps/plugin-store) if you call it from the frontend. Miss the Cargo dep and it won't compile (loud, easy). Miss the builder and the plugin does nothing. Miss the capability and the frontend commands fail with a permission error. The two-of-three states fail quietly, which is why this rule is worth memorizing.

Removing Is Also Three Surfaces

The rule cuts both ways. When you remove a plugin, you must undo all three surfaces or you leave dead weight: an unused crate bloating your build, a builder line registering nothing useful, a capability granting a permission for a plugin that's gone. Cinder recorded exactly this as a learning — pulling a plugin meant editing the Cargo dependency, the lib.rs builder, and the capabilities file, in lockstep. Treat add and remove as symmetric three-surface operations.

Why This Design

It can feel like ceremony, but each surface serves the deny-by-default model: the Cargo dep brings the code, the builder activates it in the core, and the capability is the explicit grant that says 'yes, the frontend may use this.' The separation is what lets a plugin be present in your binary but unreachable from the webview until you opt in — security by construction, paid for with three edits.

Code

Surface 1: the dependency·toml
# Surface 1 — Cargo.toml: the Rust crate.
[dependencies]
tauri-plugin-store = "2"
Surface 2: builder registration·rust
// Surface 2 — lib.rs: register in the Builder.
tauri::Builder::default()
    .plugin(tauri_plugin_store::Builder::new().build())
    // ...
Surface 3: the capability permission·json
// Surface 3 — capabilities/default.json: grant the permission.
{
  "permissions": [
    "core:default",
    "store:default"
  ]
}

External links

Exercise

Add the store plugin to your app by hand, touching all three surfaces deliberately: Cargo dependency, builder line, capability permission (plus the JS package). Then save a value and confirm it persists. Bonus: comment out just the capability line and watch the frontend call fail — that quiet failure is the whole reason the rule exists.
Hint
Surfaces: tauri-plugin-store in Cargo.toml, .plugin(tauri_plugin_store::Builder::new().build()) in lib.rs, "store:default" in capabilities, @tauri-apps/plugin-store via npm. Removing the capability gives a permission error while everything else looks fine.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.