# How the Project-Trust Dialog Integrates with File Access in Pi Web

> Discover how the Project-Trust Dialog in Pi Web integrates with file access. Learn how user trust decisions gate access to project-local resources and ensure secure file reads.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: internals
- Published: 2026-08-14

---

**The Project-Trust Dialog in Pi Web gates access to project-local resources by persisting user trust decisions in the SDK's trust store, which the file-access layer consults before allowing reads from gated paths like `.pi/extensions` or `.agents/skills`.**

Pi Web implements a security-first architecture that prevents unintentional code execution from untrusted repositories. When a project contains resources requiring explicit permission, the **Project-Trust Dialog** interacts with the file-access subsystem through a structured trust verification flow. This article examines how the dialog integrates with file permissions across the `agegr/pi-web` codebase.

## Detecting Trust-Required Projects

The trust flow begins with automatic detection of gated resources. The SDK exposes `getProjectTrustStatus()` in **[`lib/project-trust.ts`](https://github.com/agegr/pi-web/blob/main/lib/project-trust.ts)** to evaluate whether the current working directory contains trust-requiring content.

```ts
// lib/project-trust.ts – core trust evaluation
export function getProjectTrustStatus(cwd: string, agentDir: string): ProjectTrustStatus {
  const requiresTrust = Boolean(cwd) && hasTrustRequiringProjectResources(cwd);
  if (!requiresTrust) return { requiresTrust: false, trusted: true };

  const trustStore = new ProjectTrustStore(agentDir);
  return { requiresTrust: true, trusted: trustStore.get(cwd) === true };
}

```

The helper `hasTrustRequiringProjectResources(cwd)` scans for specific directory patterns: `.pi/extensions`, project-specific settings, and `.agents/skills`. If none exist, the project implicitly passes trust checks.

## Surfacing Trust Status to the UI Layer

**[`components/AppShell.tsx`](https://github.com/agegr/pi-web/blob/main/components/AppShell.tsx)** retrieves the trust status during component mount and stores it in React state. This state drives conditional UI rendering across the application.

```ts
// AppShell.tsx – trust status retrieval on mount (lines 844-854)
fetch(`/api/project-trust?cwd=${encodeURIComponent(projectTrustCwd)}`)
  .then(r => r.json())
  .then(setProjectTrust);

```

The `projectTrust` state object enables security-aware UI decisions. For example, the chat composer hides when `projectTrust.requiresTrust && !projectTrust.trusted`, preventing interaction with untrusted project resources.

## Rendering the Project-Trust Dialog

When trust is required but not yet granted, **[`components/ProjectTrustDialog.tsx`](https://github.com/agegr/pi-web/blob/main/components/ProjectTrustDialog.tsx)** renders a modal interface (lines 68-140). The component presents localized strings via `t("trust.dialogTitle")` and `t("trust.dialogBody")`, offering explicit **Cancel** and **Trust** actions.

```ts
// ProjectTrustDialog.tsx – user initiates trust
await fetch("/api/project-trust", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ cwd: currentCwd })
});

```

The POST request routes through **[`app/api/project-trust/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/project-trust/route.ts)**, which delegates to `trustProject(cwd, agentDir)` in the core library.

## Persisting Trust Decisions

The `trustProject()` function writes the affirmative decision to a `ProjectTrustStore` instance. This store persists data under the Pi Web agent directory, typically `~/.pi/agent/trust`.

```ts
// Sequential calls to getProjectTrustStatus return updated state
{ requiresTrust: true, trusted: true }  // after user confirmation

```

Persistent storage ensures trust decisions survive application restarts without requiring repeated user confirmation.

## Rebuilding File-Access Permissions After Trust

Once trusted, the client reloads gated resource options through **`projectTrustReloadOptions()`** in [`lib/project-trust.ts`](https://github.com/agegr/pi-web/blob/main/lib/project-trust.ts). This function returns a resolver that the file-access subsystem integrates into its permission model.

```ts
// lib/project-trust.ts – providing trust resolution for file access
export function projectTrustReloadOptions(
  cwd: string,
  agentDir: string,
): { resolveProjectTrust: () => Promise<boolean> } | undefined {
  const status = getProjectTrustStatus(cwd, agentDir);
  if (!status.requiresTrust) return undefined;
  const trustStore = new ProjectTrustStore(agentDir);
  return { resolveProjectTrust: async () => trustStore.get(cwd) === true };
}

```

**[`lib/file-access.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-access.ts)** consumes this resolver when constructing its *allowed roots* list. The resolver's return value determines inclusion of project-local paths:

- **`resolveProjectTrust()` resolves `true`** → Extensions, settings, and skills directories append to allowed roots
- **`resolveProjectTrust()` resolves `false`** → Project-local roots remain excluded

## Enforcing Trust at the Permission Boundary

The final security check occurs in **[`lib/path-security.ts`](https://github.com/agegr/pi-web/blob/main/lib/path-security.ts)** via `isFilePathAllowed()`. This function validates every file-access request against the constructed allowed-roots set.

Because trust status controls whether project-specific roots populate that set, any read attempt targeting gated directories fails with a permission error until explicit trust is established. This creates a fail-closed security model—deny by default, permit only after explicit user authorization.

## Trust-to-Access Integration Flow

1. **Detection** – `getProjectTrustStatus()` identifies trust-requiring projects
2. **UI gating** – [`AppShell.tsx`](https://github.com/agegr/pi-web/blob/main/AppShell.tsx) fetches status and conditionally renders components
3. **User prompt** – [`ProjectTrustDialog.tsx`](https://github.com/agegr/pi-web/blob/main/ProjectTrustDialog.tsx) solicits explicit trust decision
4. **Persistence** – `ProjectTrustStore` records the affirmative choice
5. **Permission reload** – `projectTrustReloadOptions()` provides updated resolver
6. **Access enforcement** – `isFilePathAllowed()` permits or denies based on resolved trust

## Summary

- **Project-trust detection** occurs automatically via `hasTrustRequiringProjectResources()` in [`lib/project-trust.ts`](https://github.com/agegr/pi-web/blob/main/lib/project-trust.ts)
- **UI state management** in [`components/AppShell.tsx`](https://github.com/agegr/pi-web/blob/main/components/AppShell.tsx) hides functionality until trust is established
- **Explicit user consent** is captured through [`components/ProjectTrustDialog.tsx`](https://github.com/agegr/pi-web/blob/main/components/ProjectTrustDialog.tsx) and persisted via POST to `/api/project-trust`
- **Permission reconstruction** happens through `projectTrustReloadOptions()`, which feeds the file-access layer's allowed-roots calculation
- **Runtime enforcement** in [`lib/path-security.ts`](https://github.com/agegr/pi-web/blob/main/lib/path-security.ts) ensures no gated file reads succeed without prior trust

## Frequently Asked Questions

### What triggers the Project-Trust Dialog to appear?

The dialog appears when `getProjectTrustStatus()` detects trust-gated resources in the current working directory and the project has not yet been trusted. Specifically, presence of `.pi/extensions`, project-specific settings files, or `.agents/skills` directories causes `requiresTrust` to return `true`, prompting [`AppShell.tsx`](https://github.com/agegr/pi-web/blob/main/AppShell.tsx) to render the modal.

### Where does Pi Web store trust decisions?

Trust decisions persist in a `ProjectTrustStore` located under the agent directory at `~/.pi/agent/trust`. This path is derived from the `agentDir` parameter passed to `trustProject()` and `getProjectTrustStatus()`. The store maintains a mapping of working directory paths to boolean trust values.

### How does file access change immediately after trusting a project?

After trust is granted, the client calls `projectTrustReloadOptions()` to obtain a fresh `resolveProjectTrust` function. The file-access layer rebuilds its allowed-roots set using this resolver, now including project-local directories. Subsequent `isFilePathAllowed()` checks succeed for paths within `.pi/extensions`, `.agents/skills`, and related gated locations.

### Can code execute from untrusted projects in Pi Web?

No. The fail-closed design ensures project-specific code paths—extensions, skills, and settings—remain inaccessible until explicit trust is established. The `isFilePathAllowed()` check in [`lib/path-security.ts`](https://github.com/agegr/pi-web/blob/main/lib/path-security.ts) operates as the final barrier, rejecting any file-read attempts targeting roots excluded from the allowed set due to unresolved trust status.