# External Directory Permission Auto-Deny Implementation ## Summary Automatically denies OpenCode `external_directory` permission requests when they don't match the current app's ID, preventing permission prompts from appearing in the builder UI. ## Storage Impact - **Config file size**: ~208 bytes per app - **Location**: `{workspaceDir}/opencode.json` (in each app's workspace directory) - **Overhead**: Negligible (< 0.3 KB per app) ## Implementation Details ### Files Modified 1. **chat/server.js**: - Added `ENABLE_EXTERNAL_DIR_RESTRICTION` environment variable (line 102) - Added `ensureOpencodeConfig(session)` function (lines 1960-2001) - Modified `ensureSessionPaths(session)` to call the new function (line 1956) 2. **.env.example**: - Added `ENABLE_EXTERNAL_DIR_RESTRICTION` documentation (line 35) ### How It Works 1. When a session is created, `ensureSessionPaths()` is called 2. This function calls `ensureOpencodeConfig()` which: - Extracts the app ID and user ID from the session - Creates an `opencode.json` config file in the workspace directory - Configures `external_directory` permission rules: - Deny all external directory access (`*`: "deny") - Allow access only to current app's paths: - `*/{appId}/*` - Any path containing the app ID - `apps/{userId}/{appId}/*` - The full workspace path pattern 3. OpenCode automatically loads this config when running in the workspace directory 4. Permission requests for paths matching the current app ID are auto-allowed 5. Permission requests for other apps are auto-denied (no user prompt) ### Example Config File ```json { "$schema": "https://opencode.ai/config.json", "permission": { "external_directory": { "*": "deny", "*/c7f9e5c6-e7c2-4258-a583-ccffcf9791c8/*": "allow", "apps/user123/c7f9e5c6-e7c2-4258-a583-ccffcf9791c8/*": "allow" } } } ``` ## Configuration ### Environment Variable ``` ENABLE_EXTERNAL_DIR_RESTRICTION=1 # Default: enabled ``` To disable the feature: ``` ENABLE_EXTERNAL_DIR_RESTRICTION=false ``` ### Usage 1. **New sessions**: Config is automatically created when session starts 2. **Existing sessions**: Config is created next time `ensureSessionPaths()` runs 3. **Failed writes**: Logged but doesn't block session creation ## Security Benefits 1. **App isolation**: Prevents one app from accessing another app's files 2. **No user prompts**: Permission requests are handled automatically 3. **Minimal exposure**: Only allows access to current app's workspace directory 4. **Fail-safe**: If config creation fails, session continues (logs error) ## Testing Checklist - [ ] Verify config file created in workspace directory - [ ] Confirm OpenCode loads config (no permission prompts for same app) - [ ] Test access to different app ID → auto-denied - [ ] Verify no permission dialogs appear in builder UI - [ ] Test with both UUID and slug app IDs - [ ] Test with anonymous users - [ ] Verify `ENABLE_EXTERNAL_DIR_RESTRICTION=false` disables feature - [ ] Check that config file is ~200-300 bytes (minimal storage) ## Logs On successful creation: ``` Created opencode config for session sessionId: c7f9e5c6-e7c2-4258-a583-ccffcf9791c8 appId: my-shopify-app userId: user123 ``` On failure (non-blocking): ``` Failed to create opencode config sessionId: c7f9e5c6-e7c2-4258-a583-ccffcf9791c8 error: EACCES: permission denied ```