hypha-core
v0.20.85
Published
Hypha Core
Readme
Hypha Core
A lightweight, browser-based runtime for executing Hypha Apps and ImJoy Plugins with full workspace management, RPC communication, and service orchestration capabilities.
What is Hypha Core?
Hypha Core is a client-side JavaScript library that creates a complete Hypha server environment directly in the browser. It enables you to:
- Run Hypha Apps and ImJoy Plugins without requiring a dedicated server
- Manage workspaces with isolated execution environments
- Handle RPC communication between services and plugins
- Orchestrate services with automatic discovery and registration
- Support multiple connection types including WebSocket and Redis-like connections
- Manage authentication with token-based access control
Architecture Overview
Core Components
- HyphaCore Server: Main orchestrator that manages connections, workspaces, and message routing
- Workspace Manager: Handles service registration, discovery, and workspace isolation
- Connection Management: Supports multiple connection types (WebSocket, PostMessage, Redis RPC)
- Service Registry: Automatic registration and discovery of services across workspaces
- Authentication System: Token-based authentication with support for both anonymous and authenticated users
How It Works
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Browser Tab │ │ Hypha Core │ │ Workspace │
│ │ │ Server │ │ Manager │
│ ┌───────────┐ │ │ │ │ │
│ │ Plugin A │ │◄──►│ ┌───────────┐ │◄──►│ ┌───────────┐ │
│ └───────────┘ │ │ │Connection │ │ │ │Service │ │
│ ┌───────────┐ │ │ │ Manager │ │ │ │Registry │ │
│ │ Plugin B │ │◄──►│ └───────────┘ │ │ └───────────┘ │
│ └───────────┘ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘✅ Quality Assurance & Testing
Hypha Core maintains exceptional quality through comprehensive testing:
🛡️ Enhanced Security Features Tested
- JWT HS256 Authentication with signature verification and expiration handling
- Cross-Workspace Access Control with token-based permission enforcement
- Anonymous User Security with automatic workspace assignment and access restrictions
- Service Registration Security - Only root users can register services in default/public workspaces
- Workspace Isolation with proper service visibility and permission management
- Multi-Client Authentication Workflows demonstrating provider/consumer/restricted user patterns
🚀 Advanced Integration Features
- Custom Web Worker Script Loading - Direct loading of worker scripts via HTTP, blob, and file URLs
- Full Hypha RPC Integration - Custom workers with complete service registration and communication
- Performance Optimized Workers - CPU-intensive computations in dedicated worker threads
- Multiple URL Support - HTTP/HTTPS, blob URLs, and local file loading for maximum flexibility
🌐 Cross-Browser Compatibility Verified
- Chromium ✅ - All 117 tests passing
- Firefox ✅ - All 117 tests passing
- WebKit ✅ - All 117 tests passing
⚡ Performance Verified
- Unit tests complete in ~200ms ⚡
- Full integration test suite in ~35 seconds 🔄
- Real browser testing with actual WebSocket connections
- JWT token generation and verification tested in all browsers
🔒 Security Tests Mirror Deno Example
The integration tests now include comprehensive permission and security validation similar to the TypeScript Deno example:
- JWT Token Generation with proper access control
- Workspace Access Control for cross-workspace token generation
- Service Registration Security ensuring only authorized users can register in protected workspaces
- Multi-Client Authentication Workflows with proper token validation
- Error Handling for unauthorized access attempts
- Service Listing with workspace isolation verification
🛠 Development Quality
- ES6 module compatibility verified
- Modern JavaScript features tested
- Error handling and resilience validated
- UI responsiveness across screen sizes
- Network interruption recovery tested
Installation & Basic Usage
CDN Import (Recommended)
<!DOCTYPE html>
<html>
<head>
<title>My Hypha App</title>
</head>
<body>
<script type="module">
import { HyphaCore } from "https://cdn.jsdelivr.net/npm/[email protected]/dist/hypha-core.mjs";
// Create and start Hypha Core
const hyphaCore = new HyphaCore();
// Expose globally for external access (important!)
window.hyphaCore = hyphaCore;
// Start and wait for API to be ready
const api = await hyphaCore.start();
console.log("Hypha Core started successfully!");
console.log("API available:", api);
</script>
</body>
</html>NPM Installation
npm install hypha-coreimport { HyphaCore } from 'hypha-core';
const hyphaCore = new HyphaCore();
window.hyphaCore = hyphaCore; // Expose globally if needed
const api = await hyphaCore.start();🌐 Deno/Node.js Compatibility
HyphaCore now supports server environments (Deno and Node.js) with automatic environment detection and graceful feature degradation.
📚 TypeScript Support 🎯
HyphaCore provides comprehensive TypeScript definitions with full type safety:
- ✅ Complete Type Definitions:
index.d.tswith all HyphaCore APIs typed - ✅ Deno/Node.js/Browser Support: Environment-aware type definitions
- ✅ JWT Authentication Types:
JWTPayload,TokenConfig,UserInfo - ✅ Service Management Types:
ServiceConfig,ServiceQuery,ServiceOptions - ✅ API Types:
HyphaAPI,HyphaCoreConfig, workspace management - ✅ ES Module Compatible: Works seamlessly with modern TypeScript projects
TypeScript Usage Example
import { HyphaCore, type HyphaCoreConfig, type TokenConfig } from 'hypha-core';
const config: HyphaCoreConfig = {
port: 9527,
jwtSecret: 'your-secure-secret',
baseUrl: 'http://localhost:9527/'
};
const hyphaCore = new HyphaCore(config);
const api = await hyphaCore.start();
// Type-safe token generation
const tokenConfig: TokenConfig = {
user_id: 'typescript-user',
workspace: 'typed-workspace',
expires_in: 3600
};
const token: string = await api.generateToken(tokenConfig);See examples/deno-example.ts for a complete TypeScript implementation demonstrating all features.
Deno WebSocket Server Implementation 🦕
HyphaCore now supports real WebSocket connections in Deno through the DenoWebSocketServer wrapper, enabling production-grade server deployments with full compatibility with hypha-rpc clients.
Key Features
- ✅ Real WebSocket Connections: Native Deno HTTP server with WebSocket upgrade
- ✅ Full hypha-rpc Compatibility: Python and JavaScript clients can connect seamlessly
- ✅ Production Ready: Proper error handling, graceful shutdown, and health endpoints
- ✅ Built-in Services: Default services like
echo,hello, andget_timework out of the box - ✅ Service Registration: Register services as
:built-infor system-level access - ✅ Authentication Flow: Complete JWT-based authentication with reconnection tokens
Quick Start
# Clone the repository
git clone https://github.com/amun-ai/hypha-core
cd hypha-core
# Run the Deno server example
deno run --allow-net --allow-env examples/deno-server-example.jsServer Implementation
#!/usr/bin/env -S deno run --allow-net --allow-env
import { HyphaCore } from '../src/hypha-core.js';
import { DenoWebSocketServer, DenoWebSocketClient } from '../src/deno-websocket-server.js';
const hyphaCore = new HyphaCore({
url: "http://localhost:9527",
ServerClass: DenoWebSocketServer, // Use real WebSocket server
WebSocketClass: DenoWebSocketClient, // Use real WebSocket client
jwtSecret: "deno-hypha-secret-key",
defaultService: {
// Services with context for authentication and workspace info
hello: (name, context) => {
name = name || "World";
const greeting = `Hello, ${name}! Greetings from Deno Hypha Server 🦕`;
console.log(`Hello service called: ${greeting}`, context ? `from ${context.from}` : '');
return greeting;
},
get_time: (context) => {
const now = new Date().toISOString();
console.log(`Time service called: ${now}`, context ? `from ${context.from}` : '');
return now;
}
}
});
// Start the server with proper connection handling
const api = await hyphaCore.start();
console.log(`🚀 Hypha Core server started at ${hyphaCore.url}`);
console.log(`🔌 WebSocket URL: ${hyphaCore.wsUrl}`);Client Connection Examples
Python Client (hypha-rpc)
from hypha_rpc import connect_to_server
# Connect to the Deno server
server = await connect_to_server("ws://localhost:9527/ws")
# Use built-in services
result = await server.hello("Python Client")
print(result) # "Hello, Python Client! Greetings from Deno Hypha Server 🦕"
time = await server.get_time()
print(f"Server time: {time}")
# Get server info
info = await server.get_server_info()
print(f"Running on: {info['platform']} {info['version']}")JavaScript Client
import { hyphaWebsocketClient } from 'hypha-rpc';
const server = await hyphaWebsocketClient.connectToServer({
server_url: "ws://localhost:9527/ws"
});
const greeting = await server.hello("JavaScript Client");
console.log(greeting);
const serverInfo = await server.get_server_info();
console.log("Server info:", serverInfo);DenoWebSocketServer Features
Real WebSocket Upgrade
- Uses Deno's native HTTP server with WebSocket upgrade
- Proper
Upgrade: websocketheader handling - Binary and text message support with automatic ArrayBuffer conversion
Health Monitoring
# Check server health
curl http://localhost:9527/health
# Returns: OKGraceful Shutdown
// Handles SIGINT and SIGTERM for clean shutdown
Deno.addSignalListener("SIGINT", () => {
console.log('🛑 Shutting down server...');
hyphaCore.close();
Deno.exit(0);
});Client Connection Management
- Automatic client tracking and cleanup
- Proper error handling for connection failures
- Support for multiple concurrent connections
Service Registration Security
The Deno server properly handles service registration with workspace security:
// Built-in services are registered with :built-in suffix
// This bypasses workspace security for system services
await hyphaCore.workspaceManager.setup({
client_id: hyphaCore.workspaceManagerId,
defaultService: {
// These become accessible as server.hello(), server.get_time(), etc.
hello: (name, context) => `Hello, ${name || "World"}!`,
get_time: (context) => new Date().toISOString(),
get_server_info: (context) => ({
platform: "Deno",
version: Deno.version.deno,
server: "hypha-core-deno"
})
}
});Production Deployment
Docker Container
FROM denoland/deno:1.40.0
WORKDIR /app
COPY . .
EXPOSE 9527
CMD ["run", "--allow-net", "--allow-env", "examples/deno-server-example.js"]Performance & Compatibility
Tested Compatibility
- ✅ Python hypha-rpc clients - Full compatibility
- ✅ JavaScript hypha-rpc clients - Complete feature support
- ✅ Browser WebSocket clients - Direct WebSocket connections
- ✅ Node.js clients - Cross-platform compatibility
Performance Characteristics
- Concurrent Connections: Supports multiple simultaneous clients
- Message Throughput: High-performance binary and text message handling
- Memory Efficiency: Automatic cleanup of disconnected clients
- Error Recovery: Robust error handling without server crashes
Complete Example Files
examples/deno-server-example.js- Complete server implementationexamples/deno-client-example.js- Client connection exampleexamples/test-deno-server.py- Python client testexamples/DENO_WEBSOCKET_SERVER.md- Detailed documentation
This implementation provides a complete bridge between Deno's native capabilities and the hypha-core ecosystem, enabling deployment of production-grade WebSocket servers with full compatibility with existing hypha-rpc clients.
Deno/Node.js Compatibility 🦕
HyphaCore provides cross-platform compatibility for server environments with environment-aware feature degradation:
Supported Features in Server Environments
- ✅ JWT Authentication: Full HS256 token generation and verification
- ✅ Service Registration: Register and discover services across workspaces
- ✅ RPC Communication: Real-time service-to-service communication
- ✅ Workspace Management: Multi-tenant workspace isolation
- ✅ Multi-Client Connections: Support multiple concurrent clients
- ✅ Anonymous User Security: Automatic workspace assignment with access control
- ✅ PostMessage (Deno only): Web API compatibility for message passing
- ✅ Event Listeners (Deno only): Web API compatibility for event handling
Browser-Only Features
Features that require DOM/Window APIs throw clear errors in server environments:
- ❌ Window/iframe creation:
Environment.requireBrowser()throws error - ❌ WebWorker integration: Browser-specific worker management
- ❌ DOM manipulation: Document/element operations
- ❌ PostMessage (Node.js only): Not available without polyfills
🦕 Deno Usage
import { HyphaCore } from "https://cdn.jsdelivr.net/npm/[email protected]/dist/hypha-core.mjs";
const hyphaCore = new HyphaCore({
port: 9527,
jwtSecret: 'your-secure-secret-key',
baseUrl: 'http://localhost:9527/', // Explicit base URL for server
});
// Start server
const api = await hyphaCore.start();
console.log('🚀 HyphaCore server running on Deno!');
// Generate JWT tokens
const token = await api.generateToken({
user_id: 'deno-user',
workspace: 'compute-workspace',
expires_in: 3600
});
// Register computational services
await api.registerService({
id: 'math-service',
name: 'Math Service',
config: { require_context: true, visibility: 'public' },
fibonacci: (n, context) => {
console.log(`Computing fibonacci(${n}) from ${context.from}`);
if (n <= 1) return n;
let a = 0, b = 1;
for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
return b;
}
});🟢 Node.js Usage
import { HyphaCore } from 'hypha-core';
const hyphaCore = new HyphaCore({
port: 9527,
jwtSecret: process.env.HYPHA_JWT_SECRET,
baseUrl: 'http://localhost:9527/',
});
// Environment detection
console.log(`Running in: ${hyphaCore.environment}`); // 'node'
const api = await hyphaCore.start();
console.log('🚀 HyphaCore server running on Node.js!');
// Connect clients with JWT authentication
const clientApi = await hyphaCore.connect({
token: await api.generateToken({ user_id: 'node-client' }),
workspace: 'data-processing'
});🛡️ Environment-Safe Code Examples
The library automatically detects the environment and provides helpful error messages:
import { HyphaCore } from 'hypha-core';
const hyphaCore = new HyphaCore();
const api = await hyphaCore.start();
try {
// This will work in all environments
await api.registerService({
id: 'data-processor',
process: (data) => data.map(x => x * 2)
});
// This will throw clear error in server environments
await api.createWindow({ src: 'https://example.com' });
} catch (error) {
if (error.message.includes('requires browser environment')) {
console.log('🔍 Browser-only feature attempted in server environment');
console.log('💡 Use only core HyphaCore features in Deno/Node.js');
}
}📚 Complete Server Example
See examples/deno-example.js for a full working example demonstrating:
- 🔐 JWT authentication with secure token generation
- ⚡ Service registration and cross-service communication
- 🏗️ Workspace management and client connections
- 🧮 Computational services (prime number checking, fibonacci)
- 📊 Environment detection and feature availability
Configuration Options
Constructor Options
const hyphaCore = new HyphaCore({
port: 8080, // Server port (default: 8080)
baseUrl: "https://myapp.com/", // Base URL for serving template files (must end with /)
url: "wss://myserver.com/ws", // Direct WebSocket URL (alternative to port)
defaultService: { // Default services to register
myService: async () => { /* implementation */ }
}
});Important Notes:
baseUrlmust end with a forward slash (/)- Cannot specify both
urlandport- choose one - If using
url, it must end with/ws
Start Options
const api = await hyphaCore.start({
workspace: "my-workspace", // Workspace identifier (default: "default")
client_id: "my-client", // Client identifier (default: auto-generated)
server: hyphaCore // Reference to the server instance
});🚀 Cluster Setup & Horizontal Scaling
Hypha Core supports clustered deployments for horizontal scalability, high availability, and production workloads. The cluster mode enables multiple server instances to work together, sharing workspaces and services through Redis coordination.
Quick Start
Option 1: Mock Redis Cluster (Development)
Perfect for development and testing without external dependencies:
# Clone the repository
git clone https://github.com/amun-ai/hypha-core
cd hypha-core/cluster-examples
# Start mock Redis cluster (3 servers: 8080, 8081, 8082)
deno run --allow-all cluster-example.jsFeatures:
- ✅ 3 server instances with simulated clustering behavior
- ✅ No external Redis required - uses built-in mock
- ✅ Local development friendly
- ✅ Perfect for testing load balancing logic
Option 2: Real Redis Cluster (Production)
Production-ready clustering with real Redis coordination:
# 1. Start Redis container
docker run -d --name hypha-redis -p 6379:6379 redis:7-alpine
# 2. Start real Redis cluster
deno run --allow-all cluster-example.js --real-redisFeatures:
- ✅ Real Redis pub/sub messaging for true distributed coordination
- ✅ Horizontal scalability across multiple machines
- ✅ Production performance and reliability
- ✅ Fault tolerance and automatic failover
Option 3: Docker Deployment (Recommended for Production)
Full containerized deployment with load balancer:
# Clone and navigate to cluster examples
git clone https://github.com/amun-ai/hypha-core
cd hypha-core/cluster-examples
# Start full containerized cluster
docker compose up -d
# Check cluster status
docker compose ps
# View logs
docker compose logs -f
# Stop cluster
docker compose downComponents:
- ✅ Redis server for coordination
- ✅ 3 clustered Hypha-Core servers (auto-scaling ready)
- ✅ Nginx load balancer with health checks
- ✅ Health monitoring and automatic recovery
Architecture Overview
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Load Balancer │ │ Server 1 │ │ Server 2 │
│ (Nginx:80) │ │ (Port 8080) │ │ (Port 8081) │
│ │ │ │ │ │
│ Health Checks │◄──►│ Hypha Core │◄──►│ Hypha Core │
│ Round Robin │ │ WebSocket │ │ WebSocket │
│ Failover │ │ HTTP API │ │ HTTP API │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ ▼ ▼
│ ┌─────────────────┐ ┌─────────────────┐
│ │ Server 3 │ │ Redis │
│ │ (Port 8082) │ │ (Port 6379) │
│ │ │ │ │
└─────────────►│ Hypha Core │◄──►│ Pub/Sub │
│ WebSocket │ │ Coordination │
│ HTTP API │ │ Shared State │
└─────────────────┘ └─────────────────┘Testing Your Cluster
Load Balancing Test
# Test cluster load distribution
cd hypha-core/cluster-examples
deno run --allow-all test-full-cluster.jsPerformance Benchmarks
# Run comprehensive performance tests
deno run --allow-all performance-test.jsSample results (3-server cluster):
- Throughput: 8,000-11,000 req/s per server
- Latency: <1ms average response time
- Memory: ~60MB per server instance
- Coordination overhead: <5ms for Redis operations
Configuration
Server Ports (Default)
- Server 1:
8080- Primary instance - Server 2:
8081- Secondary instance - Server 3:
8082- Tertiary instance - Redis:
6379- Coordination layer - Load Balancer:
80- Entry point (Docker only)
Environment Variables
# Redis connection
REDIS_URL=redis://localhost:6379
# Server identification
SERVER_ID=server-1
# Cluster mode
CLUSTER_MODE=real # or 'mock' for developmentCustom Configuration
// cluster-config.js
export const clusterConfig = {
redis: {
url: process.env.REDIS_URL || 'redis://localhost:6379',
channel: 'hypha-cluster',
connectionTimeout: 5000
},
servers: [
{ id: 'server-1', port: 8080, weight: 1 },
{ id: 'server-2', port: 8081, weight: 1 },
{ id: 'server-3', port: 8082, weight: 1 }
],
loadBalancer: {
strategy: 'round-robin', // 'round-robin', 'least-connections', 'weighted'
healthCheck: {
interval: 10000,
timeout: 3000,
retries: 3
}
}
};API Endpoints
All cluster servers expose the same API endpoints:
Health Check
# Check individual server health
curl http://localhost:8080/health
curl http://localhost:8081/health
curl http://localhost:8082/health
# Through load balancer (Docker)
curl http://localhost/healthServices API
# List services in workspace (returns array of service objects)
curl http://localhost:8080/default/services
# Returns: [{"id": "service1", "name": "My Service", ...}, ...]
# Get specific service info
curl http://localhost:8080/default/services/my-service
# Returns: {"id": "my-service", "name": "My Service", "config": {...}}
# Register service (distributed automatically)
curl -X POST http://localhost:8080/default/services \
-H "Content-Type: application/json" \
-d '{"name": "my-service", "config": {...}}'
# Call service function via HTTP
curl http://localhost:8080/default/services/my-service/my-function \
-H "Content-Type: application/json" \
-d '{"param1": "value1"}'Important: The /default/services endpoint returns a list of services in the workspace, not the workspace API object itself.
WebSocket Connections
// Connect to any server in the cluster
const ws1 = new WebSocket('ws://localhost:8080/ws');
const ws2 = new WebSocket('ws://localhost:8081/ws');
const ws3 = new WebSocket('ws://localhost:8082/ws');
// Or through load balancer
const ws = new WebSocket('ws://localhost/ws');Production Deployment
Docker Compose (Recommended)
# docker-compose.yml
version: '3.8'
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
command: redis-server --appendonly yes
volumes:
- redis-data:/data
hypha-server-1:
build: .
environment:
- SERVER_ID=server-1
- REDIS_URL=redis://redis:6379
depends_on:
- redis
ports:
- "8080:8080"
hypha-server-2:
build: .
environment:
- SERVER_ID=server-2
- REDIS_URL=redis://redis:6379
depends_on:
- redis
ports:
- "8081:8080"
hypha-server-3:
build: .
environment:
- SERVER_ID=server-3
- REDIS_URL=redis://redis:6379
depends_on:
- redis
ports:
- "8082:8080"
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- hypha-server-1
- hypha-server-2
- hypha-server-3
volumes:
redis-data:Kubernetes Deployment
# k8s-cluster.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hypha-cluster
spec:
replicas: 3
selector:
matchLabels:
app: hypha-core
template:
metadata:
labels:
app: hypha-core
spec:
containers:
- name: hypha-core
image: hypha-core:latest
ports:
- containerPort: 8080
env:
- name: REDIS_URL
value: "redis://redis-service:6379"
- name: CLUSTER_MODE
value: "real"
---
apiVersion: v1
kind: Service
metadata:
name: hypha-service
spec:
selector:
app: hypha-core
ports:
- port: 80
targetPort: 8080
type: LoadBalancerTroubleshooting
Common Issues
Port Conflicts
# Stop existing containers docker stop hypha-redis docker compose down # Check port usage lsof -i :8080Redis Connection Issues
# Test Redis connectivity redis-cli -h localhost -p 6379 ping # Check Redis logs docker logs hypha-redisLoad Balancer Issues
# Check Nginx configuration docker exec nginx-container nginx -t # Reload configuration docker exec nginx-container nginx -s reload
Debug Mode
Enable comprehensive logging:
# Debug cluster coordination
RUST_LOG=debug deno run --allow-all cluster-example.js --real-redis
# Debug specific components
DEBUG=hypha:cluster,hypha:redis deno run --allow-all cluster-example.jsMonitoring
# Real-time cluster status
watch 'curl -s http://localhost:8080/health && curl -s http://localhost:8081/health'
# Redis monitoring
redis-cli monitor
# Docker cluster monitoring
docker statsPerformance Optimization
Scaling Guidelines
- Small workload: 1-2 servers sufficient
- Medium workload: 3-5 servers recommended
- Large workload: 5+ servers with dedicated Redis
- High availability: Minimum 3 servers across availability zones
Redis Optimization
# redis.conf optimizations for cluster
maxmemory 2gb
maxmemory-policy allkeys-lru
tcp-keepalive 60
timeout 0Load Balancer Tuning
# nginx.conf optimizations
upstream hypha_cluster {
least_conn;
server hypha-server-1:8080 weight=1 max_fails=3 fail_timeout=30s;
server hypha-server-2:8080 weight=1 max_fails=3 fail_timeout=30s;
server hypha-server-3:8080 weight=1 max_fails=3 fail_timeout=30s;
}
location / {
proxy_pass http://hypha_cluster;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}For complete examples and configuration files, see the cluster-examples/ directory.
Advanced Usage Examples
1. Complete Application with Window Management
<!DOCTYPE html>
<html>
<head>
<title>Hypha Lite Application</title>
<script src="https://rawcdn.githack.com/nextapps-de/winbox/0.2.82/dist/winbox.bundle.min.js"></script>
<style>
.icon-container {
position: fixed;
top: 10px;
right: 10px;
z-index: 1000;
}
.icon {
width: 40px;
height: 40px;
cursor: pointer;
}
.dropdown {
display: none;
position: absolute;
top: 50px;
width: 120px;
right: 0;
background-color: white;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
z-index: 1001;
font-family: Arial, sans-serif;
}
.dropdown a {
display: block;
padding: 10px;
text-decoration: none;
color: black;
}
.dropdown a:hover {
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div class="icon-container">
<img
alt="Hypha Logo"
src="https://raw.githubusercontent.com/amun-ai/hypha/main/docs/img/hypha-icon-black.svg"
class="icon"
onclick="toggleDropdown()"
/>
<div class="dropdown" id="dropdownMenu">
<a href="#" onclick="promptLoadApp()">+ Load Plugin</a>
<a href="#" onclick="loadApp('https://if.imjoy.io')">ImJoy Fiddle</a>
</div>
</div>
<script type="module">
import { HyphaCore } from "https://cdn.jsdelivr.net/npm/[email protected]/dist/hypha-core.mjs";
const hyphaCore = new HyphaCore();
// Expose hyphaCore globally for tests and external access
window.hyphaCore = hyphaCore;
// Handle window creation for plugins
hyphaCore.on("add_window", (config) => {
const wb = new WinBox(config.name || config.src.slice(0, 128), {
background: "#448aff",
});
wb.body.innerHTML = `<iframe src="${config.src}" id="${config.window_id}" style="width: 100%; height: 100%; border: none;"></iframe>`;
});
// Start Hypha Core and wait for API
await hyphaCore.start();
const api = hyphaCore.api;
// Function to handle loading and running a plugin
async function loadAppFromUrl(url) {
try {
const plugin = await api.loadApp({ src: url });
await plugin.run({ config: {}, data: {} });
console.log("Loaded and ran plugin from URL:", url);
} catch (error) {
console.error("Failed to load plugin:", error);
}
}
// Expose functions globally
window.loadApp = loadAppFromUrl;
// Handle URL parameters for auto-loading plugins
const urlParams = new URLSearchParams(window.location.search);
const pluginUrls = urlParams.getAll("plugin");
for (const url of pluginUrls) {
await window.loadApp(url);
}
// UI Functions
window.toggleDropdown = function () {
const dropdown = document.getElementById("dropdownMenu");
dropdown.style.display =
dropdown.style.display === "block" ? "none" : "block";
};
window.promptLoadApp = async function () {
const url = prompt("Enter the plugin URL:");
if (url) {
await loadAppFromUrl(url);
}
};
// Close dropdown when clicking outside
window.onclick = function (event) {
if (!event.target.matches(".icon")) {
const dropdown = document.getElementById("dropdownMenu");
if (dropdown.style.display === "block") {
dropdown.style.display = "none";
}
}
};
// Initialize dropdown display style
const dropdown = document.getElementById("dropdownMenu");
if (dropdown) {
dropdown.style.display = "none";
}
</script>
</body>
</html>2. Multiple Workspace Management
Hypha Core supports multiple isolated workspaces for security and organization. Each workspace operates independently with its own service registry and access controls.
// Create and start the core server
const hyphaCore = new HyphaCore();
await hyphaCore.start();
// Connect to different workspaces
const workspace1 = await hyphaCore.connect({
workspace: "analysis-workspace",
client_id: "analysis-client"
});
const workspace2 = await hyphaCore.connect({
workspace: "visualization-workspace",
client_id: "viz-client"
});
// Each workspace operates independently
await workspace1.registerService({
name: "data-processor",
config: {
require_context: true,
visibility: "public",
},
process: async (data, context) => {
// context.ws === "analysis-workspace"
console.log(`Processing data in workspace: ${context.ws}`);
return data.map(x => x * 2);
}
});
await workspace2.registerService({
name: "chart-renderer",
config: {
require_context: true,
visibility: "public",
},
render: async (data, context) => {
// context.ws === "visualization-workspace"
console.log(`Rendering chart in workspace: ${context.ws}`);
return { chart: "rendered", workspace: context.ws };
}
});3. Python Integration - Creating Interactive Windows
Hypha Core enables seamless integration between Python (via Pyodide or server-side Python with hypha-rpc) and browser-based JavaScript applications. This example shows how to create an interactive todo list application where Python code controls a JavaScript UI running in a browser window.
# Python code (can run in Pyodide or server-side with hypha-rpc)
import asyncio
from hypha_rpc import connect_to_server
# Connect to Hypha Core server
server = await connect_to_server({
"server_url": "http://localhost:9527",
"workspace": "default",
"client_id": "python-controller"
})
# Define the HTML/JavaScript application as a string
src = '''
<!DOCTYPE html>
<html>
<head>
<title>Todo List</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
h1 {
color: #333;
}
#todo-list {
list-style-type: none;
padding: 0;
}
#todo-list li {
padding: 10px;
margin: 5px 0;
background: #f4f4f4;
border-radius: 4px;
}
</style>
<script type="module">
import * as hyphaWebsocketClient from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/hypha-rpc-websocket.mjs';
// Setup local client to communicate with Hypha Core
const server = await hyphaWebsocketClient.setupLocalClient();
// Export functions that Python can call
await server.export({
// Get all todos from the list
getTodos: () => {
const listItems = document.querySelectorAll('#todo-list li');
return Array.from(listItems).map(li => li.textContent);
},
// Add a new todo to the list
addTodo: (todoText) => {
const list = document.getElementById('todo-list');
const li = document.createElement('li');
li.textContent = todoText;
list.appendChild(li);
return `Added: ${todoText}`;
},
// Clear all todos
clearTodos: () => {
const list = document.getElementById('todo-list');
list.innerHTML = '';
return 'Cleared all todos';
}
});
console.log('Todo app ready - services exported');
</script>
</head>
<body>
<h1>📝 My Todo List</h1>
<ul id="todo-list">
<li>Learn Hypha RPC</li>
<li>Build awesome apps</li>
</ul>
</body>
</html>
'''
# Create a window with the HTML content
# This returns a service proxy that can call the exported JavaScript functions
todo_app = await server.create_window(src=src, name='My Todo List')
# Now we can control the JavaScript application from Python!
# Add new todos
result = await todo_app.addTodo('Buy groceries')
print(result) # Output: Added: Buy groceries
result = await todo_app.addTodo('Call dentist')
print(result) # Output: Added: Call dentist
# Get all todos
todos = await todo_app.getTodos()
print('Current todos:', todos)
# Output: Current todos: ['Learn Hypha RPC', 'Build awesome apps', 'Buy groceries', 'Call dentist']
# Clear the list
result = await todo_app.clearTodos()
print(result) # Output: Cleared all todosKey Features Demonstrated:
- Bidirectional RPC: Python can call JavaScript functions, and JavaScript can call Python functions
- HTML String Loading: The entire application is defined as a string and loaded directly
- Service Export: JavaScript exports an API that Python can invoke
- Type Safety: Return values are automatically serialized/deserialized
- Async/Await: Seamless async communication between Python and JavaScript
Use Cases:
- Building data visualization dashboards controlled from Python
- Creating interactive UI for Python data science workflows
- Automating browser-based applications from Python scripts
- Building Jupyter notebook-like experiences with custom UIs
Server-Side Python Example:
# Run this on a Python server that can reach your Hypha Core instance
from hypha_rpc import connect_to_server
import asyncio
async def main():
# Connect to remote Hypha Core server
server = await connect_to_server({
"server_url": "https://your-hypha-server.com",
"workspace": "my-workspace",
"client_id": "python-backend",
"token": "your-auth-token" # If authentication is enabled
})
# Create window and interact with it
app = await server.create_window(src=html_content, name='Data Dashboard')
# Update dashboard with computed data
data = compute_heavy_analysis()
await app.updateChart(data)
print("Dashboard updated successfully!")
asyncio.run(main())Iframe and WebWorker Integration
Hypha Core supports loading and communicating with applications in iframes and web workers. This enables you to create distributed applications where different components run in isolated environments while still communicating through the Hypha RPC system.
Two Approaches for Integration
1. Hypha App Format (ImJoy Plugin Style)
You can create applications using the ImJoy plugin format with embedded configuration and code:
<docs lang="markdown">
# My Hypha App
This is a sample Hypha application that runs in an iframe.
</docs>
<config lang="json">
{
"name": "My Hypha App",
"type": "iframe",
"version": "0.1.0",
"description": "A sample application running in an iframe",
"tags": [],
"ui": "",
"cover": "",
"inputs": null,
"outputs": null,
"flags": [],
"icon": "extension",
"api_version": "0.1.7",
"env": "",
"permissions": [],
"requirements": [],
"dependencies": []
}
</config>
<script lang="javascript">
// This code runs in the iframe
api.export({
name: "My App Service",
async setup() {
await api.log("App initialized in iframe");
},
async processData(data) {
// Process data and return results
return data.map(x => x * 2);
},
async showMessage(message) {
alert(`Message from parent: ${message}`);
return "Message displayed";
}
});
</script>Load and use the app:
// Load app from URL (e.g., GitHub raw URL)
const app = await api.loadApp({
src: "https://raw.githubusercontent.com/myuser/myrepo/main/my-app.imjoy.html"
});
// Run and interact with the app
await app.run();
const result = await app.processData([1, 2, 3, 4]);
console.log(result); // [2, 4, 6, 8]2. Standalone Web Application
You can also create standalone web applications using any framework (React, Vue, vanilla JavaScript, etc.) and connect them to Hypha Core using the WebSocket client.
Standalone App Example (my-standalone-app.html):
<!DOCTYPE html>
<html>
<head>
<title>My Standalone App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 600px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
button:hover {
background: #0056b3;
}
.status {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
background: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/hypha-rpc-websocket.min.js"></script>
</head>
<body>
<div class="container">
<h1>My Standalone Hypha App</h1>
<div class="status" id="status">Initializing...</div>
<button onclick="performCalculation()">Perform Calculation</button>
<button onclick="sendNotification()">Send Notification</button>
<button onclick="getSystemInfo()">Get System Info</button>
<div id="output"></div>
</div>
<script>
let api = null;
// Connect to Hypha Core
hyphaWebsocketClient.setupLocalClient({enable_execution: true}).then(async (hyphaApi) => {
api = hyphaApi;
console.log("Connected to Hypha Core", api);
// Export services that the parent can call
await api.export({
name: "Standalone App Services",
async processData(data) {
console.log("Processing data:", data);
const result = data.map(x => x * x); // Square the numbers
updateOutput(`Processed data: ${JSON.stringify(result)}`);
return result;
},
async updateUI(config) {
console.log("Updating UI:", config);
if (config.title) {
document.querySelector('h1').textContent = config.title;
}
if (config.message) {
updateOutput(`UI Update: ${config.message}`);
}
return "UI updated successfully";
},
async getAppState() {
return {
title: document.querySelector('h1').textContent,
timestamp: new Date().toISOString(),
status: "running"
};
}
});
document.getElementById('status').textContent = 'Connected to Hypha Core ✓';
}).catch(error => {
console.error("Failed to connect to Hypha Core:", error);
document.getElementById('status').textContent = `Connection failed: ${error.message}`;
document.getElementById('status').style.background = '#f8d7da';
document.getElementById('status').style.borderColor = '#f5c6cb';
document.getElementById('status').style.color = '#721c24';
});
// Functions called by the UI
async function performCalculation() {
if (!api) return;
try {
// Call a service from the parent Hypha Core
const numbers = [1, 2, 3, 4, 5];
const result = await api.echo(`Calculation request: ${numbers.join(', ')}`);
updateOutput(`Echo result: ${result}`);
} catch (error) {
updateOutput(`Error: ${error.message}`);
}
}
async function sendNotification() {
if (!api) return;
try {
await api.log("Notification sent from standalone app");
updateOutput("Notification sent to parent");
} catch (error) {
updateOutput(`Error: ${error.message}`);
}
}
async function getSystemInfo() {
if (!api) return;
try {
// Try to get server info if available
const info = {
userAgent: navigator.userAgent,
timestamp: new Date().toISOString(),
url: window.location.href
};
updateOutput(`System Info: ${JSON.stringify(info, null, 2)}`);
} catch (error) {
updateOutput(`Error: ${error.message}`);
}
}
function updateOutput(message) {
const output = document.getElementById('output');
output.innerHTML += `<div style="margin: 10px 0; padding: 8px; background: #e9ecef; border-radius: 4px;">${message}</div>`;
output.scrollTop = output.scrollHeight;
}
</script>
</body>
</html>Using the Standalone App:
// Create a window with your standalone app
const appWindow = await api.createWindow({
src: "/path/to/my-standalone-app.html", // or full URL
name: "My Standalone App",
pos: "main" // or "side"
});
// Wait a moment for the app to initialize
await new Promise(resolve => setTimeout(resolve, 1000));
// Interact with the app's exported services
const result = await appWindow.processData([1, 2, 3, 4, 5]);
console.log("App result:", result); // [1, 4, 9, 16, 25]
await appWindow.updateUI({
title: "Updated App Title",
message: "Hello from parent!"
});
const appState = await appWindow.getAppState();
console.log("App state:", appState);WebWorker Support
Hypha Core supports two approaches for web worker integration:
1. Traditional ImJoy Plugin Format
Load workers using the ImJoy plugin format with embedded configuration:
// Load worker with ImJoy plugin format (.imjoy.html files)
const worker = await api.loadApp({
src: "https://example.com/my-plugin.imjoy.html"
});2. Custom Web Worker Scripts (New!)
🚀 Direct Custom Worker Script Loading
You can now load custom web worker scripts directly using any URL (HTTP, HTTPS, blob, or file URLs):
// Load custom worker script directly
const customWorker = await api.loadApp({
type: 'web-worker', // Specify worker type
src: 'https://example.com/my-custom-worker.js', // Direct script URL
name: 'My Custom Worker',
description: 'Custom computational worker'
});
// Works with different URL types:
// HTTP/HTTPS URLs
const httpWorker = await api.loadApp({
type: 'web-worker',
src: 'https://cdn.example.com/workers/math-worker.js'
});
// Blob URLs (for dynamically generated scripts)
const blob = new Blob([workerCode], { type: 'application/javascript' });
const blobUrl = URL.createObjectURL(blob);
const blobWorker = await api.loadApp({
type: 'web-worker',
src: blobUrl
});
// Local file URLs
const fileWorker = await api.loadApp({
type: 'web-worker',
src: '/static/workers/data-processor.js'
});Custom Worker Script Structure
Complete Custom Worker Example (computational-worker.js):
// Import Hypha RPC client in the worker
importScripts('https://cdn.jsdelivr.net/npm/[email protected]/dist/hypha-rpc-websocket.min.js');
console.log('🔧 Custom Computational Worker: Starting...');
// Connect to Hypha Core from the worker
hyphaWebsocketClient.setupLocalClient({
enable_execution: true,
workspace: "worker-workspace",
client_id: "computational-worker-001"
}).then(async (api) => {
console.log('✅ Worker connected to Hypha Core');
// Export comprehensive worker services
const exportedServices = await api.export({
id: 'computational-services',
name: 'Computational Services',
description: 'CPU-intensive computations optimized for WebWorker environment',
// Mathematical computations
fibonacci: function(n) {
console.log(`🔢 Worker: Computing fibonacci(${n})`);
if (n <= 1) return n;
let a = 0, b = 1;
for (let i = 2; i <= n; i++) {
[a, b] = [b, a + b];
}
return b;
},
factorial: function(n) {
console.log(`🔢 Worker: Computing factorial(${n})`);
if (n <= 1) return 1;
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
},
// Prime number operations
isPrime: function(n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 === 0 || n % 3 === 0) return false;
for (let i = 5; i * i <= n; i += 6) {
if (n % i === 0 || n % (i + 2) === 0) return false;
}
return true;
},
// Array processing operations
processArray: function(arr, operation) {
const operations = {
sum: () => arr.reduce((a, b) => a + b, 0),
product: () => arr.reduce((a, b) => a * b, 1),
average: () => arr.reduce((a, b) => a + b, 0) / arr.length,
max: () => Math.max(...arr),
min: () => Math.min(...arr),
sort: () => [...arr].sort((a, b) => a - b),
reverse: () => [...arr].reverse()
};
if (!operations[operation]) {
throw new Error(`Unknown operation: ${operation}`);
}
return operations[operation]();
},
// Heavy computation simulation
heavyComputation: function(iterations = 1000000) {
console.log(`⚡ Worker: Running heavy computation with ${iterations} iterations`);
const startTime = Date.now();
let result = 0;
for (let i = 0; i < iterations; i++) {
result += Math.sin(i) * Math.cos(i) * Math.tan(i / 1000);
}
const endTime = Date.now();
const duration = endTime - startTime;
return {
result: result,
iterations: iterations,
duration: duration,
performance: `${iterations / duration} ops/ms`
};
},
// Worker info and capabilities
getWorkerInfo: function() {
return {
type: 'webworker',
environment: 'dedicated-worker',
timestamp: new Date().toISOString(),
capabilities: [
'fibonacci', 'factorial', 'isPrime',
'processArray', 'heavyComputation'
],
userAgent: navigator.userAgent,
hardwareConcurrency: navigator.hardwareConcurrency
};
}
});
console.log('✅ Custom Worker: All services registered successfully');
// Notify main thread that worker is ready
self.postMessage({
type: 'worker_ready',
message: 'Custom worker services registered successfully',
services: Object.keys(exportedServices).filter(key => typeof exportedServices[key] === 'function')
});
}).catch(error => {
console.error('❌ Custom Worker: Failed to setup Hypha client:', error);
self.postMessage({
type: 'worker_error',
error: error.message
});
});
// Handle messages from main thread
self.onmessage = function(event) {
const { type, data } = event.data;
switch (type) {
case 'ping':
self.postMessage({
type: 'pong',
message: 'Custom worker is operational',
timestamp: new Date().toISOString()
});
break;
case 'shutdown':
console.log('🛑 Custom Worker: Shutdown requested');
self.postMessage({ type: 'shutdown_acknowledged' });
self.close();
break;
default:
console.warn('⚠️ Custom Worker: Unknown message type:', type);
}
};
// Error handling
self.onerror = function(error) {
console.error('💥 Custom Worker: Unhandled error:', error);
self.postMessage({
type: 'worker_error',
error: error.message
});
};
console.log('🚀 Custom Worker: Initialization complete');Loading and Using Custom Workers
// Load the custom computational worker
const computeWorker = await api.loadApp({
type: 'web-worker',
src: '/workers/computational-worker.js',
name: 'Computational Worker',
description: 'High-performance mathematical computations'
});
console.log('Worker loaded:', computeWorker.id);
// Use computational services
const fibResult = await computeWorker.fibonacci(20);
console.log('Fibonacci(20):', fibResult); // 6765
const factResult = await computeWorker.factorial(5);
console.log('Factorial(5):', factResult); // 120
const primeCheck = await computeWorker.isPrime(97);
console.log('Is 97 prime?:', primeCheck); // true
const arraySum = await computeWorker.processArray([1, 2, 3, 4, 5], 'sum');
console.log('Array sum:', arraySum); // 15
// Heavy computation in background
const heavyResult = await computeWorker.heavyComputation(500000);
console.log('Heavy computation result:', heavyResult);
// { result: 1234.567, iterations: 500000, duration: 89, performance: "5617 ops/ms" }
// Get worker capabilities
const workerInfo = await computeWorker.getWorkerInfo();
console.log('Worker info:', workerInfo);Key Features of Custom Worker Scripts
✅ Direct Script Loading
- Support for HTTP/HTTPS URLs, blob URLs, and file URLs
- Bypasses ImJoy plugin parsing for faster loading
- Full control over worker implementation
✅ Full Hypha RPC Integration
- Complete access to Hypha RPC WebSocket client
- Service registration with
api.export() - Context-aware service calls with workspace isolation
✅ Performance Optimized
- Dedicated worker threads for CPU-intensive tasks
- Non-blocking main thread execution
- Efficient memory management
✅ Production Ready
- Error handling and graceful degradation
- Worker lifecycle management
- Comprehensive logging and debugging
Custom vs Traditional Workers
| Feature | Custom Worker Scripts | Traditional ImJoy Plugins | |---------|----------------------|---------------------------| | Loading | Direct URL loading | Plugin code parsing required | | Performance | Faster initialization | Additional parsing overhead | | Flexibility | Full control over implementation | ImJoy plugin format constraints | | Integration | Full Hypha RPC access | Full Hypha RPC access | | Use Cases | Custom algorithms, existing workers | ImJoy ecosystem plugins |
Complete Working Example
See public/test-worker.js for a complete example featuring:
- ✅ Full Hypha RPC WebSocket integration
- ✅ Mathematical computation services (fibonacci, factorial, prime checking)
- ✅ Array processing operations with multiple algorithms
- ✅ Performance benchmarking capabilities
- ✅ Matrix operations and text processing
- ✅ Worker status monitoring and error handling
- ✅ Production-ready architecture patterns
This example demonstrates how to create sophisticated custom workers that integrate seamlessly with the Hypha Core ecosystem while providing high-performance computational capabilities.
Key Points for Integration
Connection Setup
All standalone apps and workers must include this connection code:
hyphaWebsocketClient.setupLocalClient({enable_execution: true}).then(async (api) => {
// Your app code here
await api.export({
// Your exported services
});
}).catch(console.error);Important Notes
Script Loading: Always load the Hypha RPC WebSocket client:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/hypha-rpc-websocket.min.js"></script>Enable Execution: Use
{enable_execution: true}when setting up the local client to allow service exports.Error Handling: Always include proper error handling for connection failures.
Service Export: Use
await api.export({...})to make your app's functions available to the parent.Async/Await: Most Hypha API calls are asynchronous, so use
async/awaitor Promises.Viewport Meta Tag: For mobile compatibility, include the viewport meta tag in your HTML.
This integration system allows you to create complex, distributed applications where different components can run in isolation while maintaining seamless communication through the Hypha RPC system.
Mounting Existing Workers and Iframes
Hypha Core provides simple, direct APIs for mounting existing web workers and iframes without needing the ImJoy plugin format. This is perfect for integrating notebook environments (like Pyodide), custom workers, or existing web applications.
mountWorker(worker, config) - Mount a Web Worker
Mount an existing Web Worker instance to Hypha Core with automatic RPC setup and bidirectional communication.
Basic Usage
import { HyphaCore } from 'hypha-core';
// Start Hypha Core
const server = new HyphaCore({ port: 9527 });
await server.start();
// Create a web worker
const worker = new Worker('my-worker.js');
// Mount it to Hypha Core
await server.mountWorker(worker, {
workspace: 'default', // Optional: defaults to 'default'
client_id: 'my-worker', // Optional: auto-generated if not provided
timeout: 60000 // Optional: connection timeout (default: 60s)
});
// Worker is now connected and ready for RPC communication!Pyodide/Python Worker Example
// Mount a Pyodide worker with Python code to execute
const worker = new Worker('pyodide-worker.js');
await server.mountWorker(worker, {
workspace: 'default',
client_id: 'python-notebook',
config: {
name: 'My Notebook',
scripts: [{
lang: 'python',
content: `
print("Hello from Python!")
# Access Hypha services from Python
services = await api.list_services({})
print(f"Found {len(services)} services")
# Get a JavaScript service and call it
demo = await api.get_service("demo-service")
result = await demo.greet("Python")
print(f"Response: {result}")
`
}]
}
});Worker Script Structure
Your worker script should use hypha-rpc to connect:
// my-worker.js
importScripts("https://cdn.jsdelivr.net/npm/[email protected]/dist/hypha-rpc-websocket.min.js");
// Start Pyodide or other environment
loadPyodide().then(async (pyodide) => {
// Install hypha-rpc
await pyodide.loadPackage("micropip");
const micropip = pyodide.pyimport("micropip");
await micropip.install('hypha-rpc==0.20.84');
// Signal ready to receive initialization
self.postMessage({ type: "hyphaClientReady" });
// Set up the local client (will block until initializeHyphaClient arrives)
await pyodide.runPythonAsync(`
from hypha_rpc import setup_local_client
async def execute(server, config):
# Your Python code execution logic
print('Executing:', config["name"])
for script in config["scripts"]:
exec(script["content"], {'api': server})
server = await setup_local_client(enable_execution=False, on_ready=execute)
`);
});mountIframe(iframe, config) - Mount an Iframe
Mount an existing iframe element to Hypha Core for bidirectional RPC communication.
Basic Usage
// Create an iframe element
const iframe = document.createElement('iframe');
iframe.src = 'my-app.html';
document.body.appendChild(iframe);
// Wait for iframe to load
await new Promise(resolve => iframe.onload = resolve);
// Mount it to Hypha Core
await server.mountIframe(iframe, {
workspace: 'default',
client_id: 'my-iframe-app'
});
// Iframe is now connected!Example: Mounting an Existing Div with Iframe
// Mount an iframe inside an existing div
const container = document.getElementById('app-container');
// Create iframe programmatically
const iframe = document.createElement('iframe');
iframe.src = '/apps/data-viewer.html';
iframe.style.width = '100%';
iframe.style.height = '600px';
container.appendChild(iframe);
// Mount to Hypha Core
const result = await server.mountIframe(iframe, {
workspace: 'visualization',
client_id: 'data-viewer'
});
console.log('Iframe mounted:', result.client_id);
// Get the iframe's service and interact with it
const viewerService = await server.api.getService('data-viewer:default');
await viewerService.loadData({ dataset: 'my-data.csv' });Configuration Options
Both mountWorker and mountIframe accept these configuration options:
{
workspace: 'workspace-name', // Workspace to connect to (default: 'default')
client_id: 'unique-client-id', // Client identifier (default: auto-generated)
user_info: { // User information (default: anonymous)
id: 'user-123',
email: '[email protected]',
roles: ['user'],
scopes: []
},
config: {}, // Configuration passed to the worker/iframe
timeout: 60000, // Connection timeout in ms (default: 60000)
passive: false // Skip waiting for service registration (default: false)
}Passive Mode
When passive: true, the mount methods return immediately after sending the initialization message without waiting for the worker/iframe to register a service. This is useful when:
- You don't need to interact with the worker/iframe immediately
- The worker/iframe may take a long time to initialize
- You want to mount multiple workers/iframes in parallel without blocking
// Mount worker in passive mode
const result = await server.mountWorker(worker, {
workspace: 'default',
passive: true // Returns immediately, service will be null
});
console.log(result.service); // null - no service yet
// Later, get the service when needed
const service = await server.api.getService(`${result.client_id}:default`);Return Value
Both methods return a result object:
{
workspace: 'workspace-name',
client_id: 'client-id',
connection: { /* connection object */ },
service: { /* registered service object */ } // null if passive: true
}Note: When passive: true, the service field will be null since the method returns without waiting for service registration.
Complete Working Example
See examples/pyodide-adhoc-client.html for a complete example demonstrating:
- ✅ Mounting a Pyodide worker to run Python code
- ✅ Registering JavaScript services callable from Python
- ✅ Bidirectional RPC communication
- ✅ Real-time output display
- ✅ Error handling
Example Output:
Python/Pyodide Worker Started
==================================================
Found 2 services:
- default/root:default: Default workspace management service
- default/root:demo-service: JavaScript Demo Service
Calling demo.greet('Python')...
Response: Hello Python from JavaScript!
Calling demo.add(42, 58)...
Result: 100
All RPC calls successful!
==================================================Key Features
✅ Simple API
- No ImJoy plugin format required
- Mount any existing worker or iframe
- Automatic RPC connection setup
✅ Flexible Integration
- Works with Pyodide, custom workers, React apps, etc.
- Pass configuration during mounting
- Full access to Hypha services
✅ Bidirectional Communication
- Workers/iframes can call Hypha services
- Hypha can call worker/iframe services
- Automatic message forwarding
✅ Production Ready
- Timeout handling
- Error detection
- Connection state management
- Proper cleanup
Comparison: Mount vs LoadApp
| Feature | mountWorker/mountIframe | loadApp |
|---------|---------------------------|-----------|
| Input | Existing Worker/Iframe instance | URL to load |
| Format | Any format | ImJoy plugin format |
