> ## Documentation Index
> Fetch the complete documentation index at: https://docs.videodb.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Privacy Controls

> Consent patterns, permission handling, and privacy-first capture

Desktop capture handles sensitive data. Nothing records without explicit user consent. This page covers permission patterns, storage controls, and privacy-first design.

<Note>
  Desktop capture currently supports **macOS** and **Windows**.
</Note>

## Quick Example

<CodeGroup>
  ```python Python theme={null}
  from videodb.capture import CaptureClient

  client = CaptureClient(client_token=token)

  # Request explicit permissions (OS dialogs appear)
  await client.request_permission("microphone")
  await client.request_permission("screen_capture")

  # User must grant permission before capture can start
  channels = await client.list_channels()
  ```

  ```javascript Node.js theme={null}
  import { CaptureClient } from 'videodb/capture';

  const client = new CaptureClient({ sessionToken: token });

  // Request explicit permissions (OS dialogs appear)
  await client.requestPermission('microphone');
  await client.requestPermission('screen-capture');

  // User must grant permission before capture can start
  const channels = await client.listChannels();
  ```
</CodeGroup>

***

## Trust Model

### Two-Component Architecture

Security is built into the architecture:

| Component      | Has API Key     | Can See Media             |
| :------------- | :-------------- | :------------------------ |
| Backend        | Yes             | No (receives events only) |
| Desktop Client | No (uses token) | Yes (captures locally)    |

* **API key never leaves your backend**
* **Desktop client uses short-lived tokens** (10-15 min expiry recommended)
* **Compromised tokens have limited blast radius**

### Token Pattern

<CodeGroup>
  ```python Python theme={null}
  # Backend (holds API key)
  conn = videodb.connect()  # Uses VIDEODB_API_KEY

  # Generate short-lived token for client
  token = conn.generate_client_token(expires_in=600)  # 10 minutes

  # Send token to desktop app (never send API key)
  ```

  ```javascript Node.js theme={null}
  // Backend (holds API key)
  const conn = connect();  // Uses VIDEO_DB_API_KEY

  // Generate short-lived token for client
  const token = await conn.generateClientToken(600);  // 10 minutes

  // Send token to desktop app (never send API key)
  ```
</CodeGroup>

***

## Permission Handling

### Request Before Capture

<CodeGroup>
  ```python Python theme={null}
  # Each permission triggers an OS dialog
  await client.request_permission("microphone")
  await client.request_permission("screen_capture")
  # For system audio on macOS
  await client.request_permission("system_audio")
  ```

  ```javascript Node.js theme={null}
  // Each permission triggers an OS dialog
  await client.requestPermission('microphone');
  await client.requestPermission('screen-capture');
  // For system audio on macOS
  await client.requestPermission('system-audio');
  ```
</CodeGroup>

### Required UX Elements

<Warning>
  Your desktop client must include:

  * **Recording indicator** - Visual cue that capture is active
  * **Pause button** - Let users temporarily stop
  * **Stop button** - Let users end the session
</Warning>

***

## Storage Control

### Ephemeral Mode

Process in real-time without persisting media:

<CodeGroup>
  ```python Python theme={null}
  await client.start_session(
      capture_session_id=cap_id,
      channels=[
          {"name": "mic:default", "store": False},       # Process only
          {"name": "display:1", "store": False},         # Process only
          {"name": "system_audio:default", "store": False}
      ]
  )
  ```

  ```javascript Node.js theme={null}
  await client.startCaptureSession({
      sessionId: capId,
      channels: [
          { channelId: "mic:default", store: false },       // Process only
          { channelId: "display:1", store: false },         // Process only
          { channelId: "system_audio:default", store: false }
      ]
  });
  ```
</CodeGroup>

**Use ephemeral mode when:**

* Processing sensitive content
* Storage isn't needed
* Privacy regulations require it
* Building real-time assistants only

### Selective Storage

Store only what you need:

<CodeGroup>
  ```python Python theme={null}
  await client.start_session(
      capture_session_id=cap_id,
      channels=[
          {"name": "mic:default", "store": True},        # Keep for search
          {"name": "display:1", "store": False},         # Don't store screen
          {"name": "system_audio:default", "store": True}
      ]
  )
  ```

  ```javascript Node.js theme={null}
  await client.startCaptureSession({
      sessionId: capId,
      channels: [
          { channelId: "mic:default", store: true },        // Keep for search
          { channelId: "display:1", store: false },         // Don't store screen
          { channelId: "system_audio:default", store: true }
      ]
  });
  ```
</CodeGroup>

***

## Data Retention

### Default Behavior

* Media stored until explicitly deleted
* No automatic expiration
* Indexes and transcripts stored with media

### Manual Deletion

<CodeGroup>
  ```python Python theme={null}
  # Delete capture session and all associated data
  cap = conn.get_capture_session("cap-xxx")
  cap.delete()

  # Delete specific video
  video = coll.get_video("m-xxx")
  video.delete()
  ```

  ```javascript Node.js theme={null}
  // Delete capture session and all associated data
  const cap = await conn.getCaptureSession("cap-xxx");
  await cap.delete();

  // Delete specific video
  const video = await coll.getVideo("m-xxx");
  await video.delete();
  ```
</CodeGroup>

### Implement Data Subject Access

<CodeGroup>
  ```python Python theme={null}
  def get_user_data(end_user_id):
      """Get all data for a user (GDPR access request)"""
      sessions = conn.list_capture_sessions(end_user_id=end_user_id)
      return {"sessions": [s.to_dict() for s in sessions]}

  def delete_user_data(end_user_id):
      """Delete all user data (GDPR deletion request)"""
      sessions = conn.list_capture_sessions(end_user_id=end_user_id)
      for session in sessions:
          session.delete()
  ```

  ```javascript Node.js theme={null}
  async function getUserData(endUserId) {
      // Get all data for a user (GDPR access request)
      const sessions = await conn.listCaptureSessions({ endUserId });
      return { sessions };
  }

  async function deleteUserData(endUserId) {
      // Delete all user data (GDPR deletion request)
      const sessions = await conn.listCaptureSessions({ endUserId });
      for (const session of sessions) {
          await session.delete();
      }
  }
  ```
</CodeGroup>

***

## Compliance Patterns

### GDPR

* Implement data access endpoints
* Implement deletion endpoints
* Document processing purposes
* Use ephemeral mode when storage isn't needed

### HIPAA

* Use ephemeral mode for PHI
* Implement strict access controls
* Audit all data access
* Consider data minimization

### General Best Practices

| Practice              | Implementation                    |
| :-------------------- | :-------------------------------- |
| Never expose API keys | Use client tokens                 |
| Default to ephemeral  | Only persist when needed          |
| Short token lifetimes | 10-15 minutes                     |
| Implement deletion    | Honor user requests               |
| Get consent           | Permission dialogs before capture |
| Show indicators       | Recording visible to user         |

***

## Summary

<Warning>
  **Key principles:**

  * Nothing records without explicit user permission
  * Storage is optional per-channel (`store: false`)
  * Visible recording indicators are required
  * API key never leaves backend
  * Tokens are short-lived and limited scope
</Warning>

***

## Next Steps

<CardGroup cols={2}>
  <Card icon="camera" title="Capture Overview" href="/pages/ingest/capture-sdks/overview">
    Architecture and quickstart
  </Card>

  <Card icon="zap" title="Real-time Context" href="/pages/ingest/capture-sdks/realtime-context">
    Events you receive
  </Card>
</CardGroup>
