Connor McCutcheon
/ SkyLab
CLAUDE.md
md
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**SkyLab** is a browser-based interactive Python notebook environment built with Skykit. It provides a Jupyter-like experience where users can create, edit, execute, and share Python notebooks. Similar to how SkyCode wraps Monaco Editor, SkyLab wraps IPython execution with a notebook UI.
## Development Commands
```bash
# Run locally (requires Python 3.11+)
go run .
# Build
go build -o skylab .
# Run with Docker
docker build -t skylab .
docker run -p 5000:5000 skylab
# Run tests
go test ./...
```
## Architecture
### Project Structure
```
skylab/
├── main.go                 # Application entry point
├── controllers/
│   ├── notebooks.go        # Notebook CRUD, cell operations, execution
│   ├── kernel.go           # WebSocket kernel communication
│   └── gallery.go          # Public notebook gallery
├── models/
│   ├── database.go         # Database connection (LibSQL)
│   ├── notebook.go         # Notebook model and .ipynb parsing
│   ├── kernel.go           # IPython kernel process management
│   └── session.go          # User session/workspace management
└── views/
    ├── home.html           # Public gallery landing page
    ├── editor.html         # Notebook editor
    ├── view.html           # Read-only notebook view
    └── public/
        └── scripts/
            └── editor.js   # Frontend notebook editor logic
```
### Core Concepts
| Concept | Location | Purpose |
|---------|----------|---------|
| **Notebook** | Database | IPython notebooks stored as JSON |
| **Cell** | Notebook JSON | Code or markdown cell with outputs |
| **Kernel** | In-memory | Per-user Python process for execution |
| **Session** | In-memory | Ephemeral workspace for kernel execution |
| **Gallery** | Public | Published notebooks with author attribution |
### Route Handlers
**Editor & API:**
| Route | Method | Handler | Purpose |
|-------|--------|---------|---------|
| `/` | GET | home.html | Public gallery |
| `/editor` | GET | editor.html | Notebook editor (auth required) |
| `/editor/{id}` | GET | editor.html | Edit specific notebook |
| `/notebook/{id}` | GET | view.html | View published notebook |
| `/api/notebooks` | GET | listNotebooks | List user's notebooks |
| `/api/notebooks` | POST | createNotebook | Create new notebook |
| `/api/notebooks/{id}` | GET | getNotebook | Get notebook content |
| `/api/notebooks/{id}` | PUT | updateNotebook | Update notebook |
| `/api/notebooks/{id}` | DELETE | deleteNotebook | Delete notebook |
**Cell Operations:**
| Route | Method | Handler | Purpose |
|-------|--------|---------|---------|
| `/api/notebooks/{id}/cells` | POST | addCell | Add new cell |
| `/api/notebooks/{id}/cells/{cellId}` | PUT | updateCell | Update cell content |
| `/api/notebooks/{id}/cells/{cellId}` | DELETE | deleteCell | Delete cell |
| `/api/notebooks/{id}/cells/{cellId}/execute` | POST | executeCell | Run cell in kernel |
**Kernel:**
| Route | Method | Handler | Purpose |
|-------|--------|---------|---------|
| `/api/kernel` | WebSocket | handleKernelWS | Real-time kernel communication |
| `/api/kernel/interrupt` | POST | interruptKernel | Send interrupt signal |
| `/api/kernel/restart` | POST | restartKernel | Restart kernel |
| `/api/kernel/status` | GET | kernelStatus | Get kernel state |
**Publishing:**
| Route | Method | Handler | Purpose |
|-------|--------|---------|---------|
| `/api/notebooks/{id}/publish` | POST | publishNotebook | Make public |
| `/api/notebooks/{id}/unpublish` | POST | unpublishNotebook | Make private |
| `/api/gallery` | GET | listPublished | List public notebooks |
| `/api/gallery/{id}` | GET | viewNotebook | View public notebook |
| `/api/gallery/{id}/fork` | POST | forkNotebook | Copy to user's notebooks |
### Models
**Notebook** - IPython notebook stored in database:
```go
type Notebook struct {
    skykit.Model
    OwnerID     string  // User ID
    OwnerName   string  // Cached for gallery display
    OwnerHandle string
    OwnerAvatar string
    Title       string
    Description string
    Content     string  // Full .ipynb JSON
    IsPublished bool
    PublishedAt time.Time
    ViewCount   int
}
```
**NotebookContent** - Parsed .ipynb structure:
```go
type NotebookContent struct {
    Metadata      NotebookMetadata
    NBFormat      int
    NBFormatMinor int
    Cells         []Cell
}
```
**Cell** - Individual cell in notebook:
```go
type Cell struct {
    ID             string
    CellType       string      // "code" or "markdown"
    Source         []string    // Lines of code
    Outputs        []Output    // Execution outputs
    ExecutionCount *int
}
```
**Kernel** - Per-user Python process:
```go
type Kernel struct {
    ID       string
    UserID   string
    WorkDir  string
    State    KernelState  // Idle, Busy, Dead
    Process  *exec.Cmd
    Stdin    io.WriteCloser
    Stdout   io.ReadCloser
}
```
### Kernel Architecture
**Execution Flow:**
```
Frontend (editor.js)
    ↓ POST /api/notebooks/{id}/cells/{cellId}/execute
Controller (notebooks.go)
    ↓ GetOrCreateKernel()
Kernel (kernel.go)
    ↓ JSON over stdin/stdout
kernel_wrapper.py
    ↓ exec() / eval()
Python execution
    ↑ JSON results
Controller
    ↑ Update cell outputs
Frontend
```
**Kernel Wrapper** - A Python script that:
1. Reads JSON commands from stdin
2. Executes Python code with stdout/stderr capture
3. Writes JSON results to stdout
4. Handles both expressions (eval) and statements (exec)
**Per-User Isolation:**
- Each user gets one kernel process
- Kernels are started on first execution
- Idle kernels are cleaned up after 30 minutes
- Kernel state is "idle", "busy", or "dead"
### Frontend Architecture
**editor.js** handles:
- Notebook list management
- Cell rendering with CodeMirror
- Cell operations (add, delete, move, execute)
- WebSocket connection for kernel status
- Auto-save on changes
**Libraries:**
- CodeMirror 5 - Code editing with Python/Markdown modes
- Marked - Markdown rendering
- DaisyUI 5 - UI components
## Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `PORT` | No | Server port (default: 5000) |
| `DB_NAME` | For replica | Database name |
| `DB_URL` | For replica | LibSQL primary URL |
| `DB_TOKEN` | For replica | Database JWT token |
## Security Considerations
### Authentication
- All editor/API routes require authentication via Skykit OAuth
- Public gallery routes are read-only
- Notebooks are isolated per user (OwnerID filter)
### Kernel Security
- Each user has isolated kernel process
- Execution happens in per-session workspace
- 60-second timeout on cell execution
- Kernels cleaned up after 30 min idle
### Input Validation
- Notebook content size limited (10MB)
- Cell source validated before execution
- Path traversal prevented in workspaces
## Key Patterns
1. **Notebooks stored as JSON** - Full .ipynb format in database
2. **Per-user kernels** - One Python process per user for isolation
3. **Cached author info** - Owner name/handle/avatar cached on notebook for gallery display
4. **Fork on copy** - Published notebooks can be forked (copied with cleared outputs)
5. **Auto-save** - Changes saved after 1 second of inactivity
## Docker Requirements
The Dockerfile includes:
- Python 3.11 with common data science packages
- numpy, pandas, matplotlib, scipy, scikit-learn
- Non-root user for security
No comments yet.