using-webctl
$
npx mdskill add oaustegard/claude-skills/using-webctlInject proxy auth headers to enable headless Chrome automation.
- Resolves HTTPS navigation failures when webctl tunneling breaks.
- Depends on webctl CLI, Python, and authenticated HTTP proxy.
- Executes automatically when user requests browser automation.
- Delivers functional Chromium sessions through local forwarding proxy.
SKILL.md
.github/skills/using-webctlView on GitHub ↗
---
name: using-webctl
description: Browser automation via webctl CLI in Claude.ai containers with authenticated proxy support. Use when users mention webctl, browser automation, Playwright browsing, web scraping, or headless Chrome in container environments.
metadata:
version: 0.0.1
---
# Using webctl in Claude.ai Containers
Browser automation using webctl CLI with automatic proxy authentication handling for Claude.ai's egress-controlled environment.
## When This Skill Applies
- User requests browser automation, web scraping, or page interaction
- webctl commands fail with `ERR_TUNNEL_CONNECTION_FAILED`
- Playwright/Chromium needs to work through authenticated HTTP proxy
## Core Problem
Claude.ai containers route traffic through an authenticated egress proxy (`HTTP_PROXY` env var with JWT credentials). Chromium doesn't properly handle proxy authentication for HTTPS CONNECT tunnels, causing all HTTPS navigation to fail even though curl works.
## Solution
A local forwarding proxy (port 18080) intercepts Chromium connections and injects `Proxy-Authorization` headers before forwarding to the real egress proxy.
## Setup Procedure
### 1. Install webctl
```bash
pip install webctl --break-system-packages
webctl setup # Downloads Chromium if needed
```
### 2. Deploy Auth Proxy Module
Copy `scripts/auth_proxy.py` to webctl's daemon directory:
```bash
cp /mnt/skills/user/using-webctl/scripts/auth_proxy.py \
/usr/local/lib/python3.12/dist-packages/webctl/daemon/
```
### 3. Patch Session Manager
Apply this patch to `/usr/local/lib/python3.12/dist-packages/webctl/daemon/session_manager.py`:
Find the context creation block (around line 104):
```python
# Create context
context = await browser.new_context(
storage_state=storage_state, viewport={"width": 1280, "height": 720}
)
```
Replace with:
```python
# Create context with proxy from env (with auth handling)
from .auth_proxy import get_local_proxy_url
proxy_url = get_local_proxy_url()
proxy_config = {"server": proxy_url} if proxy_url else None
context = await browser.new_context(
storage_state=storage_state,
viewport={"width": 1280, "height": 720},
proxy=proxy_config
)
```
### 4. Verify
```bash
webctl start --mode unattended
webctl --quiet navigate "https://github.com"
webctl snapshot --interactive-only --limit 10
webctl stop --daemon
```
## Quick Reference
### Session Management
```bash
webctl start --mode unattended # Headless browser
webctl stop --daemon # Full shutdown
webctl status # Current state + console error counts
```
### Navigation
```bash
webctl navigate "https://..."
webctl back / webctl forward / webctl reload
```
### Observation
```bash
webctl snapshot --interactive-only --limit 30 # Buttons, links, inputs
webctl snapshot --within "role=main" # Scope to container
webctl query "role=button name~=Submit" # Debug queries
webctl screenshot --path shot.png
```
### Interaction
```bash
webctl click 'role=button name~="Submit"'
webctl type 'role=textbox name~="Email"' "user@example.com"
webctl type 'role=textbox name~="Search"' "query" --submit
webctl select 'role=combobox name~="Country"' --label "Germany"
```
### Query Syntax
- `role=button` — By ARIA role
- `name~="partial"` — Contains (preferred, more robust)
- `name="exact"` — Exact match
- `nth=0` — Select first when multiple matches
### Wait Conditions
```bash
webctl wait network-idle
webctl wait 'exists:role=button name~="Continue"'
webctl wait 'url-contains:"/dashboard"'
```
## Troubleshooting
### ERR_TUNNEL_CONNECTION_FAILED
Auth proxy not loaded. Verify:
1. `auth_proxy.py` exists in webctl daemon directory
2. Session manager is patched
3. Restart daemon: `webctl stop --daemon && webctl start --mode unattended`
### Multiple matches error
Add specificity or use `nth=0`:
```bash
webctl click 'role=link name="Sign in" nth=0'
```
### Verify proxy is running
```bash
netstat -tlnp | grep 18080
```
## Limitations
- Patch is session-local (container resets clear it)
- Only allowed domains in network config are accessible
- No Firefox support (download blocked by egress policy)
## Domain Restrictions
Check `<network_configuration>` in system prompt for allowed domains. Common allowed: `*.github.com`, `*.bsky.app`, allowed API endpoints.
## Output Filtering
Reduce context consumption:
```bash
webctl snapshot --interactive-only --limit 30 # Cap elements
webctl snapshot | grep -i "submit" # Unix filtering
webctl --quiet navigate "..." # Suppress events
```
More from oaustegard/claude-skills
- accessing-github-reposGitHub repository access in containerized environments using REST API and credential detection. Use when git clone fails, or when accessing private repos/writing files via API.
- api-credentialsSecurely manages API credentials for multiple providers (Anthropic Claude, Google Gemini, GitHub). Use when skills need to access stored API keys for external service invocations.
- asking-questionsGuidance for asking clarifying questions when user requests are ambiguous, have multiple valid approaches, or require critical decisions. Use when implementation choices exist that could significantly affect outcomes.
- browsing-blueskyBrowse Bluesky content via API and firehose - search posts, fetch user activity, sample trending topics, read feeds and lists, analyze and categorize accounts. Supports authenticated access for personalized feeds. Use for Bluesky research, user monitoring, trend analysis, feed reading, firehose sampling, account categorization.
- building-github-indexGenerate progressive disclosure indexes for GitHub repositories to use as Claude project knowledge. Use when setting up projects referencing external documentation, creating searchable indexes of technical blogs or knowledge bases, combining multiple repos into one index, or when user mentions "index", "github repo", "project knowledge", or "documentation reference".
- categorizing-bsky-accountsAnalyze and categorize Bluesky accounts by topic using keyword extraction. Use when users mention Bluesky account analysis, following/follower lists, topic discovery, account curation, or network analysis.
- chartingSelect the right Python charting library (seaborn, matplotlib, graphviz) and produce publication-quality static visualizations. Use when creating charts, plots, graphs, diagrams, heatmaps, visualizations from data, or when choosing between matplotlib/seaborn/graphviz. Also triggers for network diagrams, flowcharts, dependency trees, state machines, and entity-relationship diagrams. For interactive browser-rendered charts or uploaded data exploration, defer to charting-vega-lite instead.
- charting-vega-liteCreate interactive data visualizations using Vega-Lite declarative JSON grammar. Supports 20+ chart types (bar, line, scatter, histogram, boxplot, grouped/stacked variations, etc.) via templates and programmatic builders. Use when users upload data for charting, request specific chart types, or mention visualizations. Produces portable JSON specs with inline data islands that work in Claude artifacts and can be adapted for production.
- check-toolsValidates development tool installations across Python, Node.js, Java, Go, Rust, C/C++, Git, and system utilities. Use when verifying environments or troubleshooting dependencies.
- cloning-projectExports project instructions and knowledge files from the current Claude project. Use when users want to clone, copy, backup, or export a project's configuration and files.