# Permissions > Source: `src/content/docs/agent-os/permissions.mdx` > Canonical URL: https://rivet.dev/docs/agent-os/permissions > Description: Approve or deny agent tool use with human-in-the-loop or auto-approve patterns. --- - **Human-in-the-loop** approval for agent tool use (file writes, command execution, etc.) - **Auto-approve** patterns for trusted workloads - **Server-side hooks** for programmatic permission decisions - **Client-side subscriptions** for building approval UIs ## Permission request flow When an agent wants to use a tool (e.g. write a file, run a command), it emits a `permissionRequest` event. Your code responds with `respondPermission` to approve or deny. ```ts @nocheck client.ts import { createClient } from "rivetkit/client"; import type { registry } from "./server"; const client = createClient("http://localhost:6420"); const agent = client.vm.getOrCreate(["my-agent"]); // Listen for permission requests agent.on("permissionRequest", async (data) => { console.log("Permission requested:", data.request); // Approve this single request await agent.respondPermission( data.sessionId, data.request.permissionId, "once", ); }); const session = await agent.createSession("pi", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); await agent.sendPrompt(session.sessionId, "Create a new file at /home/user/output.txt"); ``` ```ts @nocheck server.ts import { agentOs } from "rivetkit/agent-os"; import { setup } from "rivetkit"; import common from "@rivet-dev/agent-os-common"; import pi from "@rivet-dev/agent-os-pi"; const vm = agentOs({ options: { software: [common, pi] }, }); export const registry = setup({ use: { vm } }); registry.start(); ``` ## Permission reply options | Reply | Behavior | |-------|----------| | `"once"` | Approve this single request | | `"always"` | Approve this and all future requests of the same type | | `"reject"` | Deny the request | ## Server-side auto-approve Use the `onPermissionRequest` hook in the actor config to approve permissions server-side without client involvement. This is useful for fully automated pipelines. ```ts @nocheck server.ts import { agentOs } from "rivetkit/agent-os"; import { setup } from "rivetkit"; import common from "@rivet-dev/agent-os-common"; import pi from "@rivet-dev/agent-os-pi"; const vm = agentOs({ onPermissionRequest: async (c, sessionId, request) => { // Auto-approve all file operations await c.respondPermission(sessionId, request.permissionId, "always"); }, options: { software: [common, pi] }, }); export const registry = setup({ use: { vm } }); registry.start(); ``` ```ts @nocheck client.ts import { createClient } from "rivetkit/client"; import type { registry } from "./server"; const client = createClient("http://localhost:6420"); const agent = client.vm.getOrCreate(["my-agent"]); // No need to handle permissions on the client. The server auto-approves. const session = await agent.createSession("pi", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); await agent.sendPrompt(session.sessionId, "Write files as needed"); ``` ## Selective approval Inspect the permission request to make approval decisions based on the tool or path. ```ts @nocheck server.ts import { agentOs } from "rivetkit/agent-os"; import { setup } from "rivetkit"; import common from "@rivet-dev/agent-os-common"; import pi from "@rivet-dev/agent-os-pi"; const vm = agentOs({ onPermissionRequest: async (c, sessionId, request) => { // Auto-approve reads, require manual approval for writes const toolName = (request as any).toolName ?? ""; if (toolName.includes("read")) { await c.respondPermission(sessionId, request.permissionId, "always"); } // Writes are forwarded to the client via the permissionRequest event }, options: { software: [common, pi] }, }); export const registry = setup({ use: { vm } }); registry.start(); ``` ```ts @nocheck client.ts import { createClient } from "rivetkit/client"; import type { registry } from "./server"; const client = createClient("http://localhost:6420"); const agent = client.vm.getOrCreate(["my-agent"]); // Only write permissions reach the client agent.on("permissionRequest", async (data) => { const approved = confirm(`Allow write: ${JSON.stringify(data.request)}?`); await agent.respondPermission( data.sessionId, data.request.permissionId, approved ? "once" : "reject", ); }); const session = await agent.createSession("pi", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); await agent.sendPrompt(session.sessionId, "Read config.json and update it"); ``` ## Recommendations - Use `"always"` sparingly. It approves all future requests of that type for the session lifetime. - For automated CI/CD pipelines, use the server-side `onPermissionRequest` hook to auto-approve without client round-trips. - For interactive applications, subscribe to `permissionRequest` on the client and build an approval UI. - If neither the server hook nor the client responds, the agent blocks until a response is given or the action times out. _Source doc path: /docs/agent-os/permissions_