A production-ready custom app package that enables Claude Science to run within Verily Workbench environments, with automatic authentication handling and session management.
This repository contains a complete integration solution for running Claude Science as a Verily Workbench custom app. It includes:
- Automatic binary download from official sources during build
- Intelligent authentication proxy that handles nonce-based auth transparently
- Header rewriting to satisfy Claude Science's origin/host validation
- Auto-restart mechanism to maintain service availability
- Iframe embedding support via security header stripping
The integration uses a three-layer proxy architecture to work around Claude Science's authentication and validation requirements:
Workbench (iframe)
↓ (HTTPS)
nginx:8000 (reverse proxy)
↓ (HTTP, strips X-Frame-Options/CSP headers)
nonce-redirect:8002 (authentication proxy)
↓ (HTTP, rewrites Host/Origin/Referer, manages nonces)
Claude Science:8001 (actual service)
↓
Data Volume: /home/claude-user/.claude-science
-
nginx (port 8000) - Entry point for Workbench traffic
- Strips
X-Frame-OptionsandContent-Security-Policyheaders to allow iframe embedding - Proxies all traffic to nonce-redirect service
- Strips
-
nonce-redirect.py (port 8002) - Python authentication proxy
- Detects unauthenticated requests and redirects with fresh nonce
- Rewrites
Host,Origin, andRefererheaders tolocalhost:8001 - Filters cookies to distinguish Workbench cookies from Claude Science session cookies
- Forwards all headers (including CSRF tokens) to Claude Science
-
Claude Science (port 8001) - The actual Claude Science service
- Runs as
claude-user(non-root) - Binds to
127.0.0.1:8001(internal only) - Auto-restarts on clean exit to maintain availability
- Runs as
- Access to a Verily Workbench workspace
- Internet access for downloading Claude Science binary (during build)
- Minimum 4 CPUs, 8GB RAM recommended
- Navigate to your Verily Workbench workspace
- Click "Create App" → "Custom"
- Provide Git repository URL:
https://github.com/dglazer/cs4vwb - Configure compute resources (4+ CPUs, 8GB+ RAM)
- Click "Launch"
The Docker build will automatically:
- Download the latest stable Claude Science binary from Google Cloud Storage
- Install all required dependencies (bubblewrap, socat, nginx, python3)
- Configure the three-layer proxy architecture
- Set up data persistence volume
Once deployed:
- Click on the app URL in Workbench (format:
https://<UUID>.workbench-app-prod.verily.com/) - You'll be automatically redirected with a fresh authentication nonce
- Sign in with your Claude account when prompted
- Paste the authentication code when requested
- Start using Claude Science!
This integration required working around several Claude Science behaviors that aren't designed for proxy/iframe environments:
Problem: Claude Science validates the Host header and rejects requests not matching its configured host.
Solution: nginx rewrites the Host header to localhost:8001 before proxying.
Location: nginx.conf:18
Problem: Claude Science uses single-use nonce URLs (?nonce=xxx) for authentication. Workbench loads the app without a nonce, causing immediate "session expired" errors.
Solution: Created nonce-redirect.py that:
- Detects unauthenticated requests (no nonce, no valid session cookie)
- Executes
claude-science urlto generate a fresh nonce - Redirects the user to the same URL with
?nonce=xxxappended
Location: nonce-redirect.py:101-155
Problem: Claude Science returns X-Frame-Options: DENY and Content-Security-Policy headers that prevent iframe embedding (required by Workbench).
Solution: nginx strips these headers before returning responses to Workbench.
Location: nginx.conf:11-12
Problem: Claude Science validates the Origin and Referer headers, rejecting requests from Workbench's domain.
Solution: nonce-redirect proxy rewrites Origin to http://localhost:8001 and Referer to http://localhost:8001/.
Location: nonce-redirect.py:26-28
Problem: Workbench sends many cookies (IAP, analytics, etc.) alongside Claude Science session cookies. Need to distinguish between them to detect authenticated sessions.
Solution: Implemented cookie filtering that recognizes known Workbench cookie prefixes (GCP_IAP_UID, AMCVS_, _ga, etc.) and treats any other cookie as a potential Claude Science session.
Location: nonce-redirect.py:71-99
Problem: Claude Science uses CSRF tokens in cookies and request headers. Initial implementation only whitelisted specific headers, dropping CSRF tokens and causing 403 errors.
Solution: Changed to forward ALL headers from original request, then override only the specific ones that need rewriting (Host, Origin, Referer).
Location: nonce-redirect.py:20-28
Problem: Claude Science exits cleanly (exit code 0) after ~3 minutes, possibly due to:
- Detecting no direct connections (all traffic comes through proxies)
--allow-ephemeral-data-dirtriggering auto-shutdown- Internal timeout or health check
Solution: Implemented auto-restart loop in entrypoint.sh that:
- Monitors Claude Science process
- If exit code is 0: automatically restart after 2 seconds
- If exit code is non-zero: log error, sleep 5 minutes (for debugging), then exit
Location: entrypoint.sh:113-142
Impact: User sessions may be interrupted during restart, but authentication should persist via the data volume and nonce-redirect will handle re-authentication if needed.
.
├── .devcontainer.json # Workbench devcontainer config
├── docker-compose.yaml # Service definition (container must be named "application-server")
├── Dockerfile # Downloads Claude Science, installs dependencies
├── entrypoint.sh # Startup script with auto-restart loop
├── nginx.conf # Reverse proxy config (strips security headers)
├── nonce-redirect.py # Authentication proxy (handles nonces, header rewriting)
├── .gitignore # Excludes binary, data directory, logs
└── README.md # This file
CLAUDE_SCIENCE_DATA_DIR=/home/claude-user/.claude-science- Data persistence location (mounted as Docker volume)
- 8000: External port exposed to Workbench (nginx)
- 8001: Internal port for Claude Science (not exposed)
- 8002: Internal port for nonce-redirect service (not exposed)
- Entrypoint runs as
rootto start nginx and nonce-redirect - Claude Science runs as
claude-user(non-root) viasu - claude-user - Data directory owned by
claude-user:claude-user
Claude Science is started with:
serve --host 127.0.0.1 --port 8001 --no-browser --allow-ephemeral-data-dir --dangerously-no-sandbox--host 127.0.0.1: Bind to localhost only (nginx proxies from external port)--port 8001: Internal port--no-browser: Don't attempt to open browser (no display in container)--allow-ephemeral-data-dir: Allow running in container environments--dangerously-no-sandbox: Disable sandbox (already in container, may lack CAP_SYS_ADMIN)
Note: These arguments are modified by entrypoint.sh from the original --host 0.0.0.0 in docker-compose.yaml.
Check logs in Workbench for specific errors. Common issues:
- Binary download failed: Verify internet access during build
- Permission errors: Data directory should be owned by
claude-user - Port conflicts: Ensure ports 8000, 8001, 8002 are available
Symptoms: Continuously redirected or asked to authenticate
Checks:
- Verify nonce-redirect service is running:
grep "nonce-redirect" /tmp/entrypoint.log - Check if
claude-science urlcommand works:su - claude-user -c "CLAUDE_SCIENCE_DATA_DIR=/home/claude-user/.claude-science /home/claude-user/claude-science url" - Examine cookie filtering logic in logs
Symptoms: {"detail":"forbidden origin"} or {"detail":"forbidden host"}
Solution: Verify header rewriting is working:
- Check nginx config has
proxy_set_header Host localhost:8001 - Check nonce-redirect is rewriting Origin/Referer headers
- Look for header logs in
[nonce-redirect]output
Symptoms: Page shows "reloading" or hangs
Likely cause: Claude Science exited and hasn't restarted yet
Check:
- Look for "Claude Science exited with code" messages in logs
- Verify auto-restart loop is working (should see "Starting Claude Science..." repeatedly)
- If restart loop isn't working, check entrypoint.sh syntax
Symptoms: Authentication works but actions fail with HTTP 403
Cause: CSRF tokens not being forwarded
Solution: Verify nonce-redirect.py copies ALL headers (line 20-23), not just whitelisted ones
- Health checks: Add proper health endpoints to monitor service status
- Graceful restarts: Attempt to preserve sessions during Claude Science restarts
- Configurable restart delay: Make the 2-second restart delay configurable
- Better logging: Add structured logging with timestamps and severity levels
- Metrics: Expose Prometheus metrics for restart frequency, authentication failures, etc.
- Session persistence: Investigate if sessions can be preserved across restarts
- Connection pooling: Optimize proxy connections for better performance
- Custom error pages: Provide helpful error pages instead of generic 500/502 errors
- Automatic version updates: Script to check for and download new Claude Science versions
- Multi-user support: Handle multiple concurrent users more robustly
- Horizontal scaling: Support multiple Claude Science instances behind a load balancer
- WebSocket optimization: Improve WebSocket handling for real-time features
- SSO integration: Integrate with Workbench's authentication instead of separate Claude login
- Resource management: Automatic CPU/memory allocation based on usage
These are suggested changes to Claude Science that would make integrations like this much cleaner and more robust:
-
Configuration-based host/origin validation
- Current behavior: Hard-coded validation against expected host/origin
- Desired: Environment variables or config file to specify allowed hosts/origins
- Example:
ALLOWED_ORIGINS=https://example.com,https://*.workbench-app-prod.verily.com - Impact: Would eliminate need for header rewriting proxies
-
Optional iframe embedding support
- Current behavior: Always sends
X-Frame-Options: DENYand restrictive CSP - Desired: Flag like
--allow-iframe-embeddingto omit these headers - Impact: Would eliminate need for nginx header stripping
- Current behavior: Always sends
-
Token-based authentication alternative
- Current behavior: Only nonce-based auth (single-use URLs)
- Desired: Support long-lived API tokens or OAuth flows
- Impact: Would eliminate need for nonce-redirect proxy entirely
-
Long-running service mode
- Current behavior: Appears to exit after inactivity or due to ephemeral data dir
- Desired: Explicit
--daemonmode that runs indefinitely - Impact: Would eliminate need for auto-restart loop
-
Session persistence documentation
- Current behavior: Unclear what state persists across restarts
- Desired: Document what's stored in data directory vs. memory
- Impact: Would help understand restart behavior and session continuity
-
Proxy-friendly networking
- Current behavior: Unclear how it detects connections (may see no clients when behind proxies)
- Desired: Option to trust
X-Forwarded-Forheaders and consider proxied connections as active - Impact: May prevent premature exits when behind proxies
-
Structured logging
- Current behavior: Human-readable text logs
- Desired: Option for JSON-structured logs with severity levels
- Impact: Easier to parse and monitor in container environments
-
Health check endpoint
- Current behavior: No dedicated health endpoint
- Desired:
/healthor/readyendpoint that returns service status - Impact: Better integration with orchestrators (Docker, Kubernetes, Workbench)
-
Graceful shutdown
- Current behavior: Unknown shutdown behavior
- Desired: Handle SIGTERM/SIGINT gracefully, finish in-flight requests
- Impact: Better session continuity during restarts
-
Configuration file support
- Current behavior: All configuration via CLI flags
- Desired: Support for config file (YAML/TOML) for complex setups
- Impact: Easier to manage in container environments
-
Multi-user mode
- Current behavior: Designed for single local user
- Desired: Built-in support for multiple concurrent authenticated users
- Impact: Better fit for shared/enterprise environments like Workbench
-
Metrics endpoint
- Current behavior: No metrics exposure
- Desired: Prometheus-compatible
/metricsendpoint - Impact: Better observability in production deployments
-
Configurable authentication timeout
- Current behavior: Fixed session timeout (unknown duration)
- Desired:
--session-timeout=24hflag - Impact: Reduce re-authentication frequency in trusted environments
- Session interruption on restart: When Claude Science restarts (every ~3 minutes), active sessions may be interrupted
- Single instance only: Currently supports only one Claude Science instance per container
- No offline mode: Requires internet access for authentication
- Limited error handling: Some proxy errors may not have helpful error messages
Improvements welcome! Areas where contributions would be particularly valuable:
- Testing session persistence across restarts
- Improving auto-restart logic to minimize disruption
- Adding health checks and metrics
- Testing with multiple concurrent users
- Documenting Claude Science's actual exit behavior
For issues with:
- Claude Science itself: See Claude Science documentation
- Verily Workbench: See Workbench documentation
- This integration: Open an issue at https://github.com/dglazer/cs4vwb/issues
This integration configuration is provided as-is under MIT License. Claude Science is subject to Anthropic's license terms.
Built through iterative debugging across 31 deployment attempts, solving challenges with:
- Host/origin validation
- Nonce-based authentication in iframe contexts
- Cookie filtering across proxy boundaries
- CSRF token preservation
- Service availability and auto-restart
Special thanks to the Verily Workbench team for the custom app platform and documentation.