diff --git a/03-Capn-Web/.env.example b/03-Capn-Web/.env.example
new file mode 100644
index 0000000..666b55b
--- /dev/null
+++ b/03-Capn-Web/.env.example
@@ -0,0 +1,26 @@
+# Cap'n Web + Auth0 Demo Configuration
+# IMPORTANT: Copy this file to .env and update with your actual values
+
+# Server Configuration
+PORT=3000
+HOST=localhost
+NODE_ENV=development
+
+# Auth0 Configuration
+# Get these values from your Auth0 Dashboard > Applications > [Your App]
+AUTH0_DOMAIN=your-domain.us.auth0.com
+AUTH0_AUDIENCE=https://api.your-app.com
+
+# Auth0 Client Configuration (for the web app)
+# Get this from Auth0 Dashboard > Applications > [Your App] > Settings
+AUTH0_CLIENT_ID=your-auth0-client-id
+
+# Optional: Add artificial delays for testing (in milliseconds)
+DELAY_PROFILE_MS=100
+
+# Optional: Database Configuration (for production)
+# DATABASE_URL=postgresql://username:password@localhost:5432/capnweb_demo
+
+# Optional: Redis Configuration (for session storage)
+# REDIS_URL=redis://localhost:6379
+# REDIS_URL=redis://localhost:6379
\ No newline at end of file
diff --git a/03-Capn-Web/.gitignore b/03-Capn-Web/.gitignore
new file mode 100644
index 0000000..27fa5c6
--- /dev/null
+++ b/03-Capn-Web/.gitignore
@@ -0,0 +1,96 @@
+# Dependencies
+node_modules/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Environment variables (NEVER commit these!)
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Logs
+logs
+*.log
+
+# Coverage directory used by tools like istanbul
+coverage/
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# IDE and editor files
+.vscode/settings.json
+.vscode/launch.json
+.idea/
+*.swp
+*.swo
+*~
+
+# OS generated files
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# Build outputs
+dist/
+build/
+out/
+
+# Temporary files
+*.tmp
+*.temp
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Storybook build outputs
+.out
+.storybook-out
+
+# Temporary folders
+tmp/
+temp/
\ No newline at end of file
diff --git a/03-Capn-Web/CONTRIBUTING.md b/03-Capn-Web/CONTRIBUTING.md
new file mode 100644
index 0000000..ded81bf
--- /dev/null
+++ b/03-Capn-Web/CONTRIBUTING.md
@@ -0,0 +1,57 @@
+# Contributing to Cap'n Web + Auth0 Demo
+
+Thank you for your interest in contributing to this project! This demo showcases the integration between Cap'n Web RPC and Auth0 authentication.
+
+## Getting Started
+
+1. Fork the repository
+2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/capn-web-auth0-demo.git`
+3. Install dependencies: `npm install`
+4. Set up environment: `npm run setup`
+5. Update `.env` with your Auth0 configuration
+6. Start the development server: `npm run dev`
+
+## Development Guidelines
+
+### Code Style
+- Use consistent indentation (2 spaces)
+- Follow existing naming conventions
+- Add comments for complex logic
+- Maintain the existing dark theme design patterns
+
+### Security
+- Never commit credentials or API keys
+- All authentication logic should be server-side validated
+- Follow object-capability security patterns
+- Test all authentication flows thoroughly
+
+### Documentation
+- Update README.md for any new features
+- Add inline comments for complex RPC interactions
+- Update the environment configuration examples
+
+## Pull Request Process
+
+1. Create a feature branch: `git checkout -b feature/your-feature-name`
+2. Make your changes
+3. Test thoroughly with different Auth0 configurations
+4. Update documentation if needed
+5. Submit a pull request with a clear description
+
+## Testing
+
+Before submitting a PR:
+- Test the authentication flow end-to-end
+- Verify WebSocket RPC calls work correctly
+- Check that the pipelining demo functions properly
+- Test with both development and production-like environments
+
+## Questions?
+
+Feel free to open an issue for questions about:
+- Cap'n Web RPC implementation
+- Auth0 integration patterns
+- Project structure and architecture
+- Development setup issues
+
+Thank you for contributing! ๐
\ No newline at end of file
diff --git a/03-Capn-Web/Dockerfile b/03-Capn-Web/Dockerfile
new file mode 100644
index 0000000..cb39f83
--- /dev/null
+++ b/03-Capn-Web/Dockerfile
@@ -0,0 +1,29 @@
+# Cap'n Web + Auth0 Demo Dockerfile
+FROM node:18-alpine
+
+# Set working directory
+WORKDIR /app
+
+# Copy package files
+COPY package*.json ./
+
+# Install dependencies
+RUN npm ci --only=production
+
+# Copy application code
+COPY . .
+
+# Create non-root user for security
+RUN addgroup -g 1001 -S nodejs
+RUN adduser -S capnweb -u 1001
+USER capnweb
+
+# Expose port
+EXPOSE 3000
+
+# Add health check
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+ CMD node -e "require('http').get('http://localhost:3000/', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"
+
+# Start application
+CMD ["npm", "start"]
\ No newline at end of file
diff --git a/03-Capn-Web/README.md b/03-Capn-Web/README.md
new file mode 100644
index 0000000..dc96015
--- /dev/null
+++ b/03-Capn-Web/README.md
@@ -0,0 +1,301 @@
+# Cap'n Web + Auth0 Demo: User Profile Management
+
+> **A production-ready sample application showcasing secure user profile management using Cap'n Web RPC and Auth0 authentication**
+
+## ๐ฏ What This Demo Shows
+
+This application demonstrates how to build a **user profile management system** that combines:
+
+- **๐ Auth0 Authentication**: Users log in securely using Auth0's hosted login page
+- **โก Cap'n Web RPC**: Real-time API calls for profile operations via WebSocket RPC
+- **๐ค Profile Management**: Users can view and edit their personal profile information
+- **๐ก๏ธ JWT Authorization**: Auth0 JWT tokens authorize all API calls to ensure users only access their own data
+
+### Use Case: Personal Profile Dashboard
+
+**The Story**: Users need a secure way to manage their profile information. Auth0 handles the complex authentication flow, while Cap'n Web provides fast, real-time API communication for profile updates.
+
+**User Journey**:
+1. **Landing Page** โ User clicks "Authenticate with Auth0"
+2. **Auth0 Login** โ Secure authentication via Auth0's hosted login
+3. **Profile Dashboard** โ User sees their email and can edit their bio
+4. **Real-time Updates** โ Profile changes are saved instantly via WebSocket RPC
+5. **Data Security** โ JWT tokens ensure users only see/edit their own profiles
+
+**Technical Flow**:
+- Auth0 provides secure user authentication and JWT tokens
+- Cap'n Web RPC handles profile API calls (get/update profile)
+- Server validates JWT tokens and isolates user data
+- In-memory storage keeps the demo simple while showing the patterns
+
+This demo application demonstrates how to build secure, real-time applications using [Cap'n Web RPC](https://github.com/cloudflare/capnweb) with [Auth0](https://github.com/auth0/auth0-spa-js) authentication. It features object-capability security, WebSocket-based RPC communication, and JWT token verification.
+
+   
+
+## ๐ Features
+
+- **๐ Secure Authentication**: Auth0 handles user login with industry-standard security
+- **๐ค User Profile Management**: View and edit personal profile information (email, bio)
+- **โก Real-time RPC**: Cap'n Web provides fast WebSocket-based API communication
+- **๐ก๏ธ JWT Authorization**: Auth0 tokens authorize all API calls to protect user data
+- **๐จ Modern UI**: Clean, responsive dark theme with Auth0 branding
+- **๐ Live Updates**: Profile changes saved instantly without page refreshes
+- **๏ฟฝ Production Ready**: Environment-based configuration, Docker support, comprehensive documentation
+- **๐งน Developer Friendly**: Easy setup, clear error handling, debugging tools included
+
+## ๐ Project Structure
+
+```
+capn-web-auth0-demo/
+โโโ client/ # Frontend application
+โ โโโ index.html # Main UI with Auth0 branding & dark theme
+โ โโโ client.js # Auth0 integration & Cap'n Web RPC client
+โ โโโ capnweb-browser.js # Browser-compatible Cap'n Web implementation
+โโโ server/ # Backend application
+โ โโโ index.js # WebSocket RPC server with JWT validation
+โโโ scripts/ # Maintenance and setup scripts
+โ โโโ cleanup.sh # Repository validation script
+โโโ .env.example # Environment configuration template
+โโโ .gitignore # Git ignore patterns
+โโโ CONTRIBUTING.md # Contribution guidelines
+โโโ Dockerfile # Container configuration
+โโโ docker-compose.yml # Multi-container development setup
+โโโ package.json # Dependencies, scripts, and metadata
+```
+
+## โก Quick Start
+
+1. **Clone and install dependencies:**
+ ```bash
+ git clone https://github.com/auth0-samples/auth0-javascript-samples.git
+ cd 03-Capn-Web
+ npm install
+ ```
+
+2. **Set up environment:**
+ ```bash
+ npm run setup
+ # Then edit .env with your Auth0 configuration
+ ```
+
+3. **Start the application:**
+ ```bash
+ npm run dev
+ ```
+
+4. **Open your browser:**
+ ```
+ http://localhost:3000
+ ```
+
+## ๐๏ธ Architecture
+
+### Server Side (Node.js)
+- **ProfileService**: Cap'n Web RPC target with two methods:
+ - `getProfile()`: Returns user email and bio based on Auth0 token
+ - `updateProfile(newBio)`: Updates user bio with Auth0 token validation
+- **Auth0 Integration**: JWT token verification with JWKS
+- **In-memory Storage**: User profiles stored by Auth0 user ID
+
+### Client Side (Vanilla JavaScript)
+- **Auth0 SPA SDK**: Handles login/logout flow
+- **Cap'n Web Client**: WebSocket RPC communication
+- **Profile UI**: Bio editing with save/fetch functionality
+
+## ๐ Prerequisites
+
+- **Node.js 18+** (LTS recommended)
+- **Auth0 account** - Free tier available at [auth0.com](https://auth0.com)
+- **Modern web browser** with WebSocket support
+- **Git** for cloning the repository
+
+## โ๏ธ Setup
+
+### 1. Auth0 Configuration
+
+1. Create an Auth0 account at [auth0.com](https://auth0.com)
+2. Create a new Single Page Application
+3. Configure the following settings:
+ - **Allowed Callback URLs**: `http://localhost:3000`
+ - **Allowed Logout URLs**: `http://localhost:3000`
+ - **Allowed Web Origins**: `http://localhost:3000`
+4. Note down your:
+ - Domain (e.g., `your-domain.us.auth0.com`)
+ - Client ID
+ - API Audience (if using Auth0 APIs)
+
+### 2. Environment Configuration
+
+โ ๏ธ **IMPORTANT**: Never commit credentials to your repository!
+
+1. Copy the environment template:
+ ```bash
+ cp .env.example .env
+ ```
+
+2. Update `.env` with your Auth0 configuration:
+ ```bash
+ # Auth0 Configuration
+ AUTH0_DOMAIN=your-domain.us.auth0.com
+ AUTH0_CLIENT_ID=your-auth0-client-id
+ AUTH0_AUDIENCE=https://api.your-app.com
+
+ # Server Configuration
+ PORT=3000
+ NODE_ENV=development
+ ```
+
+3. The application will automatically load these values - no code changes needed!
+
+### 3. Install Dependencies
+
+```bash
+npm install
+```
+
+## ๐ Running the Application
+
+1. Start the server:
+
+```bash
+npm run dev
+```
+
+2. Open your browser and navigate to:
+```
+http://localhost:3000
+```
+
+## ๐ User Flow
+
+1. **Landing Page**: User sees a "Log In" button
+2. **Auth0 Login**: Clicking login redirects to Auth0 authentication
+3. **Authenticated State**: After login, user sees:
+ - Their email address (from Auth0 token)
+ - A text area for bio editing
+ - Save, Fetch, and Logout buttons
+4. **Profile Management**:
+ - **Save**: Updates bio via Cap'n Web RPC call
+ - **Fetch**: Retrieves saved bio from server
+ - **Logout**: Clears session and returns to login screen
+
+## ๐ง Technical Details
+
+### Cap'n Web RPC Flow
+
+1. Client establishes WebSocket connection to `/api`
+2. Auth0 token is sent for authentication
+3. Server creates `ProfileService` instance with user token
+4. RPC calls are made through Cap'n Web protocol
+5. Server validates token on each call and isolates data by user ID
+
+### Security Features
+
+- **Token Validation**: All RPC calls validate Auth0 JWT tokens
+- **User Isolation**: Profiles are stored and retrieved by Auth0 user ID
+- **Object Capabilities**: Cap'n Web's object-capability model ensures secure RPC
+
+### API Methods
+
+#### `getProfile()`
+- **Returns**: `{ email: string, bio: string }`
+- **Security**: Validates Auth0 token and returns user-specific data
+
+#### `updateProfile(newBio)`
+- **Parameters**: `newBio: string`
+- **Returns**: `{ success: boolean, message: string }`
+- **Security**: Validates Auth0 token and updates user-specific data
+
+## ๏ฟฝ Docker Support
+
+Run the application using Docker for consistent development environments:
+
+### Using Docker Compose (Recommended)
+```bash
+# Start with docker-compose
+npm run docker:dev
+
+# Or manually
+docker-compose up --build
+```
+
+### Using Docker directly
+```bash
+# Build the image
+npm run docker:build
+
+# Run with environment file
+npm run docker:run
+```
+
+The Docker setup includes:
+- Node.js 18 Alpine base image
+- Non-root user for security
+- Health checks for monitoring
+- Volume mounts for development
+
+## ๐ Key Dependencies
+
+- **capnweb**: RPC library for browser-server communication
+- **dotenv**: Environment variable management
+- **jsonwebtoken**: JWT token verification
+- **jwks-client**: Auth0 JWKS key retrieval
+- **ws**: WebSocket server implementation
+
+## ๐ค Contributing
+
+We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for:
+
+- Development setup guidelines
+- Code style requirements
+- Security considerations
+- Pull request process
+
+For questions or issues, please:
+1. Check existing [issues](https://github.com/YOUR_USERNAME/capn-web-auth0-demo/issues)
+2. Review the [troubleshooting section](#-troubleshooting)
+3. Open a new issue with detailed information
+
+## ๐ Troubleshooting
+
+### Common Issues
+
+1. **Auth0 Configuration**: Ensure domain and client ID are correct
+2. **CORS Issues**: Check that callback URLs are properly configured
+3. **WebSocket Connection**: Verify server is running on correct port
+4. **Token Validation**: Check Auth0 domain and audience configuration
+
+### Development Tips
+
+- Check browser console for detailed error messages
+- Monitor server logs for authentication and RPC call details
+- Use browser DevTools to inspect WebSocket traffic
+- Verify Auth0 token in [jwt.io](https://jwt.io) for debugging
+- Run `npm run cleanup` to validate repository setup
+
+## ๐ Learn More
+
+- [Cap'n Web Documentation](https://github.com/cloudflare/capnweb)
+- [Auth0 SPA SDK Guide](https://auth0.com/docs/libraries/auth0-spa-js)
+- [Cap'n Web Blog Post](https://blog.cloudflare.com/capnweb-javascript-rpc-library/)
+
+## ๏ฟฝ What You'll Learn
+
+This demo teaches you how to:
+
+- **Integrate Auth0** for secure user authentication in web applications
+- **Use Cap'n Web RPC** for real-time, WebSocket-based API communication
+- **Handle JWT tokens** for API authorization and user data protection
+- **Build responsive UIs** with modern web technologies and dark themes
+- **Structure projects** for production deployment with Docker and environment management
+- **Implement user data isolation** ensuring users can only access their own profiles
+
+Perfect for developers exploring modern authentication patterns, real-time communication, or looking for a foundation to build user-centric applications.
+
+## ๐ License
+
+MIT License - see [LICENSE](LICENSE) file for details.
+
+---
+
+**Built with โค๏ธ for developers exploring Auth0 and Cap'n Web integration**
\ No newline at end of file
diff --git a/03-Capn-Web/client/capnweb-browser.js b/03-Capn-Web/client/capnweb-browser.js
new file mode 100644
index 0000000..fc9f991
--- /dev/null
+++ b/03-Capn-Web/client/capnweb-browser.js
@@ -0,0 +1,151 @@
+// Simplified Cap'n Web browser implementation for demonstration
+// In production, you would use a proper bundler like Vite, Webpack, or Rollup
+
+// Simple polyfill for Symbol.dispose if not supported
+if (!Symbol.dispose) {
+ Symbol.dispose = Symbol.for('dispose');
+}
+
+// Basic RPC Target implementation
+class RpcTarget {
+ constructor() {
+ // Mark as RPC target
+ this.__isRpcTarget = true;
+ }
+}
+
+// Simple RPC session implementation for WebSocket
+class SimpleRpcSession {
+ constructor(webSocket) {
+ this.webSocket = webSocket;
+ this.callId = 0;
+ this.pendingCalls = new Map();
+ this.isReady = false;
+ this.readyPromise = null;
+
+ // Wait for WebSocket to open
+ if (webSocket.readyState === WebSocket.OPEN) {
+ this.isReady = true;
+ this.readyPromise = Promise.resolve();
+ } else {
+ this.readyPromise = new Promise((resolve, reject) => {
+ const onOpen = () => {
+ this.isReady = true;
+ webSocket.removeEventListener('open', onOpen);
+ webSocket.removeEventListener('error', onError);
+ resolve();
+ };
+
+ const onError = (error) => {
+ webSocket.removeEventListener('open', onOpen);
+ webSocket.removeEventListener('error', onError);
+ reject(error);
+ };
+
+ webSocket.addEventListener('open', onOpen);
+ webSocket.addEventListener('error', onError);
+ });
+ }
+
+ this.webSocket.addEventListener('message', (event) => {
+ this.handleMessage(event.data);
+ });
+ }
+
+ handleMessage(data) {
+ try {
+ const message = JSON.parse(data);
+
+ if (message.type === 'response' && this.pendingCalls.has(message.id)) {
+ const { resolve, reject } = this.pendingCalls.get(message.id);
+ this.pendingCalls.delete(message.id);
+
+ if (message.error) {
+ reject(new Error(message.error));
+ } else {
+ resolve(message.result);
+ }
+ }
+ } catch (error) {
+ console.error('Error handling message:', error);
+ }
+ }
+
+ async call(method, params = []) {
+ // Wait for WebSocket to be ready
+ await this.readyPromise;
+
+ return new Promise((resolve, reject) => {
+ const id = ++this.callId;
+
+ this.pendingCalls.set(id, { resolve, reject });
+
+ const message = {
+ type: 'call',
+ id: id,
+ method: method,
+ params: params
+ };
+
+ try {
+ this.webSocket.send(JSON.stringify(message));
+ } catch (error) {
+ this.pendingCalls.delete(id);
+ reject(error);
+ return;
+ }
+
+ // Timeout after 30 seconds
+ setTimeout(() => {
+ if (this.pendingCalls.has(id)) {
+ this.pendingCalls.delete(id);
+ reject(new Error('Request timeout'));
+ }
+ }, 30000);
+ });
+ }
+
+ [Symbol.dispose]() {
+ this.webSocket.close();
+ }
+}
+
+// Create a proxy that intercepts method calls and routes them through RPC
+function createRpcProxy(session) {
+ return new Proxy({}, {
+ get(target, prop) {
+ if (prop === Symbol.dispose) {
+ return () => session[Symbol.dispose]();
+ }
+
+ // Don't intercept promise methods or private properties
+ if (typeof prop === 'string' &&
+ !prop.startsWith('_') &&
+ !['then', 'catch', 'finally', 'constructor', 'toString', 'valueOf'].includes(prop)) {
+ return (...args) => session.call(prop, args);
+ }
+
+ return target[prop];
+ }
+ });
+}
+
+// Simple WebSocket RPC session factory
+async function newWebSocketRpcSession(webSocketOrUrl) {
+ const webSocket = typeof webSocketOrUrl === 'string'
+ ? new WebSocket(webSocketOrUrl)
+ : webSocketOrUrl;
+
+ const session = new SimpleRpcSession(webSocket);
+
+ // Wait for the WebSocket to be ready
+ await session.readyPromise;
+
+ return createRpcProxy(session);
+}
+
+// Export for use in the application
+window.CapnWeb = {
+ RpcTarget,
+ newWebSocketRpcSession
+};
\ No newline at end of file
diff --git a/03-Capn-Web/client/client.js b/03-Capn-Web/client/client.js
new file mode 100644
index 0000000..3ad6564
--- /dev/null
+++ b/03-Capn-Web/client/client.js
@@ -0,0 +1,367 @@
+// Auth0 Configuration - Loaded dynamically from server
+let AUTH0_CONFIG = null;
+
+// Load configuration from server
+async function loadConfig() {
+ try {
+ const response = await fetch('/api/config');
+ if (!response.ok) {
+ throw new Error(`Failed to load configuration: ${response.status}`);
+ }
+ const config = await response.json();
+
+ AUTH0_CONFIG = {
+ domain: config.auth0.domain,
+ clientId: config.auth0.clientId,
+ authorizationParams: {
+ redirect_uri: window.location.origin,
+ audience: config.auth0.audience
+ }
+ };
+
+ console.log('โ Configuration loaded successfully');
+ return AUTH0_CONFIG;
+ } catch (error) {
+ console.error('โ Failed to load configuration:', error);
+ throw error;
+ }
+}
+
+class ProfileApp {
+ constructor() {
+ this.auth0 = null;
+ this.profileService = null;
+ this.sessionId = null;
+ this.initializeApp();
+ }
+
+ async initializeApp() {
+ try {
+ // Load configuration from server
+ this.showStatus('Loading configuration...', 'info');
+ await loadConfig();
+
+ if (!AUTH0_CONFIG) {
+ throw new Error('Failed to load Auth0 configuration');
+ }
+
+ // Initialize Auth0
+ this.showStatus('Initializing Auth0...', 'info');
+ this.auth0 = await auth0.createAuth0Client(AUTH0_CONFIG);
+
+ // Check if user is returning from login
+ const query = window.location.search;
+ if (query.includes('code=') && query.includes('state=')) {
+ this.showStatus('Processing login...', 'info');
+ await this.auth0.handleRedirectCallback();
+ window.history.replaceState({}, document.title, window.location.pathname);
+ }
+
+ // Check authentication status
+ const isAuthenticated = await this.auth0.isAuthenticated();
+
+ if (isAuthenticated) {
+ await this.handleAuthenticated();
+ } else {
+ this.showLoginScreen();
+ }
+
+ this.setupEventListeners();
+ } catch (error) {
+ console.error('Error initializing app:', error);
+ this.showStatus('Failed to initialize application', 'error');
+ }
+ }
+
+ setupEventListeners() {
+ document.getElementById('loginBtn').addEventListener('click', () => this.login());
+ document.getElementById('logoutBtn').addEventListener('click', () => this.logout());
+ document.getElementById('saveBtn').addEventListener('click', () => this.saveProfile());
+ document.getElementById('fetchBtn').addEventListener('click', () => this.fetchProfile());
+ document.getElementById('pipelineBtn').addEventListener('click', () => this.demoPipelining());
+ }
+
+ async login() {
+ try {
+ await this.auth0.loginWithRedirect({
+ authorizationParams: {
+ redirect_uri: window.location.origin,
+ audience: AUTH0_CONFIG.authorizationParams.audience,
+ scope: 'openid profile email'
+ }
+ });
+ } catch (error) {
+ console.error('Error during login:', error);
+ this.showStatus('Login failed', 'error');
+ }
+ }
+
+ async logout() {
+ try {
+ // Close Cap'n Web connection
+ if (this.profileService && this.profileService[Symbol.dispose]) {
+ this.profileService[Symbol.dispose]();
+ this.profileService = null;
+ }
+
+ await this.auth0.logout({
+ logoutParams: {
+ returnTo: window.location.origin
+ }
+ });
+ } catch (error) {
+ console.error('Error during logout:', error);
+ }
+ }
+
+ async handleAuthenticated() {
+ try {
+ const user = await this.auth0.getUser();
+
+ // Show profile section
+ this.showProfileScreen(user);
+
+ // Initialize Cap'n Web connection
+ await this.initializeCapnWeb();
+
+ // Automatically fetch profile
+ await this.fetchProfile();
+
+ } catch (error) {
+ console.error('Error handling authentication:', error);
+ this.showStatus('Authentication error', 'error');
+ }
+ }
+
+ async initializeCapnWeb() {
+ try {
+ console.log('Initializing Cap\'n Web connection...');
+
+ // Create WebSocket connection
+ const wsUrl = `ws://${window.location.host}`;
+ console.log('Connecting to WebSocket:', wsUrl);
+
+ // Use our simple Cap'n Web implementation and wait for connection
+ this.profileService = await CapnWeb.newWebSocketRpcSession(wsUrl);
+
+ console.log('Cap\'n Web RPC connection established and ready');
+
+ } catch (error) {
+ console.error('Error initializing Cap\'n Web:', error);
+ throw error;
+ }
+ }
+
+ async fetchProfile() {
+ try {
+ this.setLoading(true);
+
+ if (!this.profileService) {
+ throw new Error('RPC service not initialized');
+ }
+
+ let token;
+ try {
+ token = await this.auth0.getTokenSilently({
+ authorizationParams: {
+ audience: AUTH0_CONFIG.authorizationParams.audience
+ }
+ });
+ } catch (tokenError) {
+ console.error('Token error:', tokenError);
+
+ // If consent is required, redirect to login with consent
+ if (tokenError.error === 'consent_required' || tokenError.error === 'interaction_required') {
+ console.log('Consent required, redirecting to login...');
+ await this.auth0.loginWithRedirect({
+ authorizationParams: {
+ audience: AUTH0_CONFIG.authorizationParams.audience,
+ scope: 'openid profile email',
+ prompt: 'consent'
+ }
+ });
+ return;
+ }
+ throw tokenError;
+ }
+
+ console.log('Fetching profile with token...');
+ console.log('Token type:', typeof token);
+ console.log('Token length:', token ? token.length : 'null');
+ console.log('Token preview:', token ? token.substring(0, 50) + '...' : 'null');
+
+ // Call the getProfile method via Cap'n Web RPC
+ const profile = await this.profileService.getProfile(token);
+
+ document.getElementById('bioTextarea').value = profile.bio || '';
+ this.showStatus('Profile loaded successfully', 'success');
+
+ } catch (error) {
+ console.error('Error fetching profile:', error);
+ this.showStatus('Failed to fetch profile: ' + error.message, 'error');
+ } finally {
+ this.setLoading(false);
+ }
+ }
+
+ async saveProfile() {
+ try {
+ this.setLoading(true);
+
+ if (!this.profileService) {
+ throw new Error('RPC service not initialized');
+ }
+
+ const bio = document.getElementById('bioTextarea').value.trim();
+ const token = await this.auth0.getTokenSilently({
+ authorizationParams: {
+ audience: AUTH0_CONFIG.authorizationParams.audience
+ }
+ });
+
+ console.log('Saving profile with bio length:', bio.length);
+
+ // Call the updateProfile method via Cap'n Web RPC
+ const result = await this.profileService.updateProfile(token, bio);
+
+ if (result.success) {
+ this.showStatus('Profile saved successfully', 'success');
+ } else {
+ this.showStatus('Failed to save profile', 'error');
+ }
+
+ } catch (error) {
+ console.error('Error saving profile:', error);
+ this.showStatus('Failed to save profile: ' + error.message, 'error');
+ } finally {
+ this.setLoading(false);
+ }
+ }
+
+ async demoPipelining() {
+ try {
+ this.setLoading(true);
+
+ if (!this.profileService) {
+ throw new Error('RPC service not initialized');
+ }
+
+ const token = await this.auth0.getTokenSilently({
+ authorizationParams: {
+ audience: AUTH0_CONFIG.authorizationParams.audience
+ }
+ });
+
+ this.showStatus('๐ Demonstrating Cap\'n Web RPC Pipelining...', 'info');
+
+ // Get current bio from the textarea
+ const currentBio = document.getElementById('bioTextarea').value.trim();
+ const timestamp = new Date().toLocaleTimeString();
+ const newBio = `${currentBio} [Pipelined update at ${timestamp}]`;
+
+ console.log('Demo: Starting pipelined operations...');
+ console.log('1. Fetching current profile (before update)');
+ console.log('2. Updating profile with new bio');
+ console.log('3. Both operations running concurrently!');
+
+ // ๐ฏ PIPELINING DEMO: Start both operations simultaneously
+ // This demonstrates Cap'n Web's ability to pipeline multiple RPC calls
+ const startTime = performance.now();
+
+ const [oldProfile, updateResult] = await Promise.all([
+ // Operation 1: Fetch the current profile (before update)
+ this.profileService.getProfile(token),
+ // Operation 2: Update the profile with new bio (simultaneously)
+ this.profileService.updateProfile(token, newBio)
+ ]);
+
+ const endTime = performance.now();
+ const duration = Math.round(endTime - startTime);
+
+ console.log(`Pipelining completed in ${duration}ms`);
+ console.log('Old profile bio:', oldProfile.bio);
+ console.log('Update result:', updateResult);
+
+ // Show the results in a nice format
+ const oldBioPreview = oldProfile.bio ?
+ (oldProfile.bio.length > 50 ? oldProfile.bio.substring(0, 50) + '...' : oldProfile.bio) :
+ '(empty)';
+
+ this.showStatus(
+ `โจ Pipelining Demo Complete! ` +
+ `Fetched old bio: "${oldBioPreview}" & ` +
+ `updated profile simultaneously in ${duration}ms`,
+ 'success'
+ );
+
+ // Wait a moment, then fetch the updated profile to show the change
+ setTimeout(async () => {
+ await this.fetchProfile();
+ this.showStatus(
+ `๐ Profile refreshed! Notice how the bio now includes the pipelined update. ` +
+ `This demonstrates real-time RPC capability.`,
+ 'info'
+ );
+ }, 2000);
+
+ } catch (error) {
+ console.error('Error in pipelining demo:', error);
+ this.showStatus('โ Pipelining demo failed: ' + error.message, 'error');
+ } finally {
+ this.setLoading(false);
+ }
+ }
+
+ showLoginScreen() {
+ const authSection = document.getElementById('authSection');
+ const profileSection = document.getElementById('profileSection');
+
+ if (authSection) authSection.style.display = 'block';
+ if (profileSection) profileSection.style.display = 'none';
+ }
+
+ showProfileScreen(user) {
+ const authSection = document.getElementById('authSection');
+ const profileSection = document.getElementById('profileSection');
+ const userEmailEl = document.getElementById('userEmail');
+
+ if (authSection) authSection.style.display = 'none';
+ if (profileSection) profileSection.style.display = 'block';
+ if (userEmailEl) userEmailEl.textContent = user.email || user.name || 'Unknown User';
+ }
+
+ setLoading(loading) {
+ const container = document.querySelector('.main-container');
+ if (container) {
+ if (loading) {
+ container.classList.add('loading');
+ } else {
+ container.classList.remove('loading');
+ }
+ }
+ }
+
+ showStatus(message, type) {
+ const statusEl = document.getElementById('status');
+ if (!statusEl) {
+ console.warn('Status element not found');
+ return;
+ }
+
+ statusEl.innerHTML = message; // Use innerHTML to support icons/HTML
+ statusEl.className = `status ${type}`;
+ statusEl.style.display = 'block';
+
+ // Auto-hide success/info messages after 5 seconds, keep errors visible
+ if (type === 'success' || type === 'info') {
+ setTimeout(() => {
+ if (statusEl) statusEl.style.display = 'none';
+ }, 5000);
+ }
+ }
+}
+
+// Initialize the app when DOM is loaded
+document.addEventListener('DOMContentLoaded', () => {
+ new ProfileApp();
+});
\ No newline at end of file
diff --git a/03-Capn-Web/client/index.html b/03-Capn-Web/client/index.html
new file mode 100644
index 0000000..fef4cdf
--- /dev/null
+++ b/03-Capn-Web/client/index.html
@@ -0,0 +1,589 @@
+
+
+
+
+
+ Cap'n Web + Auth0 Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
๐ Cap'n Web
+
+
+
Object-Capability RPC with Secure Authentication
+ Developer Demo
+
+
+
+
+
+
Welcome, Developer! ๐
+
This demo showcases Cap'n Web RPC integration with Auth0 authentication.
+ Experience secure, real-time profile management with object-capability security.
+
+
+
+
+
+
+
+
+
+
Loading...
+
Authenticated via Auth0
+
+
+
+
+
+
+ Profile Management
+
+
+
+
+
+
+
+
+
RPC Pipelining Demo
+
The "Demo Pipelining" button showcases Cap'n Web's ability to execute multiple RPC calls concurrently,
+ demonstrating how you can save a profile and fetch the old profile data simultaneously for better performance.