# 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