Connor McCutcheon
/ SkyCode
CLAUDE.md
md
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**SkyCode** is a multi-tenant browser-based code editor built with Skykit. It provides a VS Code-like experience with Monaco Editor for code editing and xterm.js for an integrated PTY terminal with real-time command execution via WebSocket.
## Development Commands
```bash
# Run locally
go run .
# Build
go build -o skycode .
# Run with Docker
docker build -t skycode .
docker run -p 5000:5000 skycode
```
## Architecture
### Project Structure
```
skycode/
├── main.go                 # Application entry point, public file serving
├── controllers/
│   ├── code.go             # File CRUD, git operations, editor UI
│   ├── terminal.go         # SSE command execution (legacy)
│   ├── ws.go               # WebSocket PTY terminal (primary)
│   └── admin.go            # Admin user management
├── models/
│   ├── database.go         # Database connection
│   ├── file.go             # File model and operations
│   ├── project.go          # Project model (cloned repos, workspaces)
│   └── session.go          # Workspace session management
└── views/
    ├── home.html           # Landing page (Vercel-inspired design)
    ├── editor.html         # Monaco + xterm.js editor (loads external scripts)
    ├── admin.html          # Admin dashboard
    └── public/
        ├── scripts/        # Frontend JavaScript modules
        │   ├── main.js     # Global state, settings, Monaco init
        │   ├── terminal.js # PTY terminal multiplexing, resize
        │   ├── files.js    # File operations, tabs, file tree
        │   ├── git.js      # Git operations, project management
        │   └── search.js   # Code search functionality
        └── styles/         # CSS stylesheets
            └── home.css    # Homepage styles (grid bg, animations)
```
### Core Concepts
| Concept | Location | Purpose |
|---------|----------|---------|
| **File** | Database | Source of truth for all user files |
| **Project** | Database | Tracks cloned repos with RemoteURL for restoration |
| **Session** | In-memory | Ephemeral workspace, 30-min timeout |
| **Workspace** | Filesystem | `/tmp/skycode/{sessionID}/workspace/` |
| **Tool Directory** | Filesystem | `/tmp/skycode/users/{userID}/` (persists tool installs) |
### Route Handlers
**Editor & Files:**
| Route | Method | Handler | Purpose |
|-------|--------|---------|---------|
| `/` | GET | home.html | Landing page |
| `/editor` | GET | editor.html | Code editor (requires auth) |
| `/api/files` | GET | listFiles | List user's files |
| `/api/files/open` | GET | openFile | Get file content |
| `/api/files/save` | POST | saveFile | Save file content |
| `/api/files/rename` | POST | renameFile | Rename a file |
| `/api/files` | DELETE | deleteFile | Delete a file |
| `/api/projects` | GET | listProjects | List user's projects |
**Git Operations:**
| Route | Method | Handler | Purpose |
|-------|--------|---------|---------|
| `/api/workspace/clone` | POST | cloneRepo | Clone a git repository |
| `/api/git/status` | GET | gitStatus | Get git status for project |
| `/api/git/stage` | POST | gitStage | Stage files |
| `/api/git/commit` | POST | gitCommit | Create commit |
| `/api/git/push` | POST | gitPush | Push to remote |
| `/api/git/pull` | POST | gitPull | Pull from remote |
| `/api/git/checkout` | POST | gitCheckout | Checkout branch |
| `/clone` | GET | handleClone | Public clone endpoint (OAuth flow) |
**Terminal:**
| Route | Method | Handler | Purpose |
|-------|--------|---------|---------|
| `/api/terminal` | WebSocket | handleTerminalWS | PTY terminal (primary) |
| `/api/terminals` | GET | listTerminals | List active terminals |
| `/api/terminals` | POST | createTerminal | Create new terminal |
| `/api/terminals/{id}` | DELETE | deleteTerminal | Close terminal |
| `/api/exec` | POST | execCommand | SSE command execution (legacy) |
### Models
**File** - User's code files stored in database:
```go
type File struct {
    skykit.Model
    OwnerID  string  // User ID
    Path     string  // e.g., "project/main.go" or "src/utils.go"
    Content  string
    Language string  // Detected from extension
}
```
**Project** - Tracks cloned repositories and workspaces:
```go
type Project struct {
    skykit.Model
    OwnerID   string  // User ID
    Name      string  // Display name
    Path      string  // Directory name in workspace
    RemoteURL string  // Git remote URL (for re-cloning on restore)
    IsDefault bool    // If true, this is the default project
}
```
**Session** - Ephemeral workspace for terminal execution:
```go
type Session struct {
    ID          string
    UserID      string
    WorkDir     string    // /tmp/skycode/{sessionID}/workspace
    CreatedAt   time.Time
    LastUsed    time.Time
    initialized int32     // atomic flag
}
```
### Workspace Architecture
**Data Flow:**
```
Database (source of truth)
    ↓ MaterializeFiles()
Workspace (ephemeral filesystem)
    ↓ Terminal/Git operations
Workspace modifications
    ↓ SyncWorkspaceToDB()
Database (updated)
```
**Session Lifecycle:**
1. User accesses editor → Session created with unique ID (cookie)
2. `MaterializeFiles()` called:
   - Projects with `RemoteURL` are re-cloned (restores `.git` directory)
   - Files from DB overlaid (local modifications preserved)
3. Terminal commands execute in workspace
4. File saves update both DB and workspace
5. Session expires after 30 minutes of inactivity → workspace cleaned up
**Why Re-clone?**
The `.git` directory is not stored in the database (too large, binary data). When a session expires and restores, we re-clone repos with `RemoteURL` to restore full git functionality, then overlay any local file modifications from the database.
### Terminal Architecture
**PTY Terminal (Primary)** - `controllers/ws.go`:
- WebSocket connection at `/ws/terminal`
- True PTY via `github.com/creack/pty`
- Full shell experience (bash, interactive commands)
- Multiple terminals per session (max 5)
**SSE Terminal (Legacy)** - `controllers/terminal.go`:
- POST to `/api/exec`
- Server-Sent Events streaming
- Single command execution with timeout
- Kept for API compatibility
### Environment Configuration
Both terminals configure identical environments:
```go
cmd.Env = append(os.Environ(),
    "HOME="+workDir,           // Workspace is home (~ works correctly)
    "PATH="+userPath,          // Includes user tool directories
    "NPM_CONFIG_PREFIX="+...,  // npm global installs persist
    "GOPATH="+...,             // Go installs persist
    "CARGO_HOME="+...,         // Rust installs persist
)
```
**Directory Structure:**
```
/tmp/skycode/
├── {sessionID}/
│   └── workspace/         # User's workspace (HOME)
│       ├── project1/      # Cloned repo
│       └── project2/      # Another project
├── users/
│   └── {userID}/          # Tool directory (persists across sessions)
│       ├── .local/bin/
│       ├── .npm-global/
│       ├── go/
│       └── .cargo/
└── cache/                 # Shared cache
    ├── npm/
    └── go/mod/
```
## Frontend Components
### JavaScript Modules
Scripts are served from `/public/scripts/` (embedded in binary):
| File | Purpose |
|------|---------|
| `main.js` | Global state, user settings, Monaco editor initialization |
| `terminal.js` | Terminal multiplexing, resize handling, show/hide toggle |
| `files.js` | File tree, tabs, open/save/rename/delete operations |
| `git.js` | Git status, staging, commit, push, pull, clone modal |
| `search.js` | Code search with regex support, result navigation |
### CSS Stylesheets
Styles are served from `/public/styles/` (embedded in binary):
| File | Purpose |
|------|---------|
| `home.css` | Homepage styles: grid background pattern, gradient borders, terminal mockup, feature card hover effects, animations (pulse, blink cursor) |
### Monaco Editor
- Version: `monaco-editor@0.52.0` (CDN)
- Theme: Synced with DaisyUI theme (light/dark detection)
- Features: Syntax highlighting, multi-file tabs, Ctrl+S save
- Language detection from file extension
### xterm.js Terminal
- Version: `xterm@5.3.0` (CDN)
- Addons: fit (auto-resize)
- WebSocket connection to PTY backend
- Multiple terminals per session (tabs)
- Resizable panel with drag handle
- Keyboard shortcut: Ctrl+` to toggle
### UI Components (DaisyUI 5)
- File tree with project grouping
- Context menus for file operations
- Git sidebar with status, staging, commit
- Clone modal for adding repositories
- Settings modal for theme, font size, tab size, word wrap, minimap
## 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 Architecture
### Authentication
- All editor/API routes require authentication via Skykit
- Session cookies for workspace association
- Files isolated per user (`OwnerID` filter on all queries)
### Workspace Isolation
- Each user gets isolated workspace directory
- Sessions are user-bound (validated on access)
- Path traversal prevented via `safePath()` validation
### Git Security
- Git URLs validated: `isValidGitURL()` checks protocol and shell safety
- Git refs validated: `isValidGitRef()` prevents command injection
- Only HTTPS, HTTP, and git:// protocols allowed
### Terminal Security
- Commands run in isolated workspace
- 60-second timeout prevents runaway processes
- PTY sessions limited to 5 per user
- **Future:** Add sandboxing (nsjail/firejail) for production
### Rate Limiting
- Authentication handled by Skykit with built-in rate limiting
- Terminal sessions limited per user
## Git Operations
### Clone Flow
1. Validate URL (protocol, shell safety)
2. Create project record with `RemoteURL`
3. Run `git clone --depth 1 --branch main --single-branch` (shallow clone)
   - Falls back to default branch if `main` doesn't exist
4. Configure git user (email, name)
5. Sync files to database
6. UI selects new project
### Session Restore Flow
1. Get projects with `RemoteURL`
2. Re-clone each with `--branch main --single-branch` to restore `.git`
3. Overlay files from database (preserves local modifications)
4. Git operations work normally (pull/push/commit)
## Docker Configuration
The Dockerfile includes development tools:
- Go 1.24
- Python 3
- Node.js/npm
- git, curl, make, vim
## Best Practices (Inspired by code-server)
1. **Never expose without auth** - Terminal access = machine access
2. **Use WebSocket for terminals** - Real-time, bidirectional
3. **Persist tool installs** - Separate from ephemeral workspace
4. **Database as source of truth** - Filesystem is ephemeral
5. **Re-clone for git** - Don't store `.git` in database
6. **Validate all git inputs** - Prevent command injection
7. **Timeout all operations** - Prevent resource exhaustion
No comments yet.