# Local plugins Local plugins contribute daemon RPCs and native app surfaces from one `index.tsx`. Paseo executes the server contribution in a subprocess and evaluates the client contribution in the app runtime. Plugin code is trusted code; this first slice does not sandbox it. ## Configure a directory source Add the plugin to the root `plugins` object in `$PASEO_HOME/config.json`: ```json { "plugins": { "my-plugin": { "source": "directory", "path": "/absolute/path/to/my-plugin" } } } ``` The directory contains an identity-only manifest and one entry point: ```text my-plugin/ paseo-plugin.json index.tsx ``` ```json { "id": "my-plugin" } ``` The manifest ID must match the config key. Restart the development daemon after changing plugin source or configuration; directory watching is outside the first slice. ## Contribute behavior and UI Default export one contribution function. Paseo calls it with a plugin-scoped context. The compiler removes UI registrations from the server bundle and RPC registrations from the client bundle before resolving dependencies. ```tsx import { Text } from "react-native"; import { useQuery } from "@tanstack/react-query"; import { z } from "zod"; import { defineRpc, type PluginContext, useRpc } from "@paseo/plugin"; const greetRpc = defineRpc({ name: "greet", input: z.object({ name: z.string() }), output: z.object({ message: z.string() }), }); function Greeting() { const greet = useRpc(greetRpc); const query = useQuery({ queryKey: ["greeting", "Paseo"], queryFn: () => greet({ name: "Paseo" }), }); return {query.data?.message}; } export default function contribute(plugin: PluginContext) { plugin.handle(greetRpc, async ({ name }) => ({ message: `Hello, ${name}` })); plugin.addSurface("main", Greeting); plugin.addSidebarItem({ id: "main", title: "Greeting", icon: "MessageCircle", surface: "main", }); } ``` Paseo owns the route, screen header, Lucide icon validation, close action, theme DTO, layout facts, and render error boundary. The contributed component owns the complete body below the header. RPC contracts validate inputs and outputs in both the app and plugin subprocess. `useRpc` returns a typed async function. Use the host-provided `@tanstack/react-query` for request state and caching; Paseo gives each plugin installation its own query client. When the same plugin contribution exists on multiple hosts, Paseo shows it once in the sidebar and adds a host picker to the screen header. The selected host supplies the bundle, RPC transport, and query cache. Plugin code cannot address another host. See `examples/local-plugin` for a runnable plugin.