CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-jupyterhub

A multi-user server for Jupyter notebooks that provides authentication, spawning, and proxying for multiple users simultaneously

Pending
Overview
Eval results
Files

singleuser-integration.mddocs/

Single-user Server Integration

JupyterHub provides comprehensive tools and mixins for creating Hub-authenticated single-user servers and Jupyter server extensions. This system enables notebook servers to authenticate with the Hub and integrate seamlessly with the multi-user environment.

Capabilities

Single-user Application Factory

Core function for creating Hub-authenticated single-user server applications.

def make_singleuser_app(cls):
    """
    Create a single-user application class with Hub authentication.
    
    Decorates a Jupyter server application class to add JupyterHub
    authentication and integration capabilities.
    
    Args:
        cls: Jupyter server application class to enhance
        
    Returns:
        Enhanced application class with Hub authentication
    """

# Example usage
from jupyter_server.serverapp import ServerApp
from jupyterhub.singleuser import make_singleuser_app

@make_singleuser_app
class SingleUserNotebookApp(ServerApp):
    """Hub-authenticated notebook server"""
    pass

Hub Authentication Handlers

Base classes for implementing Hub-authenticated request handlers in single-user servers.

class HubAuthenticatedHandler(BaseHandler):
    """
    Base handler for Hub-authenticated requests in single-user servers.
    
    Provides authentication integration with JupyterHub and handles
    user verification and permission checking.
    """
    
    @property
    def hub_auth(self):
        """Hub authentication helper instance"""
    
    @property
    def hub_user(self):
        """Current Hub user information"""
    
    @property
    def current_user(self):
        """Current authenticated user (compatible with Jupyter handlers)"""
    
    def get_current_user(self):
        """
        Get current user from Hub authentication.
        
        Returns:
            User information dictionary or None if not authenticated
        """
    
    def hub_user_from_cookie(self, cookie_name, cookie_value):
        """
        Get user information from Hub cookie.
        
        Args:
            cookie_name: Name of the authentication cookie
            cookie_value: Cookie value
            
        Returns:
            User information or None if invalid
        """
    
    def check_hub_user(self, model):
        """
        Check if current user matches the Hub user for this server.
        
        Args:
            model: User model to verify
            
        Returns:
            True if user is authorized
        """

class HubOAuthHandler(HubAuthenticatedHandler):
    """
    OAuth-based Hub authentication handler for single-user servers.
    
    Handles OAuth token validation and user authorization.
    """
    
    def get_current_user_oauth(self, handler):
        """
        Get current user via OAuth token validation.
        
        Args:
            handler: Request handler instance
            
        Returns:
            User information from OAuth validation
        """

Jupyter Server Extension

Extension system for integrating JupyterHub with Jupyter server.

def _jupyter_server_extension_points() -> List[Dict[str, str]]:
    """
    Jupyter server extension discovery function.
    
    Makes the JupyterHub single-user extension discoverable
    by the Jupyter server extension system.
    
    Returns:
        List of extension metadata dictionaries with module and app info
    """

class JupyterHubSingleUser(ExtensionApp):
    """
    Jupyter server extension for JupyterHub single-user integration.
    
    Provides the extension implementation for Hub-authenticated
    single-user servers.
    """
    
    name: str = 'jupyterhub-singleuser'
    description: str = 'JupyterHub single-user server extension'
    
    def initialize_settings(self):
        """Initialize extension settings"""
    
    def initialize_handlers(self):
        """Initialize request handlers"""
    
    def initialize_templates(self):
        """Initialize Jinja2 templates"""

Single-user Server Configuration

Configuration and startup utilities for single-user servers.

class SingleUserNotebookApp(JupyterApp):
    """
    Single-user notebook server application.
    
    Main application class for JupyterHub single-user servers
    with Hub authentication and integration.
    """
    
    # Hub connection configuration
    hub_host: str  # JupyterHub host
    hub_prefix: str  # Hub URL prefix  
    hub_api_url: str  # Hub API URL
    
    # Authentication configuration
    user: str  # Username for this server
    group: str  # User group
    cookie_name: str  # Hub authentication cookie name
    
    # Server configuration
    base_url: str  # Server base URL
    default_url: str  # Default redirect URL
    
    # OAuth configuration (optional)
    oauth_client_id: str  # OAuth client ID
    oauth_redirect_uri: str  # OAuth redirect URI
    
    def initialize(self, argv=None):
        """
        Initialize single-user server.
        
        Args:
            argv: Command line arguments
        """
    
    def start(self):
        """Start the single-user server"""
    
    def make_singleuser_app(self):
        """Create single-user application with Hub integration"""

def main(argv=None) -> int:
    """
    Main entry point for jupyterhub-singleuser command.
    
    Args:
        argv: Command line arguments
        
    Returns:
        Exit code (0 for success)
    """

Hub API Integration

Tools for single-user servers to communicate with the JupyterHub API.

class HubAPIClient:
    """
    API client for single-user servers to communicate with Hub.
    
    Provides methods for servers to report status and interact
    with the central Hub.
    """
    
    def __init__(self, hub_api_url: str, api_token: str):
        """
        Initialize Hub API client.
        
        Args:
            hub_api_url: JupyterHub API base URL
            api_token: API token for authentication
        """
    
    async def get_user(self, username: str) -> Dict[str, Any]:
        """
        Get user information from Hub.
        
        Args:
            username: Username to look up
            
        Returns:
            User information dictionary
        """
    
    async def update_server_activity(self):
        """Update server activity timestamp in Hub"""
    
    async def report_server_status(self, status: str):
        """
        Report server status to Hub.
        
        Args:
            status: Server status ('running', 'ready', 'error', etc.)
        """
    
    async def get_server_info(self, username: str, server_name: str = '') -> Dict[str, Any]:
        """
        Get server information from Hub.
        
        Args:
            username: Server owner username
            server_name: Named server (empty for default)
            
        Returns:
            Server information dictionary
        """

Usage Examples

Basic Single-user Server Setup

# Create Hub-authenticated server application
from jupyter_server.serverapp import ServerApp
from jupyterhub.singleuser import make_singleuser_app

@make_singleuser_app  
class MyHubServer(ServerApp):
    """Custom Hub-authenticated server"""
    
    # Custom configuration
    custom_setting = Unicode(
        'default_value',
        config=True,
        help="Custom server setting"
    )
    
    def initialize_settings(self):
        """Initialize custom settings"""
        super().initialize_settings()
        
        # Add custom settings to web app
        self.web_app.settings.update({
            'custom_setting': self.custom_setting
        })

# Start the server
if __name__ == '__main__':
    MyHubServer.launch_instance()

Custom Authentication Handler

from jupyterhub.singleuser.mixins import HubAuthenticatedHandler
from tornado import web

class CustomAPIHandler(HubAuthenticatedHandler):
    """Custom API endpoint with Hub authentication"""
    
    @web.authenticated
    async def get(self):
        """GET endpoint with user authentication"""
        user = self.current_user
        if not user:
            raise web.HTTPError(401, "Authentication required")
        
        # Verify user matches server owner
        if user['name'] != self.hub_user['name']:
            raise web.HTTPError(403, "Access denied")
        
        # Custom API logic
        data = await self.get_user_data(user)
        self.write({'data': data, 'user': user['name']})
    
    async def get_user_data(self, user):
        """Get data specific to authenticated user"""
        # Your custom data retrieval logic
        return {'files': [], 'settings': {}}

# Register handler in server application
class CustomHubServer(SingleUserNotebookApp):
    """Server with custom API endpoints"""
    
    def initialize_handlers(self):
        """Add custom handlers"""
        super().initialize_handlers()
        
        # Add custom API endpoint
        self.web_app.add_handlers('.*', [
            (r'/api/custom', CustomAPIHandler),
        ])

Hub API Integration

import os
from jupyterhub.singleuser import HubAPIClient

class IntegratedServer(SingleUserNotebookApp):
    """Server with Hub API integration"""
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        
        # Initialize Hub API client
        self.hub_client = HubAPIClient(
            hub_api_url=os.environ['JUPYTERHUB_API_URL'],
            api_token=os.environ['JUPYTERHUB_API_TOKEN']
        )
    
    async def initialize(self):
        """Initialize with Hub registration"""
        await super().initialize()
        
        # Report server startup to Hub
        await self.hub_client.report_server_status('starting')
        
        # Get user information from Hub
        user_info = await self.hub_client.get_user(self.user)
        self.log.info(f"Starting server for user: {user_info['name']}")
    
    def start(self):
        """Start server and report to Hub"""
        super().start()
        
        # Report ready status
        asyncio.create_task(self.hub_client.report_server_status('ready'))
    
    async def periodic_status_update(self):
        """Periodic status updates to Hub"""
        while True:
            try:
                await self.hub_client.update_server_activity()
                await asyncio.sleep(300)  # Update every 5 minutes
            except Exception as e:
                self.log.error(f"Failed to update Hub status: {e}")
                await asyncio.sleep(60)  # Retry after 1 minute

Extension Integration

from jupyterhub.singleuser.extension import JupyterHubSingleUser
from jupyter_server.extension.application import ExtensionApp

class CustomExtension(ExtensionApp):
    """Custom extension with Hub integration"""
    
    name = 'my-hub-extension'
    description = 'Custom JupyterHub extension'
    
    def initialize_settings(self):
        """Initialize extension settings"""
        # Get Hub authentication info
        hub_user = self.serverapp.web_app.settings.get('hub_user')
        if hub_user:
            self.log.info(f"Extension initialized for user: {hub_user['name']}")
        
        # Add custom settings
        self.serverapp.web_app.settings.update({
            'custom_extension_enabled': True
        })
    
    def initialize_handlers(self):
        """Initialize extension handlers"""
        from .handlers import CustomHandler
        
        self.handlers.extend([
            (r'/my-extension/api/.*', CustomHandler),
        ])

# Register extension
def _jupyter_server_extension_paths():
    """Extension discovery function"""
    return [{
        'module': 'my_extension',
        'app': CustomExtension
    }]

OAuth Integration

class OAuthIntegratedServer(SingleUserNotebookApp):
    """Single-user server with OAuth integration"""
    
    oauth_client_id = Unicode(
        config=True,
        help="OAuth client ID for Hub integration"
    )
    
    def initialize_settings(self):
        """Initialize OAuth settings"""
        super().initialize_settings()
        
        # Configure OAuth settings
        if self.oauth_client_id:
            self.web_app.settings.update({
                'oauth_client_id': self.oauth_client_id,
                'oauth_redirect_uri': f'{self.base_url}oauth-callback'
            })
    
    def initialize_handlers(self):
        """Add OAuth handlers"""
        super().initialize_handlers()
        
        # Add OAuth callback handler
        self.web_app.add_handlers('.*', [
            (r'/oauth-callback', OAuthCallbackHandler),
            (r'/oauth-login', OAuthLoginHandler),
        ])

class OAuthCallbackHandler(HubAuthenticatedHandler):
    """Handle OAuth callback from Hub"""
    
    async def get(self):
        """Process OAuth authorization callback"""
        code = self.get_argument('code', None)
        state = self.get_argument('state', None)
        
        if not code:
            raise web.HTTPError(400, "Missing authorization code")
        
        # Exchange code for token with Hub
        token_info = await self.exchange_oauth_code(code)
        
        # Validate user and redirect
        user = await self.validate_oauth_token(token_info['access_token'])
        self.redirect(self.get_argument('next', '/'))
    
    async def exchange_oauth_code(self, code):
        """Exchange OAuth code for access token"""
        # Implementation depends on Hub OAuth configuration
        pass

Advanced Integration Patterns

Multi-server User Environment

class MultiServerManager:
    """Manage multiple servers for a single user"""
    
    def __init__(self, hub_client, user_name):
        self.hub_client = hub_client
        self.user_name = user_name
        self.servers = {}
    
    async def get_user_servers(self):
        """Get all servers for user from Hub"""
        user_info = await self.hub_client.get_user(self.user_name)
        return user_info.get('servers', {})
    
    async def start_named_server(self, server_name, server_options=None):
        """Start a named server for user"""
        response = await self.hub_client.start_server(
            username=self.user_name,
            server_name=server_name,
            options=server_options or {}
        )
        return response
    
    async def stop_named_server(self, server_name):
        """Stop a named server"""
        await self.hub_client.stop_server(
            username=self.user_name,
            server_name=server_name
        )

Server Health Monitoring

class HealthMonitoredServer(SingleUserNotebookApp):
    """Server with health monitoring and auto-restart"""
    
    health_check_interval = Integer(
        60,
        config=True,
        help="Health check interval in seconds"
    )
    
    def start(self):
        """Start server with health monitoring"""
        super().start()
        
        # Start health monitoring task
        asyncio.create_task(self.health_monitor())
    
    async def health_monitor(self):
        """Monitor server health and report to Hub"""
        while True:
            try:
                # Check server health
                health_status = await self.check_health()
                
                # Report to Hub
                status = 'healthy' if health_status else 'unhealthy'
                await self.hub_client.report_server_status(status)
                
                # Auto-restart if unhealthy
                if not health_status:
                    self.log.warning("Server unhealthy, requesting restart")
                    await self.request_restart()
                
                await asyncio.sleep(self.health_check_interval)
            except Exception as e:
                self.log.error(f"Health monitoring error: {e}")
                await asyncio.sleep(60)
    
    async def check_health(self):
        """Check server health status"""
        try:
            # Custom health check logic
            return True
        except Exception:
            return False
    
    async def request_restart(self):
        """Request server restart from Hub"""
        await self.hub_client.report_server_status('restart_requested')

Shared Resource Access

class SharedResourceServer(SingleUserNotebookApp):
    """Server with shared resource access controls"""
    
    def initialize_settings(self):
        """Initialize with resource access controls"""
        super().initialize_settings()
        
        # Get user's resource permissions from Hub
        user_scopes = self.hub_user.get('scopes', [])
        
        # Configure resource access based on scopes
        self.web_app.settings.update({
            'shared_storage_access': 'shared-storage' in user_scopes,
            'gpu_access': 'gpu-resources' in user_scopes,
            'admin_access': 'admin' in user_scopes
        })
    
    def initialize_handlers(self):
        """Add resource access handlers"""
        super().initialize_handlers()
        
        # Add shared resource handlers
        self.web_app.add_handlers('.*', [
            (r'/shared-storage/.*', SharedStorageHandler),
            (r'/gpu-status', GPUStatusHandler),
        ])

class SharedStorageHandler(HubAuthenticatedHandler):
    """Handler for shared storage access"""
    
    @web.authenticated
    async def get(self, path):
        """Access shared storage with permission check"""
        if not self.settings.get('shared_storage_access'):
            raise web.HTTPError(403, "Shared storage access denied")
        
        # Shared storage access logic
        pass

Install with Tessl CLI

npx tessl i tessl/pypi-jupyterhub

docs

authentication.md

configuration-utilities.md

core-application.md

database-models.md

index.md

monitoring-metrics.md

rbac-permissions.md

rest-api.md

services-oauth.md

singleuser-integration.md

spawners.md

tile.json