docs: add comprehensive documentation with MkDocs site
- Add complete API documentation and architecture guides - Include quick start, installation, and deployment guides - Add troubleshooting and security documentation - Include CLI reference and configuration schema docs - Add production monitoring and operations guides - Implement MkDocs configuration with search functionality - Include comprehensive user and developer documentation Provides complete documentation for users and developers covering all aspects of the FetchML platform.
This commit is contained in:
parent
4aecd469a1
commit
385d2cf386
102 changed files with 60392 additions and 0 deletions
88
docs/_config.yml
Normal file
88
docs/_config.yml
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
# GitHub Pages configuration
|
||||||
|
|
||||||
|
# Site settings
|
||||||
|
title: "Fetch ML Documentation"
|
||||||
|
description: "Secure Machine Learning Platform"
|
||||||
|
baseurl: "/fetch_ml"
|
||||||
|
url: "https://fetch-ml.github.io"
|
||||||
|
|
||||||
|
# Build settings
|
||||||
|
markdown: kramdown
|
||||||
|
highlighter: rouge
|
||||||
|
theme: minima
|
||||||
|
plugins:
|
||||||
|
- jekyll-sitemap
|
||||||
|
- jekyll-feed
|
||||||
|
- jekyll-optional-front-matter
|
||||||
|
- jekyll-readme-index
|
||||||
|
- jekyll-titles-from-headings
|
||||||
|
- jekyll-seo-tag
|
||||||
|
|
||||||
|
# Versioning
|
||||||
|
version: "1.0.0"
|
||||||
|
versions:
|
||||||
|
- "1.0.0"
|
||||||
|
- "0.9.0"
|
||||||
|
latest_version: "1.0.0"
|
||||||
|
|
||||||
|
# Navigation
|
||||||
|
nav:
|
||||||
|
- title: "Getting Started"
|
||||||
|
subnav:
|
||||||
|
- title: "Quick Start"
|
||||||
|
url: "/quick-start/"
|
||||||
|
- title: "Guides"
|
||||||
|
subnav:
|
||||||
|
- title: "CLI Reference"
|
||||||
|
url: "/cli-reference/"
|
||||||
|
- title: "Architecture"
|
||||||
|
url: "/architecture/"
|
||||||
|
- title: "Server Setup"
|
||||||
|
url: "/server-setup/"
|
||||||
|
- title: "Development"
|
||||||
|
subnav:
|
||||||
|
- title: "Contributing"
|
||||||
|
url: "/contributing/"
|
||||||
|
- title: "API Reference"
|
||||||
|
url: "/api/"
|
||||||
|
|
||||||
|
# Collections
|
||||||
|
collections:
|
||||||
|
docs:
|
||||||
|
output: true
|
||||||
|
permalink: /:collection/:name/
|
||||||
|
api:
|
||||||
|
output: true
|
||||||
|
permalink: /api/:name/
|
||||||
|
|
||||||
|
# Exclude files from processing
|
||||||
|
exclude:
|
||||||
|
- Gemfile
|
||||||
|
- Gemfile.lock
|
||||||
|
- node_modules
|
||||||
|
- vendor
|
||||||
|
- .gitignore
|
||||||
|
- README.md
|
||||||
|
- Makefile
|
||||||
|
|
||||||
|
# Include files
|
||||||
|
include:
|
||||||
|
- _pages
|
||||||
|
|
||||||
|
# SEO
|
||||||
|
author: "Fetch ML Team"
|
||||||
|
twitter:
|
||||||
|
username: "fetch_ml"
|
||||||
|
card: "summary"
|
||||||
|
|
||||||
|
# Google Analytics (optional)
|
||||||
|
google_analytics: ""
|
||||||
|
|
||||||
|
# Mermaid diagrams for architecture
|
||||||
|
mermaid:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Code highlighting
|
||||||
|
kramdown:
|
||||||
|
input: GFM
|
||||||
|
syntax_highlighter: rouge
|
||||||
738
docs/_pages/architecture.md
Normal file
738
docs/_pages/architecture.md
Normal file
|
|
@ -0,0 +1,738 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Homelab Architecture"
|
||||||
|
permalink: /architecture/
|
||||||
|
nav_order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Homelab Architecture
|
||||||
|
|
||||||
|
Simple, secure architecture for ML experiments in your homelab.
|
||||||
|
|
||||||
|
## Components Overview
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Homelab Stack"
|
||||||
|
CLI[Zig CLI]
|
||||||
|
API[HTTPS API]
|
||||||
|
REDIS[Redis Cache]
|
||||||
|
FS[Local Storage]
|
||||||
|
end
|
||||||
|
|
||||||
|
CLI --> API
|
||||||
|
API --> REDIS
|
||||||
|
API --> FS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Services
|
||||||
|
|
||||||
|
### API Server
|
||||||
|
- **Purpose**: Secure HTTPS API for ML experiments
|
||||||
|
- **Port**: 9101 (HTTPS only)
|
||||||
|
- **Auth**: API key authentication
|
||||||
|
- **Security**: Rate limiting, IP whitelisting
|
||||||
|
|
||||||
|
### Redis
|
||||||
|
- **Purpose**: Caching and job queuing
|
||||||
|
- **Port**: 6379 (localhost only)
|
||||||
|
- **Storage**: Temporary data only
|
||||||
|
- **Persistence**: Local volume
|
||||||
|
|
||||||
|
### Zig CLI
|
||||||
|
- **Purpose**: High-performance experiment management
|
||||||
|
- **Language**: Zig for maximum speed and efficiency
|
||||||
|
- **Features**:
|
||||||
|
- Content-addressed storage with deduplication
|
||||||
|
- SHA256-based commit ID generation
|
||||||
|
- WebSocket communication for real-time updates
|
||||||
|
- Rsync-based incremental file transfers
|
||||||
|
- Multi-threaded operations
|
||||||
|
- Secure API key authentication
|
||||||
|
- Auto-sync monitoring with file system watching
|
||||||
|
- Priority-based job queuing
|
||||||
|
- Memory-efficient operations with arena allocators
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
USER[User] --> AUTH[API Key Auth]
|
||||||
|
AUTH --> RATE[Rate Limiting]
|
||||||
|
RATE --> WHITELIST[IP Whitelist]
|
||||||
|
WHITELIST --> API[Secure API]
|
||||||
|
API --> AUDIT[Audit Logging]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security Layers
|
||||||
|
1. **API Key Authentication** - Hashed keys with roles
|
||||||
|
2. **Rate Limiting** - 30 requests/minute
|
||||||
|
3. **IP Whitelisting** - Local networks only
|
||||||
|
4. **Fail2Ban** - Automatic IP blocking
|
||||||
|
5. **HTTPS/TLS** - Encrypted communication
|
||||||
|
6. **Audit Logging** - Complete action tracking
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant CLI
|
||||||
|
participant API
|
||||||
|
participant Redis
|
||||||
|
participant Storage
|
||||||
|
|
||||||
|
CLI->>API: HTTPS Request
|
||||||
|
API->>API: Validate Auth
|
||||||
|
API->>Redis: Cache/Queue
|
||||||
|
API->>Storage: Experiment Data
|
||||||
|
Storage->>API: Results
|
||||||
|
API->>CLI: Response
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment Options
|
||||||
|
|
||||||
|
### Docker Compose (Recommended)
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
ports: ["6379:6379"]
|
||||||
|
volumes: [redis_data:/data]
|
||||||
|
|
||||||
|
api-server:
|
||||||
|
build: .
|
||||||
|
ports: ["9101:9101"]
|
||||||
|
depends_on: [redis]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Setup
|
||||||
|
```bash
|
||||||
|
./setup.sh && ./manage.sh start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network Architecture
|
||||||
|
|
||||||
|
- **Private Network**: Docker internal network
|
||||||
|
- **Localhost Access**: Redis only on localhost
|
||||||
|
- **HTTPS API**: Port 9101, TLS encrypted
|
||||||
|
- **No External Dependencies**: Everything runs locally
|
||||||
|
|
||||||
|
## Storage Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
data/
|
||||||
|
├── experiments/ # ML experiment results
|
||||||
|
├── cache/ # Temporary cache files
|
||||||
|
└── backups/ # Local backups
|
||||||
|
|
||||||
|
logs/
|
||||||
|
├── app.log # Application logs
|
||||||
|
├── audit.log # Security events
|
||||||
|
└── access.log # API access logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring Architecture
|
||||||
|
|
||||||
|
Simple, lightweight monitoring:
|
||||||
|
- **Health Checks**: Service availability
|
||||||
|
- **Log Files**: Structured logging
|
||||||
|
- **Basic Metrics**: Request counts, error rates
|
||||||
|
- **Security Events**: Failed auth, rate limits
|
||||||
|
|
||||||
|
## Homelab Benefits
|
||||||
|
|
||||||
|
- ✅ **Simple Setup**: One-command installation
|
||||||
|
- ✅ **Local Only**: No external dependencies
|
||||||
|
- ✅ **Secure by Default**: HTTPS, auth, rate limiting
|
||||||
|
- ✅ **Low Resource**: Minimal CPU/memory usage
|
||||||
|
- ✅ **Easy Backup**: Local file system
|
||||||
|
- ✅ **Privacy**: Everything stays on your network
|
||||||
|
|
||||||
|
## High-Level Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Client Layer"
|
||||||
|
CLI[CLI Tools]
|
||||||
|
TUI[Terminal UI]
|
||||||
|
API[REST API]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Authentication Layer"
|
||||||
|
Auth[Authentication Service]
|
||||||
|
RBAC[Role-Based Access Control]
|
||||||
|
Perm[Permission Manager]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Core Services"
|
||||||
|
Worker[ML Worker Service]
|
||||||
|
DataMgr[Data Manager Service]
|
||||||
|
Queue[Job Queue]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Storage Layer"
|
||||||
|
Redis[(Redis Cache)]
|
||||||
|
DB[(SQLite/PostgreSQL)]
|
||||||
|
Files[File Storage]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Container Runtime"
|
||||||
|
Podman[Podman/Docker]
|
||||||
|
Containers[ML Containers]
|
||||||
|
end
|
||||||
|
|
||||||
|
CLI --> Auth
|
||||||
|
TUI --> Auth
|
||||||
|
API --> Auth
|
||||||
|
|
||||||
|
Auth --> RBAC
|
||||||
|
RBAC --> Perm
|
||||||
|
|
||||||
|
Worker --> Queue
|
||||||
|
Worker --> DataMgr
|
||||||
|
Worker --> Podman
|
||||||
|
|
||||||
|
DataMgr --> DB
|
||||||
|
DataMgr --> Files
|
||||||
|
|
||||||
|
Queue --> Redis
|
||||||
|
|
||||||
|
Podman --> Containers
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zig CLI Architecture
|
||||||
|
|
||||||
|
### Component Structure
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Zig CLI Components"
|
||||||
|
Main[main.zig] --> Commands[commands/]
|
||||||
|
Commands --> Config[config.zig]
|
||||||
|
Commands --> Utils[utils/]
|
||||||
|
Commands --> Net[net/]
|
||||||
|
Commands --> Errors[errors.zig]
|
||||||
|
|
||||||
|
subgraph "Commands"
|
||||||
|
Init[init.zig]
|
||||||
|
Sync[sync.zig]
|
||||||
|
Queue[queue.zig]
|
||||||
|
Watch[watch.zig]
|
||||||
|
Status[status.zig]
|
||||||
|
Monitor[monitor.zig]
|
||||||
|
Cancel[cancel.zig]
|
||||||
|
Prune[prune.zig]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Utils"
|
||||||
|
Crypto[crypto.zig]
|
||||||
|
Storage[storage.zig]
|
||||||
|
Rsync[rsync.zig]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Network"
|
||||||
|
WS[ws.zig]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Optimizations
|
||||||
|
|
||||||
|
#### Content-Addressed Storage
|
||||||
|
- **Deduplication**: Files stored by SHA256 hash
|
||||||
|
- **Space Efficiency**: Shared files across experiments
|
||||||
|
- **Fast Lookup**: Hash-based file retrieval
|
||||||
|
|
||||||
|
#### Memory Management
|
||||||
|
- **Arena Allocators**: Efficient bulk allocation
|
||||||
|
- **Zero-Copy Operations**: Minimized memory copying
|
||||||
|
- **Automatic Cleanup**: Resource deallocation
|
||||||
|
|
||||||
|
#### Network Communication
|
||||||
|
- **WebSocket Protocol**: Real-time bidirectional communication
|
||||||
|
- **Connection Pooling**: Reused connections
|
||||||
|
- **Binary Messaging**: Efficient data transfer
|
||||||
|
|
||||||
|
### Security Implementation
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
subgraph "CLI Security"
|
||||||
|
Config[Config File] --> Hash[SHA256 Hashing]
|
||||||
|
Hash --> Auth[API Authentication]
|
||||||
|
Auth --> SSH[SSH Transfer]
|
||||||
|
SSH --> WS[WebSocket Security]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### 1. Authentication & Authorization
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
subgraph "Auth Flow"
|
||||||
|
Client[Client] --> APIKey[API Key]
|
||||||
|
APIKey --> Hash[Hash Validation]
|
||||||
|
Hash --> Roles[Role Resolution]
|
||||||
|
Roles --> Perms[Permission Check]
|
||||||
|
Perms --> Access[Grant/Deny Access]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Permission Sources"
|
||||||
|
YAML[YAML Config]
|
||||||
|
Inline[Inline Fallback]
|
||||||
|
Roles --> YAML
|
||||||
|
Roles --> Inline
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- API key-based authentication
|
||||||
|
- Role-based access control (RBAC)
|
||||||
|
- YAML-based permission configuration
|
||||||
|
- Fallback to inline permissions
|
||||||
|
- Admin wildcard permissions
|
||||||
|
|
||||||
|
### 2. Worker Service
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Worker Architecture"
|
||||||
|
API[HTTP API] --> Router[Request Router]
|
||||||
|
Router --> Auth[Auth Middleware]
|
||||||
|
Auth --> Queue[Job Queue]
|
||||||
|
Queue --> Processor[Job Processor]
|
||||||
|
Processor --> Runtime[Container Runtime]
|
||||||
|
Runtime --> Storage[Result Storage]
|
||||||
|
|
||||||
|
subgraph "Job Lifecycle"
|
||||||
|
Submit[Submit Job] --> Queue
|
||||||
|
Queue --> Execute[Execute]
|
||||||
|
Execute --> Monitor[Monitor]
|
||||||
|
Monitor --> Complete[Complete]
|
||||||
|
Complete --> Store[Store Results]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responsibilities:**
|
||||||
|
- HTTP API for job submission
|
||||||
|
- Job queue management
|
||||||
|
- Container orchestration
|
||||||
|
- Result collection and storage
|
||||||
|
- Metrics and monitoring
|
||||||
|
|
||||||
|
### 3. Data Manager Service
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Data Management"
|
||||||
|
API[Data API] --> Storage[Storage Layer]
|
||||||
|
Storage --> Metadata[Metadata DB]
|
||||||
|
Storage --> Files[File System]
|
||||||
|
Storage --> Cache[Redis Cache]
|
||||||
|
|
||||||
|
subgraph "Data Operations"
|
||||||
|
Upload[Upload Data] --> Validate[Validate]
|
||||||
|
Validate --> Store[Store]
|
||||||
|
Store --> Index[Index]
|
||||||
|
Index --> Catalog[Catalog]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Data upload and validation
|
||||||
|
- Metadata management
|
||||||
|
- File system abstraction
|
||||||
|
- Caching layer
|
||||||
|
- Data catalog
|
||||||
|
|
||||||
|
### 4. Terminal UI (TUI)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "TUI Architecture"
|
||||||
|
UI[UI Components] --> Model[Data Model]
|
||||||
|
Model --> Update[Update Loop]
|
||||||
|
Update --> Render[Render]
|
||||||
|
|
||||||
|
subgraph "UI Panels"
|
||||||
|
Jobs[Job List]
|
||||||
|
Details[Job Details]
|
||||||
|
Logs[Log Viewer]
|
||||||
|
Status[Status Bar]
|
||||||
|
end
|
||||||
|
|
||||||
|
UI --> Jobs
|
||||||
|
UI --> Details
|
||||||
|
UI --> Logs
|
||||||
|
UI --> Status
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Components:**
|
||||||
|
- Bubble Tea framework
|
||||||
|
- Component-based architecture
|
||||||
|
- Real-time updates
|
||||||
|
- Keyboard navigation
|
||||||
|
- Theme support
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### Job Execution Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant Auth
|
||||||
|
participant Worker
|
||||||
|
participant Queue
|
||||||
|
participant Container
|
||||||
|
participant Storage
|
||||||
|
|
||||||
|
Client->>Auth: Submit job with API key
|
||||||
|
Auth->>Client: Validate and return job ID
|
||||||
|
|
||||||
|
Client->>Worker: Execute job request
|
||||||
|
Worker->>Queue: Queue job
|
||||||
|
Queue->>Worker: Job ready
|
||||||
|
Worker->>Container: Start ML container
|
||||||
|
Container->>Worker: Execute experiment
|
||||||
|
Worker->>Storage: Store results
|
||||||
|
Worker->>Client: Return results
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant Auth
|
||||||
|
participant PermMgr
|
||||||
|
participant Config
|
||||||
|
|
||||||
|
Client->>Auth: Request with API key
|
||||||
|
Auth->>Auth: Validate key hash
|
||||||
|
Auth->>PermMgr: Get user permissions
|
||||||
|
PermMgr->>Config: Load YAML permissions
|
||||||
|
Config->>PermMgr: Return permissions
|
||||||
|
PermMgr->>Auth: Return resolved permissions
|
||||||
|
Auth->>Client: Grant/deny access
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
### Defense in Depth
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Security Layers"
|
||||||
|
Network[Network Security]
|
||||||
|
Auth[Authentication]
|
||||||
|
AuthZ[Authorization]
|
||||||
|
Container[Container Security]
|
||||||
|
Data[Data Protection]
|
||||||
|
Audit[Audit Logging]
|
||||||
|
end
|
||||||
|
|
||||||
|
Network --> Auth
|
||||||
|
Auth --> AuthZ
|
||||||
|
AuthZ --> Container
|
||||||
|
Container --> Data
|
||||||
|
Data --> Audit
|
||||||
|
```
|
||||||
|
|
||||||
|
**Security Features:**
|
||||||
|
- API key authentication
|
||||||
|
- Role-based permissions
|
||||||
|
- Container isolation
|
||||||
|
- File system sandboxing
|
||||||
|
- Comprehensive audit logs
|
||||||
|
- Input validation and sanitization
|
||||||
|
|
||||||
|
### Container Security
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Container Isolation"
|
||||||
|
Host[Host System]
|
||||||
|
Podman[Podman Runtime]
|
||||||
|
Network[Network Isolation]
|
||||||
|
FS[File System Isolation]
|
||||||
|
User[User Namespaces]
|
||||||
|
ML[ML Container]
|
||||||
|
|
||||||
|
Host --> Podman
|
||||||
|
Podman --> Network
|
||||||
|
Podman --> FS
|
||||||
|
Podman --> User
|
||||||
|
User --> ML
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Isolation Features:**
|
||||||
|
- Rootless containers
|
||||||
|
- Network isolation
|
||||||
|
- File system sandboxing
|
||||||
|
- User namespace mapping
|
||||||
|
- Resource limits
|
||||||
|
|
||||||
|
## Configuration Architecture
|
||||||
|
|
||||||
|
### Configuration Hierarchy
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Config Sources"
|
||||||
|
Env[Environment Variables]
|
||||||
|
File[Config Files]
|
||||||
|
CLI[CLI Flags]
|
||||||
|
Defaults[Default Values]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Config Processing"
|
||||||
|
Merge[Config Merger]
|
||||||
|
Validate[Schema Validator]
|
||||||
|
Apply[Config Applier]
|
||||||
|
end
|
||||||
|
|
||||||
|
Env --> Merge
|
||||||
|
File --> Merge
|
||||||
|
CLI --> Merge
|
||||||
|
Defaults --> Merge
|
||||||
|
|
||||||
|
Merge --> Validate
|
||||||
|
Validate --> Apply
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configuration Priority:**
|
||||||
|
1. CLI flags (highest)
|
||||||
|
2. Environment variables
|
||||||
|
3. Configuration files
|
||||||
|
4. Default values (lowest)
|
||||||
|
|
||||||
|
## Scalability Architecture
|
||||||
|
|
||||||
|
### Horizontal Scaling
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Scaled Architecture"
|
||||||
|
LB[Load Balancer]
|
||||||
|
W1[Worker 1]
|
||||||
|
W2[Worker 2]
|
||||||
|
W3[Worker N]
|
||||||
|
Redis[Redis Cluster]
|
||||||
|
Storage[Shared Storage]
|
||||||
|
|
||||||
|
LB --> W1
|
||||||
|
LB --> W2
|
||||||
|
LB --> W3
|
||||||
|
|
||||||
|
W1 --> Redis
|
||||||
|
W2 --> Redis
|
||||||
|
W3 --> Redis
|
||||||
|
|
||||||
|
W1 --> Storage
|
||||||
|
W2 --> Storage
|
||||||
|
W3 --> Storage
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Scaling Features:**
|
||||||
|
- Stateless worker services
|
||||||
|
- Shared job queue (Redis)
|
||||||
|
- Distributed storage
|
||||||
|
- Load balancer ready
|
||||||
|
- Health checks and monitoring
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
### Backend Technologies
|
||||||
|
|
||||||
|
| Component | Technology | Purpose |
|
||||||
|
|-----------|------------|---------|
|
||||||
|
| **Language** | Go 1.25+ | Core application |
|
||||||
|
| **Web Framework** | Standard library | HTTP server |
|
||||||
|
| **Authentication** | Custom | API key + RBAC |
|
||||||
|
| **Database** | SQLite/PostgreSQL | Metadata storage |
|
||||||
|
| **Cache** | Redis | Job queue & caching |
|
||||||
|
| **Containers** | Podman/Docker | Job isolation |
|
||||||
|
| **UI Framework** | Bubble Tea | Terminal UI |
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Core dependencies
|
||||||
|
require (
|
||||||
|
github.com/charmbracelet/bubbletea v1.3.10 // TUI framework
|
||||||
|
github.com/go-redis/redis/v8 v8.11.5 // Redis client
|
||||||
|
github.com/google/uuid v1.6.0 // UUID generation
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32 // SQLite driver
|
||||||
|
golang.org/x/crypto v0.45.0 // Crypto utilities
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // YAML parsing
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Architecture
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
fetch_ml/
|
||||||
|
├── cmd/ # CLI applications
|
||||||
|
│ ├── worker/ # ML worker service
|
||||||
|
│ ├── tui/ # Terminal UI
|
||||||
|
│ ├── data_manager/ # Data management
|
||||||
|
│ └── user_manager/ # User management
|
||||||
|
├── internal/ # Internal packages
|
||||||
|
│ ├── auth/ # Authentication system
|
||||||
|
│ ├── config/ # Configuration management
|
||||||
|
│ ├── container/ # Container operations
|
||||||
|
│ ├── database/ # Database operations
|
||||||
|
│ ├── logging/ # Logging utilities
|
||||||
|
│ ├── metrics/ # Metrics collection
|
||||||
|
│ └── network/ # Network utilities
|
||||||
|
├── configs/ # Configuration files
|
||||||
|
├── scripts/ # Setup and utility scripts
|
||||||
|
├── tests/ # Test suites
|
||||||
|
└── docs/ # Documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Package Dependencies
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Application Layer"
|
||||||
|
Worker[cmd/worker]
|
||||||
|
TUI[cmd/tui]
|
||||||
|
DataMgr[cmd/data_manager]
|
||||||
|
UserMgr[cmd/user_manager]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Service Layer"
|
||||||
|
Auth[internal/auth]
|
||||||
|
Config[internal/config]
|
||||||
|
Container[internal/container]
|
||||||
|
Database[internal/database]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Utility Layer"
|
||||||
|
Logging[internal/logging]
|
||||||
|
Metrics[internal/metrics]
|
||||||
|
Network[internal/network]
|
||||||
|
end
|
||||||
|
|
||||||
|
Worker --> Auth
|
||||||
|
Worker --> Config
|
||||||
|
Worker --> Container
|
||||||
|
TUI --> Auth
|
||||||
|
DataMgr --> Database
|
||||||
|
UserMgr --> Auth
|
||||||
|
|
||||||
|
Auth --> Logging
|
||||||
|
Container --> Network
|
||||||
|
Database --> Metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring & Observability
|
||||||
|
|
||||||
|
### Metrics Collection
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Metrics Pipeline"
|
||||||
|
App[Application] --> Metrics[Metrics Collector]
|
||||||
|
Metrics --> Export[Prometheus Exporter]
|
||||||
|
Export --> Prometheus[Prometheus Server]
|
||||||
|
Prometheus --> Grafana[Grafana Dashboard]
|
||||||
|
|
||||||
|
subgraph "Metric Types"
|
||||||
|
Counter[Counters]
|
||||||
|
Gauge[Gauges]
|
||||||
|
Histogram[Histograms]
|
||||||
|
Timer[Timers]
|
||||||
|
end
|
||||||
|
|
||||||
|
App --> Counter
|
||||||
|
App --> Gauge
|
||||||
|
App --> Histogram
|
||||||
|
App --> Timer
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logging Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Logging Pipeline"
|
||||||
|
App[Application] --> Logger[Structured Logger]
|
||||||
|
Logger --> File[File Output]
|
||||||
|
Logger --> Console[Console Output]
|
||||||
|
Logger --> Syslog[Syslog Forwarder]
|
||||||
|
Syslog --> Aggregator[Log Aggregator]
|
||||||
|
Aggregator --> Storage[Log Storage]
|
||||||
|
Storage --> Viewer[Log Viewer]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment Architecture
|
||||||
|
|
||||||
|
### Container Deployment
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Deployment Stack"
|
||||||
|
Image[Container Image]
|
||||||
|
Registry[Container Registry]
|
||||||
|
Orchestrator[Docker Compose]
|
||||||
|
Config[ConfigMaps/Secrets]
|
||||||
|
Storage[Persistent Storage]
|
||||||
|
|
||||||
|
Image --> Registry
|
||||||
|
Registry --> Orchestrator
|
||||||
|
Config --> Orchestrator
|
||||||
|
Storage --> Orchestrator
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Discovery
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Service Mesh"
|
||||||
|
Gateway[API Gateway]
|
||||||
|
Discovery[Service Discovery]
|
||||||
|
Worker[Worker Service]
|
||||||
|
Data[Data Service]
|
||||||
|
Redis[Redis Cluster]
|
||||||
|
|
||||||
|
Gateway --> Discovery
|
||||||
|
Discovery --> Worker
|
||||||
|
Discovery --> Data
|
||||||
|
Discovery --> Redis
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future Architecture Considerations
|
||||||
|
|
||||||
|
### Microservices Evolution
|
||||||
|
|
||||||
|
- **API Gateway**: Centralized routing and authentication
|
||||||
|
- **Service Mesh**: Inter-service communication
|
||||||
|
- **Event Streaming**: Kafka for job events
|
||||||
|
- **Distributed Tracing**: OpenTelemetry integration
|
||||||
|
- **Multi-tenant**: Tenant isolation and quotas
|
||||||
|
|
||||||
|
### Homelab Features
|
||||||
|
|
||||||
|
- **Docker Compose**: Simple container orchestration
|
||||||
|
- **Local Development**: Easy setup and testing
|
||||||
|
- **Security**: Built-in authentication and encryption
|
||||||
|
- **Monitoring**: Basic health checks and logging
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This architecture provides a solid foundation for secure, scalable machine learning experiments while maintaining simplicity and developer productivity.
|
||||||
165
docs/_pages/cicd.md
Normal file
165
docs/_pages/cicd.md
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "CI/CD Pipeline"
|
||||||
|
permalink: /cicd/
|
||||||
|
nav_order: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
# CI/CD Pipeline
|
||||||
|
|
||||||
|
Automated testing, building, and releasing for fetch_ml.
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### CI Workflow (`.github/workflows/ci.yml`)
|
||||||
|
|
||||||
|
Runs on every push to `main`/`develop` and all pull requests.
|
||||||
|
|
||||||
|
**Jobs:**
|
||||||
|
1. **test** - Go backend tests with Redis
|
||||||
|
2. **build** - Build all binaries (Go + Zig CLI)
|
||||||
|
3. **test-scripts** - Validate deployment scripts
|
||||||
|
4. **security-scan** - Trivy and Gosec security scans
|
||||||
|
5. **docker-build** - Build and push Docker images (main branch only)
|
||||||
|
|
||||||
|
**Test Coverage:**
|
||||||
|
- Go unit tests with race detection
|
||||||
|
- `internal/queue` package tests
|
||||||
|
- Zig CLI tests
|
||||||
|
- Integration tests
|
||||||
|
- Security audits
|
||||||
|
|
||||||
|
### Release Workflow (`.github/workflows/release.yml`)
|
||||||
|
|
||||||
|
Runs on version tags (e.g., `v1.0.0`).
|
||||||
|
|
||||||
|
**Jobs:**
|
||||||
|
|
||||||
|
1. **build-cli** (matrix build)
|
||||||
|
- Linux x86_64 (static musl)
|
||||||
|
- macOS x86_64
|
||||||
|
- macOS ARM64
|
||||||
|
- Downloads platform-specific static rsync
|
||||||
|
- Embeds rsync for zero-dependency releases
|
||||||
|
|
||||||
|
2. **build-go-backends**
|
||||||
|
- Cross-platform Go builds
|
||||||
|
- api-server, worker, tui, data_manager, user_manager
|
||||||
|
|
||||||
|
3. **create-release**
|
||||||
|
- Collects all artifacts
|
||||||
|
- Generates SHA256 checksums
|
||||||
|
- Creates GitHub release with notes
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
### Creating a Release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Update version
|
||||||
|
git tag v1.0.0
|
||||||
|
|
||||||
|
# 2. Push tag
|
||||||
|
git push origin v1.0.0
|
||||||
|
|
||||||
|
# 3. CI automatically builds and releases
|
||||||
|
```
|
||||||
|
|
||||||
|
### Release Artifacts
|
||||||
|
|
||||||
|
**CLI Binaries (with embedded rsync):**
|
||||||
|
- `ml-linux-x86_64.tar.gz` (~450-650KB)
|
||||||
|
- `ml-macos-x86_64.tar.gz` (~450-650KB)
|
||||||
|
- `ml-macos-arm64.tar.gz` (~450-650KB)
|
||||||
|
|
||||||
|
**Go Backends:**
|
||||||
|
- `fetch_ml_api-server.tar.gz`
|
||||||
|
- `fetch_ml_worker.tar.gz`
|
||||||
|
- `fetch_ml_tui.tar.gz`
|
||||||
|
- `fetch_ml_data_manager.tar.gz`
|
||||||
|
- `fetch_ml_user_manager.tar.gz`
|
||||||
|
|
||||||
|
**Checksums:**
|
||||||
|
- `checksums.txt` - Combined SHA256 sums
|
||||||
|
- Individual `.sha256` files per binary
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Local Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Run specific package tests
|
||||||
|
go test ./internal/queue/...
|
||||||
|
|
||||||
|
# Build CLI
|
||||||
|
cd cli && zig build dev
|
||||||
|
|
||||||
|
# Run formatters and linters
|
||||||
|
make lint
|
||||||
|
|
||||||
|
# Security scans are handled automatically in CI by the `security-scan` job
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Optional heavy end-to-end tests
|
||||||
|
|
||||||
|
Some e2e tests exercise full Docker deployments and performance scenarios and are
|
||||||
|
**skipped by default** to keep local/CI runs fast. You can enable them explicitly
|
||||||
|
with environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run Docker deployment e2e tests
|
||||||
|
FETCH_ML_E2E_DOCKER=1 go test ./tests/e2e/...
|
||||||
|
|
||||||
|
# Run performance-oriented e2e tests
|
||||||
|
FETCH_ML_E2E_PERF=1 go test ./tests/e2e/...
|
||||||
|
```
|
||||||
|
|
||||||
|
Without these variables, `TestDockerDeploymentE2E` and `TestPerformanceE2E` will
|
||||||
|
`t.Skip`, while all lighter e2e tests still run.
|
||||||
|
|
||||||
|
### Pull Request Checks
|
||||||
|
|
||||||
|
All PRs must pass:
|
||||||
|
- ✅ Go tests (with Redis)
|
||||||
|
- ✅ CLI tests
|
||||||
|
- ✅ Security scans
|
||||||
|
- ✅ Code linting
|
||||||
|
- ✅ Build verification
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
GO_VERSION: '1.25.0'
|
||||||
|
ZIG_VERSION: '0.15.2'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secrets
|
||||||
|
|
||||||
|
Required for releases:
|
||||||
|
- `GITHUB_TOKEN` - Automatic, provided by GitHub Actions
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Build Status
|
||||||
|
|
||||||
|
Check workflow runs at:
|
||||||
|
```
|
||||||
|
https://github.com/jfraeys/fetch_ml/actions
|
||||||
|
```
|
||||||
|
|
||||||
|
### Artifacts
|
||||||
|
|
||||||
|
Download build artifacts from:
|
||||||
|
- Successful workflow runs (30-day retention)
|
||||||
|
- GitHub Releases (permanent)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For implementation details:
|
||||||
|
- [.github/workflows/ci.yml](https://github.com/jfraeys/fetch_ml/blob/main/.github/workflows/ci.yml)
|
||||||
|
- [.github/workflows/release.yml](https://github.com/jfraeys/fetch_ml/blob/main/.github/workflows/release.yml)
|
||||||
404
docs/_pages/cli-reference.md
Normal file
404
docs/_pages/cli-reference.md
Normal file
|
|
@ -0,0 +1,404 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "CLI Reference"
|
||||||
|
permalink: /cli-reference/
|
||||||
|
nav_order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fetch ML CLI Reference
|
||||||
|
|
||||||
|
Comprehensive command-line tools for managing ML experiments in your homelab with Zig-based high-performance CLI.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Fetch ML provides a comprehensive CLI toolkit built with performance and security in mind:
|
||||||
|
|
||||||
|
- **Zig CLI** - High-performance experiment management written in Zig
|
||||||
|
- **Go Commands** - API server, TUI, and data management utilities
|
||||||
|
- **Management Scripts** - Service orchestration and deployment
|
||||||
|
- **Setup Scripts** - One-command installation and configuration
|
||||||
|
|
||||||
|
## Zig CLI (`./cli/zig-out/bin/ml`)
|
||||||
|
|
||||||
|
High-performance command-line interface for experiment management, written in Zig for speed and efficiency.
|
||||||
|
|
||||||
|
### Available Commands
|
||||||
|
|
||||||
|
| Command | Description | Example |
|
||||||
|
|---------|-------------|----------|
|
||||||
|
| `init` | Interactive configuration setup | `ml init` |
|
||||||
|
| `sync` | Sync project to worker with deduplication | `ml sync ./project --name myjob --queue` |
|
||||||
|
| `queue` | Queue job for execution | `ml queue myjob --commit abc123 --priority 8` |
|
||||||
|
| `status` | Get system and worker status | `ml status` |
|
||||||
|
| `monitor` | Launch TUI monitoring via SSH | `ml monitor` |
|
||||||
|
| `cancel` | Cancel running job | `ml cancel job123` |
|
||||||
|
| `prune` | Clean up old experiments | `ml prune --keep 10` |
|
||||||
|
| `watch` | Auto-sync directory on changes | `ml watch ./project --queue` |
|
||||||
|
|
||||||
|
### Command Details
|
||||||
|
|
||||||
|
#### `init` - Configuration Setup
|
||||||
|
```bash
|
||||||
|
ml init
|
||||||
|
```
|
||||||
|
Creates a configuration template at `~/.ml/config.toml` with:
|
||||||
|
- Worker connection details
|
||||||
|
- API authentication
|
||||||
|
- Base paths and ports
|
||||||
|
|
||||||
|
#### `sync` - Project Synchronization
|
||||||
|
```bash
|
||||||
|
# Basic sync
|
||||||
|
ml sync ./my-project
|
||||||
|
|
||||||
|
# Sync with custom name and queue
|
||||||
|
ml sync ./my-project --name "experiment-1" --queue
|
||||||
|
|
||||||
|
# Sync with priority
|
||||||
|
ml sync ./my-project --priority 9
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Content-addressed storage for deduplication
|
||||||
|
- SHA256 commit ID generation
|
||||||
|
- Rsync-based file transfer
|
||||||
|
- Automatic queuing (with `--queue` flag)
|
||||||
|
|
||||||
|
#### `queue` - Job Management
|
||||||
|
```bash
|
||||||
|
# Queue with commit ID
|
||||||
|
ml queue my-job --commit abc123def456
|
||||||
|
|
||||||
|
# Queue with priority (1-10, default 5)
|
||||||
|
ml queue my-job --commit abc123 --priority 8
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- WebSocket-based communication
|
||||||
|
- Priority queuing system
|
||||||
|
- API key authentication
|
||||||
|
|
||||||
|
#### `watch` - Auto-Sync Monitoring
|
||||||
|
```bash
|
||||||
|
# Watch directory for changes
|
||||||
|
ml watch ./project
|
||||||
|
|
||||||
|
# Watch and auto-queue on changes
|
||||||
|
ml watch ./project --name "dev-exp" --queue
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Real-time file system monitoring
|
||||||
|
- Automatic re-sync on changes
|
||||||
|
- Configurable polling interval (2 seconds)
|
||||||
|
- Commit ID comparison for efficiency
|
||||||
|
|
||||||
|
#### `prune` - Cleanup Management
|
||||||
|
```bash
|
||||||
|
# Keep last N experiments
|
||||||
|
ml prune --keep 20
|
||||||
|
|
||||||
|
# Remove experiments older than N days
|
||||||
|
ml prune --older-than 30
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `monitor` - Remote Monitoring
|
||||||
|
```bash
|
||||||
|
ml monitor
|
||||||
|
```
|
||||||
|
Launches TUI interface via SSH for real-time monitoring.
|
||||||
|
|
||||||
|
#### `cancel` - Job Cancellation
|
||||||
|
```bash
|
||||||
|
ml cancel running-job-id
|
||||||
|
```
|
||||||
|
Cancels currently running jobs by ID.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
The Zig CLI reads configuration from `~/.ml/config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
worker_host = "worker.local"
|
||||||
|
worker_user = "mluser"
|
||||||
|
worker_base = "/data/ml-experiments"
|
||||||
|
worker_port = 22
|
||||||
|
api_key = "your-api-key"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Features
|
||||||
|
|
||||||
|
- **Content-Addressed Storage**: Automatic deduplication of identical files
|
||||||
|
- **Incremental Sync**: Only transfers changed files
|
||||||
|
- **SHA256 Hashing**: Reliable commit ID generation
|
||||||
|
- **WebSocket Communication**: Efficient real-time messaging
|
||||||
|
- **Multi-threaded**: Concurrent operations where applicable
|
||||||
|
|
||||||
|
## Go Commands
|
||||||
|
|
||||||
|
### API Server (`./cmd/api-server/main.go`)
|
||||||
|
Main HTTPS API server for experiment management.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build and run
|
||||||
|
go run ./cmd/api-server/main.go
|
||||||
|
|
||||||
|
# With configuration
|
||||||
|
./bin/api-server --config configs/config-local.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- HTTPS-only communication
|
||||||
|
- API key authentication
|
||||||
|
- Rate limiting and IP whitelisting
|
||||||
|
- WebSocket support for real-time updates
|
||||||
|
- Redis integration for caching
|
||||||
|
|
||||||
|
### TUI (`./cmd/tui/main.go`)
|
||||||
|
Terminal User Interface for monitoring experiments.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Launch TUI
|
||||||
|
go run ./cmd/tui/main.go
|
||||||
|
|
||||||
|
# With custom config
|
||||||
|
./tui --config configs/config-local.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Real-time experiment monitoring
|
||||||
|
- Interactive job management
|
||||||
|
- Status visualization
|
||||||
|
- Log viewing
|
||||||
|
|
||||||
|
### Data Manager (`./cmd/data_manager/`)
|
||||||
|
Utilities for data synchronization and management.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Sync data
|
||||||
|
./data_manager --sync ./data
|
||||||
|
|
||||||
|
# Clean old data
|
||||||
|
./data_manager --cleanup --older-than 30d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Config Lint (`./cmd/configlint/main.go`)
|
||||||
|
Configuration validation and linting tool.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Validate configuration
|
||||||
|
./configlint configs/config-local.yaml
|
||||||
|
|
||||||
|
# Check schema compliance
|
||||||
|
./configlint --schema configs/schema/config_schema.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Management Script (`./tools/manage.sh`)
|
||||||
|
|
||||||
|
Simple service management for your homelab.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
```bash
|
||||||
|
./tools/manage.sh start # Start all services
|
||||||
|
./tools/manage.sh stop # Stop all services
|
||||||
|
./tools/manage.sh status # Check service status
|
||||||
|
./tools/manage.sh logs # View logs
|
||||||
|
./tools/manage.sh monitor # Basic monitoring
|
||||||
|
./tools/manage.sh security # Security status
|
||||||
|
./tools/manage.sh cleanup # Clean project artifacts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup Script (`./setup.sh`)
|
||||||
|
|
||||||
|
One-command homelab setup.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
```bash
|
||||||
|
# Full setup
|
||||||
|
./setup.sh
|
||||||
|
|
||||||
|
# Setup includes:
|
||||||
|
# - SSL certificate generation
|
||||||
|
# - Configuration creation
|
||||||
|
# - Build all components
|
||||||
|
# - Start Redis
|
||||||
|
# - Setup Fail2Ban (if available)
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Testing
|
||||||
|
|
||||||
|
Test the API with curl:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Health check
|
||||||
|
curl -k -H 'X-API-Key: password' https://localhost:9101/health
|
||||||
|
|
||||||
|
# List experiments
|
||||||
|
curl -k -H 'X-API-Key: password' https://localhost:9101/experiments
|
||||||
|
|
||||||
|
# Submit experiment
|
||||||
|
curl -k -X POST -H 'X-API-Key: password' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"name":"test","config":{"type":"basic"}}' \
|
||||||
|
https://localhost:9101/experiments
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zig CLI Architecture
|
||||||
|
|
||||||
|
The Zig CLI is designed for performance and reliability:
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
- **Commands** (`cli/src/commands/`): Individual command implementations
|
||||||
|
- **Config** (`cli/src/config.zig`): Configuration management
|
||||||
|
- **Network** (`cli/src/net/ws.zig`): WebSocket client implementation
|
||||||
|
- **Utils** (`cli/src/utils/`): Cryptography, storage, and rsync utilities
|
||||||
|
- **Errors** (`cli/src/errors.zig`): Centralized error handling
|
||||||
|
|
||||||
|
### Performance Optimizations
|
||||||
|
- **Content-Addressed Storage**: Deduplicates identical files across experiments
|
||||||
|
- **SHA256 Hashing**: Fast, reliable commit ID generation
|
||||||
|
- **Rsync Integration**: Efficient incremental file transfers
|
||||||
|
- **WebSocket Protocol**: Low-latency communication with worker
|
||||||
|
- **Memory Management**: Efficient allocation with Zig's allocator system
|
||||||
|
|
||||||
|
### Security Features
|
||||||
|
- **API Key Hashing**: Secure authentication token handling
|
||||||
|
- **SSH Integration**: Secure file transfers
|
||||||
|
- **Input Validation**: Comprehensive argument checking
|
||||||
|
- **Error Handling**: Secure error reporting without information leakage
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Main configuration file: `configs/config-local.yaml`
|
||||||
|
|
||||||
|
### Key Settings
|
||||||
|
```yaml
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
api_keys:
|
||||||
|
homelab_user:
|
||||||
|
hash: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
|
||||||
|
admin: true
|
||||||
|
|
||||||
|
server:
|
||||||
|
address: ":9101"
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
cert_file: "./ssl/cert.pem"
|
||||||
|
key_file: "./ssl/key.pem"
|
||||||
|
|
||||||
|
security:
|
||||||
|
rate_limit:
|
||||||
|
enabled: true
|
||||||
|
requests_per_minute: 30
|
||||||
|
ip_whitelist:
|
||||||
|
- "127.0.0.1"
|
||||||
|
- "::1"
|
||||||
|
- "192.168.0.0/16"
|
||||||
|
- "10.0.0.0/8"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Commands
|
||||||
|
|
||||||
|
If using Docker Compose:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start services
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# Stop services
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
docker-compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**Zig CLI not found:**
|
||||||
|
```bash
|
||||||
|
# Build the CLI
|
||||||
|
cd cli && make build
|
||||||
|
|
||||||
|
# Check binary exists
|
||||||
|
ls -la ./cli/zig-out/bin/ml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configuration not found:**
|
||||||
|
```bash
|
||||||
|
# Create configuration
|
||||||
|
./cli/zig-out/bin/ml init
|
||||||
|
|
||||||
|
# Check config file
|
||||||
|
ls -la ~/.ml/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Worker connection failed:**
|
||||||
|
```bash
|
||||||
|
# Test SSH connection
|
||||||
|
ssh -p 22 mluser@worker.local
|
||||||
|
|
||||||
|
# Check configuration
|
||||||
|
cat ~/.ml/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sync not working:**
|
||||||
|
```bash
|
||||||
|
# Check rsync availability
|
||||||
|
rsync --version
|
||||||
|
|
||||||
|
# Test manual sync
|
||||||
|
rsync -avz ./project/ mluser@worker.local:/tmp/test/
|
||||||
|
```
|
||||||
|
|
||||||
|
**WebSocket connection failed:**
|
||||||
|
```bash
|
||||||
|
# Check worker WebSocket port
|
||||||
|
telnet worker.local 9100
|
||||||
|
|
||||||
|
# Verify API key
|
||||||
|
./cli/zig-out/bin/ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
**API not responding:**
|
||||||
|
```bash
|
||||||
|
./tools/manage.sh status
|
||||||
|
./tools/manage.sh logs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Authentication failed:**
|
||||||
|
```bash
|
||||||
|
# Check API key in config-local.yaml
|
||||||
|
grep -A 5 "api_keys:" configs/config-local.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Redis connection failed:**
|
||||||
|
```bash
|
||||||
|
# Check Redis status
|
||||||
|
redis-cli ping
|
||||||
|
|
||||||
|
# Start Redis
|
||||||
|
redis-server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# CLI help
|
||||||
|
./cli/zig-out/bin/ml help
|
||||||
|
|
||||||
|
# Management script help
|
||||||
|
./tools/manage.sh help
|
||||||
|
|
||||||
|
# Check all available commands
|
||||||
|
make help
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**That's it for the CLI reference!** For complete setup instructions, see the main [README](/).
|
||||||
310
docs/_pages/operations.md
Normal file
310
docs/_pages/operations.md
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Operations Runbook"
|
||||||
|
permalink: /operations/
|
||||||
|
nav_order: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
# Operations Runbook
|
||||||
|
|
||||||
|
Operational guide for troubleshooting and maintaining the ML experiment system.
|
||||||
|
|
||||||
|
## Task Queue Operations
|
||||||
|
|
||||||
|
### Monitoring Queue Health
|
||||||
|
|
||||||
|
```redis
|
||||||
|
# Check queue depth
|
||||||
|
ZCARD task:queue
|
||||||
|
|
||||||
|
# List pending tasks
|
||||||
|
ZRANGE task:queue 0 -1 WITHSCORES
|
||||||
|
|
||||||
|
# Check dead letter queue
|
||||||
|
KEYS task:dlq:*
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handling Stuck Tasks
|
||||||
|
|
||||||
|
**Symptom:** Tasks stuck in "running" status
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check for expired leases
|
||||||
|
redis-cli GET task:{task-id}
|
||||||
|
# Look for LeaseExpiry in past
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rem
|
||||||
|
|
||||||
|
ediation:**
|
||||||
|
Tasks with expired leases are automatically reclaimed every 1 minute. To force immediate reclamation:
|
||||||
|
```bash
|
||||||
|
# Restart worker to trigger reclaim cycle
|
||||||
|
systemctl restart ml-worker
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dead Letter Queue Management
|
||||||
|
|
||||||
|
**View failed tasks:**
|
||||||
|
```redis
|
||||||
|
KEYS task:dlq:*
|
||||||
|
```
|
||||||
|
|
||||||
|
**Inspect failed task:**
|
||||||
|
```redis
|
||||||
|
GET task:dlq:{task-id}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Retry from DLQ:**
|
||||||
|
```bash
|
||||||
|
# Manual retry (requires custom script)
|
||||||
|
# 1. Get task from DLQ
|
||||||
|
# 2. Reset retry count
|
||||||
|
# 3. Re-queue task
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker Crashes
|
||||||
|
|
||||||
|
**Symptom:** Worker disappeared mid-task
|
||||||
|
|
||||||
|
**What Happens:**
|
||||||
|
1. Lease expires after 30 minutes (default)
|
||||||
|
2. Background reclaim job detects expired lease
|
||||||
|
3. Task is retried (up to 3 attempts)
|
||||||
|
4. After max retries → Dead Letter Queue
|
||||||
|
|
||||||
|
**Prevention:**
|
||||||
|
- Monitor worker heartbeats
|
||||||
|
- Set up alerts for worker down
|
||||||
|
- Use process manager (systemd, supervisor)
|
||||||
|
|
||||||
|
## Worker Operations
|
||||||
|
|
||||||
|
### Graceful Shutdown
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Send SIGTERM for graceful shutdown
|
||||||
|
kill -TERM $(pgrep ml-worker)
|
||||||
|
|
||||||
|
# Worker will:
|
||||||
|
# 1. Stop accepting new tasks
|
||||||
|
# 2. Finish active tasks (up to 5min timeout)
|
||||||
|
# 3. Release all leases
|
||||||
|
# 4. Exit cleanly
|
||||||
|
```
|
||||||
|
|
||||||
|
### Force Shutdown
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Force kill (leases will be reclaimed automatically)
|
||||||
|
kill -9 $(pgrep ml-worker)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker Heartbeat Monitoring
|
||||||
|
|
||||||
|
```redis
|
||||||
|
# Check worker heartbeats
|
||||||
|
HGETALL worker:heartbeat
|
||||||
|
|
||||||
|
# Example output:
|
||||||
|
# worker-abc123 1701234567
|
||||||
|
# worker-def456 1701234580
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alert if:** Heartbeat timestamp > 5 minutes old
|
||||||
|
|
||||||
|
## Redis Operations
|
||||||
|
|
||||||
|
### Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manual backup
|
||||||
|
redis-cli SAVE
|
||||||
|
cp /var/lib/redis/dump.rdb /backup/redis-$(date +%Y%m%d).rdb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop Redis
|
||||||
|
systemctl stop redis
|
||||||
|
|
||||||
|
# Restore snapshot
|
||||||
|
cp /backup/redis-20231201.rdb /var/lib/redis/dump.rdb
|
||||||
|
|
||||||
|
# Start Redis
|
||||||
|
systemctl start redis
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
|
||||||
|
```redis
|
||||||
|
# Check memory usage
|
||||||
|
INFO memory
|
||||||
|
|
||||||
|
# Evict old data if needed
|
||||||
|
FLUSHDB # DANGER: Clears all data!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Issue: Queue Growing Unbounded
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- `ZCARD task:queue` keeps increasing
|
||||||
|
- No workers processing tasks
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check worker status
|
||||||
|
systemctl status ml-worker
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
journalctl -u ml-worker -n 100
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resolution:**
|
||||||
|
1. Verify workers are running
|
||||||
|
2. Check Redis connectivity
|
||||||
|
3. Verify lease configuration
|
||||||
|
|
||||||
|
### Issue: High Retry Rate
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Many tasks in DLQ
|
||||||
|
- `retry_count` field high on tasks
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check worker logs for errors
|
||||||
|
journalctl -u ml-worker | grep "retry"
|
||||||
|
|
||||||
|
# Look for patterns (network issues, resource limits, etc)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resolution:**
|
||||||
|
- Fix underlying issue (network, resources, etc)
|
||||||
|
- Adjust retry limits if permanent failures
|
||||||
|
- Increase task timeout if jobs are slow
|
||||||
|
|
||||||
|
### Issue: Leases Expiring Prematurely
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Tasks retried even though worker is healthy
|
||||||
|
- Logs show "lease expired" frequently
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```yaml
|
||||||
|
# Check worker config
|
||||||
|
cat configs/worker-config.yaml | grep -A3 "lease"
|
||||||
|
|
||||||
|
task_lease_duration: 30m # Too short?
|
||||||
|
heartbeat_interval: 1m # Too infrequent?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resolution:**
|
||||||
|
```yaml
|
||||||
|
# Increase lease duration for long-running jobs
|
||||||
|
task_lease_duration: 60m
|
||||||
|
heartbeat_interval: 30s # More frequent heartbeats
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tuning
|
||||||
|
|
||||||
|
### Worker Concurrency
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# worker-config.yaml
|
||||||
|
max_workers: 4 # Number of parallel tasks
|
||||||
|
|
||||||
|
# Adjust based on:
|
||||||
|
# - CPU cores available
|
||||||
|
# - Memory per task
|
||||||
|
# - GPU availability
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redis Configuration
|
||||||
|
|
||||||
|
```conf
|
||||||
|
# /etc/redis/redis.conf
|
||||||
|
|
||||||
|
# Persistence
|
||||||
|
save 900 1
|
||||||
|
save 300 10
|
||||||
|
|
||||||
|
# Memory
|
||||||
|
maxmemory 2gb
|
||||||
|
maxmemory-policy noeviction
|
||||||
|
|
||||||
|
# Performance
|
||||||
|
tcp-keepalive 300
|
||||||
|
timeout 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alerting Rules
|
||||||
|
|
||||||
|
### Critical Alerts
|
||||||
|
|
||||||
|
1. **Worker Down** (no heartbeat > 5min)
|
||||||
|
2. **Queue Depth** > 1000 tasks
|
||||||
|
3. **DLQ Growth** > 100 tasks/hour
|
||||||
|
4. **Redis Down** (connection failed)
|
||||||
|
|
||||||
|
### Warning Alerts
|
||||||
|
|
||||||
|
1. **High Retry Rate** > 10% of tasks
|
||||||
|
2. **Slow Queue Drain** (depth increasing over 1 hour)
|
||||||
|
3. **Worker Memory** > 80% usage
|
||||||
|
|
||||||
|
## Health Checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# health-check.sh
|
||||||
|
|
||||||
|
# Check Redis
|
||||||
|
redis-cli PING || echo "Redis DOWN"
|
||||||
|
|
||||||
|
# Check worker heartbeat
|
||||||
|
WORKER_ID=$(cat /var/run/ml-worker.pid)
|
||||||
|
LAST_HB=$(redis-cli HGET worker:heartbeat "$WORKER_ID")
|
||||||
|
NOW=$(date +%s)
|
||||||
|
if [ $((NOW - LAST_HB)) -gt 300 ]; then
|
||||||
|
echo "Worker heartbeat stale"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check queue depth
|
||||||
|
DEPTH=$(redis-cli ZCARD task:queue)
|
||||||
|
if [ "$DEPTH" -gt 1000 ]; then
|
||||||
|
echo "Queue depth critical: $DEPTH"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Runbook Checklist
|
||||||
|
|
||||||
|
### Daily Operations
|
||||||
|
- [ ] Check queue depth
|
||||||
|
- [ ] Verify worker heartbeats
|
||||||
|
- [ ] Review DLQ for patterns
|
||||||
|
- [ ] Check Redis memory usage
|
||||||
|
|
||||||
|
### Weekly Operations
|
||||||
|
- [ ] Review retry rates
|
||||||
|
- [ ] Analyze failed task patterns
|
||||||
|
- [ ] Backup Redis snapshot
|
||||||
|
- [ ] Review worker logs
|
||||||
|
|
||||||
|
### Monthly Operations
|
||||||
|
- [ ] Performance tuning review
|
||||||
|
- [ ] Capacity planning
|
||||||
|
- [ ] Update documentation
|
||||||
|
- [ ] Test disaster recovery
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**For homelab setups:**
|
||||||
|
Most of these operations can be simplified. Focus on:
|
||||||
|
- Basic monitoring (queue depth, worker status)
|
||||||
|
- Periodic Redis backups
|
||||||
|
- Graceful shutdowns for maintenance
|
||||||
322
docs/_pages/queue.md
Normal file
322
docs/_pages/queue.md
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Task Queue Architecture"
|
||||||
|
permalink: /queue/
|
||||||
|
nav_order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task Queue Architecture
|
||||||
|
|
||||||
|
The task queue system enables reliable job processing between the API server and workers using Redis.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
CLI[CLI/Client] -->|WebSocket| API[API Server]
|
||||||
|
API -->|Enqueue| Redis[(Redis)]
|
||||||
|
Redis -->|Dequeue| Worker[Worker]
|
||||||
|
Worker -->|Update Status| Redis
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### TaskQueue (`internal/queue`)
|
||||||
|
|
||||||
|
Shared package used by both API server and worker for job management.
|
||||||
|
|
||||||
|
#### Task Structure
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Task struct {
|
||||||
|
ID string // Unique task ID (UUID)
|
||||||
|
JobName string // User-defined job name
|
||||||
|
Args string // Job arguments
|
||||||
|
Status string // queued, running, completed, failed
|
||||||
|
Priority int64 // Higher = executed first
|
||||||
|
CreatedAt time.Time
|
||||||
|
StartedAt *time.Time
|
||||||
|
EndedAt *time.Time
|
||||||
|
WorkerID string
|
||||||
|
Error string
|
||||||
|
Datasets []string
|
||||||
|
Metadata map[string]string // commit_id, user, etc
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### TaskQueue Interface
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Initialize queue
|
||||||
|
queue, err := queue.NewTaskQueue(queue.Config{
|
||||||
|
RedisAddr: "localhost:6379",
|
||||||
|
RedisPassword: "",
|
||||||
|
RedisDB: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add task (API server)
|
||||||
|
task := &queue.Task{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
JobName: "train-model",
|
||||||
|
Status: "queued",
|
||||||
|
Priority: 5,
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"commit_id": commitID,
|
||||||
|
"user": username,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = queue.AddTask(task)
|
||||||
|
|
||||||
|
// Get next task (Worker)
|
||||||
|
task, err := queue.GetNextTask()
|
||||||
|
|
||||||
|
// Update task status
|
||||||
|
task.Status = "running"
|
||||||
|
err = queue.UpdateTask(task)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### Job Submission Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant CLI
|
||||||
|
participant API
|
||||||
|
participant Redis
|
||||||
|
participant Worker
|
||||||
|
|
||||||
|
CLI->>API: Queue Job (WebSocket)
|
||||||
|
API->>API: Create Task (UUID)
|
||||||
|
API->>Redis: ZADD task:queue
|
||||||
|
API->>Redis: SET task:{id}
|
||||||
|
API->>CLI: Success Response
|
||||||
|
|
||||||
|
Worker->>Redis: ZPOPMAX task:queue
|
||||||
|
Redis->>Worker: Task ID
|
||||||
|
Worker->>Redis: GET task:{id}
|
||||||
|
Redis->>Worker: Task Data
|
||||||
|
Worker->>Worker: Execute Job
|
||||||
|
Worker->>Redis: Update Status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Protocol
|
||||||
|
|
||||||
|
**CLI → API** (Binary WebSocket):
|
||||||
|
```
|
||||||
|
[opcode:1][api_key_hash:64][commit_id:64][priority:1][job_name_len:1][job_name:var]
|
||||||
|
```
|
||||||
|
|
||||||
|
**API → Redis**:
|
||||||
|
- Priority queue: `ZADD task:queue {priority} {task_id}`
|
||||||
|
- Task data: `SET task:{id} {json}`
|
||||||
|
- Status: `HSET task:status:{job_name} ...`
|
||||||
|
|
||||||
|
**Worker ← Redis**:
|
||||||
|
- Poll: `ZPOPMAX task:queue 1` (highest priority first)
|
||||||
|
- Fetch: `GET task:{id}`
|
||||||
|
|
||||||
|
## Redis Data Structures
|
||||||
|
|
||||||
|
### Keys
|
||||||
|
|
||||||
|
```
|
||||||
|
task:queue # ZSET: priority queue
|
||||||
|
task:{uuid} # STRING: task JSON data
|
||||||
|
task:status:{job_name} # HASH: job status
|
||||||
|
worker:heartbeat # HASH: worker health
|
||||||
|
job:metrics:{job_name} # HASH: job metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
### Priority Queue (ZSET)
|
||||||
|
|
||||||
|
```redis
|
||||||
|
ZADD task:queue 10 "uuid-1" # Priority 10
|
||||||
|
ZADD task:queue 5 "uuid-2" # Priority 5
|
||||||
|
ZPOPMAX task:queue 1 # Returns uuid-1 (highest)
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Server Integration
|
||||||
|
|
||||||
|
### Initialization
|
||||||
|
|
||||||
|
```go
|
||||||
|
// cmd/api-server/main.go
|
||||||
|
queueCfg := queue.Config{
|
||||||
|
RedisAddr: cfg.Redis.Addr,
|
||||||
|
RedisPassword: cfg.Redis.Password,
|
||||||
|
RedisDB: cfg.Redis.DB,
|
||||||
|
}
|
||||||
|
taskQueue, err := queue.NewTaskQueue(queueCfg)
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Handler
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/api/ws.go
|
||||||
|
func (h *WSHandler) handleQueueJob(conn *websocket.Conn, payload []byte) error {
|
||||||
|
// Parse request
|
||||||
|
apiKeyHash, commitID, priority, jobName := parsePayload(payload)
|
||||||
|
|
||||||
|
// Create task with unique ID
|
||||||
|
taskID := uuid.New().String()
|
||||||
|
task := &queue.Task{
|
||||||
|
ID: taskID,
|
||||||
|
JobName: jobName,
|
||||||
|
Status: "queued",
|
||||||
|
Priority: int64(priority),
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"commit_id": commitID,
|
||||||
|
"user": user,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue
|
||||||
|
if err := h.queue.AddTask(task); err != nil {
|
||||||
|
return h.sendErrorPacket(conn, ErrorCodeDatabaseError, ...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.sendSuccessPacket(conn, "Job queued")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Worker Integration
|
||||||
|
|
||||||
|
### Task Polling
|
||||||
|
|
||||||
|
```go
|
||||||
|
// cmd/worker/worker_server.go
|
||||||
|
func (w *Worker) Start() error {
|
||||||
|
for {
|
||||||
|
task, err := w.queue.WaitForNextTask(ctx, 5*time.Second)
|
||||||
|
if task != nil {
|
||||||
|
go w.executeTask(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task Execution
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (w *Worker) executeTask(task *queue.Task) {
|
||||||
|
// Update status
|
||||||
|
task.Status = "running"
|
||||||
|
task.StartedAt = &now
|
||||||
|
w.queue.UpdateTaskWithMetrics(task, "start")
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
err := w.runJob(task)
|
||||||
|
|
||||||
|
// Finalize
|
||||||
|
task.Status = "completed" // or "failed"
|
||||||
|
task.EndedAt = &endTime
|
||||||
|
task.Error = err.Error() // if err != nil
|
||||||
|
w.queue.UpdateTaskWithMetrics(task, "final")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### API Server (`configs/config.yaml`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
redis:
|
||||||
|
addr: "localhost:6379"
|
||||||
|
password: ""
|
||||||
|
db: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker (`configs/worker-config.yaml`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
redis:
|
||||||
|
addr: "localhost:6379"
|
||||||
|
password: ""
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
metrics_flush_interval: 500ms
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Queue Depth
|
||||||
|
|
||||||
|
```go
|
||||||
|
depth, err := queue.QueueDepth()
|
||||||
|
fmt.Printf("Pending tasks: %d\n", depth)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker Heartbeat
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Worker sends heartbeat every 30s
|
||||||
|
err := queue.Heartbeat(workerID)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Metrics
|
||||||
|
|
||||||
|
```redis
|
||||||
|
HGETALL job:metrics:{job_name}
|
||||||
|
# Returns: timestamp, tasks_start, tasks_final, etc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Task Failures
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := w.runJob(task); err != nil {
|
||||||
|
task.Status = "failed"
|
||||||
|
task.Error = err.Error()
|
||||||
|
w.queue.UpdateTask(task)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redis Connection Loss
|
||||||
|
|
||||||
|
```go
|
||||||
|
// TaskQueue automatically reconnects
|
||||||
|
// Workers should implement retry logic
|
||||||
|
for retries := 0; retries < 3; retries++ {
|
||||||
|
task, err := queue.GetNextTask()
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(backoff)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```go
|
||||||
|
// tests using miniredis
|
||||||
|
s, _ := miniredis.Run()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
tq, _ := queue.NewTaskQueue(queue.Config{
|
||||||
|
RedisAddr: s.Addr(),
|
||||||
|
})
|
||||||
|
|
||||||
|
task := &queue.Task{ID: "test-1", JobName: "test"}
|
||||||
|
tq.AddTask(task)
|
||||||
|
|
||||||
|
fetched, _ := tq.GetNextTask()
|
||||||
|
// assert fetched.ID == "test-1"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Unique Task IDs**: Always use UUIDs to avoid conflicts
|
||||||
|
2. **Metadata**: Store commit_id and user in task metadata
|
||||||
|
3. **Priority**: Higher values execute first (0-255 range)
|
||||||
|
4. **Status Updates**: Update status at each lifecycle stage
|
||||||
|
5. **Error Logging**: Store detailed errors in task.Error
|
||||||
|
6. **Heartbeats**: Workers should send heartbeats regularly
|
||||||
|
7. **Metrics**: Use UpdateTaskWithMetrics for atomic updates
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For implementation details, see:
|
||||||
|
- [internal/queue/task.go](https://github.com/jfraeys/fetch_ml/blob/main/internal/queue/task.go)
|
||||||
|
- [internal/queue/queue.go](https://github.com/jfraeys/fetch_ml/blob/main/internal/queue/queue.go)
|
||||||
95
docs/_pages/redis-ha.md
Normal file
95
docs/_pages/redis-ha.md
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Redis High Availability (Optional)"
|
||||||
|
permalink: /redis-ha/
|
||||||
|
nav_order: 7
|
||||||
|
---
|
||||||
|
|
||||||
|
# Redis High Availability
|
||||||
|
|
||||||
|
**Note:** This is optional for homelab setups. Single Redis instance is sufficient for most use cases.
|
||||||
|
|
||||||
|
## When You Need HA
|
||||||
|
|
||||||
|
Consider Redis HA if:
|
||||||
|
- Running production workloads
|
||||||
|
- Uptime > 99.9% required
|
||||||
|
- Can't afford to lose queued tasks
|
||||||
|
- Multiple workers across machines
|
||||||
|
|
||||||
|
## Redis Sentinel (Recommended)
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
redis-master:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --maxmemory 2gb
|
||||||
|
|
||||||
|
redis-replica:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --slaveof redis-master 6379
|
||||||
|
|
||||||
|
redis-sentinel-1:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-sentinel /etc/redis/sentinel.conf
|
||||||
|
volumes:
|
||||||
|
- ./sentinel.conf:/etc/redis/sentinel.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
**sentinel.conf:**
|
||||||
|
```conf
|
||||||
|
sentinel monitor mymaster redis-master 6379 2
|
||||||
|
sentinel down-after-milliseconds mymaster 5000
|
||||||
|
sentinel parallel-syncs mymaster 1
|
||||||
|
sentinel failover-timeout mymaster 10000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Application Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# worker-config.yaml
|
||||||
|
redis_addr: "redis-sentinel-1:26379,redis-sentinel-2:26379"
|
||||||
|
redis_master_name: "mymaster"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Redis Cluster (Advanced)
|
||||||
|
|
||||||
|
For larger deployments with sharding needs.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Minimum 3 masters + 3 replicas
|
||||||
|
services:
|
||||||
|
redis-1:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --cluster-enabled yes
|
||||||
|
|
||||||
|
redis-2:
|
||||||
|
# ... similar config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Homelab Alternative: Persistence Only
|
||||||
|
|
||||||
|
**For most homelabs, just enable persistence:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures tasks survive Redis restarts without full HA complexity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Recommendation:** Start simple. Add HA only if you experience actual downtime issues.
|
||||||
452
docs/_pages/zig-cli.md
Normal file
452
docs/_pages/zig-cli.md
Normal file
|
|
@ -0,0 +1,452 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Zig CLI Guide"
|
||||||
|
permalink: /zig-cli/
|
||||||
|
nav_order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Zig CLI Guide
|
||||||
|
|
||||||
|
High-performance command-line interface for ML experiment management, written in Zig for maximum speed and efficiency.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Zig CLI (`ml`) is the primary interface for managing ML experiments in your homelab. Built with Zig, it provides exceptional performance for file operations, network communication, and experiment management.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Pre-built Binaries (Recommended)
|
||||||
|
|
||||||
|
Download from [GitHub Releases](https://github.com/jfraeys/fetch_ml/releases):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download for your platform
|
||||||
|
curl -LO https://github.com/jfraeys/fetch_ml/releases/latest/download/ml-<platform>.tar.gz
|
||||||
|
|
||||||
|
# Extract
|
||||||
|
tar -xzf ml-<platform>.tar.gz
|
||||||
|
|
||||||
|
# Install
|
||||||
|
chmod +x ml-<platform>
|
||||||
|
sudo mv ml-<platform> /usr/local/bin/ml
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
ml --help
|
||||||
|
```
|
||||||
|
|
||||||
|
**Platforms:**
|
||||||
|
- `ml-linux-x86_64.tar.gz` - Linux (fully static, zero dependencies)
|
||||||
|
- `ml-macos-x86_64.tar.gz` - macOS Intel
|
||||||
|
- `ml-macos-arm64.tar.gz` - macOS Apple Silicon
|
||||||
|
|
||||||
|
All release binaries include **embedded static rsync** for complete independence.
|
||||||
|
|
||||||
|
### Build from Source
|
||||||
|
|
||||||
|
**Development Build** (uses system rsync):
|
||||||
|
```bash
|
||||||
|
cd cli
|
||||||
|
zig build dev
|
||||||
|
./zig-out/dev/ml-dev --help
|
||||||
|
```
|
||||||
|
|
||||||
|
**Production Build** (embedded rsync):
|
||||||
|
```bash
|
||||||
|
cd cli
|
||||||
|
# For testing: uses rsync wrapper
|
||||||
|
zig build prod
|
||||||
|
|
||||||
|
# For release with static rsync:
|
||||||
|
# 1. Place static rsync binary at src/assets/rsync_release.bin
|
||||||
|
# 2. Build
|
||||||
|
zig build prod
|
||||||
|
strip zig-out/prod/ml # Optional: reduce size
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
./zig-out/prod/ml --help
|
||||||
|
ls -lh zig-out/prod/ml
|
||||||
|
```
|
||||||
|
|
||||||
|
See [cli/src/assets/README.md](https://github.com/jfraeys/fetch_ml/blob/main/cli/src/assets/README.md) for details on obtaining static rsync binaries.
|
||||||
|
|
||||||
|
### Verify Installation
|
||||||
|
```bash
|
||||||
|
ml --help
|
||||||
|
ml --version # Shows build config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Initialize Configuration**
|
||||||
|
```bash
|
||||||
|
./cli/zig-out/bin/ml init
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Sync Your First Project**
|
||||||
|
```bash
|
||||||
|
./cli/zig-out/bin/ml sync ./my-project --queue
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Monitor Progress**
|
||||||
|
```bash
|
||||||
|
./cli/zig-out/bin/ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
## Command Reference
|
||||||
|
|
||||||
|
### `init` - Configuration Setup
|
||||||
|
|
||||||
|
Initialize the CLI configuration file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ml init
|
||||||
|
```
|
||||||
|
|
||||||
|
**Creates:** `~/.ml/config.toml`
|
||||||
|
|
||||||
|
**Configuration Template:**
|
||||||
|
```toml
|
||||||
|
worker_host = "worker.local"
|
||||||
|
worker_user = "mluser"
|
||||||
|
worker_base = "/data/ml-experiments"
|
||||||
|
worker_port = 22
|
||||||
|
api_key = "your-api-key"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `sync` - Project Synchronization
|
||||||
|
|
||||||
|
Sync project files to the worker with intelligent deduplication.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic sync
|
||||||
|
ml sync ./project
|
||||||
|
|
||||||
|
# Sync with custom name and auto-queue
|
||||||
|
ml sync ./project --name "experiment-1" --queue
|
||||||
|
|
||||||
|
# Sync with priority
|
||||||
|
ml sync ./project --priority 8
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
- `--name <name>`: Custom experiment name
|
||||||
|
- `--queue`: Automatically queue after sync
|
||||||
|
- `--priority N`: Set priority (1-10, default 5)
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- **Content-Addressed Storage**: Automatic deduplication
|
||||||
|
- **SHA256 Commit IDs**: Reliable change detection
|
||||||
|
- **Incremental Transfer**: Only sync changed files
|
||||||
|
- **Rsync Backend**: Efficient file transfer
|
||||||
|
|
||||||
|
### `queue` - Job Management
|
||||||
|
|
||||||
|
Queue experiments for execution on the worker.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Queue with commit ID
|
||||||
|
ml queue my-job --commit abc123def456
|
||||||
|
|
||||||
|
# Queue with priority
|
||||||
|
ml queue my-job --commit abc123 --priority 8
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
- `--commit <id>`: Commit ID from sync output
|
||||||
|
- `--priority N`: Execution priority (1-10)
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- **WebSocket Communication**: Real-time job submission
|
||||||
|
- **Priority Queuing**: Higher priority jobs run first
|
||||||
|
- **API Authentication**: Secure job submission
|
||||||
|
|
||||||
|
### `watch` - Auto-Sync Monitoring
|
||||||
|
|
||||||
|
Monitor directories for changes and auto-sync.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Watch for changes
|
||||||
|
ml watch ./project
|
||||||
|
|
||||||
|
# Watch and auto-queue on changes
|
||||||
|
ml watch ./project --name "dev-exp" --queue
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
- `--name <name>`: Custom experiment name
|
||||||
|
- `--queue`: Auto-queue on changes
|
||||||
|
- `--priority N`: Set priority for queued jobs
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- **Real-time Monitoring**: 2-second polling interval
|
||||||
|
- **Change Detection**: File modification time tracking
|
||||||
|
- **Commit Comparison**: Only sync when content changes
|
||||||
|
- **Automatic Queuing**: Seamless development workflow
|
||||||
|
|
||||||
|
### `status` - System Status
|
||||||
|
|
||||||
|
Check system and worker status.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
**Displays:**
|
||||||
|
- Worker connectivity
|
||||||
|
- Queue status
|
||||||
|
- Running jobs
|
||||||
|
- System health
|
||||||
|
|
||||||
|
### `monitor` - Remote Monitoring
|
||||||
|
|
||||||
|
Launch TUI interface via SSH for real-time monitoring.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ml monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- **Real-time Updates**: Live experiment status
|
||||||
|
- **Interactive Interface**: Browse and manage experiments
|
||||||
|
- **SSH Integration**: Secure remote access
|
||||||
|
|
||||||
|
### `cancel` - Job Cancellation
|
||||||
|
|
||||||
|
Cancel running or queued jobs.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ml cancel job-id
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
- `job-id`: Job identifier from status output
|
||||||
|
|
||||||
|
### `prune` - Cleanup Management
|
||||||
|
|
||||||
|
Clean up old experiments to save space.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Keep last N experiments
|
||||||
|
ml prune --keep 20
|
||||||
|
|
||||||
|
# Remove experiments older than N days
|
||||||
|
ml prune --older-than 30
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
- `--keep N`: Keep N most recent experiments
|
||||||
|
- `--older-than N`: Remove experiments older than N days
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
```
|
||||||
|
cli/src/
|
||||||
|
├── commands/ # Command implementations
|
||||||
|
│ ├── init.zig # Configuration setup
|
||||||
|
│ ├── sync.zig # Project synchronization
|
||||||
|
│ ├── queue.zig # Job management
|
||||||
|
│ ├── watch.zig # Auto-sync monitoring
|
||||||
|
│ ├── status.zig # System status
|
||||||
|
│ ├── monitor.zig # Remote monitoring
|
||||||
|
│ ├── cancel.zig # Job cancellation
|
||||||
|
│ └── prune.zig # Cleanup operations
|
||||||
|
├── config.zig # Configuration management
|
||||||
|
├── errors.zig # Error handling
|
||||||
|
├── net/ # Network utilities
|
||||||
|
│ └── ws.zig # WebSocket client
|
||||||
|
└── utils/ # Utility functions
|
||||||
|
├── crypto.zig # Hashing and encryption
|
||||||
|
├── storage.zig # Content-addressed storage
|
||||||
|
└── rsync.zig # File synchronization
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Features
|
||||||
|
|
||||||
|
#### Content-Addressed Storage
|
||||||
|
- **Deduplication**: Identical files shared across experiments
|
||||||
|
- **Hash-based Storage**: Files stored by SHA256 hash
|
||||||
|
- **Space Efficiency**: Reduces storage by up to 90%
|
||||||
|
|
||||||
|
#### SHA256 Commit IDs
|
||||||
|
- **Reliable Detection**: Cryptographic change detection
|
||||||
|
- **Collision Resistance**: Guaranteed unique identifiers
|
||||||
|
- **Fast Computation**: Optimized for large directories
|
||||||
|
|
||||||
|
#### WebSocket Protocol
|
||||||
|
- **Low Latency**: Real-time communication
|
||||||
|
- **Binary Protocol**: Efficient message format
|
||||||
|
- **Connection Pooling**: Reused connections
|
||||||
|
|
||||||
|
#### Memory Management
|
||||||
|
- **Arena Allocators**: Efficient memory allocation
|
||||||
|
- **Zero-copy Operations**: Minimized memory usage
|
||||||
|
- **Resource Cleanup**: Automatic resource management
|
||||||
|
|
||||||
|
### Security Features
|
||||||
|
|
||||||
|
#### Authentication
|
||||||
|
- **API Key Hashing**: Secure token storage
|
||||||
|
- **SHA256 Hashes**: Irreversible token protection
|
||||||
|
- **Config Validation**: Input sanitization
|
||||||
|
|
||||||
|
#### Secure Communication
|
||||||
|
- **SSH Integration**: Encrypted file transfers
|
||||||
|
- **WebSocket Security**: TLS-protected communication
|
||||||
|
- **Input Validation**: Comprehensive argument checking
|
||||||
|
|
||||||
|
#### Error Handling
|
||||||
|
- **Secure Reporting**: No sensitive information leakage
|
||||||
|
- **Graceful Degradation**: Safe error recovery
|
||||||
|
- **Audit Logging**: Operation tracking
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Workflow Integration
|
||||||
|
|
||||||
|
#### Development Workflow
|
||||||
|
```bash
|
||||||
|
# 1. Initialize project
|
||||||
|
ml sync ./project --name "dev" --queue
|
||||||
|
|
||||||
|
# 2. Auto-sync during development
|
||||||
|
ml watch ./project --name "dev" --queue
|
||||||
|
|
||||||
|
# 3. Monitor progress
|
||||||
|
ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Batch Processing
|
||||||
|
```bash
|
||||||
|
# Process multiple experiments
|
||||||
|
for dir in experiments/*/; do
|
||||||
|
ml sync "$dir" --queue
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Priority Management
|
||||||
|
```bash
|
||||||
|
# High priority experiment
|
||||||
|
ml sync ./urgent --priority 10 --queue
|
||||||
|
|
||||||
|
# Background processing
|
||||||
|
ml sync ./background --priority 1 --queue
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Management
|
||||||
|
|
||||||
|
#### Multiple Workers
|
||||||
|
```toml
|
||||||
|
# ~/.ml/config.toml
|
||||||
|
worker_host = "worker.local"
|
||||||
|
worker_user = "mluser"
|
||||||
|
worker_base = "/data/ml-experiments"
|
||||||
|
worker_port = 22
|
||||||
|
api_key = "your-api-key"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Security Settings
|
||||||
|
```bash
|
||||||
|
# Set restrictive permissions
|
||||||
|
chmod 600 ~/.ml/config.toml
|
||||||
|
|
||||||
|
# Verify configuration
|
||||||
|
ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### Build Problems
|
||||||
|
```bash
|
||||||
|
# Check Zig installation
|
||||||
|
zig version
|
||||||
|
|
||||||
|
# Clean build
|
||||||
|
cd cli && make clean && make build
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Connection Issues
|
||||||
|
```bash
|
||||||
|
# Test SSH connectivity
|
||||||
|
ssh -p $worker_port $worker_user@$worker_host
|
||||||
|
|
||||||
|
# Verify configuration
|
||||||
|
cat ~/.ml/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Sync Failures
|
||||||
|
```bash
|
||||||
|
# Check rsync
|
||||||
|
rsync --version
|
||||||
|
|
||||||
|
# Manual sync test
|
||||||
|
rsync -avz ./test/ $worker_user@$worker_host:/tmp/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Performance Issues
|
||||||
|
```bash
|
||||||
|
# Monitor resource usage
|
||||||
|
top -p $(pgrep ml)
|
||||||
|
|
||||||
|
# Check disk space
|
||||||
|
df -h $worker_base
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
|
||||||
|
Enable verbose logging:
|
||||||
|
```bash
|
||||||
|
# Environment variable
|
||||||
|
export ML_DEBUG=1
|
||||||
|
ml sync ./project
|
||||||
|
|
||||||
|
# Or use debug build
|
||||||
|
cd cli && make debug
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Benchmarks
|
||||||
|
|
||||||
|
### File Operations
|
||||||
|
- **Sync Speed**: 100MB/s+ (network limited)
|
||||||
|
- **Hash Computation**: 500MB/s+ (CPU limited)
|
||||||
|
- **Deduplication**: 90%+ space savings
|
||||||
|
|
||||||
|
### Memory Usage
|
||||||
|
- **Base Memory**: ~10MB
|
||||||
|
- **Large Projects**: ~50MB (1GB+ projects)
|
||||||
|
- **Memory Efficiency**: Constant per-file overhead
|
||||||
|
|
||||||
|
### Network Performance
|
||||||
|
- **WebSocket Latency**: <10ms (local network)
|
||||||
|
- **Connection Setup**: <100ms
|
||||||
|
- **Throughput**: Network limited
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
### Development Setup
|
||||||
|
```bash
|
||||||
|
cd cli
|
||||||
|
zig build-exe src/main.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
```bash
|
||||||
|
# Run tests
|
||||||
|
cd cli && zig test src/
|
||||||
|
|
||||||
|
# Integration tests
|
||||||
|
zig test tests/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
- Follow Zig style guidelines
|
||||||
|
- Use explicit error handling
|
||||||
|
- Document public APIs
|
||||||
|
- Add comprehensive tests
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**For more information, see the [CLI Reference](/cli-reference/) and [Architecture](/architecture/) pages.**
|
||||||
1442
docs/_site/404.html
Normal file
1442
docs/_site/404.html
Normal file
File diff suppressed because it is too large
Load diff
1751
docs/_site/api-key-process/index.html
Normal file
1751
docs/_site/api-key-process/index.html
Normal file
File diff suppressed because it is too large
Load diff
2867
docs/_site/architecture/index.html
Normal file
2867
docs/_site/architecture/index.html
Normal file
File diff suppressed because it is too large
Load diff
BIN
docs/_site/assets/images/favicon.png
Normal file
BIN
docs/_site/assets/images/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
16
docs/_site/assets/javascripts/bundle.e71a0d61.min.js
vendored
Normal file
16
docs/_site/assets/javascripts/bundle.e71a0d61.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
docs/_site/assets/javascripts/bundle.e71a0d61.min.js.map
Normal file
7
docs/_site/assets/javascripts/bundle.e71a0d61.min.js.map
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.ar.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.ar.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/_site/assets/javascripts/lunr/min/lunr.da.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.da.min.js
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*!
|
||||||
|
* Lunr languages, `Danish` language
|
||||||
|
* https://github.com/MihaiValentin/lunr-languages
|
||||||
|
*
|
||||||
|
* Copyright 2014, Mihai Valentin
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
* based on
|
||||||
|
* Snowball JavaScript Library v0.3
|
||||||
|
* http://code.google.com/p/urim/
|
||||||
|
* http://snowball.tartarus.org/
|
||||||
|
*
|
||||||
|
* Copyright 2010, Oleg Mazko
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=function(){var r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){function e(){var e,r=f.cursor+3;if(d=f.limit,0<=r&&r<=f.limit){for(a=r;;){if(e=f.cursor,f.in_grouping(w,97,248)){f.cursor=e;break}if(f.cursor=e,e>=f.limit)return;f.cursor++}for(;!f.out_grouping(w,97,248);){if(f.cursor>=f.limit)return;f.cursor++}d=f.cursor,d<a&&(d=a)}}function n(){var e,r;if(f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(c,32),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del();break;case 2:f.in_grouping_b(p,97,229)&&f.slice_del()}}function t(){var e,r=f.limit-f.cursor;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.find_among_b(l,4)?(f.bra=f.cursor,f.limit_backward=e,f.cursor=f.limit-r,f.cursor>f.limit_backward&&(f.cursor--,f.bra=f.cursor,f.slice_del())):f.limit_backward=e)}function s(){var e,r,i,n=f.limit-f.cursor;if(f.ket=f.cursor,f.eq_s_b(2,"st")&&(f.bra=f.cursor,f.eq_s_b(2,"ig")&&f.slice_del()),f.cursor=f.limit-n,f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(m,5),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del(),i=f.limit-f.cursor,t(),f.cursor=f.limit-i;break;case 2:f.slice_from("løs")}}function o(){var e;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.out_grouping_b(w,97,248)?(f.bra=f.cursor,u=f.slice_to(u),f.limit_backward=e,f.eq_v_b(u)&&f.slice_del()):f.limit_backward=e)}var a,d,u,c=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],l=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],w=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],p=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],f=new i;this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var r=f.cursor;return e(),f.limit_backward=r,f.cursor=f.limit,n(),f.cursor=f.limit,t(),f.cursor=f.limit,s(),f.cursor=f.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}});
|
||||||
18
docs/_site/assets/javascripts/lunr/min/lunr.de.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.de.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/_site/assets/javascripts/lunr/min/lunr.du.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.du.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.el.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.el.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/_site/assets/javascripts/lunr/min/lunr.es.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.es.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/_site/assets/javascripts/lunr/min/lunr.fi.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.fi.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/_site/assets/javascripts/lunr/min/lunr.fr.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.fr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.he.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.he.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.hi.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.hi.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hi=function(){this.pipeline.reset(),this.pipeline.add(e.hi.trimmer,e.hi.stopWordFilter,e.hi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hi.stemmer))},e.hi.wordCharacters="ऀ-ःऄ-एऐ-टठ-यर-िी-ॏॐ-य़ॠ-९॰-ॿa-zA-Za-zA-Z0-90-9",e.hi.trimmer=e.trimmerSupport.generateTrimmer(e.hi.wordCharacters),e.Pipeline.registerFunction(e.hi.trimmer,"trimmer-hi"),e.hi.stopWordFilter=e.generateStopWordFilter("अत अपना अपनी अपने अभी अंदर आदि आप इत्यादि इन इनका इन्हीं इन्हें इन्हों इस इसका इसकी इसके इसमें इसी इसे उन उनका उनकी उनके उनको उन्हीं उन्हें उन्हों उस उसके उसी उसे एक एवं एस ऐसे और कई कर करता करते करना करने करें कहते कहा का काफ़ी कि कितना किन्हें किन्हों किया किर किस किसी किसे की कुछ कुल के को कोई कौन कौनसा गया घर जब जहाँ जा जितना जिन जिन्हें जिन्हों जिस जिसे जीधर जैसा जैसे जो तक तब तरह तिन तिन्हें तिन्हों तिस तिसे तो था थी थे दबारा दिया दुसरा दूसरे दो द्वारा न नके नहीं ना निहायत नीचे ने पर पहले पूरा पे फिर बनी बही बहुत बाद बाला बिलकुल भी भीतर मगर मानो मे में यदि यह यहाँ यही या यिह ये रखें रहा रहे ऱ्वासा लिए लिये लेकिन व वग़ैरह वर्ग वह वहाँ वहीं वाले वुह वे वो सकता सकते सबसे सभी साथ साबुत साभ सारा से सो संग ही हुआ हुई हुए है हैं हो होता होती होते होना होने".split(" ")),e.hi.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.hi.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var t=i.toString().toLowerCase().replace(/^\s+/,"");return r.cut(t).split("|")},e.Pipeline.registerFunction(e.hi.stemmer,"stemmer-hi"),e.Pipeline.registerFunction(e.hi.stopWordFilter,"stopWordFilter-hi")}});
|
||||||
18
docs/_site/assets/javascripts/lunr/min/lunr.hu.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.hu.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.hy.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.hy.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hy=function(){this.pipeline.reset(),this.pipeline.add(e.hy.trimmer,e.hy.stopWordFilter)},e.hy.wordCharacters="[A-Za-z-֏ff-ﭏ]",e.hy.trimmer=e.trimmerSupport.generateTrimmer(e.hy.wordCharacters),e.Pipeline.registerFunction(e.hy.trimmer,"trimmer-hy"),e.hy.stopWordFilter=e.generateStopWordFilter("դու և եք էիր էիք հետո նաև նրանք որը վրա է որ պիտի են այս մեջ ն իր ու ի այդ որոնք այն կամ էր մի ես համար այլ իսկ էին ենք հետ ին թ էինք մենք նրա նա դուք եմ էի ըստ որպես ում".split(" ")),e.Pipeline.registerFunction(e.hy.stopWordFilter,"stopWordFilter-hy"),e.hy.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}(),e.Pipeline.registerFunction(e.hy.stemmer,"stemmer-hy")}});
|
||||||
18
docs/_site/assets/javascripts/lunr/min/lunr.it.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.it.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.ja.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.ja.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.ja=function(){this.pipeline.reset(),this.pipeline.add(e.ja.trimmer,e.ja.stopWordFilter,e.ja.stemmer),r?this.tokenizer=e.ja.tokenizer:(e.tokenizer&&(e.tokenizer=e.ja.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.ja.tokenizer))};var t=new e.TinySegmenter;e.ja.tokenizer=function(i){var n,o,s,p,a,u,m,l,c,f;if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(o=i.toString().toLowerCase().replace(/^\s+/,""),n=o.length-1;n>=0;n--)if(/\S/.test(o.charAt(n))){o=o.substring(0,n+1);break}for(a=[],s=o.length,c=0,l=0;c<=s;c++)if(u=o.charAt(c),m=c-l,u.match(/\s/)||c==s){if(m>0)for(p=t.segment(o.slice(l,c)).filter(function(e){return!!e}),f=l,n=0;n<p.length;n++)r?a.push(new e.Token(p[n],{position:[f,p[n].length],index:a.length})):a.push(p[n]),f+=p[n].length;l=c+1}return a},e.ja.stemmer=function(){return function(e){return e}}(),e.Pipeline.registerFunction(e.ja.stemmer,"stemmer-ja"),e.ja.wordCharacters="一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9",e.ja.trimmer=e.trimmerSupport.generateTrimmer(e.ja.wordCharacters),e.Pipeline.registerFunction(e.ja.trimmer,"trimmer-ja"),e.ja.stopWordFilter=e.generateStopWordFilter("これ それ あれ この その あの ここ そこ あそこ こちら どこ だれ なに なん 何 私 貴方 貴方方 我々 私達 あの人 あのかた 彼女 彼 です あります おります います は が の に を で え から まで より も どの と し それで しかし".split(" ")),e.Pipeline.registerFunction(e.ja.stopWordFilter,"stopWordFilter-ja"),e.jp=e.ja,e.Pipeline.registerFunction(e.jp.stemmer,"stemmer-jp"),e.Pipeline.registerFunction(e.jp.trimmer,"trimmer-jp"),e.Pipeline.registerFunction(e.jp.stopWordFilter,"stopWordFilter-jp")}});
|
||||||
1
docs/_site/assets/javascripts/lunr/min/lunr.jp.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.jp.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports=require("./lunr.ja");
|
||||||
1
docs/_site/assets/javascripts/lunr/min/lunr.kn.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.kn.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.kn=function(){this.pipeline.reset(),this.pipeline.add(e.kn.trimmer,e.kn.stopWordFilter,e.kn.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.kn.stemmer))},e.kn.wordCharacters="ಀ-಄ಅ-ಔಕ-ಹಾ-ೌ಼-ಽೕ-ೖೝ-ೞೠ-ೡೢ-ೣ೦-೯ೱ-ೳ",e.kn.trimmer=e.trimmerSupport.generateTrimmer(e.kn.wordCharacters),e.Pipeline.registerFunction(e.kn.trimmer,"trimmer-kn"),e.kn.stopWordFilter=e.generateStopWordFilter("ಮತ್ತು ಈ ಒಂದು ರಲ್ಲಿ ಹಾಗೂ ಎಂದು ಅಥವಾ ಇದು ರ ಅವರು ಎಂಬ ಮೇಲೆ ಅವರ ತನ್ನ ಆದರೆ ತಮ್ಮ ನಂತರ ಮೂಲಕ ಹೆಚ್ಚು ನ ಆ ಕೆಲವು ಅನೇಕ ಎರಡು ಹಾಗು ಪ್ರಮುಖ ಇದನ್ನು ಇದರ ಸುಮಾರು ಅದರ ಅದು ಮೊದಲ ಬಗ್ಗೆ ನಲ್ಲಿ ರಂದು ಇತರ ಅತ್ಯಂತ ಹೆಚ್ಚಿನ ಸಹ ಸಾಮಾನ್ಯವಾಗಿ ನೇ ಹಲವಾರು ಹೊಸ ದಿ ಕಡಿಮೆ ಯಾವುದೇ ಹೊಂದಿದೆ ದೊಡ್ಡ ಅನ್ನು ಇವರು ಪ್ರಕಾರ ಇದೆ ಮಾತ್ರ ಕೂಡ ಇಲ್ಲಿ ಎಲ್ಲಾ ವಿವಿಧ ಅದನ್ನು ಹಲವು ರಿಂದ ಕೇವಲ ದ ದಕ್ಷಿಣ ಗೆ ಅವನ ಅತಿ ನೆಯ ಬಹಳ ಕೆಲಸ ಎಲ್ಲ ಪ್ರತಿ ಇತ್ಯಾದಿ ಇವು ಬೇರೆ ಹೀಗೆ ನಡುವೆ ಇದಕ್ಕೆ ಎಸ್ ಇವರ ಮೊದಲು ಶ್ರೀ ಮಾಡುವ ಇದರಲ್ಲಿ ರೀತಿಯ ಮಾಡಿದ ಕಾಲ ಅಲ್ಲಿ ಮಾಡಲು ಅದೇ ಈಗ ಅವು ಗಳು ಎ ಎಂಬುದು ಅವನು ಅಂದರೆ ಅವರಿಗೆ ಇರುವ ವಿಶೇಷ ಮುಂದೆ ಅವುಗಳ ಮುಂತಾದ ಮೂಲ ಬಿ ಮೀ ಒಂದೇ ಇನ್ನೂ ಹೆಚ್ಚಾಗಿ ಮಾಡಿ ಅವರನ್ನು ಇದೇ ಯ ರೀತಿಯಲ್ಲಿ ಜೊತೆ ಅದರಲ್ಲಿ ಮಾಡಿದರು ನಡೆದ ಆಗ ಮತ್ತೆ ಪೂರ್ವ ಆತ ಬಂದ ಯಾವ ಒಟ್ಟು ಇತರೆ ಹಿಂದೆ ಪ್ರಮಾಣದ ಗಳನ್ನು ಕುರಿತು ಯು ಆದ್ದರಿಂದ ಅಲ್ಲದೆ ನಗರದ ಮೇಲಿನ ಏಕೆಂದರೆ ರಷ್ಟು ಎಂಬುದನ್ನು ಬಾರಿ ಎಂದರೆ ಹಿಂದಿನ ಆದರೂ ಆದ ಸಂಬಂಧಿಸಿದ ಮತ್ತೊಂದು ಸಿ ಆತನ ".split(" ")),e.kn.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.kn.tokenizer=function(t){if(!arguments.length||null==t||void 0==t)return[];if(Array.isArray(t))return t.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var n=t.toString().toLowerCase().replace(/^\s+/,"");return r.cut(n).split("|")},e.Pipeline.registerFunction(e.kn.stemmer,"stemmer-kn"),e.Pipeline.registerFunction(e.kn.stopWordFilter,"stopWordFilter-kn")}});
|
||||||
1
docs/_site/assets/javascripts/lunr/min/lunr.ko.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.ko.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.multi.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.multi.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){e.multiLanguage=function(){for(var t=Array.prototype.slice.call(arguments),i=t.join("-"),r="",n=[],s=[],p=0;p<t.length;++p)"en"==t[p]?(r+="\\w",n.unshift(e.stopWordFilter),n.push(e.stemmer),s.push(e.stemmer)):(r+=e[t[p]].wordCharacters,e[t[p]].stopWordFilter&&n.unshift(e[t[p]].stopWordFilter),e[t[p]].stemmer&&(n.push(e[t[p]].stemmer),s.push(e[t[p]].stemmer)));var o=e.trimmerSupport.generateTrimmer(r);return e.Pipeline.registerFunction(o,"lunr-multi-trimmer-"+i),n.unshift(o),function(){this.pipeline.reset(),this.pipeline.add.apply(this.pipeline,n),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add.apply(this.searchPipeline,s))}}}});
|
||||||
18
docs/_site/assets/javascripts/lunr/min/lunr.nl.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.nl.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/_site/assets/javascripts/lunr/min/lunr.no.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.no.min.js
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*!
|
||||||
|
* Lunr languages, `Norwegian` language
|
||||||
|
* https://github.com/MihaiValentin/lunr-languages
|
||||||
|
*
|
||||||
|
* Copyright 2014, Mihai Valentin
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
* based on
|
||||||
|
* Snowball JavaScript Library v0.3
|
||||||
|
* http://code.google.com/p/urim/
|
||||||
|
* http://snowball.tartarus.org/
|
||||||
|
*
|
||||||
|
* Copyright 2010, Oleg Mazko
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,r=w.cursor+3;if(a=w.limit,0<=r||r<=w.limit){for(s=r;;){if(e=w.cursor,w.in_grouping(d,97,248)){w.cursor=e;break}if(e>=w.limit)return;w.cursor=e+1}for(;!w.out_grouping(d,97,248);){if(w.cursor>=w.limit)return;w.cursor++}a=w.cursor,a<s&&(a=s)}}function i(){var e,r,n;if(w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(m,29),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:n=w.limit-w.cursor,w.in_grouping_b(c,98,122)?w.slice_del():(w.cursor=w.limit-n,w.eq_s_b(1,"k")&&w.out_grouping_b(d,97,248)&&w.slice_del());break;case 3:w.slice_from("er")}}function t(){var e,r=w.limit-w.cursor;w.cursor>=a&&(e=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,w.find_among_b(u,2)?(w.bra=w.cursor,w.limit_backward=e,w.cursor=w.limit-r,w.cursor>w.limit_backward&&(w.cursor--,w.bra=w.cursor,w.slice_del())):w.limit_backward=e)}function o(){var e,r;w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(l,11),e?(w.bra=w.cursor,w.limit_backward=r,1==e&&w.slice_del()):w.limit_backward=r)}var s,a,m=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],u=[new r("dt",-1,-1),new r("vt",-1,-1)],l=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],c=[119,125,149,1],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,i(),w.cursor=w.limit,t(),w.cursor=w.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}});
|
||||||
18
docs/_site/assets/javascripts/lunr/min/lunr.pt.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.pt.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/_site/assets/javascripts/lunr/min/lunr.ro.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.ro.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
docs/_site/assets/javascripts/lunr/min/lunr.ru.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.ru.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.sa.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.sa.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sa=function(){this.pipeline.reset(),this.pipeline.add(e.sa.trimmer,e.sa.stopWordFilter,e.sa.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sa.stemmer))},e.sa.wordCharacters="ऀ-ःऄ-एऐ-टठ-यर-िी-ॏॐ-य़ॠ-९॰-ॿ꣠-꣱ꣲ-ꣷ꣸-ꣻ꣼-ꣽꣾ-ꣿᆰ0-ᆰ9",e.sa.trimmer=e.trimmerSupport.generateTrimmer(e.sa.wordCharacters),e.Pipeline.registerFunction(e.sa.trimmer,"trimmer-sa"),e.sa.stopWordFilter=e.generateStopWordFilter('तथा अयम् एकम् इत्यस्मिन् तथा तत् वा अयम् इत्यस्य ते आहूत उपरि तेषाम् किन्तु तेषाम् तदा इत्यनेन अधिकः इत्यस्य तत् केचन बहवः द्वि तथा महत्वपूर्णः अयम् अस्य विषये अयं अस्ति तत् प्रथमः विषये इत्युपरि इत्युपरि इतर अधिकतमः अधिकः अपि सामान्यतया ठ इतरेतर नूतनम् द न्यूनम् कश्चित् वा विशालः द सः अस्ति तदनुसारम् तत्र अस्ति केवलम् अपि अत्र सर्वे विविधाः तत् बहवः यतः इदानीम् द दक्षिण इत्यस्मै तस्य उपरि नथ अतीव कार्यम् सर्वे एकैकम् इत्यादि। एते सन्ति उत इत्थम् मध्ये एतदर्थं . स कस्य प्रथमः श्री. करोति अस्मिन् प्रकारः निर्मिता कालः तत्र कर्तुं समान अधुना ते सन्ति स एकः अस्ति सः अर्थात् तेषां कृते . स्थितम् विशेषः अग्रिम तेषाम् समान स्रोतः ख म समान इदानीमपि अधिकतया करोतु ते समान इत्यस्य वीथी सह यस्मिन् कृतवान् धृतः तदा पुनः पूर्वं सः आगतः किम् कुल इतर पुरा मात्रा स विषये उ अतएव अपि नगरस्य उपरि यतः प्रतिशतं कतरः कालः साधनानि भूत तथापि जात सम्बन्धि अन्यत् ग अतः अस्माकं स्वकीयाः अस्माकं इदानीं अन्तः इत्यादयः भवन्तः इत्यादयः एते एताः तस्य अस्य इदम् एते तेषां तेषां तेषां तान् तेषां तेषां तेषां समानः सः एकः च तादृशाः बहवः अन्ये च वदन्ति यत् कियत् कस्मै कस्मै यस्मै यस्मै यस्मै यस्मै न अतिनीचः किन्तु प्रथमं सम्पूर्णतया ततः चिरकालानन्तरं पुस्तकं सम्पूर्णतया अन्तः किन्तु अत्र वा इह इव श्रद्धाय अवशिष्यते परन्तु अन्ये वर्गाः सन्ति ते सन्ति शक्नुवन्ति सर्वे मिलित्वा सर्वे एकत्र"'.split(" ")),e.sa.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.sa.tokenizer=function(t){if(!arguments.length||null==t||void 0==t)return[];if(Array.isArray(t))return t.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var i=t.toString().toLowerCase().replace(/^\s+/,"");return r.cut(i).split("|")},e.Pipeline.registerFunction(e.sa.stemmer,"stemmer-sa"),e.Pipeline.registerFunction(e.sa.stopWordFilter,"stopWordFilter-sa")}});
|
||||||
1
docs/_site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(r,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(r.lunr)}(this,function(){return function(r){r.stemmerSupport={Among:function(r,t,i,s){if(this.toCharArray=function(r){for(var t=r.length,i=new Array(t),s=0;s<t;s++)i[s]=r.charCodeAt(s);return i},!r&&""!=r||!t&&0!=t||!i)throw"Bad Among initialisation: s:"+r+", substring_i: "+t+", result: "+i;this.s_size=r.length,this.s=this.toCharArray(r),this.substring_i=t,this.result=i,this.method=s},SnowballProgram:function(){var r;return{bra:0,ket:0,limit:0,cursor:0,limit_backward:0,setCurrent:function(t){r=t,this.cursor=0,this.limit=t.length,this.limit_backward=0,this.bra=this.cursor,this.ket=this.limit},getCurrent:function(){var t=r;return r=null,t},in_grouping:function(t,i,s){if(this.cursor<this.limit){var e=r.charCodeAt(this.cursor);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursor<this.limit){var e=r.charCodeAt(this.cursor);if(e>s||e<i)return this.cursor++,!0;if(e-=i,!(t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e<i)return this.cursor--,!0;if(e-=i,!(t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor<t)return!1;for(var s=0;s<t;s++)if(r.charCodeAt(this.cursor+s)!=i.charCodeAt(s))return!1;return this.cursor+=t,!0},eq_s_b:function(t,i){if(this.cursor-this.limit_backward<t)return!1;for(var s=0;s<t;s++)if(r.charCodeAt(this.cursor-t+s)!=i.charCodeAt(s))return!1;return this.cursor-=t,!0},find_among:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o<h?o:h,_=t[a],m=l;m<_.s_size;m++){if(n+l==u){f=-1;break}if(f=r.charCodeAt(n+l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o<h?o:h,_=t[a],m=_.s_size-1-l;m>=0;m--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n-_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n-_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}});
|
||||||
18
docs/_site/assets/javascripts/lunr/min/lunr.sv.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.sv.min.js
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*!
|
||||||
|
* Lunr languages, `Swedish` language
|
||||||
|
* https://github.com/MihaiValentin/lunr-languages
|
||||||
|
*
|
||||||
|
* Copyright 2014, Mihai Valentin
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
* based on
|
||||||
|
* Snowball JavaScript Library v0.3
|
||||||
|
* http://code.google.com/p/urim/
|
||||||
|
* http://snowball.tartarus.org/
|
||||||
|
*
|
||||||
|
* Copyright 2010, Oleg Mazko
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){function e(){var e,r=w.cursor+3;if(o=w.limit,0<=r||r<=w.limit){for(a=r;;){if(e=w.cursor,w.in_grouping(l,97,246)){w.cursor=e;break}if(w.cursor=e,w.cursor>=w.limit)return;w.cursor++}for(;!w.out_grouping(l,97,246);){if(w.cursor>=w.limit)return;w.cursor++}o=w.cursor,o<a&&(o=a)}}function t(){var e,r=w.limit_backward;if(w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(u,37),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.in_grouping_b(d,98,121)&&w.slice_del()}}function i(){var e=w.limit_backward;w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.find_among_b(c,7)&&(w.cursor=w.limit,w.ket=w.cursor,w.cursor>w.limit_backward&&(w.bra=--w.cursor,w.slice_del())),w.limit_backward=e)}function s(){var e,r;if(w.cursor>=o){if(r=w.limit_backward,w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(m,5))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.slice_from("lös");break;case 3:w.slice_from("full")}w.limit_backward=r}}var a,o,u=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],c=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],l=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],d=[119,127,149],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,t(),w.cursor=w.limit,i(),w.cursor=w.limit,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}}(),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}});
|
||||||
1
docs/_site/assets/javascripts/lunr/min/lunr.ta.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.ta.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ta=function(){this.pipeline.reset(),this.pipeline.add(e.ta.trimmer,e.ta.stopWordFilter,e.ta.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ta.stemmer))},e.ta.wordCharacters="-உஊ-ஏஐ-ஙச-ட-னப-யர-ஹ-ிீ-ொ-ௐ---௩௪-௯௰-௹௺-a-zA-Za-zA-Z0-90-9",e.ta.trimmer=e.trimmerSupport.generateTrimmer(e.ta.wordCharacters),e.Pipeline.registerFunction(e.ta.trimmer,"trimmer-ta"),e.ta.stopWordFilter=e.generateStopWordFilter("அங்கு அங்கே அது அதை அந்த அவர் அவர்கள் அவள் அவன் அவை ஆக ஆகவே ஆகையால் ஆதலால் ஆதலினால் ஆனாலும் ஆனால் இங்கு இங்கே இது இதை இந்த இப்படி இவர் இவர்கள் இவள் இவன் இவை இவ்வளவு உனக்கு உனது உன் உன்னால் எங்கு எங்கே எது எதை எந்த எப்படி எவர் எவர்கள் எவள் எவன் எவை எவ்வளவு எனக்கு எனது எனவே என் என்ன என்னால் ஏது ஏன் தனது தன்னால் தானே தான் நாங்கள் நாம் நான் நீ நீங்கள்".split(" ")),e.ta.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.ta.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.ta.stemmer,"stemmer-ta"),e.Pipeline.registerFunction(e.ta.stopWordFilter,"stopWordFilter-ta")}});
|
||||||
1
docs/_site/assets/javascripts/lunr/min/lunr.te.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.te.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.te=function(){this.pipeline.reset(),this.pipeline.add(e.te.trimmer,e.te.stopWordFilter,e.te.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.te.stemmer))},e.te.wordCharacters="ఀ-ఄఅ-ఔక-హా-ౌౕ-ౖౘ-ౚౠ-ౡౢ-ౣ౦-౯౸-౿఼ఽ్ౝ౷",e.te.trimmer=e.trimmerSupport.generateTrimmer(e.te.wordCharacters),e.Pipeline.registerFunction(e.te.trimmer,"trimmer-te"),e.te.stopWordFilter=e.generateStopWordFilter("అందరూ అందుబాటులో అడగండి అడగడం అడ్డంగా అనుగుణంగా అనుమతించు అనుమతిస్తుంది అయితే ఇప్పటికే ఉన్నారు ఎక్కడైనా ఎప్పుడు ఎవరైనా ఎవరో ఏ ఏదైనా ఏమైనప్పటికి ఒక ఒకరు కనిపిస్తాయి కాదు కూడా గా గురించి చుట్టూ చేయగలిగింది తగిన తర్వాత దాదాపు దూరంగా నిజంగా పై ప్రకారం ప్రక్కన మధ్య మరియు మరొక మళ్ళీ మాత్రమే మెచ్చుకో వద్ద వెంట వేరుగా వ్యతిరేకంగా సంబంధం".split(" ")),e.te.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.te.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.te.stemmer,"stemmer-te"),e.Pipeline.registerFunction(e.te.stopWordFilter,"stopWordFilter-te")}});
|
||||||
1
docs/_site/assets/javascripts/lunr/min/lunr.th.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.th.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.th=function(){this.pipeline.reset(),this.pipeline.add(e.th.trimmer),r?this.tokenizer=e.th.tokenizer:(e.tokenizer&&(e.tokenizer=e.th.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.th.tokenizer))},e.th.wordCharacters="[-]",e.th.trimmer=e.trimmerSupport.generateTrimmer(e.th.wordCharacters),e.Pipeline.registerFunction(e.th.trimmer,"trimmer-th");var t=e.wordcut;t.init(),e.th.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t):t});var n=i.toString().replace(/^\s+/,"");return t.cut(n).split("|")}}});
|
||||||
18
docs/_site/assets/javascripts/lunr/min/lunr.tr.min.js
vendored
Normal file
18
docs/_site/assets/javascripts/lunr/min/lunr.tr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/javascripts/lunr/min/lunr.vi.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.vi.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-ẓ̀͐́͑̉̃̓ÂâÊêÔôĂ-ăĐ-đƠ-ơƯ-ư]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("là cái nhưng mà".split(" "))}});
|
||||||
1
docs/_site/assets/javascripts/lunr/min/lunr.zh.min.js
vendored
Normal file
1
docs/_site/assets/javascripts/lunr/min/lunr.zh.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r(require("@node-rs/jieba")):r()(e.lunr)}(this,function(e){return function(r,t){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i="2"==r.version[0];r.zh=function(){this.pipeline.reset(),this.pipeline.add(r.zh.trimmer,r.zh.stopWordFilter,r.zh.stemmer),i?this.tokenizer=r.zh.tokenizer:(r.tokenizer&&(r.tokenizer=r.zh.tokenizer),this.tokenizerFn&&(this.tokenizerFn=r.zh.tokenizer))},r.zh.tokenizer=function(n){if(!arguments.length||null==n||void 0==n)return[];if(Array.isArray(n))return n.map(function(e){return i?new r.Token(e.toLowerCase()):e.toLowerCase()});t&&e.load(t);var o=n.toString().trim().toLowerCase(),s=[];e.cut(o,!0).forEach(function(e){s=s.concat(e.split(" "))}),s=s.filter(function(e){return!!e});var u=0;return s.map(function(e,t){if(i){var n=o.indexOf(e,u),s={};return s.position=[n,e.length],s.index=t,u=n,new r.Token(e,s)}return e})},r.zh.wordCharacters="\\w一-龥",r.zh.trimmer=r.trimmerSupport.generateTrimmer(r.zh.wordCharacters),r.Pipeline.registerFunction(r.zh.trimmer,"trimmer-zh"),r.zh.stemmer=function(){return function(e){return e}}(),r.Pipeline.registerFunction(r.zh.stemmer,"stemmer-zh"),r.zh.stopWordFilter=r.generateStopWordFilter("的 一 不 在 人 有 是 为 為 以 于 於 上 他 而 后 後 之 来 來 及 了 因 下 可 到 由 这 這 与 與 也 此 但 并 並 个 個 其 已 无 無 小 我 们 們 起 最 再 今 去 好 只 又 或 很 亦 某 把 那 你 乃 它 吧 被 比 别 趁 当 當 从 從 得 打 凡 儿 兒 尔 爾 该 該 各 给 給 跟 和 何 还 還 即 几 幾 既 看 据 據 距 靠 啦 另 么 麽 每 嘛 拿 哪 您 凭 憑 且 却 卻 让 讓 仍 啥 如 若 使 谁 誰 虽 雖 随 隨 同 所 她 哇 嗡 往 些 向 沿 哟 喲 用 咱 则 則 怎 曾 至 致 着 著 诸 諸 自".split(" ")),r.Pipeline.registerFunction(r.zh.stopWordFilter,"stopWordFilter-zh")}});
|
||||||
206
docs/_site/assets/javascripts/lunr/tinyseg.js
Normal file
206
docs/_site/assets/javascripts/lunr/tinyseg.js
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
/**
|
||||||
|
* export the module via AMD, CommonJS or as a browser global
|
||||||
|
* Export code from https://github.com/umdjs/umd/blob/master/returnExports.js
|
||||||
|
*/
|
||||||
|
;(function (root, factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define(factory)
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
/**
|
||||||
|
* Node. Does not work with strict CommonJS, but
|
||||||
|
* only CommonJS-like environments that support module.exports,
|
||||||
|
* like Node.
|
||||||
|
*/
|
||||||
|
module.exports = factory()
|
||||||
|
} else {
|
||||||
|
// Browser globals (root is window)
|
||||||
|
factory()(root.lunr);
|
||||||
|
}
|
||||||
|
}(this, function () {
|
||||||
|
/**
|
||||||
|
* Just return a value to define the module export.
|
||||||
|
* This example returns an object, but the module
|
||||||
|
* can return a function as the exported value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return function(lunr) {
|
||||||
|
// TinySegmenter 0.1 -- Super compact Japanese tokenizer in Javascript
|
||||||
|
// (c) 2008 Taku Kudo <taku@chasen.org>
|
||||||
|
// TinySegmenter is freely distributable under the terms of a new BSD licence.
|
||||||
|
// For details, see http://chasen.org/~taku/software/TinySegmenter/LICENCE.txt
|
||||||
|
|
||||||
|
function TinySegmenter() {
|
||||||
|
var patterns = {
|
||||||
|
"[一二三四五六七八九十百千万億兆]":"M",
|
||||||
|
"[一-龠々〆ヵヶ]":"H",
|
||||||
|
"[ぁ-ん]":"I",
|
||||||
|
"[ァ-ヴーア-ン゙ー]":"K",
|
||||||
|
"[a-zA-Za-zA-Z]":"A",
|
||||||
|
"[0-90-9]":"N"
|
||||||
|
}
|
||||||
|
this.chartype_ = [];
|
||||||
|
for (var i in patterns) {
|
||||||
|
var regexp = new RegExp(i);
|
||||||
|
this.chartype_.push([regexp, patterns[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.BIAS__ = -332
|
||||||
|
this.BC1__ = {"HH":6,"II":2461,"KH":406,"OH":-1378};
|
||||||
|
this.BC2__ = {"AA":-3267,"AI":2744,"AN":-878,"HH":-4070,"HM":-1711,"HN":4012,"HO":3761,"IA":1327,"IH":-1184,"II":-1332,"IK":1721,"IO":5492,"KI":3831,"KK":-8741,"MH":-3132,"MK":3334,"OO":-2920};
|
||||||
|
this.BC3__ = {"HH":996,"HI":626,"HK":-721,"HN":-1307,"HO":-836,"IH":-301,"KK":2762,"MK":1079,"MM":4034,"OA":-1652,"OH":266};
|
||||||
|
this.BP1__ = {"BB":295,"OB":304,"OO":-125,"UB":352};
|
||||||
|
this.BP2__ = {"BO":60,"OO":-1762};
|
||||||
|
this.BQ1__ = {"BHH":1150,"BHM":1521,"BII":-1158,"BIM":886,"BMH":1208,"BNH":449,"BOH":-91,"BOO":-2597,"OHI":451,"OIH":-296,"OKA":1851,"OKH":-1020,"OKK":904,"OOO":2965};
|
||||||
|
this.BQ2__ = {"BHH":118,"BHI":-1159,"BHM":466,"BIH":-919,"BKK":-1720,"BKO":864,"OHH":-1139,"OHM":-181,"OIH":153,"UHI":-1146};
|
||||||
|
this.BQ3__ = {"BHH":-792,"BHI":2664,"BII":-299,"BKI":419,"BMH":937,"BMM":8335,"BNN":998,"BOH":775,"OHH":2174,"OHM":439,"OII":280,"OKH":1798,"OKI":-793,"OKO":-2242,"OMH":-2402,"OOO":11699};
|
||||||
|
this.BQ4__ = {"BHH":-3895,"BIH":3761,"BII":-4654,"BIK":1348,"BKK":-1806,"BMI":-3385,"BOO":-12396,"OAH":926,"OHH":266,"OHK":-2036,"ONN":-973};
|
||||||
|
this.BW1__ = {",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682};
|
||||||
|
this.BW2__ = {"..":-11822,"11":-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669};
|
||||||
|
this.BW3__ = {"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1000,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990};
|
||||||
|
this.TC1__ = {"AAA":1093,"HHH":1029,"HHM":580,"HII":998,"HOH":-390,"HOM":-331,"IHI":1169,"IOH":-142,"IOI":-1015,"IOM":467,"MMH":187,"OOI":-1832};
|
||||||
|
this.TC2__ = {"HHO":2088,"HII":-1023,"HMM":-1154,"IHI":-1965,"KKH":703,"OII":-2649};
|
||||||
|
this.TC3__ = {"AAA":-294,"HHH":346,"HHI":-341,"HII":-1088,"HIK":731,"HOH":-1486,"IHH":128,"IHI":-3041,"IHO":-1935,"IIH":-825,"IIM":-1035,"IOI":-542,"KHH":-1216,"KKA":491,"KKH":-1217,"KOK":-1009,"MHH":-2694,"MHM":-457,"MHO":123,"MMH":-471,"NNH":-1689,"NNO":662,"OHO":-3393};
|
||||||
|
this.TC4__ = {"HHH":-203,"HHI":1344,"HHK":365,"HHM":-122,"HHN":182,"HHO":669,"HIH":804,"HII":679,"HOH":446,"IHH":695,"IHO":-2324,"IIH":321,"III":1497,"IIO":656,"IOO":54,"KAK":4845,"KKA":3386,"KKK":3065,"MHH":-405,"MHI":201,"MMH":-241,"MMM":661,"MOM":841};
|
||||||
|
this.TQ1__ = {"BHHH":-227,"BHHI":316,"BHIH":-132,"BIHH":60,"BIII":1595,"BNHH":-744,"BOHH":225,"BOOO":-908,"OAKK":482,"OHHH":281,"OHIH":249,"OIHI":200,"OIIH":-68};
|
||||||
|
this.TQ2__ = {"BIHH":-1401,"BIII":-1033,"BKAK":-543,"BOOO":-5591};
|
||||||
|
this.TQ3__ = {"BHHH":478,"BHHM":-1073,"BHIH":222,"BHII":-504,"BIIH":-116,"BIII":-105,"BMHI":-863,"BMHM":-464,"BOMH":620,"OHHH":346,"OHHI":1729,"OHII":997,"OHMH":481,"OIHH":623,"OIIH":1344,"OKAK":2792,"OKHH":587,"OKKA":679,"OOHH":110,"OOII":-685};
|
||||||
|
this.TQ4__ = {"BHHH":-721,"BHHM":-3604,"BHII":-966,"BIIH":-607,"BIII":-2181,"OAAA":-2763,"OAKK":180,"OHHH":-294,"OHHI":2446,"OHHO":480,"OHIH":-1573,"OIHH":1935,"OIHI":-493,"OIIH":626,"OIII":-4007,"OKAK":-8156};
|
||||||
|
this.TW1__ = {"につい":-4681,"東京都":2026};
|
||||||
|
this.TW2__ = {"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216};
|
||||||
|
this.TW3__ = {"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287};
|
||||||
|
this.TW4__ = {"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865};
|
||||||
|
this.UC1__ = {"A":484,"K":93,"M":645,"O":-505};
|
||||||
|
this.UC2__ = {"A":819,"H":1059,"I":409,"M":3987,"N":5775,"O":646};
|
||||||
|
this.UC3__ = {"A":-1370,"I":2311};
|
||||||
|
this.UC4__ = {"A":-2643,"H":1809,"I":-1032,"K":-3450,"M":3565,"N":3876,"O":6646};
|
||||||
|
this.UC5__ = {"H":313,"I":-1238,"K":-799,"M":539,"O":-831};
|
||||||
|
this.UC6__ = {"H":-506,"I":-253,"K":87,"M":247,"O":-387};
|
||||||
|
this.UP1__ = {"O":-214};
|
||||||
|
this.UP2__ = {"B":69,"O":935};
|
||||||
|
this.UP3__ = {"B":189};
|
||||||
|
this.UQ1__ = {"BH":21,"BI":-12,"BK":-99,"BN":142,"BO":-56,"OH":-95,"OI":477,"OK":410,"OO":-2422};
|
||||||
|
this.UQ2__ = {"BH":216,"BI":113,"OK":1759};
|
||||||
|
this.UQ3__ = {"BA":-479,"BH":42,"BI":1913,"BK":-7198,"BM":3160,"BN":6427,"BO":14761,"OI":-827,"ON":-3212};
|
||||||
|
this.UW1__ = {",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135};
|
||||||
|
this.UW2__ = {",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568};
|
||||||
|
this.UW3__ = {",":4889,"1":-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278};
|
||||||
|
this.UW4__ = {",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1000,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637};
|
||||||
|
this.UW5__ = {",":465,".":-299,"1":-514,"E2":-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343};
|
||||||
|
this.UW6__ = {",":227,".":808,"1":-270,"E1":306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
TinySegmenter.prototype.ctype_ = function(str) {
|
||||||
|
for (var i in this.chartype_) {
|
||||||
|
if (str.match(this.chartype_[i][0])) {
|
||||||
|
return this.chartype_[i][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "O";
|
||||||
|
}
|
||||||
|
|
||||||
|
TinySegmenter.prototype.ts_ = function(v) {
|
||||||
|
if (v) { return v; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TinySegmenter.prototype.segment = function(input) {
|
||||||
|
if (input == null || input == undefined || input == "") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var result = [];
|
||||||
|
var seg = ["B3","B2","B1"];
|
||||||
|
var ctype = ["O","O","O"];
|
||||||
|
var o = input.split("");
|
||||||
|
for (i = 0; i < o.length; ++i) {
|
||||||
|
seg.push(o[i]);
|
||||||
|
ctype.push(this.ctype_(o[i]))
|
||||||
|
}
|
||||||
|
seg.push("E1");
|
||||||
|
seg.push("E2");
|
||||||
|
seg.push("E3");
|
||||||
|
ctype.push("O");
|
||||||
|
ctype.push("O");
|
||||||
|
ctype.push("O");
|
||||||
|
var word = seg[3];
|
||||||
|
var p1 = "U";
|
||||||
|
var p2 = "U";
|
||||||
|
var p3 = "U";
|
||||||
|
for (var i = 4; i < seg.length - 3; ++i) {
|
||||||
|
var score = this.BIAS__;
|
||||||
|
var w1 = seg[i-3];
|
||||||
|
var w2 = seg[i-2];
|
||||||
|
var w3 = seg[i-1];
|
||||||
|
var w4 = seg[i];
|
||||||
|
var w5 = seg[i+1];
|
||||||
|
var w6 = seg[i+2];
|
||||||
|
var c1 = ctype[i-3];
|
||||||
|
var c2 = ctype[i-2];
|
||||||
|
var c3 = ctype[i-1];
|
||||||
|
var c4 = ctype[i];
|
||||||
|
var c5 = ctype[i+1];
|
||||||
|
var c6 = ctype[i+2];
|
||||||
|
score += this.ts_(this.UP1__[p1]);
|
||||||
|
score += this.ts_(this.UP2__[p2]);
|
||||||
|
score += this.ts_(this.UP3__[p3]);
|
||||||
|
score += this.ts_(this.BP1__[p1 + p2]);
|
||||||
|
score += this.ts_(this.BP2__[p2 + p3]);
|
||||||
|
score += this.ts_(this.UW1__[w1]);
|
||||||
|
score += this.ts_(this.UW2__[w2]);
|
||||||
|
score += this.ts_(this.UW3__[w3]);
|
||||||
|
score += this.ts_(this.UW4__[w4]);
|
||||||
|
score += this.ts_(this.UW5__[w5]);
|
||||||
|
score += this.ts_(this.UW6__[w6]);
|
||||||
|
score += this.ts_(this.BW1__[w2 + w3]);
|
||||||
|
score += this.ts_(this.BW2__[w3 + w4]);
|
||||||
|
score += this.ts_(this.BW3__[w4 + w5]);
|
||||||
|
score += this.ts_(this.TW1__[w1 + w2 + w3]);
|
||||||
|
score += this.ts_(this.TW2__[w2 + w3 + w4]);
|
||||||
|
score += this.ts_(this.TW3__[w3 + w4 + w5]);
|
||||||
|
score += this.ts_(this.TW4__[w4 + w5 + w6]);
|
||||||
|
score += this.ts_(this.UC1__[c1]);
|
||||||
|
score += this.ts_(this.UC2__[c2]);
|
||||||
|
score += this.ts_(this.UC3__[c3]);
|
||||||
|
score += this.ts_(this.UC4__[c4]);
|
||||||
|
score += this.ts_(this.UC5__[c5]);
|
||||||
|
score += this.ts_(this.UC6__[c6]);
|
||||||
|
score += this.ts_(this.BC1__[c2 + c3]);
|
||||||
|
score += this.ts_(this.BC2__[c3 + c4]);
|
||||||
|
score += this.ts_(this.BC3__[c4 + c5]);
|
||||||
|
score += this.ts_(this.TC1__[c1 + c2 + c3]);
|
||||||
|
score += this.ts_(this.TC2__[c2 + c3 + c4]);
|
||||||
|
score += this.ts_(this.TC3__[c3 + c4 + c5]);
|
||||||
|
score += this.ts_(this.TC4__[c4 + c5 + c6]);
|
||||||
|
// score += this.ts_(this.TC5__[c4 + c5 + c6]);
|
||||||
|
score += this.ts_(this.UQ1__[p1 + c1]);
|
||||||
|
score += this.ts_(this.UQ2__[p2 + c2]);
|
||||||
|
score += this.ts_(this.UQ3__[p3 + c3]);
|
||||||
|
score += this.ts_(this.BQ1__[p2 + c2 + c3]);
|
||||||
|
score += this.ts_(this.BQ2__[p2 + c3 + c4]);
|
||||||
|
score += this.ts_(this.BQ3__[p3 + c2 + c3]);
|
||||||
|
score += this.ts_(this.BQ4__[p3 + c3 + c4]);
|
||||||
|
score += this.ts_(this.TQ1__[p2 + c1 + c2 + c3]);
|
||||||
|
score += this.ts_(this.TQ2__[p2 + c2 + c3 + c4]);
|
||||||
|
score += this.ts_(this.TQ3__[p3 + c1 + c2 + c3]);
|
||||||
|
score += this.ts_(this.TQ4__[p3 + c2 + c3 + c4]);
|
||||||
|
var p = "O";
|
||||||
|
if (score > 0) {
|
||||||
|
result.push(word);
|
||||||
|
word = "";
|
||||||
|
p = "B";
|
||||||
|
}
|
||||||
|
p1 = p2;
|
||||||
|
p2 = p3;
|
||||||
|
p3 = p;
|
||||||
|
word += seg[i];
|
||||||
|
}
|
||||||
|
result.push(word);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
lunr.TinySegmenter = TinySegmenter;
|
||||||
|
};
|
||||||
|
|
||||||
|
}));
|
||||||
6708
docs/_site/assets/javascripts/lunr/wordcut.js
Normal file
6708
docs/_site/assets/javascripts/lunr/wordcut.js
Normal file
File diff suppressed because one or more lines are too long
42
docs/_site/assets/javascripts/workers/search.7a47a382.min.js
vendored
Normal file
42
docs/_site/assets/javascripts/workers/search.7a47a382.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/_site/assets/stylesheets/main.618322db.min.css
vendored
Normal file
1
docs/_site/assets/stylesheets/main.618322db.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/stylesheets/main.618322db.min.css.map
Normal file
1
docs/_site/assets/stylesheets/main.618322db.min.css.map
Normal file
File diff suppressed because one or more lines are too long
1
docs/_site/assets/stylesheets/palette.ab4e12ef.min.css
vendored
Normal file
1
docs/_site/assets/stylesheets/palette.ab4e12ef.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sources":["src/templates/assets/stylesheets/palette/_scheme.scss","../../../../src/templates/assets/stylesheets/palette.scss","src/templates/assets/stylesheets/palette/_accent.scss","src/templates/assets/stylesheets/palette/_primary.scss","src/templates/assets/stylesheets/utilities/_break.scss"],"names":[],"mappings":"AA2BA,cAGE,6BAME,sDAAA,CACA,6DAAA,CACA,+DAAA,CACA,gEAAA,CACA,mDAAA,CACA,6DAAA,CACA,+DAAA,CACA,gEAAA,CAGA,mDAAA,CACA,gDAAA,CACA,yDAAA,CACA,4DAAA,CAGA,0BAAA,CACA,mCAAA,CAGA,iCAAA,CACA,kCAAA,CACA,mCAAA,CACA,mCAAA,CACA,kCAAA,CACA,iCAAA,CACA,+CAAA,CACA,6DAAA,CACA,gEAAA,CACA,4DAAA,CACA,4DAAA,CACA,6DAAA,CAGA,6CAAA,CAGA,+CAAA,CAGA,uDAAA,CACA,6DAAA,CACA,2DAAA,CAGA,iCAAA,CAGA,yDAAA,CACA,iEAAA,CAGA,mDAAA,CACA,mDAAA,CAGA,qDAAA,CACA,uDAAA,CAGA,8DAAA,CAKA,8DAAA,CAKA,0DAAA,CAzEA,iBCiBF,CD6DE,kHAEE,YC3DJ,CDkFE,yDACE,4BChFJ,CD+EE,2DACE,4BC7EJ,CD4EE,gEACE,4BC1EJ,CDyEE,2DACE,4BCvEJ,CDsEE,yDACE,4BCpEJ,CDmEE,0DACE,4BCjEJ,CDgEE,gEACE,4BC9DJ,CD6DE,0DACE,4BC3DJ,CD0DE,2OACE,4BC/CJ,CDsDA,+FAGE,iCCpDF,CACF,CCjDE,2BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD6CN,CCvDE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDoDN,CC9DE,8BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD2DN,CCrEE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDkEN,CC5EE,8BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDyEN,CCnFE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDgFN,CC1FE,kCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDuFN,CCjGE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD8FN,CCxGE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDqGN,CC/GE,6BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD4GN,CCtHE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDmHN,CC7HE,4BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCD6HN,CCpIE,8BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCDoIN,CC3IE,6BACE,yBAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCD2IN,CClJE,8BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCDkJN,CCzJE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDsJN,CE3JE,4BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwJN,CEnKE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgKN,CE3KE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwKN,CEnLE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgLN,CE3LE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwLN,CEnME,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgMN,CE3ME,mCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwMN,CEnNE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgNN,CE3NE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwNN,CEnOE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgON,CE3OE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwON,CEnPE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCFmPN,CE3PE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCF2PN,CEnQE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCFmQN,CE3QE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCF2QN,CEnRE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgRN,CE3RE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwRN,CEnSE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCAAA,CAKA,4BF4RN,CE5SE,kCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCAAA,CAKA,4BFqSN,CEtRE,sEACE,4BFyRJ,CE1RE,+DACE,4BF6RJ,CE9RE,iEACE,4BFiSJ,CElSE,gEACE,4BFqSJ,CEtSE,iEACE,4BFySJ,CEhSA,8BACE,mDAAA,CACA,4DAAA,CACA,0DAAA,CACA,oDAAA,CACA,2DAAA,CAGA,4BFiSF,CE9RE,yCACE,+BFgSJ,CE7RI,kDAEE,0CAAA,CACA,sCAAA,CAFA,mCFiSN,CG7MI,mCD1EA,+CACE,8CF0RJ,CEvRI,qDACE,8CFyRN,CEpRE,iEACE,mCFsRJ,CACF,CGxNI,sCDvDA,uCACE,oCFkRJ,CACF,CEzQA,8BACE,kDAAA,CACA,4DAAA,CACA,wDAAA,CACA,oDAAA,CACA,6DAAA,CAGA,4BF0QF,CEvQE,yCACE,+BFyQJ,CEtQI,kDAEE,0CAAA,CACA,sCAAA,CAFA,mCF0QN,CEnQE,yCACE,6CFqQJ,CG9NI,0CDhCA,8CACE,gDFiQJ,CACF,CGnOI,0CDvBA,iFACE,6CF6PJ,CACF,CG3PI,sCDKA,uCACE,6CFyPJ,CACF","file":"palette.css"}
|
||||||
1859
docs/_site/cicd/index.html
Normal file
1859
docs/_site/cicd/index.html
Normal file
File diff suppressed because it is too large
Load diff
2288
docs/_site/cli-reference/index.html
Normal file
2288
docs/_site/cli-reference/index.html
Normal file
File diff suppressed because it is too large
Load diff
1607
docs/_site/configuration-schema/index.html
Normal file
1607
docs/_site/configuration-schema/index.html
Normal file
File diff suppressed because it is too large
Load diff
2290
docs/_site/deployment/index.html
Normal file
2290
docs/_site/deployment/index.html
Normal file
File diff suppressed because it is too large
Load diff
1635
docs/_site/development-setup/index.html
Normal file
1635
docs/_site/development-setup/index.html
Normal file
File diff suppressed because it is too large
Load diff
1898
docs/_site/environment-variables/index.html
Normal file
1898
docs/_site/environment-variables/index.html
Normal file
File diff suppressed because it is too large
Load diff
1937
docs/_site/first-experiment/index.html
Normal file
1937
docs/_site/first-experiment/index.html
Normal file
File diff suppressed because it is too large
Load diff
1713
docs/_site/index.html
Normal file
1713
docs/_site/index.html
Normal file
File diff suppressed because it is too large
Load diff
1639
docs/_site/installation/index.html
Normal file
1639
docs/_site/installation/index.html
Normal file
File diff suppressed because it is too large
Load diff
2105
docs/_site/operations/index.html
Normal file
2105
docs/_site/operations/index.html
Normal file
File diff suppressed because it is too large
Load diff
2003
docs/_site/production-monitoring/index.html
Normal file
2003
docs/_site/production-monitoring/index.html
Normal file
File diff suppressed because it is too large
Load diff
2140
docs/_site/queue/index.html
Normal file
2140
docs/_site/queue/index.html
Normal file
File diff suppressed because it is too large
Load diff
1662
docs/_site/quick-start/index.html
Normal file
1662
docs/_site/quick-start/index.html
Normal file
File diff suppressed because it is too large
Load diff
1661
docs/_site/redis-ha/index.html
Normal file
1661
docs/_site/redis-ha/index.html
Normal file
File diff suppressed because it is too large
Load diff
1617
docs/_site/release-checklist/index.html
Normal file
1617
docs/_site/release-checklist/index.html
Normal file
File diff suppressed because it is too large
Load diff
1
docs/_site/search/search_index.json
Normal file
1
docs/_site/search/search_index.json
Normal file
File diff suppressed because one or more lines are too long
2124
docs/_site/security/index.html
Normal file
2124
docs/_site/security/index.html
Normal file
File diff suppressed because it is too large
Load diff
3
docs/_site/sitemap.xml
Normal file
3
docs/_site/sitemap.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
</urlset>
|
||||||
BIN
docs/_site/sitemap.xml.gz
Normal file
BIN
docs/_site/sitemap.xml.gz
Normal file
Binary file not shown.
1959
docs/_site/smart-defaults/index.html
Normal file
1959
docs/_site/smart-defaults/index.html
Normal file
File diff suppressed because it is too large
Load diff
1608
docs/_site/testing/index.html
Normal file
1608
docs/_site/testing/index.html
Normal file
File diff suppressed because it is too large
Load diff
1781
docs/_site/troubleshooting/index.html
Normal file
1781
docs/_site/troubleshooting/index.html
Normal file
File diff suppressed because it is too large
Load diff
1810
docs/_site/user-permissions/index.html
Normal file
1810
docs/_site/user-permissions/index.html
Normal file
File diff suppressed because it is too large
Load diff
2491
docs/_site/zig-cli/index.html
Normal file
2491
docs/_site/zig-cli/index.html
Normal file
File diff suppressed because it is too large
Load diff
71
docs/mkdocs.yml
Normal file
71
docs/mkdocs.yml
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
site_name: Fetch ML Documentation
|
||||||
|
site_description: Secure Machine Learning Platform
|
||||||
|
repo_url: https://github.com/jfraeys/fetch_ml
|
||||||
|
edit_uri: edit/main/docs/
|
||||||
|
|
||||||
|
docs_dir: src
|
||||||
|
site_dir: _site
|
||||||
|
|
||||||
|
nav:
|
||||||
|
- Home: index.md
|
||||||
|
- Getting Started:
|
||||||
|
- quick-start.md
|
||||||
|
- installation.md
|
||||||
|
- first-experiment.md
|
||||||
|
- Development:
|
||||||
|
- development-setup.md
|
||||||
|
- testing.md
|
||||||
|
- architecture.md
|
||||||
|
- cli-reference.md
|
||||||
|
- zig-cli.md
|
||||||
|
- queue.md
|
||||||
|
- smart-defaults.md
|
||||||
|
- cicd.md
|
||||||
|
- Operations & Production:
|
||||||
|
- deployment.md
|
||||||
|
- environment-variables.md
|
||||||
|
- production-monitoring.md
|
||||||
|
- operations.md
|
||||||
|
- redis-ha.md
|
||||||
|
- release-checklist.md
|
||||||
|
- Security:
|
||||||
|
- security.md
|
||||||
|
- api-key-process.md
|
||||||
|
- user-permissions.md
|
||||||
|
- Reference:
|
||||||
|
- configuration-schema.md
|
||||||
|
- troubleshooting.md
|
||||||
|
|
||||||
|
theme:
|
||||||
|
name: material
|
||||||
|
palette:
|
||||||
|
- scheme: default
|
||||||
|
primary: blue
|
||||||
|
accent: blue
|
||||||
|
- scheme: slate
|
||||||
|
primary: blue
|
||||||
|
accent: blue
|
||||||
|
font:
|
||||||
|
text: Roboto
|
||||||
|
code: Roboto Mono
|
||||||
|
features:
|
||||||
|
- navigation.instant
|
||||||
|
- navigation.tracking
|
||||||
|
- navigation.tabs
|
||||||
|
- navigation.sections
|
||||||
|
- navigation.expand
|
||||||
|
- navigation.indexes
|
||||||
|
- toc.integrate
|
||||||
|
- search.highlight
|
||||||
|
- search.share
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- search
|
||||||
|
|
||||||
|
markdown_extensions:
|
||||||
|
- codehilite
|
||||||
|
- admonition
|
||||||
|
- toc:
|
||||||
|
permalink: true
|
||||||
|
- pymdownx.superfences
|
||||||
|
- pymdownx.highlight
|
||||||
126
docs/src/api-key-process.md
Normal file
126
docs/src/api-key-process.md
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
# FetchML API Key Process
|
||||||
|
|
||||||
|
This document describes how API keys are issued and how team members should configure the `ml` CLI to use them.
|
||||||
|
|
||||||
|
The goal is to keep access easy for your homelab while treating API keys as sensitive secrets.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
- Each user gets a **personal API key** (no shared admin keys for normal use).
|
||||||
|
- API keys are used by the `ml` CLI to authenticate to the FetchML API.
|
||||||
|
- API keys and their **SHA256 hashes** must both be treated as secrets.
|
||||||
|
|
||||||
|
There are two supported ways to receive your key:
|
||||||
|
|
||||||
|
1. **Bitwarden (recommended)** – for users who already use Bitwarden.
|
||||||
|
2. **Direct share (minimal tools)** – for users who do not use Bitwarden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Bitwarden-based process (recommended)
|
||||||
|
|
||||||
|
### For the admin
|
||||||
|
|
||||||
|
- Use the helper script to create a Bitwarden item for each user:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/create_bitwarden_fetchml_item.sh <username> <api_key> <api_key_hash>
|
||||||
|
```
|
||||||
|
|
||||||
|
This script:
|
||||||
|
|
||||||
|
- Creates a Bitwarden item named `FetchML API – <username>`.
|
||||||
|
- Stores:
|
||||||
|
- Username: `<username>`
|
||||||
|
- Password: `<api_key>` (the actual API key)
|
||||||
|
- Custom field `api_key_hash`: `<api_key_hash>`
|
||||||
|
|
||||||
|
- Share that item with the user in Bitwarden (for example, via a shared collection like `FetchML`).
|
||||||
|
|
||||||
|
### For the user
|
||||||
|
|
||||||
|
1. Open Bitwarden and locate the item:
|
||||||
|
|
||||||
|
- **Name:** `FetchML API – <your-name>`
|
||||||
|
|
||||||
|
2. Copy the **password** field (this is your FetchML API key).
|
||||||
|
|
||||||
|
3. Configure the CLI, e.g. in `~/.ml/config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
api_key = "<paste-from-bitwarden>"
|
||||||
|
worker_host = "localhost"
|
||||||
|
worker_port = 9100
|
||||||
|
api_url = "ws://localhost:9100/ws"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Test your setup:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
If the command works, your key and tunnel/config are correct.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Direct share (no password manager required)
|
||||||
|
|
||||||
|
For users who do not use Bitwarden, a lightweight alternative is a direct one-to-one share.
|
||||||
|
|
||||||
|
### For the admin
|
||||||
|
|
||||||
|
1. Generate a **per-user** API key and hash as usual.
|
||||||
|
2. Store them securely on your side (for example, in your own Bitwarden vault or configuration files).
|
||||||
|
3. Share **only the API key** with the user via a direct channel you both trust, such as:
|
||||||
|
|
||||||
|
- Signal / WhatsApp direct message
|
||||||
|
- SMS
|
||||||
|
- Short call/meeting where you read it to them
|
||||||
|
|
||||||
|
4. Ask the user to:
|
||||||
|
|
||||||
|
- Paste the key into their local config.
|
||||||
|
- Avoid keeping the key in plain chat history if possible.
|
||||||
|
|
||||||
|
### For the user
|
||||||
|
|
||||||
|
1. When you receive your FetchML API key from the admin, create or edit `~/.ml/config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
api_key = "<your-api-key>"
|
||||||
|
worker_host = "localhost"
|
||||||
|
worker_port = 9100
|
||||||
|
api_url = "ws://localhost:9100/ws"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Save the file and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
3. If it works, you are ready to use the CLI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ml queue my-training-job
|
||||||
|
ml cancel my-training-job
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Security notes
|
||||||
|
|
||||||
|
- **API key and hash are secrets**
|
||||||
|
- The 64-character `api_key_hash` is as sensitive as the API key itself.
|
||||||
|
- Do not commit keys or hashes to Git or share them in screenshots or tickets.
|
||||||
|
|
||||||
|
- **Rotation**
|
||||||
|
- If you suspect a key has leaked, notify the admin.
|
||||||
|
- The admin will revoke the old key, generate a new one, and update Bitwarden or share a new key.
|
||||||
|
|
||||||
|
- **Transport security**
|
||||||
|
- The `api_url` is typically `ws://localhost:9100/ws` when used through an SSH tunnel to the homelab.
|
||||||
|
- The SSH tunnel and nginx/TLS provide encryption over the network.
|
||||||
|
|
||||||
|
Following these steps keeps API access easy for the team while maintaining a reasonable security posture for a personal homelab deployment.
|
||||||
738
docs/src/architecture.md
Normal file
738
docs/src/architecture.md
Normal file
|
|
@ -0,0 +1,738 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Homelab Architecture"
|
||||||
|
permalink: /architecture/
|
||||||
|
nav_order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Homelab Architecture
|
||||||
|
|
||||||
|
Simple, secure architecture for ML experiments in your homelab.
|
||||||
|
|
||||||
|
## Components Overview
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Homelab Stack"
|
||||||
|
CLI[Zig CLI]
|
||||||
|
API[HTTPS API]
|
||||||
|
REDIS[Redis Cache]
|
||||||
|
FS[Local Storage]
|
||||||
|
end
|
||||||
|
|
||||||
|
CLI --> API
|
||||||
|
API --> REDIS
|
||||||
|
API --> FS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Services
|
||||||
|
|
||||||
|
### API Server
|
||||||
|
- **Purpose**: Secure HTTPS API for ML experiments
|
||||||
|
- **Port**: 9101 (HTTPS only)
|
||||||
|
- **Auth**: API key authentication
|
||||||
|
- **Security**: Rate limiting, IP whitelisting
|
||||||
|
|
||||||
|
### Redis
|
||||||
|
- **Purpose**: Caching and job queuing
|
||||||
|
- **Port**: 6379 (localhost only)
|
||||||
|
- **Storage**: Temporary data only
|
||||||
|
- **Persistence**: Local volume
|
||||||
|
|
||||||
|
### Zig CLI
|
||||||
|
- **Purpose**: High-performance experiment management
|
||||||
|
- **Language**: Zig for maximum speed and efficiency
|
||||||
|
- **Features**:
|
||||||
|
- Content-addressed storage with deduplication
|
||||||
|
- SHA256-based commit ID generation
|
||||||
|
- WebSocket communication for real-time updates
|
||||||
|
- Rsync-based incremental file transfers
|
||||||
|
- Multi-threaded operations
|
||||||
|
- Secure API key authentication
|
||||||
|
- Auto-sync monitoring with file system watching
|
||||||
|
- Priority-based job queuing
|
||||||
|
- Memory-efficient operations with arena allocators
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
USER[User] --> AUTH[API Key Auth]
|
||||||
|
AUTH --> RATE[Rate Limiting]
|
||||||
|
RATE --> WHITELIST[IP Whitelist]
|
||||||
|
WHITELIST --> API[Secure API]
|
||||||
|
API --> AUDIT[Audit Logging]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security Layers
|
||||||
|
1. **API Key Authentication** - Hashed keys with roles
|
||||||
|
2. **Rate Limiting** - 30 requests/minute
|
||||||
|
3. **IP Whitelisting** - Local networks only
|
||||||
|
4. **Fail2Ban** - Automatic IP blocking
|
||||||
|
5. **HTTPS/TLS** - Encrypted communication
|
||||||
|
6. **Audit Logging** - Complete action tracking
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant CLI
|
||||||
|
participant API
|
||||||
|
participant Redis
|
||||||
|
participant Storage
|
||||||
|
|
||||||
|
CLI->>API: HTTPS Request
|
||||||
|
API->>API: Validate Auth
|
||||||
|
API->>Redis: Cache/Queue
|
||||||
|
API->>Storage: Experiment Data
|
||||||
|
Storage->>API: Results
|
||||||
|
API->>CLI: Response
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment Options
|
||||||
|
|
||||||
|
### Docker Compose (Recommended)
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
ports: ["6379:6379"]
|
||||||
|
volumes: [redis_data:/data]
|
||||||
|
|
||||||
|
api-server:
|
||||||
|
build: .
|
||||||
|
ports: ["9101:9101"]
|
||||||
|
depends_on: [redis]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Setup
|
||||||
|
```bash
|
||||||
|
./setup.sh && ./manage.sh start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network Architecture
|
||||||
|
|
||||||
|
- **Private Network**: Docker internal network
|
||||||
|
- **Localhost Access**: Redis only on localhost
|
||||||
|
- **HTTPS API**: Port 9101, TLS encrypted
|
||||||
|
- **No External Dependencies**: Everything runs locally
|
||||||
|
|
||||||
|
## Storage Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
data/
|
||||||
|
├── experiments/ # ML experiment results
|
||||||
|
├── cache/ # Temporary cache files
|
||||||
|
└── backups/ # Local backups
|
||||||
|
|
||||||
|
logs/
|
||||||
|
├── app.log # Application logs
|
||||||
|
├── audit.log # Security events
|
||||||
|
└── access.log # API access logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring Architecture
|
||||||
|
|
||||||
|
Simple, lightweight monitoring:
|
||||||
|
- **Health Checks**: Service availability
|
||||||
|
- **Log Files**: Structured logging
|
||||||
|
- **Basic Metrics**: Request counts, error rates
|
||||||
|
- **Security Events**: Failed auth, rate limits
|
||||||
|
|
||||||
|
## Homelab Benefits
|
||||||
|
|
||||||
|
- ✅ **Simple Setup**: One-command installation
|
||||||
|
- ✅ **Local Only**: No external dependencies
|
||||||
|
- ✅ **Secure by Default**: HTTPS, auth, rate limiting
|
||||||
|
- ✅ **Low Resource**: Minimal CPU/memory usage
|
||||||
|
- ✅ **Easy Backup**: Local file system
|
||||||
|
- ✅ **Privacy**: Everything stays on your network
|
||||||
|
|
||||||
|
## High-Level Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Client Layer"
|
||||||
|
CLI[CLI Tools]
|
||||||
|
TUI[Terminal UI]
|
||||||
|
API[REST API]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Authentication Layer"
|
||||||
|
Auth[Authentication Service]
|
||||||
|
RBAC[Role-Based Access Control]
|
||||||
|
Perm[Permission Manager]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Core Services"
|
||||||
|
Worker[ML Worker Service]
|
||||||
|
DataMgr[Data Manager Service]
|
||||||
|
Queue[Job Queue]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Storage Layer"
|
||||||
|
Redis[(Redis Cache)]
|
||||||
|
DB[(SQLite/PostgreSQL)]
|
||||||
|
Files[File Storage]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Container Runtime"
|
||||||
|
Podman[Podman/Docker]
|
||||||
|
Containers[ML Containers]
|
||||||
|
end
|
||||||
|
|
||||||
|
CLI --> Auth
|
||||||
|
TUI --> Auth
|
||||||
|
API --> Auth
|
||||||
|
|
||||||
|
Auth --> RBAC
|
||||||
|
RBAC --> Perm
|
||||||
|
|
||||||
|
Worker --> Queue
|
||||||
|
Worker --> DataMgr
|
||||||
|
Worker --> Podman
|
||||||
|
|
||||||
|
DataMgr --> DB
|
||||||
|
DataMgr --> Files
|
||||||
|
|
||||||
|
Queue --> Redis
|
||||||
|
|
||||||
|
Podman --> Containers
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zig CLI Architecture
|
||||||
|
|
||||||
|
### Component Structure
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Zig CLI Components"
|
||||||
|
Main[main.zig] --> Commands[commands/]
|
||||||
|
Commands --> Config[config.zig]
|
||||||
|
Commands --> Utils[utils/]
|
||||||
|
Commands --> Net[net/]
|
||||||
|
Commands --> Errors[errors.zig]
|
||||||
|
|
||||||
|
subgraph "Commands"
|
||||||
|
Init[init.zig]
|
||||||
|
Sync[sync.zig]
|
||||||
|
Queue[queue.zig]
|
||||||
|
Watch[watch.zig]
|
||||||
|
Status[status.zig]
|
||||||
|
Monitor[monitor.zig]
|
||||||
|
Cancel[cancel.zig]
|
||||||
|
Prune[prune.zig]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Utils"
|
||||||
|
Crypto[crypto.zig]
|
||||||
|
Storage[storage.zig]
|
||||||
|
Rsync[rsync.zig]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Network"
|
||||||
|
WS[ws.zig]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Optimizations
|
||||||
|
|
||||||
|
#### Content-Addressed Storage
|
||||||
|
- **Deduplication**: Files stored by SHA256 hash
|
||||||
|
- **Space Efficiency**: Shared files across experiments
|
||||||
|
- **Fast Lookup**: Hash-based file retrieval
|
||||||
|
|
||||||
|
#### Memory Management
|
||||||
|
- **Arena Allocators**: Efficient bulk allocation
|
||||||
|
- **Zero-Copy Operations**: Minimized memory copying
|
||||||
|
- **Automatic Cleanup**: Resource deallocation
|
||||||
|
|
||||||
|
#### Network Communication
|
||||||
|
- **WebSocket Protocol**: Real-time bidirectional communication
|
||||||
|
- **Connection Pooling**: Reused connections
|
||||||
|
- **Binary Messaging**: Efficient data transfer
|
||||||
|
|
||||||
|
### Security Implementation
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
subgraph "CLI Security"
|
||||||
|
Config[Config File] --> Hash[SHA256 Hashing]
|
||||||
|
Hash --> Auth[API Authentication]
|
||||||
|
Auth --> SSH[SSH Transfer]
|
||||||
|
SSH --> WS[WebSocket Security]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### 1. Authentication & Authorization
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
subgraph "Auth Flow"
|
||||||
|
Client[Client] --> APIKey[API Key]
|
||||||
|
APIKey --> Hash[Hash Validation]
|
||||||
|
Hash --> Roles[Role Resolution]
|
||||||
|
Roles --> Perms[Permission Check]
|
||||||
|
Perms --> Access[Grant/Deny Access]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Permission Sources"
|
||||||
|
YAML[YAML Config]
|
||||||
|
Inline[Inline Fallback]
|
||||||
|
Roles --> YAML
|
||||||
|
Roles --> Inline
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- API key-based authentication
|
||||||
|
- Role-based access control (RBAC)
|
||||||
|
- YAML-based permission configuration
|
||||||
|
- Fallback to inline permissions
|
||||||
|
- Admin wildcard permissions
|
||||||
|
|
||||||
|
### 2. Worker Service
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Worker Architecture"
|
||||||
|
API[HTTP API] --> Router[Request Router]
|
||||||
|
Router --> Auth[Auth Middleware]
|
||||||
|
Auth --> Queue[Job Queue]
|
||||||
|
Queue --> Processor[Job Processor]
|
||||||
|
Processor --> Runtime[Container Runtime]
|
||||||
|
Runtime --> Storage[Result Storage]
|
||||||
|
|
||||||
|
subgraph "Job Lifecycle"
|
||||||
|
Submit[Submit Job] --> Queue
|
||||||
|
Queue --> Execute[Execute]
|
||||||
|
Execute --> Monitor[Monitor]
|
||||||
|
Monitor --> Complete[Complete]
|
||||||
|
Complete --> Store[Store Results]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responsibilities:**
|
||||||
|
- HTTP API for job submission
|
||||||
|
- Job queue management
|
||||||
|
- Container orchestration
|
||||||
|
- Result collection and storage
|
||||||
|
- Metrics and monitoring
|
||||||
|
|
||||||
|
### 3. Data Manager Service
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Data Management"
|
||||||
|
API[Data API] --> Storage[Storage Layer]
|
||||||
|
Storage --> Metadata[Metadata DB]
|
||||||
|
Storage --> Files[File System]
|
||||||
|
Storage --> Cache[Redis Cache]
|
||||||
|
|
||||||
|
subgraph "Data Operations"
|
||||||
|
Upload[Upload Data] --> Validate[Validate]
|
||||||
|
Validate --> Store[Store]
|
||||||
|
Store --> Index[Index]
|
||||||
|
Index --> Catalog[Catalog]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Data upload and validation
|
||||||
|
- Metadata management
|
||||||
|
- File system abstraction
|
||||||
|
- Caching layer
|
||||||
|
- Data catalog
|
||||||
|
|
||||||
|
### 4. Terminal UI (TUI)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "TUI Architecture"
|
||||||
|
UI[UI Components] --> Model[Data Model]
|
||||||
|
Model --> Update[Update Loop]
|
||||||
|
Update --> Render[Render]
|
||||||
|
|
||||||
|
subgraph "UI Panels"
|
||||||
|
Jobs[Job List]
|
||||||
|
Details[Job Details]
|
||||||
|
Logs[Log Viewer]
|
||||||
|
Status[Status Bar]
|
||||||
|
end
|
||||||
|
|
||||||
|
UI --> Jobs
|
||||||
|
UI --> Details
|
||||||
|
UI --> Logs
|
||||||
|
UI --> Status
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Components:**
|
||||||
|
- Bubble Tea framework
|
||||||
|
- Component-based architecture
|
||||||
|
- Real-time updates
|
||||||
|
- Keyboard navigation
|
||||||
|
- Theme support
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### Job Execution Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant Auth
|
||||||
|
participant Worker
|
||||||
|
participant Queue
|
||||||
|
participant Container
|
||||||
|
participant Storage
|
||||||
|
|
||||||
|
Client->>Auth: Submit job with API key
|
||||||
|
Auth->>Client: Validate and return job ID
|
||||||
|
|
||||||
|
Client->>Worker: Execute job request
|
||||||
|
Worker->>Queue: Queue job
|
||||||
|
Queue->>Worker: Job ready
|
||||||
|
Worker->>Container: Start ML container
|
||||||
|
Container->>Worker: Execute experiment
|
||||||
|
Worker->>Storage: Store results
|
||||||
|
Worker->>Client: Return results
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant Auth
|
||||||
|
participant PermMgr
|
||||||
|
participant Config
|
||||||
|
|
||||||
|
Client->>Auth: Request with API key
|
||||||
|
Auth->>Auth: Validate key hash
|
||||||
|
Auth->>PermMgr: Get user permissions
|
||||||
|
PermMgr->>Config: Load YAML permissions
|
||||||
|
Config->>PermMgr: Return permissions
|
||||||
|
PermMgr->>Auth: Return resolved permissions
|
||||||
|
Auth->>Client: Grant/deny access
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
### Defense in Depth
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Security Layers"
|
||||||
|
Network[Network Security]
|
||||||
|
Auth[Authentication]
|
||||||
|
AuthZ[Authorization]
|
||||||
|
Container[Container Security]
|
||||||
|
Data[Data Protection]
|
||||||
|
Audit[Audit Logging]
|
||||||
|
end
|
||||||
|
|
||||||
|
Network --> Auth
|
||||||
|
Auth --> AuthZ
|
||||||
|
AuthZ --> Container
|
||||||
|
Container --> Data
|
||||||
|
Data --> Audit
|
||||||
|
```
|
||||||
|
|
||||||
|
**Security Features:**
|
||||||
|
- API key authentication
|
||||||
|
- Role-based permissions
|
||||||
|
- Container isolation
|
||||||
|
- File system sandboxing
|
||||||
|
- Comprehensive audit logs
|
||||||
|
- Input validation and sanitization
|
||||||
|
|
||||||
|
### Container Security
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Container Isolation"
|
||||||
|
Host[Host System]
|
||||||
|
Podman[Podman Runtime]
|
||||||
|
Network[Network Isolation]
|
||||||
|
FS[File System Isolation]
|
||||||
|
User[User Namespaces]
|
||||||
|
ML[ML Container]
|
||||||
|
|
||||||
|
Host --> Podman
|
||||||
|
Podman --> Network
|
||||||
|
Podman --> FS
|
||||||
|
Podman --> User
|
||||||
|
User --> ML
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Isolation Features:**
|
||||||
|
- Rootless containers
|
||||||
|
- Network isolation
|
||||||
|
- File system sandboxing
|
||||||
|
- User namespace mapping
|
||||||
|
- Resource limits
|
||||||
|
|
||||||
|
## Configuration Architecture
|
||||||
|
|
||||||
|
### Configuration Hierarchy
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Config Sources"
|
||||||
|
Env[Environment Variables]
|
||||||
|
File[Config Files]
|
||||||
|
CLI[CLI Flags]
|
||||||
|
Defaults[Default Values]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Config Processing"
|
||||||
|
Merge[Config Merger]
|
||||||
|
Validate[Schema Validator]
|
||||||
|
Apply[Config Applier]
|
||||||
|
end
|
||||||
|
|
||||||
|
Env --> Merge
|
||||||
|
File --> Merge
|
||||||
|
CLI --> Merge
|
||||||
|
Defaults --> Merge
|
||||||
|
|
||||||
|
Merge --> Validate
|
||||||
|
Validate --> Apply
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configuration Priority:**
|
||||||
|
1. CLI flags (highest)
|
||||||
|
2. Environment variables
|
||||||
|
3. Configuration files
|
||||||
|
4. Default values (lowest)
|
||||||
|
|
||||||
|
## Scalability Architecture
|
||||||
|
|
||||||
|
### Horizontal Scaling
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Scaled Architecture"
|
||||||
|
LB[Load Balancer]
|
||||||
|
W1[Worker 1]
|
||||||
|
W2[Worker 2]
|
||||||
|
W3[Worker N]
|
||||||
|
Redis[Redis Cluster]
|
||||||
|
Storage[Shared Storage]
|
||||||
|
|
||||||
|
LB --> W1
|
||||||
|
LB --> W2
|
||||||
|
LB --> W3
|
||||||
|
|
||||||
|
W1 --> Redis
|
||||||
|
W2 --> Redis
|
||||||
|
W3 --> Redis
|
||||||
|
|
||||||
|
W1 --> Storage
|
||||||
|
W2 --> Storage
|
||||||
|
W3 --> Storage
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Scaling Features:**
|
||||||
|
- Stateless worker services
|
||||||
|
- Shared job queue (Redis)
|
||||||
|
- Distributed storage
|
||||||
|
- Load balancer ready
|
||||||
|
- Health checks and monitoring
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
### Backend Technologies
|
||||||
|
|
||||||
|
| Component | Technology | Purpose |
|
||||||
|
|-----------|------------|---------|
|
||||||
|
| **Language** | Go 1.25+ | Core application |
|
||||||
|
| **Web Framework** | Standard library | HTTP server |
|
||||||
|
| **Authentication** | Custom | API key + RBAC |
|
||||||
|
| **Database** | SQLite/PostgreSQL | Metadata storage |
|
||||||
|
| **Cache** | Redis | Job queue & caching |
|
||||||
|
| **Containers** | Podman/Docker | Job isolation |
|
||||||
|
| **UI Framework** | Bubble Tea | Terminal UI |
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Core dependencies
|
||||||
|
require (
|
||||||
|
github.com/charmbracelet/bubbletea v1.3.10 // TUI framework
|
||||||
|
github.com/go-redis/redis/v8 v8.11.5 // Redis client
|
||||||
|
github.com/google/uuid v1.6.0 // UUID generation
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32 // SQLite driver
|
||||||
|
golang.org/x/crypto v0.45.0 // Crypto utilities
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // YAML parsing
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Architecture
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
fetch_ml/
|
||||||
|
├── cmd/ # CLI applications
|
||||||
|
│ ├── worker/ # ML worker service
|
||||||
|
│ ├── tui/ # Terminal UI
|
||||||
|
│ ├── data_manager/ # Data management
|
||||||
|
│ └── user_manager/ # User management
|
||||||
|
├── internal/ # Internal packages
|
||||||
|
│ ├── auth/ # Authentication system
|
||||||
|
│ ├── config/ # Configuration management
|
||||||
|
│ ├── container/ # Container operations
|
||||||
|
│ ├── database/ # Database operations
|
||||||
|
│ ├── logging/ # Logging utilities
|
||||||
|
│ ├── metrics/ # Metrics collection
|
||||||
|
│ └── network/ # Network utilities
|
||||||
|
├── configs/ # Configuration files
|
||||||
|
├── scripts/ # Setup and utility scripts
|
||||||
|
├── tests/ # Test suites
|
||||||
|
└── docs/ # Documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Package Dependencies
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Application Layer"
|
||||||
|
Worker[cmd/worker]
|
||||||
|
TUI[cmd/tui]
|
||||||
|
DataMgr[cmd/data_manager]
|
||||||
|
UserMgr[cmd/user_manager]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Service Layer"
|
||||||
|
Auth[internal/auth]
|
||||||
|
Config[internal/config]
|
||||||
|
Container[internal/container]
|
||||||
|
Database[internal/database]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Utility Layer"
|
||||||
|
Logging[internal/logging]
|
||||||
|
Metrics[internal/metrics]
|
||||||
|
Network[internal/network]
|
||||||
|
end
|
||||||
|
|
||||||
|
Worker --> Auth
|
||||||
|
Worker --> Config
|
||||||
|
Worker --> Container
|
||||||
|
TUI --> Auth
|
||||||
|
DataMgr --> Database
|
||||||
|
UserMgr --> Auth
|
||||||
|
|
||||||
|
Auth --> Logging
|
||||||
|
Container --> Network
|
||||||
|
Database --> Metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring & Observability
|
||||||
|
|
||||||
|
### Metrics Collection
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Metrics Pipeline"
|
||||||
|
App[Application] --> Metrics[Metrics Collector]
|
||||||
|
Metrics --> Export[Prometheus Exporter]
|
||||||
|
Export --> Prometheus[Prometheus Server]
|
||||||
|
Prometheus --> Grafana[Grafana Dashboard]
|
||||||
|
|
||||||
|
subgraph "Metric Types"
|
||||||
|
Counter[Counters]
|
||||||
|
Gauge[Gauges]
|
||||||
|
Histogram[Histograms]
|
||||||
|
Timer[Timers]
|
||||||
|
end
|
||||||
|
|
||||||
|
App --> Counter
|
||||||
|
App --> Gauge
|
||||||
|
App --> Histogram
|
||||||
|
App --> Timer
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logging Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Logging Pipeline"
|
||||||
|
App[Application] --> Logger[Structured Logger]
|
||||||
|
Logger --> File[File Output]
|
||||||
|
Logger --> Console[Console Output]
|
||||||
|
Logger --> Syslog[Syslog Forwarder]
|
||||||
|
Syslog --> Aggregator[Log Aggregator]
|
||||||
|
Aggregator --> Storage[Log Storage]
|
||||||
|
Storage --> Viewer[Log Viewer]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment Architecture
|
||||||
|
|
||||||
|
### Container Deployment
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Deployment Stack"
|
||||||
|
Image[Container Image]
|
||||||
|
Registry[Container Registry]
|
||||||
|
Orchestrator[Docker Compose]
|
||||||
|
Config[ConfigMaps/Secrets]
|
||||||
|
Storage[Persistent Storage]
|
||||||
|
|
||||||
|
Image --> Registry
|
||||||
|
Registry --> Orchestrator
|
||||||
|
Config --> Orchestrator
|
||||||
|
Storage --> Orchestrator
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Discovery
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Service Mesh"
|
||||||
|
Gateway[API Gateway]
|
||||||
|
Discovery[Service Discovery]
|
||||||
|
Worker[Worker Service]
|
||||||
|
Data[Data Service]
|
||||||
|
Redis[Redis Cluster]
|
||||||
|
|
||||||
|
Gateway --> Discovery
|
||||||
|
Discovery --> Worker
|
||||||
|
Discovery --> Data
|
||||||
|
Discovery --> Redis
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future Architecture Considerations
|
||||||
|
|
||||||
|
### Microservices Evolution
|
||||||
|
|
||||||
|
- **API Gateway**: Centralized routing and authentication
|
||||||
|
- **Service Mesh**: Inter-service communication
|
||||||
|
- **Event Streaming**: Kafka for job events
|
||||||
|
- **Distributed Tracing**: OpenTelemetry integration
|
||||||
|
- **Multi-tenant**: Tenant isolation and quotas
|
||||||
|
|
||||||
|
### Homelab Features
|
||||||
|
|
||||||
|
- **Docker Compose**: Simple container orchestration
|
||||||
|
- **Local Development**: Easy setup and testing
|
||||||
|
- **Security**: Built-in authentication and encryption
|
||||||
|
- **Monitoring**: Basic health checks and logging
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This architecture provides a solid foundation for secure, scalable machine learning experiments while maintaining simplicity and developer productivity.
|
||||||
165
docs/src/cicd.md
Normal file
165
docs/src/cicd.md
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "CI/CD Pipeline"
|
||||||
|
permalink: /cicd/
|
||||||
|
nav_order: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
# CI/CD Pipeline
|
||||||
|
|
||||||
|
Automated testing, building, and releasing for fetch_ml.
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### CI Workflow (`.github/workflows/ci.yml`)
|
||||||
|
|
||||||
|
Runs on every push to `main`/`develop` and all pull requests.
|
||||||
|
|
||||||
|
**Jobs:**
|
||||||
|
1. **test** - Go backend tests with Redis
|
||||||
|
2. **build** - Build all binaries (Go + Zig CLI)
|
||||||
|
3. **test-scripts** - Validate deployment scripts
|
||||||
|
4. **security-scan** - Trivy and Gosec security scans
|
||||||
|
5. **docker-build** - Build and push Docker images (main branch only)
|
||||||
|
|
||||||
|
**Test Coverage:**
|
||||||
|
- Go unit tests with race detection
|
||||||
|
- `internal/queue` package tests
|
||||||
|
- Zig CLI tests
|
||||||
|
- Integration tests
|
||||||
|
- Security audits
|
||||||
|
|
||||||
|
### Release Workflow (`.github/workflows/release.yml`)
|
||||||
|
|
||||||
|
Runs on version tags (e.g., `v1.0.0`).
|
||||||
|
|
||||||
|
**Jobs:**
|
||||||
|
|
||||||
|
1. **build-cli** (matrix build)
|
||||||
|
- Linux x86_64 (static musl)
|
||||||
|
- macOS x86_64
|
||||||
|
- macOS ARM64
|
||||||
|
- Downloads platform-specific static rsync
|
||||||
|
- Embeds rsync for zero-dependency releases
|
||||||
|
|
||||||
|
2. **build-go-backends**
|
||||||
|
- Cross-platform Go builds
|
||||||
|
- api-server, worker, tui, data_manager, user_manager
|
||||||
|
|
||||||
|
3. **create-release**
|
||||||
|
- Collects all artifacts
|
||||||
|
- Generates SHA256 checksums
|
||||||
|
- Creates GitHub release with notes
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
### Creating a Release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Update version
|
||||||
|
git tag v1.0.0
|
||||||
|
|
||||||
|
# 2. Push tag
|
||||||
|
git push origin v1.0.0
|
||||||
|
|
||||||
|
# 3. CI automatically builds and releases
|
||||||
|
```
|
||||||
|
|
||||||
|
### Release Artifacts
|
||||||
|
|
||||||
|
**CLI Binaries (with embedded rsync):**
|
||||||
|
- `ml-linux-x86_64.tar.gz` (~450-650KB)
|
||||||
|
- `ml-macos-x86_64.tar.gz` (~450-650KB)
|
||||||
|
- `ml-macos-arm64.tar.gz` (~450-650KB)
|
||||||
|
|
||||||
|
**Go Backends:**
|
||||||
|
- `fetch_ml_api-server.tar.gz`
|
||||||
|
- `fetch_ml_worker.tar.gz`
|
||||||
|
- `fetch_ml_tui.tar.gz`
|
||||||
|
- `fetch_ml_data_manager.tar.gz`
|
||||||
|
- `fetch_ml_user_manager.tar.gz`
|
||||||
|
|
||||||
|
**Checksums:**
|
||||||
|
- `checksums.txt` - Combined SHA256 sums
|
||||||
|
- Individual `.sha256` files per binary
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Local Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Run specific package tests
|
||||||
|
go test ./internal/queue/...
|
||||||
|
|
||||||
|
# Build CLI
|
||||||
|
cd cli && zig build dev
|
||||||
|
|
||||||
|
# Run formatters and linters
|
||||||
|
make lint
|
||||||
|
|
||||||
|
# Security scans are handled automatically in CI by the `security-scan` job
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Optional heavy end-to-end tests
|
||||||
|
|
||||||
|
Some e2e tests exercise full Docker deployments and performance scenarios and are
|
||||||
|
**skipped by default** to keep local/CI runs fast. You can enable them explicitly
|
||||||
|
with environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run Docker deployment e2e tests
|
||||||
|
FETCH_ML_E2E_DOCKER=1 go test ./tests/e2e/...
|
||||||
|
|
||||||
|
# Run performance-oriented e2e tests
|
||||||
|
FETCH_ML_E2E_PERF=1 go test ./tests/e2e/...
|
||||||
|
```
|
||||||
|
|
||||||
|
Without these variables, `TestDockerDeploymentE2E` and `TestPerformanceE2E` will
|
||||||
|
`t.Skip`, while all lighter e2e tests still run.
|
||||||
|
|
||||||
|
### Pull Request Checks
|
||||||
|
|
||||||
|
All PRs must pass:
|
||||||
|
- ✅ Go tests (with Redis)
|
||||||
|
- ✅ CLI tests
|
||||||
|
- ✅ Security scans
|
||||||
|
- ✅ Code linting
|
||||||
|
- ✅ Build verification
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
GO_VERSION: '1.25.0'
|
||||||
|
ZIG_VERSION: '0.15.2'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secrets
|
||||||
|
|
||||||
|
Required for releases:
|
||||||
|
- `GITHUB_TOKEN` - Automatic, provided by GitHub Actions
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Build Status
|
||||||
|
|
||||||
|
Check workflow runs at:
|
||||||
|
```
|
||||||
|
https://github.com/jfraeys/fetch_ml/actions
|
||||||
|
```
|
||||||
|
|
||||||
|
### Artifacts
|
||||||
|
|
||||||
|
Download build artifacts from:
|
||||||
|
- Successful workflow runs (30-day retention)
|
||||||
|
- GitHub Releases (permanent)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For implementation details:
|
||||||
|
- [.github/workflows/ci.yml](https://github.com/jfraeys/fetch_ml/blob/main/.github/workflows/ci.yml)
|
||||||
|
- [.github/workflows/release.yml](https://github.com/jfraeys/fetch_ml/blob/main/.github/workflows/release.yml)
|
||||||
404
docs/src/cli-reference.md
Normal file
404
docs/src/cli-reference.md
Normal file
|
|
@ -0,0 +1,404 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "CLI Reference"
|
||||||
|
permalink: /cli-reference/
|
||||||
|
nav_order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fetch ML CLI Reference
|
||||||
|
|
||||||
|
Comprehensive command-line tools for managing ML experiments in your homelab with Zig-based high-performance CLI.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Fetch ML provides a comprehensive CLI toolkit built with performance and security in mind:
|
||||||
|
|
||||||
|
- **Zig CLI** - High-performance experiment management written in Zig
|
||||||
|
- **Go Commands** - API server, TUI, and data management utilities
|
||||||
|
- **Management Scripts** - Service orchestration and deployment
|
||||||
|
- **Setup Scripts** - One-command installation and configuration
|
||||||
|
|
||||||
|
## Zig CLI (`./cli/zig-out/bin/ml`)
|
||||||
|
|
||||||
|
High-performance command-line interface for experiment management, written in Zig for speed and efficiency.
|
||||||
|
|
||||||
|
### Available Commands
|
||||||
|
|
||||||
|
| Command | Description | Example |
|
||||||
|
|---------|-------------|----------|
|
||||||
|
| `init` | Interactive configuration setup | `ml init` |
|
||||||
|
| `sync` | Sync project to worker with deduplication | `ml sync ./project --name myjob --queue` |
|
||||||
|
| `queue` | Queue job for execution | `ml queue myjob --commit abc123 --priority 8` |
|
||||||
|
| `status` | Get system and worker status | `ml status` |
|
||||||
|
| `monitor` | Launch TUI monitoring via SSH | `ml monitor` |
|
||||||
|
| `cancel` | Cancel running job | `ml cancel job123` |
|
||||||
|
| `prune` | Clean up old experiments | `ml prune --keep 10` |
|
||||||
|
| `watch` | Auto-sync directory on changes | `ml watch ./project --queue` |
|
||||||
|
|
||||||
|
### Command Details
|
||||||
|
|
||||||
|
#### `init` - Configuration Setup
|
||||||
|
```bash
|
||||||
|
ml init
|
||||||
|
```
|
||||||
|
Creates a configuration template at `~/.ml/config.toml` with:
|
||||||
|
- Worker connection details
|
||||||
|
- API authentication
|
||||||
|
- Base paths and ports
|
||||||
|
|
||||||
|
#### `sync` - Project Synchronization
|
||||||
|
```bash
|
||||||
|
# Basic sync
|
||||||
|
ml sync ./my-project
|
||||||
|
|
||||||
|
# Sync with custom name and queue
|
||||||
|
ml sync ./my-project --name "experiment-1" --queue
|
||||||
|
|
||||||
|
# Sync with priority
|
||||||
|
ml sync ./my-project --priority 9
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Content-addressed storage for deduplication
|
||||||
|
- SHA256 commit ID generation
|
||||||
|
- Rsync-based file transfer
|
||||||
|
- Automatic queuing (with `--queue` flag)
|
||||||
|
|
||||||
|
#### `queue` - Job Management
|
||||||
|
```bash
|
||||||
|
# Queue with commit ID
|
||||||
|
ml queue my-job --commit abc123def456
|
||||||
|
|
||||||
|
# Queue with priority (1-10, default 5)
|
||||||
|
ml queue my-job --commit abc123 --priority 8
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- WebSocket-based communication
|
||||||
|
- Priority queuing system
|
||||||
|
- API key authentication
|
||||||
|
|
||||||
|
#### `watch` - Auto-Sync Monitoring
|
||||||
|
```bash
|
||||||
|
# Watch directory for changes
|
||||||
|
ml watch ./project
|
||||||
|
|
||||||
|
# Watch and auto-queue on changes
|
||||||
|
ml watch ./project --name "dev-exp" --queue
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Real-time file system monitoring
|
||||||
|
- Automatic re-sync on changes
|
||||||
|
- Configurable polling interval (2 seconds)
|
||||||
|
- Commit ID comparison for efficiency
|
||||||
|
|
||||||
|
#### `prune` - Cleanup Management
|
||||||
|
```bash
|
||||||
|
# Keep last N experiments
|
||||||
|
ml prune --keep 20
|
||||||
|
|
||||||
|
# Remove experiments older than N days
|
||||||
|
ml prune --older-than 30
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `monitor` - Remote Monitoring
|
||||||
|
```bash
|
||||||
|
ml monitor
|
||||||
|
```
|
||||||
|
Launches TUI interface via SSH for real-time monitoring.
|
||||||
|
|
||||||
|
#### `cancel` - Job Cancellation
|
||||||
|
```bash
|
||||||
|
ml cancel running-job-id
|
||||||
|
```
|
||||||
|
Cancels currently running jobs by ID.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
The Zig CLI reads configuration from `~/.ml/config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
worker_host = "worker.local"
|
||||||
|
worker_user = "mluser"
|
||||||
|
worker_base = "/data/ml-experiments"
|
||||||
|
worker_port = 22
|
||||||
|
api_key = "your-api-key"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Features
|
||||||
|
|
||||||
|
- **Content-Addressed Storage**: Automatic deduplication of identical files
|
||||||
|
- **Incremental Sync**: Only transfers changed files
|
||||||
|
- **SHA256 Hashing**: Reliable commit ID generation
|
||||||
|
- **WebSocket Communication**: Efficient real-time messaging
|
||||||
|
- **Multi-threaded**: Concurrent operations where applicable
|
||||||
|
|
||||||
|
## Go Commands
|
||||||
|
|
||||||
|
### API Server (`./cmd/api-server/main.go`)
|
||||||
|
Main HTTPS API server for experiment management.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build and run
|
||||||
|
go run ./cmd/api-server/main.go
|
||||||
|
|
||||||
|
# With configuration
|
||||||
|
./bin/api-server --config configs/config-local.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- HTTPS-only communication
|
||||||
|
- API key authentication
|
||||||
|
- Rate limiting and IP whitelisting
|
||||||
|
- WebSocket support for real-time updates
|
||||||
|
- Redis integration for caching
|
||||||
|
|
||||||
|
### TUI (`./cmd/tui/main.go`)
|
||||||
|
Terminal User Interface for monitoring experiments.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Launch TUI
|
||||||
|
go run ./cmd/tui/main.go
|
||||||
|
|
||||||
|
# With custom config
|
||||||
|
./tui --config configs/config-local.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Real-time experiment monitoring
|
||||||
|
- Interactive job management
|
||||||
|
- Status visualization
|
||||||
|
- Log viewing
|
||||||
|
|
||||||
|
### Data Manager (`./cmd/data_manager/`)
|
||||||
|
Utilities for data synchronization and management.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Sync data
|
||||||
|
./data_manager --sync ./data
|
||||||
|
|
||||||
|
# Clean old data
|
||||||
|
./data_manager --cleanup --older-than 30d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Config Lint (`./cmd/configlint/main.go`)
|
||||||
|
Configuration validation and linting tool.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Validate configuration
|
||||||
|
./configlint configs/config-local.yaml
|
||||||
|
|
||||||
|
# Check schema compliance
|
||||||
|
./configlint --schema configs/schema/config_schema.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Management Script (`./tools/manage.sh`)
|
||||||
|
|
||||||
|
Simple service management for your homelab.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
```bash
|
||||||
|
./tools/manage.sh start # Start all services
|
||||||
|
./tools/manage.sh stop # Stop all services
|
||||||
|
./tools/manage.sh status # Check service status
|
||||||
|
./tools/manage.sh logs # View logs
|
||||||
|
./tools/manage.sh monitor # Basic monitoring
|
||||||
|
./tools/manage.sh security # Security status
|
||||||
|
./tools/manage.sh cleanup # Clean project artifacts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup Script (`./setup.sh`)
|
||||||
|
|
||||||
|
One-command homelab setup.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
```bash
|
||||||
|
# Full setup
|
||||||
|
./setup.sh
|
||||||
|
|
||||||
|
# Setup includes:
|
||||||
|
# - SSL certificate generation
|
||||||
|
# - Configuration creation
|
||||||
|
# - Build all components
|
||||||
|
# - Start Redis
|
||||||
|
# - Setup Fail2Ban (if available)
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Testing
|
||||||
|
|
||||||
|
Test the API with curl:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Health check
|
||||||
|
curl -k -H 'X-API-Key: password' https://localhost:9101/health
|
||||||
|
|
||||||
|
# List experiments
|
||||||
|
curl -k -H 'X-API-Key: password' https://localhost:9101/experiments
|
||||||
|
|
||||||
|
# Submit experiment
|
||||||
|
curl -k -X POST -H 'X-API-Key: password' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"name":"test","config":{"type":"basic"}}' \
|
||||||
|
https://localhost:9101/experiments
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zig CLI Architecture
|
||||||
|
|
||||||
|
The Zig CLI is designed for performance and reliability:
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
- **Commands** (`cli/src/commands/`): Individual command implementations
|
||||||
|
- **Config** (`cli/src/config.zig`): Configuration management
|
||||||
|
- **Network** (`cli/src/net/ws.zig`): WebSocket client implementation
|
||||||
|
- **Utils** (`cli/src/utils/`): Cryptography, storage, and rsync utilities
|
||||||
|
- **Errors** (`cli/src/errors.zig`): Centralized error handling
|
||||||
|
|
||||||
|
### Performance Optimizations
|
||||||
|
- **Content-Addressed Storage**: Deduplicates identical files across experiments
|
||||||
|
- **SHA256 Hashing**: Fast, reliable commit ID generation
|
||||||
|
- **Rsync Integration**: Efficient incremental file transfers
|
||||||
|
- **WebSocket Protocol**: Low-latency communication with worker
|
||||||
|
- **Memory Management**: Efficient allocation with Zig's allocator system
|
||||||
|
|
||||||
|
### Security Features
|
||||||
|
- **API Key Hashing**: Secure authentication token handling
|
||||||
|
- **SSH Integration**: Secure file transfers
|
||||||
|
- **Input Validation**: Comprehensive argument checking
|
||||||
|
- **Error Handling**: Secure error reporting without information leakage
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Main configuration file: `configs/config-local.yaml`
|
||||||
|
|
||||||
|
### Key Settings
|
||||||
|
```yaml
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
api_keys:
|
||||||
|
homelab_user:
|
||||||
|
hash: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
|
||||||
|
admin: true
|
||||||
|
|
||||||
|
server:
|
||||||
|
address: ":9101"
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
cert_file: "./ssl/cert.pem"
|
||||||
|
key_file: "./ssl/key.pem"
|
||||||
|
|
||||||
|
security:
|
||||||
|
rate_limit:
|
||||||
|
enabled: true
|
||||||
|
requests_per_minute: 30
|
||||||
|
ip_whitelist:
|
||||||
|
- "127.0.0.1"
|
||||||
|
- "::1"
|
||||||
|
- "192.168.0.0/16"
|
||||||
|
- "10.0.0.0/8"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Commands
|
||||||
|
|
||||||
|
If using Docker Compose:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start services
|
||||||
|
docker-compose up -d (testing only)
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# Stop services
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
docker-compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**Zig CLI not found:**
|
||||||
|
```bash
|
||||||
|
# Build the CLI
|
||||||
|
cd cli && make build
|
||||||
|
|
||||||
|
# Check binary exists
|
||||||
|
ls -la ./cli/zig-out/bin/ml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configuration not found:**
|
||||||
|
```bash
|
||||||
|
# Create configuration
|
||||||
|
./cli/zig-out/bin/ml init
|
||||||
|
|
||||||
|
# Check config file
|
||||||
|
ls -la ~/.ml/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Worker connection failed:**
|
||||||
|
```bash
|
||||||
|
# Test SSH connection
|
||||||
|
ssh -p 22 mluser@worker.local
|
||||||
|
|
||||||
|
# Check configuration
|
||||||
|
cat ~/.ml/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sync not working:**
|
||||||
|
```bash
|
||||||
|
# Check rsync availability
|
||||||
|
rsync --version
|
||||||
|
|
||||||
|
# Test manual sync
|
||||||
|
rsync -avz ./project/ mluser@worker.local:/tmp/test/
|
||||||
|
```
|
||||||
|
|
||||||
|
**WebSocket connection failed:**
|
||||||
|
```bash
|
||||||
|
# Check worker WebSocket port
|
||||||
|
telnet worker.local 9100
|
||||||
|
|
||||||
|
# Verify API key
|
||||||
|
./cli/zig-out/bin/ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
**API not responding:**
|
||||||
|
```bash
|
||||||
|
./tools/manage.sh status
|
||||||
|
./tools/manage.sh logs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Authentication failed:**
|
||||||
|
```bash
|
||||||
|
# Check API key in config-local.yaml
|
||||||
|
grep -A 5 "api_keys:" configs/config-local.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Redis connection failed:**
|
||||||
|
```bash
|
||||||
|
# Check Redis status
|
||||||
|
redis-cli ping
|
||||||
|
|
||||||
|
# Start Redis
|
||||||
|
redis-server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# CLI help
|
||||||
|
./cli/zig-out/bin/ml help
|
||||||
|
|
||||||
|
# Management script help
|
||||||
|
./tools/manage.sh help
|
||||||
|
|
||||||
|
# Check all available commands
|
||||||
|
make help
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**That's it for the CLI reference!** For complete setup instructions, see the main [index](index.md).
|
||||||
55
docs/src/configuration-schema.md
Normal file
55
docs/src/configuration-schema.md
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Configuration Schema
|
||||||
|
|
||||||
|
Complete reference for Fetch ML configuration options.
|
||||||
|
|
||||||
|
## Configuration File Structure
|
||||||
|
|
||||||
|
Fetch ML uses YAML configuration files. The main configuration file is typically `config.yaml`.
|
||||||
|
|
||||||
|
### Full Schema
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Server Configuration
|
||||||
|
server:
|
||||||
|
address: ":9101"
|
||||||
|
tls:
|
||||||
|
enabled: false
|
||||||
|
cert_file: ""
|
||||||
|
key_file: ""
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
database:
|
||||||
|
type: "sqlite" # sqlite, postgres, mysql
|
||||||
|
connection: "fetch_ml.db"
|
||||||
|
host: "localhost"
|
||||||
|
port: 5432
|
||||||
|
username: "postgres"
|
||||||
|
password: ""
|
||||||
|
database: "fetch_ml"
|
||||||
|
|
||||||
|
# Redis Configuration
|
||||||
|
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Database Types
|
||||||
|
- **SQLite**: `type: sqlite, connection: file.db`
|
||||||
|
- **PostgreSQL**: `type: postgres, host: localhost, port: 5432`
|
||||||
|
|
||||||
|
### Key Settings
|
||||||
|
- `server.address: :9101`
|
||||||
|
- `database.type: sqlite`
|
||||||
|
- `redis.addr: localhost:6379`
|
||||||
|
- `auth.enabled: true`
|
||||||
|
- `logging.level: info`
|
||||||
|
|
||||||
|
### Environment Override
|
||||||
|
```bash
|
||||||
|
export FETCHML_SERVER_ADDRESS=:8080
|
||||||
|
export FETCHML_DATABASE_TYPE=postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
```bash
|
||||||
|
make configlint
|
||||||
|
```
|
||||||
288
docs/src/deployment.md
Normal file
288
docs/src/deployment.md
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
# ML Experiment Manager - Deployment Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The ML Experiment Manager supports multiple deployment methods from local development to homelab Docker setups.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Docker Compose (Recommended for Development)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone repository
|
||||||
|
git clone https://github.com/your-org/fetch_ml.git
|
||||||
|
cd fetch_ml
|
||||||
|
|
||||||
|
# Start all services
|
||||||
|
docker-compose up -d (testing only)
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker-compose logs -f api-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Access the API at `http://localhost:9100`
|
||||||
|
|
||||||
|
## Deployment Options
|
||||||
|
|
||||||
|
### 1. Local Development
|
||||||
|
|
||||||
|
#### Prerequisites
|
||||||
|
|
||||||
|
**Container Runtimes:**
|
||||||
|
- **Docker Compose**: For testing and development only
|
||||||
|
- **Podman**: For production experiment execution
|
||||||
|
- Go 1.25+
|
||||||
|
- Zig 0.15.2
|
||||||
|
- Redis 7+
|
||||||
|
- Docker & Docker Compose (optional)
|
||||||
|
|
||||||
|
#### Manual Setup
|
||||||
|
```bash
|
||||||
|
# Start Redis
|
||||||
|
redis-server
|
||||||
|
|
||||||
|
# Build and run Go server
|
||||||
|
go build -o bin/api-server ./cmd/api-server
|
||||||
|
./bin/api-server -config configs/config-local.yaml
|
||||||
|
|
||||||
|
# Build Zig CLI
|
||||||
|
cd cli
|
||||||
|
zig build prod
|
||||||
|
./zig-out/bin/ml --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Docker Deployment
|
||||||
|
|
||||||
|
#### Build Image
|
||||||
|
```bash
|
||||||
|
docker build -t ml-experiment-manager:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Run Container
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name ml-api \
|
||||||
|
-p 9100:9100 \
|
||||||
|
-p 9101:9101 \
|
||||||
|
-v $(pwd)/configs:/app/configs:ro \
|
||||||
|
-v experiment-data:/data/ml-experiments \
|
||||||
|
ml-experiment-manager:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Docker Compose
|
||||||
|
```bash
|
||||||
|
# Production mode
|
||||||
|
docker-compose -f docker-compose.yml up -d
|
||||||
|
|
||||||
|
# Development mode with logs
|
||||||
|
docker-compose -f docker-compose.yml up
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Homelab Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Use the simple setup script
|
||||||
|
./setup.sh
|
||||||
|
|
||||||
|
# Or manually with Docker Compose
|
||||||
|
docker-compose up -d (testing only)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Cloud Deployment
|
||||||
|
|
||||||
|
#### AWS ECS
|
||||||
|
```bash
|
||||||
|
# Build and push to ECR
|
||||||
|
aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_REGISTRY
|
||||||
|
docker build -t $ECR_REGISTRY/ml-experiment-manager:latest .
|
||||||
|
docker push $ECR_REGISTRY/ml-experiment-manager:latest
|
||||||
|
|
||||||
|
# Deploy with ECS CLI
|
||||||
|
ecs-cli compose --project-name ml-experiment-manager up
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Google Cloud Run
|
||||||
|
```bash
|
||||||
|
# Build and push
|
||||||
|
gcloud builds submit --tag gcr.io/$PROJECT_ID/ml-experiment-manager
|
||||||
|
|
||||||
|
# Deploy
|
||||||
|
gcloud run deploy ml-experiment-manager \
|
||||||
|
--image gcr.io/$PROJECT_ID/ml-experiment-manager \
|
||||||
|
--platform managed \
|
||||||
|
--region us-central1 \
|
||||||
|
--allow-unauthenticated
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
```yaml
|
||||||
|
# configs/config-local.yaml
|
||||||
|
base_path: "/data/ml-experiments"
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
api_keys:
|
||||||
|
- "your-production-api-key"
|
||||||
|
server:
|
||||||
|
address: ":9100"
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
cert_file: "/app/ssl/cert.pem"
|
||||||
|
key_file: "/app/ssl/key.pem"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose Environment
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
api-server:
|
||||||
|
environment:
|
||||||
|
- REDIS_URL=redis://redis:6379
|
||||||
|
- LOG_LEVEL=info
|
||||||
|
volumes:
|
||||||
|
- ./configs:/configs:ro
|
||||||
|
- ./data:/data/experiments
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring & Logging
|
||||||
|
|
||||||
|
### Health Checks
|
||||||
|
- HTTP: `GET /health`
|
||||||
|
- WebSocket: Connection test
|
||||||
|
- Redis: Ping check
|
||||||
|
|
||||||
|
### Metrics
|
||||||
|
- Prometheus metrics at `/metrics`
|
||||||
|
- Custom application metrics
|
||||||
|
- Container resource usage
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
- Structured JSON logging
|
||||||
|
- Log levels: DEBUG, INFO, WARN, ERROR
|
||||||
|
- Centralized logging via ELK stack
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
### TLS Configuration
|
||||||
|
```bash
|
||||||
|
# Generate self-signed cert (development)
|
||||||
|
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
|
||||||
|
|
||||||
|
# Production - use Let's Encrypt
|
||||||
|
certbot certonly --standalone -d ml-experiments.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
- Firewall rules (ports 9100, 9101, 6379)
|
||||||
|
- VPN access for internal services
|
||||||
|
- API key authentication
|
||||||
|
- Rate limiting
|
||||||
|
|
||||||
|
## Performance Tuning
|
||||||
|
|
||||||
|
### Resource Allocation
|
||||||
|
```yaml
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "250m"
|
||||||
|
limits:
|
||||||
|
memory: "1Gi"
|
||||||
|
cpu: "1000m"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scaling Strategies
|
||||||
|
- Horizontal pod autoscaling
|
||||||
|
- Redis clustering
|
||||||
|
- Load balancing
|
||||||
|
- CDN for static assets
|
||||||
|
|
||||||
|
## Backup & Recovery
|
||||||
|
|
||||||
|
### Data Backup
|
||||||
|
```bash
|
||||||
|
# Backup experiment data
|
||||||
|
docker-compose exec redis redis-cli BGSAVE
|
||||||
|
docker cp $(docker-compose ps -q redis):/data/dump.rdb ./redis-backup.rdb
|
||||||
|
|
||||||
|
# Backup data volume
|
||||||
|
docker run --rm -v ml-experiments_redis_data:/data -v $(pwd):/backup alpine tar czf /backup/redis-backup.tar.gz -C /data .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disaster Recovery
|
||||||
|
1. Restore Redis data
|
||||||
|
2. Restart services
|
||||||
|
3. Verify experiment metadata
|
||||||
|
4. Test API endpoints
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### API Server Not Starting
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker-compose logs api-server
|
||||||
|
|
||||||
|
# Check configuration
|
||||||
|
cat configs/config-local.yaml
|
||||||
|
|
||||||
|
# Check Redis connection
|
||||||
|
docker-compose exec redis redis-cli ping
|
||||||
|
```
|
||||||
|
|
||||||
|
#### WebSocket Connection Issues
|
||||||
|
```bash
|
||||||
|
# Test WebSocket
|
||||||
|
wscat -c ws://localhost:9100/ws
|
||||||
|
|
||||||
|
# Check TLS
|
||||||
|
openssl s_client -connect localhost:9101 -servername localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Performance Issues
|
||||||
|
```bash
|
||||||
|
# Check resource usage
|
||||||
|
docker-compose exec api-server ps aux
|
||||||
|
|
||||||
|
# Check Redis memory
|
||||||
|
docker-compose exec redis redis-cli info memory
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
```bash
|
||||||
|
# Enable debug logging
|
||||||
|
export LOG_LEVEL=debug
|
||||||
|
./bin/api-server -config configs/config-local.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Integration
|
||||||
|
|
||||||
|
### GitHub Actions
|
||||||
|
- Automated testing on PR
|
||||||
|
- Multi-platform builds
|
||||||
|
- Security scanning
|
||||||
|
- Automatic releases
|
||||||
|
|
||||||
|
### Deployment Pipeline
|
||||||
|
1. Code commit → GitHub
|
||||||
|
2. CI/CD pipeline triggers
|
||||||
|
3. Build and test
|
||||||
|
4. Security scan
|
||||||
|
5. Deploy to staging
|
||||||
|
6. Run integration tests
|
||||||
|
7. Deploy to production
|
||||||
|
8. Post-deployment verification
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For deployment issues:
|
||||||
|
1. Check this guide
|
||||||
|
2. Review logs
|
||||||
|
3. Check GitHub Issues
|
||||||
|
4. Contact maintainers
|
||||||
55
docs/src/development-setup.md
Normal file
55
docs/src/development-setup.md
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Development Setup
|
||||||
|
|
||||||
|
Set up your local development environment for Fetch ML.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
**Container Runtimes:**
|
||||||
|
- **Docker Compose**: For testing and development only
|
||||||
|
- **Podman**: For production experiment execution
|
||||||
|
|
||||||
|
- Go 1.21+
|
||||||
|
- Zig 0.11+
|
||||||
|
- Docker Compose (testing only)
|
||||||
|
- Redis (or use Docker)
|
||||||
|
- Git
|
||||||
|
|
||||||
|
## Quick Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone repository
|
||||||
|
git clone https://github.com/jfraeys/fetch_ml.git
|
||||||
|
cd fetch_ml
|
||||||
|
|
||||||
|
# Start dependencies
|
||||||
|
see [Quick Start](quick-start.md) for Docker setup redis postgres
|
||||||
|
|
||||||
|
# Build all components
|
||||||
|
make build
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
see [Testing Guide](testing.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detailed Setup
|
||||||
|
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/jfraeys/fetch_ml.git
|
||||||
|
cd fetch_ml
|
||||||
|
see [Quick Start](quick-start.md) for Docker setup
|
||||||
|
make build
|
||||||
|
see [Testing Guide](testing.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Commands
|
||||||
|
- `make build` - Build all components
|
||||||
|
- `see [Testing Guide](testing.md)` - Run tests
|
||||||
|
- `make dev` - Development build
|
||||||
|
- `see [CLI Reference](cli-reference.md) and [Zig CLI](zig-cli.md)` - Build CLI
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
- Build fails: `go mod tidy`
|
||||||
|
- Zig errors: `cd cli && rm -rf zig-out zig-cache`
|
||||||
|
- Port conflicts: `lsof -i :9101`
|
||||||
112
docs/src/environment-variables.md
Normal file
112
docs/src/environment-variables.md
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
# Environment Variables
|
||||||
|
|
||||||
|
Fetch ML supports environment variables for configuration, allowing you to override config file settings and deploy in different environments.
|
||||||
|
|
||||||
|
## Priority Order
|
||||||
|
|
||||||
|
1. Environment variables (highest priority)
|
||||||
|
2. Configuration file values
|
||||||
|
3. Default values (lowest priority)
|
||||||
|
|
||||||
|
## Variable Prefixes
|
||||||
|
|
||||||
|
### General Configuration
|
||||||
|
- `FETCH_ML_*` - General server and application settings
|
||||||
|
|
||||||
|
### CLI Configuration
|
||||||
|
- `FETCH_ML_CLI_*` - CLI-specific settings (overrides `~/.ml/config.toml`)
|
||||||
|
|
||||||
|
### TUI Configuration
|
||||||
|
- `FETCH_ML_TUI_*` - TUI-specific settings (overrides TUI config file)
|
||||||
|
|
||||||
|
## CLI Environment Variables
|
||||||
|
|
||||||
|
| Variable | Config Field | Example |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `FETCH_ML_CLI_HOST` | `worker_host` | `localhost` |
|
||||||
|
| `FETCH_ML_CLI_USER` | `worker_user` | `mluser` |
|
||||||
|
| `FETCH_ML_CLI_BASE` | `worker_base` | `/opt/ml` |
|
||||||
|
| `FETCH_ML_CLI_PORT` | `worker_port` | `22` |
|
||||||
|
| `FETCH_ML_CLI_API_KEY` | `api_key` | `your-api-key-here` |
|
||||||
|
|
||||||
|
## TUI Environment Variables
|
||||||
|
|
||||||
|
| Variable | Config Field | Example |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `FETCH_ML_TUI_HOST` | `host` | `localhost` |
|
||||||
|
| `FETCH_ML_TUI_USER` | `user` | `mluser` |
|
||||||
|
| `FETCH_ML_TUI_SSH_KEY` | `ssh_key` | `~/.ssh/id_rsa` |
|
||||||
|
| `FETCH_ML_TUI_PORT` | `port` | `22` |
|
||||||
|
| `FETCH_ML_TUI_BASE_PATH` | `base_path` | `/opt/ml` |
|
||||||
|
| `FETCH_ML_TUI_TRAIN_SCRIPT` | `train_script` | `train.py` |
|
||||||
|
| `FETCH_ML_TUI_REDIS_ADDR` | `redis_addr` | `localhost:6379` |
|
||||||
|
| `FETCH_ML_TUI_REDIS_PASSWORD` | `redis_password` | `` |
|
||||||
|
| `FETCH_ML_TUI_REDIS_DB` | `redis_db` | `0` |
|
||||||
|
| `FETCH_ML_TUI_KNOWN_HOSTS` | `known_hosts` | `~/.ssh/known_hosts` |
|
||||||
|
|
||||||
|
## Server Environment Variables (Auth & Debug)
|
||||||
|
|
||||||
|
These variables control **server-side** authentication behavior and are intended **only for local development and debugging**.
|
||||||
|
|
||||||
|
| Variable | Purpose | Allowed In Production? |
|
||||||
|
|----------|---------|------------------------|
|
||||||
|
| `FETCH_ML_ALLOW_INSECURE_AUTH` | When set to `1` *and* `FETCH_ML_DEBUG=1`, allows the API server to run with `auth.enabled: false` by injecting a default admin user. | **No**. Must never be set in production. |
|
||||||
|
| `FETCH_ML_DEBUG` | Enables additional debug behaviors. Required (set to `1`) to activate the insecure auth bypass above. | **No**. Must never be set in production. |
|
||||||
|
|
||||||
|
When both variables are set to `1` and `auth.enabled` is `false`, the server logs a clear warning and treats all requests as coming from a default admin user. This mode is convenient for local homelab experiments but is **insecure by design** and must not be used on any shared or internet-facing environment.
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Development Environment
|
||||||
|
```bash
|
||||||
|
export FETCH_ML_CLI_HOST=localhost
|
||||||
|
export FETCH_ML_CLI_USER=devuser
|
||||||
|
export FETCH_ML_CLI_API_KEY=dev-key-123456789012
|
||||||
|
./ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Environment
|
||||||
|
```bash
|
||||||
|
export FETCH_ML_CLI_HOST=prod-server.example.com
|
||||||
|
export FETCH_ML_CLI_USER=mluser
|
||||||
|
export FETCH_ML_CLI_API_KEY=prod-key-abcdef1234567890
|
||||||
|
./ml status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker/Kubernetes
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
- name: FETCH_ML_CLI_HOST
|
||||||
|
value: "ml-server.internal"
|
||||||
|
- name: FETCH_ML_CLI_USER
|
||||||
|
value: "mluser"
|
||||||
|
- name: FETCH_ML_CLI_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: ml-secrets
|
||||||
|
key: api-key
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using .env file
|
||||||
|
```bash
|
||||||
|
# Copy the example file
|
||||||
|
cp .env.example .env
|
||||||
|
|
||||||
|
# Edit with your values
|
||||||
|
vim .env
|
||||||
|
|
||||||
|
# Load in your shell
|
||||||
|
export $(cat .env | xargs)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backward Compatibility
|
||||||
|
|
||||||
|
The CLI also supports the legacy `ML_*` prefix for backward compatibility, but `FETCH_ML_CLI_*` takes priority if both are set.
|
||||||
|
|
||||||
|
| Legacy Variable | New Variable |
|
||||||
|
|-----------------|--------------|
|
||||||
|
| `ML_HOST` | `FETCH_ML_CLI_HOST` |
|
||||||
|
| `ML_USER` | `FETCH_ML_CLI_USER` |
|
||||||
|
| `ML_BASE` | `FETCH_ML_CLI_BASE` |
|
||||||
|
| `ML_PORT` | `FETCH_ML_CLI_PORT` |
|
||||||
|
| `ML_API_KEY` | `FETCH_ML_CLI_API_KEY` |
|
||||||
235
docs/src/first-experiment.md
Normal file
235
docs/src/first-experiment.md
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
# First Experiment
|
||||||
|
|
||||||
|
Run your first machine learning experiment with Fetch ML.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
**Container Runtimes:**
|
||||||
|
- **Docker Compose**: For testing and development only
|
||||||
|
- **Podman**: For production experiment execution
|
||||||
|
|
||||||
|
- Fetch ML installed and running
|
||||||
|
- API key (see [Security](security.md) and [API Key Process](api-key-process.md))
|
||||||
|
- Basic ML knowledge
|
||||||
|
|
||||||
|
## Experiment Workflow
|
||||||
|
|
||||||
|
### 1. Prepare Your ML Code
|
||||||
|
|
||||||
|
Create a simple Python script:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# experiment.py
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--epochs', type=int, default=10)
|
||||||
|
parser.add_argument('--lr', type=float, default=0.001)
|
||||||
|
parser.add_argument('--output', default='results.json')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Simulate training
|
||||||
|
results = {
|
||||||
|
'epochs': args.epochs,
|
||||||
|
'learning_rate': args.lr,
|
||||||
|
'accuracy': 0.85 + (args.lr * 0.1),
|
||||||
|
'loss': 0.5 - (args.epochs * 0.01),
|
||||||
|
'training_time': args.epochs * 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save results
|
||||||
|
with open(args.output, 'w') as f:
|
||||||
|
json.dump(results, f, indent=2)
|
||||||
|
|
||||||
|
print(f"Training completed: {results}")
|
||||||
|
return results
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Submit Job via API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Submit experiment
|
||||||
|
curl -X POST http://localhost:9101/api/v1/jobs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-API-Key: your-api-key" \
|
||||||
|
-d '{
|
||||||
|
"job_name": "first-experiment",
|
||||||
|
"args": "--epochs 20 --lr 0.01 --output experiment_results.json",
|
||||||
|
"priority": 1,
|
||||||
|
"metadata": {
|
||||||
|
"experiment_type": "training",
|
||||||
|
"dataset": "sample_data"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Monitor Progress
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check job status
|
||||||
|
curl -H "X-API-Key: your-api-key" \
|
||||||
|
http://localhost:9101/api/v1/jobs/first-experiment
|
||||||
|
|
||||||
|
# List all jobs
|
||||||
|
curl -H "X-API-Key: your-api-key" \
|
||||||
|
http://localhost:9101/api/v1/jobs
|
||||||
|
|
||||||
|
# Get job metrics
|
||||||
|
curl -H "X-API-Key: your-api-key" \
|
||||||
|
http://localhost:9101/api/v1/jobs/first-experiment/metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Use CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Submit with CLI
|
||||||
|
cd cli && zig build dev
|
||||||
|
./cli/zig-out/dev/ml submit \
|
||||||
|
--name "cli-experiment" \
|
||||||
|
--args "--epochs 15 --lr 0.005" \
|
||||||
|
--server http://localhost:9101
|
||||||
|
|
||||||
|
# Monitor with CLI
|
||||||
|
./cli/zig-out/dev/ml list-jobs --server http://localhost:9101
|
||||||
|
./cli/zig-out/dev/ml job-status cli-experiment --server http://localhost:9101
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Experiment
|
||||||
|
|
||||||
|
### Hyperparameter Tuning
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Submit multiple experiments
|
||||||
|
for lr in 0.001 0.01 0.1; do
|
||||||
|
curl -X POST http://localhost:9101/api/v1/jobs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-API-Key: your-api-key" \
|
||||||
|
-d "{
|
||||||
|
\"job_name\": \"tune-lr-$lr\",
|
||||||
|
\"args\": \"--epochs 10 --lr $lr\",
|
||||||
|
\"metadata\": {\"learning_rate\": $lr}
|
||||||
|
}"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Batch Processing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Submit batch job
|
||||||
|
curl -X POST http://localhost:9101/api/v1/jobs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-API-Key: your-api-key" \
|
||||||
|
-d '{
|
||||||
|
"job_name": "batch-processing",
|
||||||
|
"args": "--input data/ --output results/ --batch-size 32",
|
||||||
|
"priority": 2,
|
||||||
|
"datasets": ["training_data", "validation_data"]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Results and Output
|
||||||
|
|
||||||
|
### Access Results
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download results
|
||||||
|
curl -H "X-API-Key: your-api-key" \
|
||||||
|
http://localhost:9101/api/v1/jobs/first-experiment/results
|
||||||
|
|
||||||
|
# View job details
|
||||||
|
curl -H "X-API-Key: your-api-key" \
|
||||||
|
http://localhost:9101/api/v1/jobs/first-experiment | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Result Format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"job_id": "first-experiment",
|
||||||
|
"status": "completed",
|
||||||
|
"results": {
|
||||||
|
"epochs": 20,
|
||||||
|
"learning_rate": 0.01,
|
||||||
|
"accuracy": 0.86,
|
||||||
|
"loss": 0.3,
|
||||||
|
"training_time": 2.0
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"gpu_utilization": "85%",
|
||||||
|
"memory_usage": "2GB",
|
||||||
|
"execution_time": "120s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Job Naming
|
||||||
|
|
||||||
|
- Use descriptive names: `model-training-v2`, `data-preprocessing`
|
||||||
|
- Include version numbers: `experiment-v1`, `experiment-v2`
|
||||||
|
- Add timestamps: `daily-batch-2024-01-15`
|
||||||
|
|
||||||
|
### Metadata Usage
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"experiment_type": "training",
|
||||||
|
"model_version": "v2.1",
|
||||||
|
"dataset": "imagenet-2024",
|
||||||
|
"environment": "gpu",
|
||||||
|
"team": "ml-team"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check failed jobs
|
||||||
|
curl -H "X-API-Key: your-api-key" \
|
||||||
|
"http://localhost:9101/api/v1/jobs?status=failed"
|
||||||
|
|
||||||
|
# Retry failed job
|
||||||
|
curl -X POST http://localhost:9101/api/v1/jobs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-API-Key: your-api-key" \
|
||||||
|
-d '{
|
||||||
|
"job_name": "retry-experiment",
|
||||||
|
"args": "--epochs 20 --lr 0.01",
|
||||||
|
"metadata": {"retry_of": "first-experiment"}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## ## Related Documentation
|
||||||
|
|
||||||
|
- [Development Setup (see [Development Setup](development-setup.md))](development-setup.md) - Local development environment
|
||||||
|
- [Testing Guide (see [Testing Guide](testing.md))](testing.md) - Test your experiments
|
||||||
|
- [Production Deployment (see [Deployment](deployment.md))](deployment.md) - Scale to production
|
||||||
|
- [Monitoring](production-monitoring.md) - Track experiment performance
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Job stuck in pending?**
|
||||||
|
- Check worker status: `curl /api/v1/workers`
|
||||||
|
- Verify resources: `docker stats`
|
||||||
|
- Check logs: `docker-compose logs api-server`
|
||||||
|
|
||||||
|
**Job failed?**
|
||||||
|
- Check error message: `curl /api/v1/jobs/job-id`
|
||||||
|
- Review job arguments
|
||||||
|
- Verify input data
|
||||||
|
|
||||||
|
**No results?**
|
||||||
|
- Check job completion status
|
||||||
|
- Verify output file paths
|
||||||
|
- Check storage permissions
|
||||||
89
docs/src/index.md
Normal file
89
docs/src/index.md
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Fetch ML Documentation
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fetch ML - Secure Machine Learning Platform
|
||||||
|
|
||||||
|
A secure, containerized platform for running machine learning experiments with role-based access control and comprehensive audit trails.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
**New to the project?** Start here!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://github.com/your-username/fetch_ml.git
|
||||||
|
cd fetch_ml
|
||||||
|
|
||||||
|
# Quick setup (builds everything, creates test user)
|
||||||
|
make quick-start
|
||||||
|
|
||||||
|
# Create your API key
|
||||||
|
./bin/user_manager --config configs/config_dev.yaml --cmd generate-key --username your_name --role data_scientist
|
||||||
|
|
||||||
|
# Run your first experiment
|
||||||
|
./bin/worker --config configs/config_dev.yaml --api-key YOUR_GENERATED_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Navigation
|
||||||
|
|
||||||
|
### 🚀 Getting Started
|
||||||
|
- [**Getting Started Guide**](quick-start.md) - Complete setup instructions
|
||||||
|
- [**Simple Install**](installation.md) - Quick installation guide
|
||||||
|
|
||||||
|
### 🔒 Security & Authentication
|
||||||
|
- [**Security Overview**](security.md) - Security best practices
|
||||||
|
- [**API Key Process**](api-key-process.md) - Generate and manage API keys
|
||||||
|
- [**User Permissions**](user-permissions.md) - Role-based access control
|
||||||
|
|
||||||
|
### ⚙️ Configuration
|
||||||
|
- [**Environment Variables**](environment-variables.md) - Configuration options
|
||||||
|
- [**Smart Defaults**](smart-defaults.md) - Default configuration settings
|
||||||
|
|
||||||
|
### 🛠️ Development
|
||||||
|
- [**Architecture**](architecture.md) - System architecture and design
|
||||||
|
- [**CLI Reference**](cli-reference.md) - Command-line interface documentation
|
||||||
|
- [**Testing Guide**](testing.md) - Testing procedures and guidelines
|
||||||
|
- [**Queue System**](queue.md) - Job queue implementation
|
||||||
|
|
||||||
|
### 🏭 Production Deployment
|
||||||
|
- [**Deployment Guide**](deployment.md) - Production deployment instructions
|
||||||
|
- [**Production Monitoring**](production-monitoring.md) - Monitoring and observability
|
||||||
|
- [**Operations Guide**](operations.md) - Production operations
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **🔐 Secure Authentication** - RBAC with API keys, roles, and permissions
|
||||||
|
- **🐳 Containerized** - Podman-based secure execution environments
|
||||||
|
- **🗄️ Database Storage** - SQLite backend for user management (optional)
|
||||||
|
- **📋 Audit Trail** - Complete logging of all actions
|
||||||
|
- **🚀 Production Ready** - Security audits, systemd services, log rotation
|
||||||
|
|
||||||
|
## Available Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Core commands
|
||||||
|
make help # See all available commands
|
||||||
|
make build # Build all binaries
|
||||||
|
make test-unit # Run tests
|
||||||
|
|
||||||
|
# User management
|
||||||
|
./bin/user_manager --config configs/config_dev.yaml --cmd generate-key --username new_user --role data_scientist
|
||||||
|
./bin/user_manager --config configs/config_dev.yaml --cmd list-users
|
||||||
|
|
||||||
|
# Run services
|
||||||
|
./bin/worker --config configs/config_dev.yaml --api-key YOUR_KEY
|
||||||
|
./bin/tui --config configs/config_dev.yaml
|
||||||
|
./bin/data_manager --config configs/config_dev.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Need Help?
|
||||||
|
|
||||||
|
- **📖 Documentation**: Use the navigation menu on the left
|
||||||
|
- **⚡ Quick help**: `make help`
|
||||||
|
- **🧪 Tests**: `make test-unit`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Happy ML experimenting!**
|
||||||
63
docs/src/installation.md
Normal file
63
docs/src/installation.md
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Simple Installation Guide
|
||||||
|
|
||||||
|
## Quick Start (5 minutes)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Install
|
||||||
|
git clone https://github.com/jfraeys/fetch_ml.git
|
||||||
|
cd fetch_ml
|
||||||
|
make install
|
||||||
|
|
||||||
|
# 2. Setup (auto-configures)
|
||||||
|
./bin/ml setup
|
||||||
|
|
||||||
|
# 3. Run experiments
|
||||||
|
./bin/ml run my-experiment.py
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. Everything else is optional.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What If I Want More Control?
|
||||||
|
|
||||||
|
### Manual Configuration (Optional)
|
||||||
|
```bash
|
||||||
|
# Edit settings if defaults don't work
|
||||||
|
nano ~/.ml/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitoring Dashboard (Optional)
|
||||||
|
```bash
|
||||||
|
# Real-time monitoring
|
||||||
|
./bin/tui
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Senior Developer Feedback
|
||||||
|
|
||||||
|
**"Keep it simple"** - Most data scientists want:
|
||||||
|
1. One installation command
|
||||||
|
2. Sensible defaults
|
||||||
|
3. Works without configuration
|
||||||
|
4. Advanced features available when needed
|
||||||
|
|
||||||
|
**Current plan is too complex** because it asks users to decide between:
|
||||||
|
- CLI vs TUI vs Both
|
||||||
|
- Zig vs Go build tools
|
||||||
|
- Manual vs auto config
|
||||||
|
- Multiple environment variables
|
||||||
|
|
||||||
|
**Better approach:** Start simple, add complexity gradually.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommended Simplified Workflow
|
||||||
|
|
||||||
|
1. **Single Binary** - Combine CLI + basic TUI functionality
|
||||||
|
2. **Auto-Discovery** - Detect common ML environments automatically
|
||||||
|
3. **Progressive Disclosure** - Show advanced options only when needed
|
||||||
|
4. **Zero Config** - Work out-of-the-box with localhost defaults
|
||||||
|
|
||||||
|
The goal: "It just works" for 80% of use cases.
|
||||||
310
docs/src/operations.md
Normal file
310
docs/src/operations.md
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Operations Runbook"
|
||||||
|
permalink: /operations/
|
||||||
|
nav_order: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
# Operations Runbook
|
||||||
|
|
||||||
|
Operational guide for troubleshooting and maintaining the ML experiment system.
|
||||||
|
|
||||||
|
## Task Queue Operations
|
||||||
|
|
||||||
|
### Monitoring Queue Health
|
||||||
|
|
||||||
|
```redis
|
||||||
|
# Check queue depth
|
||||||
|
ZCARD task:queue
|
||||||
|
|
||||||
|
# List pending tasks
|
||||||
|
ZRANGE task:queue 0 -1 WITHSCORES
|
||||||
|
|
||||||
|
# Check dead letter queue
|
||||||
|
KEYS task:dlq:*
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handling Stuck Tasks
|
||||||
|
|
||||||
|
**Symptom:** Tasks stuck in "running" status
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check for expired leases
|
||||||
|
redis-cli GET task:{task-id}
|
||||||
|
# Look for LeaseExpiry in past
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rem
|
||||||
|
|
||||||
|
ediation:**
|
||||||
|
Tasks with expired leases are automatically reclaimed every 1 minute. To force immediate reclamation:
|
||||||
|
```bash
|
||||||
|
# Restart worker to trigger reclaim cycle
|
||||||
|
systemctl restart ml-worker
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dead Letter Queue Management
|
||||||
|
|
||||||
|
**View failed tasks:**
|
||||||
|
```redis
|
||||||
|
KEYS task:dlq:*
|
||||||
|
```
|
||||||
|
|
||||||
|
**Inspect failed task:**
|
||||||
|
```redis
|
||||||
|
GET task:dlq:{task-id}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Retry from DLQ:**
|
||||||
|
```bash
|
||||||
|
# Manual retry (requires custom script)
|
||||||
|
# 1. Get task from DLQ
|
||||||
|
# 2. Reset retry count
|
||||||
|
# 3. Re-queue task
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker Crashes
|
||||||
|
|
||||||
|
**Symptom:** Worker disappeared mid-task
|
||||||
|
|
||||||
|
**What Happens:**
|
||||||
|
1. Lease expires after 30 minutes (default)
|
||||||
|
2. Background reclaim job detects expired lease
|
||||||
|
3. Task is retried (up to 3 attempts)
|
||||||
|
4. After max retries → Dead Letter Queue
|
||||||
|
|
||||||
|
**Prevention:**
|
||||||
|
- Monitor worker heartbeats
|
||||||
|
- Set up alerts for worker down
|
||||||
|
- Use process manager (systemd, supervisor)
|
||||||
|
|
||||||
|
## Worker Operations
|
||||||
|
|
||||||
|
### Graceful Shutdown
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Send SIGTERM for graceful shutdown
|
||||||
|
kill -TERM $(pgrep ml-worker)
|
||||||
|
|
||||||
|
# Worker will:
|
||||||
|
# 1. Stop accepting new tasks
|
||||||
|
# 2. Finish active tasks (up to 5min timeout)
|
||||||
|
# 3. Release all leases
|
||||||
|
# 4. Exit cleanly
|
||||||
|
```
|
||||||
|
|
||||||
|
### Force Shutdown
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Force kill (leases will be reclaimed automatically)
|
||||||
|
kill -9 $(pgrep ml-worker)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker Heartbeat Monitoring
|
||||||
|
|
||||||
|
```redis
|
||||||
|
# Check worker heartbeats
|
||||||
|
HGETALL worker:heartbeat
|
||||||
|
|
||||||
|
# Example output:
|
||||||
|
# worker-abc123 1701234567
|
||||||
|
# worker-def456 1701234580
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alert if:** Heartbeat timestamp > 5 minutes old
|
||||||
|
|
||||||
|
## Redis Operations
|
||||||
|
|
||||||
|
### Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manual backup
|
||||||
|
redis-cli SAVE
|
||||||
|
cp /var/lib/redis/dump.rdb /backup/redis-$(date +%Y%m%d).rdb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop Redis
|
||||||
|
systemctl stop redis
|
||||||
|
|
||||||
|
# Restore snapshot
|
||||||
|
cp /backup/redis-20231201.rdb /var/lib/redis/dump.rdb
|
||||||
|
|
||||||
|
# Start Redis
|
||||||
|
systemctl start redis
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
|
||||||
|
```redis
|
||||||
|
# Check memory usage
|
||||||
|
INFO memory
|
||||||
|
|
||||||
|
# Evict old data if needed
|
||||||
|
FLUSHDB # DANGER: Clears all data!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Issue: Queue Growing Unbounded
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- `ZCARD task:queue` keeps increasing
|
||||||
|
- No workers processing tasks
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check worker status
|
||||||
|
systemctl status ml-worker
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
journalctl -u ml-worker -n 100
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resolution:**
|
||||||
|
1. Verify workers are running
|
||||||
|
2. Check Redis connectivity
|
||||||
|
3. Verify lease configuration
|
||||||
|
|
||||||
|
### Issue: High Retry Rate
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Many tasks in DLQ
|
||||||
|
- `retry_count` field high on tasks
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check worker logs for errors
|
||||||
|
journalctl -u ml-worker | grep "retry"
|
||||||
|
|
||||||
|
# Look for patterns (network issues, resource limits, etc)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resolution:**
|
||||||
|
- Fix underlying issue (network, resources, etc)
|
||||||
|
- Adjust retry limits if permanent failures
|
||||||
|
- Increase task timeout if jobs are slow
|
||||||
|
|
||||||
|
### Issue: Leases Expiring Prematurely
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Tasks retried even though worker is healthy
|
||||||
|
- Logs show "lease expired" frequently
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```yaml
|
||||||
|
# Check worker config
|
||||||
|
cat configs/worker-config.yaml | grep -A3 "lease"
|
||||||
|
|
||||||
|
task_lease_duration: 30m # Too short?
|
||||||
|
heartbeat_interval: 1m # Too infrequent?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resolution:**
|
||||||
|
```yaml
|
||||||
|
# Increase lease duration for long-running jobs
|
||||||
|
task_lease_duration: 60m
|
||||||
|
heartbeat_interval: 30s # More frequent heartbeats
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tuning
|
||||||
|
|
||||||
|
### Worker Concurrency
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# worker-config.yaml
|
||||||
|
max_workers: 4 # Number of parallel tasks
|
||||||
|
|
||||||
|
# Adjust based on:
|
||||||
|
# - CPU cores available
|
||||||
|
# - Memory per task
|
||||||
|
# - GPU availability
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redis Configuration
|
||||||
|
|
||||||
|
```conf
|
||||||
|
# /etc/redis/redis.conf
|
||||||
|
|
||||||
|
# Persistence
|
||||||
|
save 900 1
|
||||||
|
save 300 10
|
||||||
|
|
||||||
|
# Memory
|
||||||
|
maxmemory 2gb
|
||||||
|
maxmemory-policy noeviction
|
||||||
|
|
||||||
|
# Performance
|
||||||
|
tcp-keepalive 300
|
||||||
|
timeout 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alerting Rules
|
||||||
|
|
||||||
|
### Critical Alerts
|
||||||
|
|
||||||
|
1. **Worker Down** (no heartbeat > 5min)
|
||||||
|
2. **Queue Depth** > 1000 tasks
|
||||||
|
3. **DLQ Growth** > 100 tasks/hour
|
||||||
|
4. **Redis Down** (connection failed)
|
||||||
|
|
||||||
|
### Warning Alerts
|
||||||
|
|
||||||
|
1. **High Retry Rate** > 10% of tasks
|
||||||
|
2. **Slow Queue Drain** (depth increasing over 1 hour)
|
||||||
|
3. **Worker Memory** > 80% usage
|
||||||
|
|
||||||
|
## Health Checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# health-check.sh
|
||||||
|
|
||||||
|
# Check Redis
|
||||||
|
redis-cli PING || echo "Redis DOWN"
|
||||||
|
|
||||||
|
# Check worker heartbeat
|
||||||
|
WORKER_ID=$(cat /var/run/ml-worker.pid)
|
||||||
|
LAST_HB=$(redis-cli HGET worker:heartbeat "$WORKER_ID")
|
||||||
|
NOW=$(date +%s)
|
||||||
|
if [ $((NOW - LAST_HB)) -gt 300 ]; then
|
||||||
|
echo "Worker heartbeat stale"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check queue depth
|
||||||
|
DEPTH=$(redis-cli ZCARD task:queue)
|
||||||
|
if [ "$DEPTH" -gt 1000 ]; then
|
||||||
|
echo "Queue depth critical: $DEPTH"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Runbook Checklist
|
||||||
|
|
||||||
|
### Daily Operations
|
||||||
|
1. Check queue depth
|
||||||
|
2. Verify worker heartbeats
|
||||||
|
3. Review DLQ for patterns
|
||||||
|
4. Check Redis memory usage
|
||||||
|
|
||||||
|
### Weekly Operations
|
||||||
|
1. Review retry rates
|
||||||
|
2. Analyze failed task patterns
|
||||||
|
3. Backup Redis snapshot
|
||||||
|
4. Review worker logs
|
||||||
|
|
||||||
|
### Monthly Operations
|
||||||
|
1. Performance tuning review
|
||||||
|
2. Capacity planning
|
||||||
|
3. Update documentation
|
||||||
|
4. Test disaster recovery
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**For homelab setups:**
|
||||||
|
Most of these operations can be simplified. Focus on:
|
||||||
|
- Basic monitoring (queue depth, worker status)
|
||||||
|
- Periodic Redis backups
|
||||||
|
- Graceful shutdowns for maintenance
|
||||||
217
docs/src/production-monitoring.md
Normal file
217
docs/src/production-monitoring.md
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
# Production Monitoring Deployment Guide (Linux)
|
||||||
|
|
||||||
|
This guide covers deploying the monitoring stack (Prometheus, Grafana, Loki, Promtail) on Linux production servers.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
**Testing**: Docker Compose (macOS/Linux)
|
||||||
|
**Production**: Podman + systemd (Linux)
|
||||||
|
|
||||||
|
**Important**: Docker is for testing only. Podman is used for running actual ML experiments in production.
|
||||||
|
|
||||||
|
**Dev (Testing)**: Docker Compose
|
||||||
|
**Prod (Experiments)**: Podman + systemd
|
||||||
|
|
||||||
|
Each service runs as a separate Podman container managed by systemd for automatic restarts and proper lifecycle management.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
**Container Runtimes:**
|
||||||
|
- **Docker Compose**: For testing and development only
|
||||||
|
- **Podman**: For production experiment execution
|
||||||
|
|
||||||
|
- Linux distribution with systemd (Rocky/RHEL/CentOS, Ubuntu/Debian, Arch, SUSE, etc.)
|
||||||
|
- Production app already deployed (see `scripts/setup-prod.sh`)
|
||||||
|
- Root or sudo access
|
||||||
|
- Ports 3000, 9090, 3100 available
|
||||||
|
|
||||||
|
## Quick Setup
|
||||||
|
|
||||||
|
### 1. Run Setup Script
|
||||||
|
```bash
|
||||||
|
cd /path/to/fetch_ml
|
||||||
|
sudo ./scripts/setup-monitoring-prod.sh /data/monitoring ml-user ml-group
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
- Create directory structure at `/data/monitoring`
|
||||||
|
- Copy configuration files to `/etc/fetch_ml/monitoring`
|
||||||
|
- Create systemd services for each component
|
||||||
|
- Set up firewall rules
|
||||||
|
|
||||||
|
### 2. Start Services
|
||||||
|
```bash
|
||||||
|
# Start all monitoring services
|
||||||
|
sudo systemctl start prometheus
|
||||||
|
sudo systemctl start loki
|
||||||
|
sudo systemctl start promtail
|
||||||
|
sudo systemctl start grafana
|
||||||
|
|
||||||
|
# Enable on boot
|
||||||
|
sudo systemctl enable prometheus loki promtail grafana
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Access Grafana
|
||||||
|
- URL: `http://YOUR_SERVER_IP:3000`
|
||||||
|
- Username: `admin`
|
||||||
|
- Password: `admin` (change on first login)
|
||||||
|
|
||||||
|
Dashboards will auto-load:
|
||||||
|
- **ML Task Queue Monitoring** (metrics)
|
||||||
|
- **Application Logs** (Loki logs)
|
||||||
|
|
||||||
|
## Service Details
|
||||||
|
|
||||||
|
### Prometheus
|
||||||
|
- **Port**: 9090
|
||||||
|
- **Config**: `/etc/fetch_ml/monitoring/prometheus.yml`
|
||||||
|
- **Data**: `/data/monitoring/prometheus`
|
||||||
|
- **Purpose**: Scrapes metrics from API server
|
||||||
|
|
||||||
|
### Loki
|
||||||
|
- **Port**: 3100
|
||||||
|
- **Config**: `/etc/fetch_ml/monitoring/loki-config.yml`
|
||||||
|
- **Data**: `/data/monitoring/loki`
|
||||||
|
- **Purpose**: Log aggregation
|
||||||
|
|
||||||
|
### Promtail
|
||||||
|
- **Config**: `/etc/fetch_ml/monitoring/promtail-config.yml`
|
||||||
|
- **Log Source**: `/var/log/fetch_ml/*.log`
|
||||||
|
- **Purpose**: Ships logs to Loki
|
||||||
|
|
||||||
|
### Grafana
|
||||||
|
- **Port**: 3000
|
||||||
|
- **Config**: `/etc/fetch_ml/monitoring/grafana/provisioning`
|
||||||
|
- **Data**: `/data/monitoring/grafana`
|
||||||
|
- **Dashboards**: `/var/lib/grafana/dashboards`
|
||||||
|
|
||||||
|
## Management Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check status
|
||||||
|
sudo systemctl status prometheus grafana loki promtail
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
sudo journalctl -u prometheus -f
|
||||||
|
sudo journalctl -u grafana -f
|
||||||
|
sudo journalctl -u loki -f
|
||||||
|
sudo journalctl -u promtail -f
|
||||||
|
|
||||||
|
# Restart services
|
||||||
|
sudo systemctl restart prometheus
|
||||||
|
sudo systemctl restart grafana
|
||||||
|
|
||||||
|
# Stop all monitoring
|
||||||
|
sudo systemctl stop prometheus grafana loki promtail
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Retention
|
||||||
|
|
||||||
|
### Prometheus
|
||||||
|
Default: 15 days. Edit `/etc/fetch_ml/monitoring/prometheus.yml`:
|
||||||
|
```yaml
|
||||||
|
storage:
|
||||||
|
tsdb:
|
||||||
|
retention.time: 30d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loki
|
||||||
|
Default: 30 days. Edit `/etc/fetch_ml/monitoring/loki-config.yml`:
|
||||||
|
```yaml
|
||||||
|
limits_config:
|
||||||
|
retention_period: 30d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
### Firewall
|
||||||
|
The setup script automatically configures firewall rules using the detected firewall manager (firewalld or ufw).
|
||||||
|
|
||||||
|
For manual firewall configuration:
|
||||||
|
|
||||||
|
**RHEL/Rocky/Fedora (firewalld)**:
|
||||||
|
```bash
|
||||||
|
# Remove public access
|
||||||
|
sudo firewall-cmd --permanent --remove-port=3000/tcp
|
||||||
|
sudo firewall-cmd --permanent --remove-port=9090/tcp
|
||||||
|
|
||||||
|
# Add specific source
|
||||||
|
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="3000" protocol="tcp" accept'
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ubuntu/Debian (ufw)**:
|
||||||
|
```bash
|
||||||
|
# Remove public access
|
||||||
|
sudo ufw delete allow 3000/tcp
|
||||||
|
sudo ufw delete allow 9090/tcp
|
||||||
|
|
||||||
|
# Add specific source
|
||||||
|
sudo ufw allow from 10.0.0.0/24 to any port 3000 proto tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
Change Grafana admin password:
|
||||||
|
1. Login to Grafana
|
||||||
|
2. User menu → Profile → Change Password
|
||||||
|
|
||||||
|
### TLS (Optional)
|
||||||
|
For HTTPS, configure reverse proxy (nginx/Apache) in front of Grafana.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Grafana shows no data
|
||||||
|
```bash
|
||||||
|
# Check if Prometheus is reachable
|
||||||
|
curl http://localhost:9090/-/healthy
|
||||||
|
|
||||||
|
# Check datasource in Grafana
|
||||||
|
# Settings → Data Sources → Prometheus → Save & Test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loki not receiving logs
|
||||||
|
```bash
|
||||||
|
# Check Promtail is running
|
||||||
|
sudo systemctl status promtail
|
||||||
|
|
||||||
|
# Verify log file exists
|
||||||
|
ls -l /var/log/fetch_ml/
|
||||||
|
|
||||||
|
# Check Promtail can reach Loki
|
||||||
|
curl http://localhost:3100/ready
|
||||||
|
```
|
||||||
|
|
||||||
|
### Podman containers not starting
|
||||||
|
```bash
|
||||||
|
# Check pod status
|
||||||
|
sudo -u ml-user podman pod ps
|
||||||
|
sudo -u ml-user podman ps -a
|
||||||
|
|
||||||
|
# Remove and recreate
|
||||||
|
sudo -u ml-user podman pod stop monitoring
|
||||||
|
sudo -u ml-user podman pod rm monitoring
|
||||||
|
sudo systemctl restart prometheus
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backup Grafana dashboards and data
|
||||||
|
sudo tar -czf grafana-backup.tar.gz /data/monitoring/grafana
|
||||||
|
|
||||||
|
# Backup Prometheus data
|
||||||
|
sudo tar -czf prometheus-backup.tar.gz /data/monitoring/prometheus
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull latest images
|
||||||
|
sudo -u ml-user podman pull docker.io/grafana/grafana:latest
|
||||||
|
sudo -u ml-user podman pull docker.io/prom/prometheus:latest
|
||||||
|
sudo -u ml-user podman pull docker.io/grafana/loki:latest
|
||||||
|
sudo -u ml-user podman pull docker.io/grafana/promtail:latest
|
||||||
|
|
||||||
|
# Restart services to use new images
|
||||||
|
sudo systemctl restart grafana prometheus loki promtail
|
||||||
|
```
|
||||||
322
docs/src/queue.md
Normal file
322
docs/src/queue.md
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Task Queue Architecture"
|
||||||
|
permalink: /queue/
|
||||||
|
nav_order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task Queue Architecture
|
||||||
|
|
||||||
|
The task queue system enables reliable job processing between the API server and workers using Redis.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
CLI[CLI/Client] -->|WebSocket| API[API Server]
|
||||||
|
API -->|Enqueue| Redis[(Redis)]
|
||||||
|
Redis -->|Dequeue| Worker[Worker]
|
||||||
|
Worker -->|Update Status| Redis
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### TaskQueue (`internal/queue`)
|
||||||
|
|
||||||
|
Shared package used by both API server and worker for job management.
|
||||||
|
|
||||||
|
#### Task Structure
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Task struct {
|
||||||
|
ID string // Unique task ID (UUID)
|
||||||
|
JobName string // User-defined job name
|
||||||
|
Args string // Job arguments
|
||||||
|
Status string // queued, running, completed, failed
|
||||||
|
Priority int64 // Higher = executed first
|
||||||
|
CreatedAt time.Time
|
||||||
|
StartedAt *time.Time
|
||||||
|
EndedAt *time.Time
|
||||||
|
WorkerID string
|
||||||
|
Error string
|
||||||
|
Datasets []string
|
||||||
|
Metadata map[string]string // commit_id, user, etc
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### TaskQueue Interface
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Initialize queue
|
||||||
|
queue, err := queue.NewTaskQueue(queue.Config{
|
||||||
|
RedisAddr: "localhost:6379",
|
||||||
|
RedisPassword: "",
|
||||||
|
RedisDB: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add task (API server)
|
||||||
|
task := &queue.Task{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
JobName: "train-model",
|
||||||
|
Status: "queued",
|
||||||
|
Priority: 5,
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"commit_id": commitID,
|
||||||
|
"user": username,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = queue.AddTask(task)
|
||||||
|
|
||||||
|
// Get next task (Worker)
|
||||||
|
task, err := queue.GetNextTask()
|
||||||
|
|
||||||
|
// Update task status
|
||||||
|
task.Status = "running"
|
||||||
|
err = queue.UpdateTask(task)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### Job Submission Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant CLI
|
||||||
|
participant API
|
||||||
|
participant Redis
|
||||||
|
participant Worker
|
||||||
|
|
||||||
|
CLI->>API: Queue Job (WebSocket)
|
||||||
|
API->>API: Create Task (UUID)
|
||||||
|
API->>Redis: ZADD task:queue
|
||||||
|
API->>Redis: SET task:{id}
|
||||||
|
API->>CLI: Success Response
|
||||||
|
|
||||||
|
Worker->>Redis: ZPOPMAX task:queue
|
||||||
|
Redis->>Worker: Task ID
|
||||||
|
Worker->>Redis: GET task:{id}
|
||||||
|
Redis->>Worker: Task Data
|
||||||
|
Worker->>Worker: Execute Job
|
||||||
|
Worker->>Redis: Update Status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Protocol
|
||||||
|
|
||||||
|
**CLI → API** (Binary WebSocket):
|
||||||
|
```
|
||||||
|
[opcode:1][api_key_hash:64][commit_id:64][priority:1][job_name_len:1][job_name:var]
|
||||||
|
```
|
||||||
|
|
||||||
|
**API → Redis**:
|
||||||
|
- Priority queue: `ZADD task:queue {priority} {task_id}`
|
||||||
|
- Task data: `SET task:{id} {json}`
|
||||||
|
- Status: `HSET task:status:{job_name} ...`
|
||||||
|
|
||||||
|
**Worker ← Redis**:
|
||||||
|
- Poll: `ZPOPMAX task:queue 1` (highest priority first)
|
||||||
|
- Fetch: `GET task:{id}`
|
||||||
|
|
||||||
|
## Redis Data Structures
|
||||||
|
|
||||||
|
### Keys
|
||||||
|
|
||||||
|
```
|
||||||
|
task:queue # ZSET: priority queue
|
||||||
|
task:{uuid} # STRING: task JSON data
|
||||||
|
task:status:{job_name} # HASH: job status
|
||||||
|
worker:heartbeat # HASH: worker health
|
||||||
|
job:metrics:{job_name} # HASH: job metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
### Priority Queue (ZSET)
|
||||||
|
|
||||||
|
```redis
|
||||||
|
ZADD task:queue 10 "uuid-1" # Priority 10
|
||||||
|
ZADD task:queue 5 "uuid-2" # Priority 5
|
||||||
|
ZPOPMAX task:queue 1 # Returns uuid-1 (highest)
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Server Integration
|
||||||
|
|
||||||
|
### Initialization
|
||||||
|
|
||||||
|
```go
|
||||||
|
// cmd/api-server/main.go
|
||||||
|
queueCfg := queue.Config{
|
||||||
|
RedisAddr: cfg.Redis.Addr,
|
||||||
|
RedisPassword: cfg.Redis.Password,
|
||||||
|
RedisDB: cfg.Redis.DB,
|
||||||
|
}
|
||||||
|
taskQueue, err := queue.NewTaskQueue(queueCfg)
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Handler
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/api/ws.go
|
||||||
|
func (h *WSHandler) handleQueueJob(conn *websocket.Conn, payload []byte) error {
|
||||||
|
// Parse request
|
||||||
|
apiKeyHash, commitID, priority, jobName := parsePayload(payload)
|
||||||
|
|
||||||
|
// Create task with unique ID
|
||||||
|
taskID := uuid.New().String()
|
||||||
|
task := &queue.Task{
|
||||||
|
ID: taskID,
|
||||||
|
JobName: jobName,
|
||||||
|
Status: "queued",
|
||||||
|
Priority: int64(priority),
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"commit_id": commitID,
|
||||||
|
"user": user,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue
|
||||||
|
if err := h.queue.AddTask(task); err != nil {
|
||||||
|
return h.sendErrorPacket(conn, ErrorCodeDatabaseError, ...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.sendSuccessPacket(conn, "Job queued")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Worker Integration
|
||||||
|
|
||||||
|
### Task Polling
|
||||||
|
|
||||||
|
```go
|
||||||
|
// cmd/worker/worker_server.go
|
||||||
|
func (w *Worker) Start() error {
|
||||||
|
for {
|
||||||
|
task, err := w.queue.WaitForNextTask(ctx, 5*time.Second)
|
||||||
|
if task != nil {
|
||||||
|
go w.executeTask(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task Execution
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (w *Worker) executeTask(task *queue.Task) {
|
||||||
|
// Update status
|
||||||
|
task.Status = "running"
|
||||||
|
task.StartedAt = &now
|
||||||
|
w.queue.UpdateTaskWithMetrics(task, "start")
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
err := w.runJob(task)
|
||||||
|
|
||||||
|
// Finalize
|
||||||
|
task.Status = "completed" // or "failed"
|
||||||
|
task.EndedAt = &endTime
|
||||||
|
task.Error = err.Error() // if err != nil
|
||||||
|
w.queue.UpdateTaskWithMetrics(task, "final")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### API Server (`configs/config.yaml`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
redis:
|
||||||
|
addr: "localhost:6379"
|
||||||
|
password: ""
|
||||||
|
db: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker (`configs/worker-config.yaml`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
redis:
|
||||||
|
addr: "localhost:6379"
|
||||||
|
password: ""
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
metrics_flush_interval: 500ms
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Queue Depth
|
||||||
|
|
||||||
|
```go
|
||||||
|
depth, err := queue.QueueDepth()
|
||||||
|
fmt.Printf("Pending tasks: %d\n", depth)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker Heartbeat
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Worker sends heartbeat every 30s
|
||||||
|
err := queue.Heartbeat(workerID)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Metrics
|
||||||
|
|
||||||
|
```redis
|
||||||
|
HGETALL job:metrics:{job_name}
|
||||||
|
# Returns: timestamp, tasks_start, tasks_final, etc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Task Failures
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := w.runJob(task); err != nil {
|
||||||
|
task.Status = "failed"
|
||||||
|
task.Error = err.Error()
|
||||||
|
w.queue.UpdateTask(task)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redis Connection Loss
|
||||||
|
|
||||||
|
```go
|
||||||
|
// TaskQueue automatically reconnects
|
||||||
|
// Workers should implement retry logic
|
||||||
|
for retries := 0; retries < 3; retries++ {
|
||||||
|
task, err := queue.GetNextTask()
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(backoff)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```go
|
||||||
|
// tests using miniredis
|
||||||
|
s, _ := miniredis.Run()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
tq, _ := queue.NewTaskQueue(queue.Config{
|
||||||
|
RedisAddr: s.Addr(),
|
||||||
|
})
|
||||||
|
|
||||||
|
task := &queue.Task{ID: "test-1", JobName: "test"}
|
||||||
|
tq.AddTask(task)
|
||||||
|
|
||||||
|
fetched, _ := tq.GetNextTask()
|
||||||
|
// assert fetched.ID == "test-1"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Unique Task IDs**: Always use UUIDs to avoid conflicts
|
||||||
|
2. **Metadata**: Store commit_id and user in task metadata
|
||||||
|
3. **Priority**: Higher values execute first (0-255 range)
|
||||||
|
4. **Status Updates**: Update status at each lifecycle stage
|
||||||
|
5. **Error Logging**: Store detailed errors in task.Error
|
||||||
|
6. **Heartbeats**: Workers should send heartbeats regularly
|
||||||
|
7. **Metrics**: Use UpdateTaskWithMetrics for atomic updates
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For implementation details, see:
|
||||||
|
- [internal/queue/task.go](https://github.com/jfraeys/fetch_ml/blob/main/internal/queue/task.go)
|
||||||
|
- [internal/queue/queue.go](https://github.com/jfraeys/fetch_ml/blob/main/internal/queue/queue.go)
|
||||||
93
docs/src/quick-start.md
Normal file
93
docs/src/quick-start.md
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
# Quick Start
|
||||||
|
|
||||||
|
Get Fetch ML running in minutes with Docker Compose.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
**Container Runtimes:**
|
||||||
|
- **Docker Compose**: For testing and development only
|
||||||
|
- **Podman**: For production experiment execution
|
||||||
|
|
||||||
|
- Docker Compose (testing only)
|
||||||
|
- 4GB+ RAM
|
||||||
|
- 2GB+ disk space
|
||||||
|
|
||||||
|
## One-Command Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone and start
|
||||||
|
git clone https://github.com/jfraeys/fetch_ml.git
|
||||||
|
cd fetch_ml
|
||||||
|
docker-compose up -d (testing only)
|
||||||
|
|
||||||
|
# Wait for services (30 seconds)
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Verify setup
|
||||||
|
curl http://localhost:9101/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## First Experiment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Submit a simple ML job (see [First Experiment](first-experiment.md) for details)
|
||||||
|
curl -X POST http://localhost:9101/api/v1/jobs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-API-Key: admin" \
|
||||||
|
-d '{
|
||||||
|
"job_name": "hello-world",
|
||||||
|
"args": "--echo Hello World",
|
||||||
|
"priority": 1
|
||||||
|
}'
|
||||||
|
|
||||||
|
# Check job status
|
||||||
|
curl http://localhost:9101/api/v1/jobs \
|
||||||
|
-H "X-API-Key: admin"
|
||||||
|
```
|
||||||
|
|
||||||
|
## CLI Access
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build CLI
|
||||||
|
cd cli && zig build dev
|
||||||
|
|
||||||
|
# List jobs
|
||||||
|
./cli/zig-out/dev/ml --server http://localhost:9101 list-jobs
|
||||||
|
|
||||||
|
# Submit new job
|
||||||
|
./cli/zig-out/dev/ml --server http://localhost:9101 submit \
|
||||||
|
--name "test-job" --args "--epochs 10"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- [Installation Guide](installation.md) - Detailed setup options
|
||||||
|
- [First Experiment](first-experiment.md) - Complete ML workflow
|
||||||
|
- [Development Setup](development-setup.md) - Local development
|
||||||
|
- [Security](security.md) - Authentication and permissions
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Services not starting?**
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker-compose logs
|
||||||
|
|
||||||
|
# Restart services
|
||||||
|
docker-compose down && docker-compose up -d (testing only)
|
||||||
|
```
|
||||||
|
|
||||||
|
**API not responding?**
|
||||||
|
```bash
|
||||||
|
# Check health
|
||||||
|
curl http://localhost:9101/health
|
||||||
|
|
||||||
|
# Verify ports
|
||||||
|
docker-compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
**Permission denied?**
|
||||||
|
```bash
|
||||||
|
# Check API key
|
||||||
|
curl -H "X-API-Key: admin" http://localhost:9101/api/v1/jobs
|
||||||
|
```
|
||||||
95
docs/src/redis-ha.md
Normal file
95
docs/src/redis-ha.md
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
---
|
||||||
|
layout: page
|
||||||
|
title: "Redis High Availability (Optional)"
|
||||||
|
permalink: /redis-ha/
|
||||||
|
nav_order: 7
|
||||||
|
---
|
||||||
|
|
||||||
|
# Redis High Availability
|
||||||
|
|
||||||
|
**Note:** This is optional for homelab setups. Single Redis instance is sufficient for most use cases.
|
||||||
|
|
||||||
|
## When You Need HA
|
||||||
|
|
||||||
|
Consider Redis HA if:
|
||||||
|
- Running production workloads
|
||||||
|
- Uptime > 99.9% required
|
||||||
|
- Can't afford to lose queued tasks
|
||||||
|
- Multiple workers across machines
|
||||||
|
|
||||||
|
## Redis Sentinel (Recommended)
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
redis-master:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --maxmemory 2gb
|
||||||
|
|
||||||
|
redis-replica:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --slaveof redis-master 6379
|
||||||
|
|
||||||
|
redis-sentinel-1:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-sentinel /etc/redis/sentinel.conf
|
||||||
|
volumes:
|
||||||
|
- ./sentinel.conf:/etc/redis/sentinel.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
**sentinel.conf:**
|
||||||
|
```conf
|
||||||
|
sentinel monitor mymaster redis-master 6379 2
|
||||||
|
sentinel down-after-milliseconds mymaster 5000
|
||||||
|
sentinel parallel-syncs mymaster 1
|
||||||
|
sentinel failover-timeout mymaster 10000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Application Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# worker-config.yaml
|
||||||
|
redis_addr: "redis-sentinel-1:26379,redis-sentinel-2:26379"
|
||||||
|
redis_master_name: "mymaster"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Redis Cluster (Advanced)
|
||||||
|
|
||||||
|
For larger deployments with sharding needs.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Minimum 3 masters + 3 replicas
|
||||||
|
services:
|
||||||
|
redis-1:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --cluster-enabled yes
|
||||||
|
|
||||||
|
redis-2:
|
||||||
|
# ... similar config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Homelab Alternative: Persistence Only
|
||||||
|
|
||||||
|
**For most homelabs, just enable persistence:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures tasks survive Redis restarts without full HA complexity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Recommendation:** Start simple. Add HA only if you experience actual downtime issues.
|
||||||
37
docs/src/release-checklist.md
Normal file
37
docs/src/release-checklist.md
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Release Checklist
|
||||||
|
|
||||||
|
This checklist captures the work required before cutting a release that includes
|
||||||
|
the graceful worker shutdown feature.
|
||||||
|
|
||||||
|
## 1. Code Hygiene / Compilation
|
||||||
|
1. Merge the graceful-shutdown helpers into the canonical worker type to avoid
|
||||||
|
`Worker redeclared` errors (see `cmd/worker/worker_graceful_shutdown.go` and
|
||||||
|
`cmd/worker/worker_server.go`).
|
||||||
|
2. Ensure the worker struct exposes the fields referenced by the new helpers
|
||||||
|
(`logger`, `queue`, `cfg`, `metrics`).
|
||||||
|
3. `go build ./cmd/worker` succeeds without undefined-field errors.
|
||||||
|
|
||||||
|
## 2. Graceful Shutdown Logic
|
||||||
|
1. Initialize `shutdownCh`, `activeTasks`, and `gracefulWait` during worker start-up.
|
||||||
|
2. Confirm the heartbeat/lease helpers compile and handle queue errors gracefully
|
||||||
|
(`heartbeatLoop`, `releaseAllLeases`).
|
||||||
|
3. Add tests (unit or integration) that simulate SIGINT/SIGTERM and verify leases
|
||||||
|
are released or tasks complete.
|
||||||
|
|
||||||
|
## 3. Task Execution Flow
|
||||||
|
1. Align `executeTaskWithLease` with the real `executeTask` signature so the
|
||||||
|
"no value used as value" compile error disappears.
|
||||||
|
2. Double-check retry/metrics paths still match existing worker behavior after
|
||||||
|
the new wrapper is added.
|
||||||
|
|
||||||
|
## 4. Server Wiring
|
||||||
|
1. Ensure worker construction in `cmd/worker/worker_server.go` wires up config,
|
||||||
|
queue, metrics, and logger instances used by the shutdown logic.
|
||||||
|
2. Re-run worker unit tests plus any queue/lease e2e tests.
|
||||||
|
|
||||||
|
## 5. Validation Before Tagging
|
||||||
|
1. `go test ./cmd/worker/...` and `make test` (or equivalent) pass locally.
|
||||||
|
2. Manual smoke test: start worker, queue jobs, send SIGTERM, confirm tasks finish
|
||||||
|
or leases are released and the process exits cleanly.
|
||||||
|
3. Update release notes describing the new shutdown capability and any config
|
||||||
|
changes required (e.g., graceful timeout settings).
|
||||||
297
docs/src/security.md
Normal file
297
docs/src/security.md
Normal file
|
|
@ -0,0 +1,297 @@
|
||||||
|
# Security Guide
|
||||||
|
|
||||||
|
This document outlines security features, best practices, and hardening procedures for FetchML.
|
||||||
|
|
||||||
|
## Security Features
|
||||||
|
|
||||||
|
### Authentication & Authorization
|
||||||
|
- **API Keys**: SHA256-hashed with role-based access control (RBAC)
|
||||||
|
- **Permissions**: Granular read/write/delete permissions per user
|
||||||
|
- **IP Whitelisting**: Network-level access control
|
||||||
|
- **Rate Limiting**: Per-user request quotas
|
||||||
|
|
||||||
|
### Communication Security
|
||||||
|
- **TLS/HTTPS**: End-to-end encryption for API traffic
|
||||||
|
- **WebSocket Auth**: API key required before upgrade
|
||||||
|
- **Redis Auth**: Password-protected task queue
|
||||||
|
|
||||||
|
### Data Privacy
|
||||||
|
- **Log Sanitization**: Automatically redacts API keys, passwords, tokens
|
||||||
|
- **Experiment Isolation**: User-specific experiment directories
|
||||||
|
- **No Anonymous Access**: All services require authentication
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
- **Internal Networks**: Backend services (Redis, Loki) not exposed publicly
|
||||||
|
- **Firewall Rules**: Restrictive port access
|
||||||
|
- **Container Isolation**: Services run in separate containers/pods
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
### Initial Setup
|
||||||
|
|
||||||
|
1. **Generate Strong Passwords**
|
||||||
|
```bash
|
||||||
|
# Grafana admin password
|
||||||
|
openssl rand -base64 32 > .grafana-password
|
||||||
|
|
||||||
|
# Redis password
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure Environment Variables**
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# Edit .env and set:
|
||||||
|
# - GRAFANA_ADMIN_PASSWORD
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Enable TLS** (Production only)
|
||||||
|
```yaml
|
||||||
|
# configs/config-prod.yaml
|
||||||
|
server:
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
cert_file: "/secrets/cert.pem"
|
||||||
|
key_file: "/secrets/key.pem"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Configure Firewall**
|
||||||
|
```bash
|
||||||
|
# Allow only necessary ports
|
||||||
|
sudo ufw allow 22/tcp # SSH
|
||||||
|
sudo ufw allow 443/tcp # HTTPS
|
||||||
|
sudo ufw allow 80/tcp # HTTP (redirect to HTTPS)
|
||||||
|
sudo ufw enable
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Hardening
|
||||||
|
|
||||||
|
5. **Restrict IP Access**
|
||||||
|
```yaml
|
||||||
|
# configs/config-prod.yaml
|
||||||
|
auth:
|
||||||
|
ip_whitelist:
|
||||||
|
- "10.0.0.0/8"
|
||||||
|
- "192.168.0.0/16"
|
||||||
|
- "127.0.0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Enable Audit Logging**
|
||||||
|
```yaml
|
||||||
|
logging:
|
||||||
|
level: "info"
|
||||||
|
audit: true
|
||||||
|
file: "/var/log/fetch_ml/audit.log"
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Harden Redis**
|
||||||
|
```bash
|
||||||
|
# Redis security
|
||||||
|
redis-cli CONFIG SET requirepass "your-strong-password"
|
||||||
|
redis-cli CONFIG SET rename-command FLUSHDB ""
|
||||||
|
redis-cli CONFIG SET rename-command FLUSHALL ""
|
||||||
|
```
|
||||||
|
|
||||||
|
8. **Secure Grafana**
|
||||||
|
```bash
|
||||||
|
# Change default admin password
|
||||||
|
docker-compose exec grafana grafana-cli admin reset-admin-password new-strong-password
|
||||||
|
```
|
||||||
|
|
||||||
|
9. **Regular Updates**
|
||||||
|
```bash
|
||||||
|
# Update system packages
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
|
||||||
|
# Update containers
|
||||||
|
docker-compose pull
|
||||||
|
docker-compose up -d (testing only)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Password Management
|
||||||
|
|
||||||
|
### Generate Secure Passwords
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Method 1: OpenSSL
|
||||||
|
openssl rand -base64 32
|
||||||
|
|
||||||
|
# Method 2: pwgen (if installed)
|
||||||
|
pwgen -s 32 1
|
||||||
|
|
||||||
|
# Method 3: /dev/urandom
|
||||||
|
head -c 32 /dev/urandom | base64
|
||||||
|
```
|
||||||
|
|
||||||
|
### Store Passwords Securely
|
||||||
|
|
||||||
|
**Development**: Use `.env` file (gitignored)
|
||||||
|
```bash
|
||||||
|
echo "REDIS_PASSWORD=$(openssl rand -base64 32)" >> .env
|
||||||
|
echo "GRAFANA_ADMIN_PASSWORD=$(openssl rand -base64 32)" >> .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**Production**: Use systemd environment files
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /etc/fetch_ml/secrets
|
||||||
|
sudo chmod 700 /etc/fetch_ml/secrets
|
||||||
|
echo "REDIS_PASSWORD=..." | sudo tee /etc/fetch_ml/secrets/redis.env
|
||||||
|
sudo chmod 600 /etc/fetch_ml/secrets/redis.env
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Key Management
|
||||||
|
|
||||||
|
### Generate API Keys
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate random API key
|
||||||
|
openssl rand -hex 32
|
||||||
|
|
||||||
|
# Hash for storage
|
||||||
|
echo -n "your-api-key" | sha256sum
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rotate API Keys
|
||||||
|
|
||||||
|
1. Generate new API key
|
||||||
|
2. Update `config-local.yaml` with new hash
|
||||||
|
3. Distribute new key to users
|
||||||
|
4. Remove old key after grace period
|
||||||
|
|
||||||
|
### Revoke API Keys
|
||||||
|
|
||||||
|
Remove user entry from `config-local.yaml`:
|
||||||
|
```yaml
|
||||||
|
auth:
|
||||||
|
apikeys:
|
||||||
|
# user_to_revoke: # Comment out or delete
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network Security
|
||||||
|
|
||||||
|
### Production Network Topology
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet
|
||||||
|
↓
|
||||||
|
[Firewall] (ports 3000, 9102)
|
||||||
|
↓
|
||||||
|
[Reverse Proxy] (nginx/Apache) - TLS termination
|
||||||
|
↓
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ Application Pod │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
│ │ API Server │ │ ← Public (via reverse proxy)
|
||||||
|
│ └──────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
│ │ Redis │ │ ← Internal only
|
||||||
|
│ └──────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
│ │ Grafana │ │ ← Public (via reverse proxy)
|
||||||
|
│ └──────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
│ │ Prometheus │ │ ← Internal only
|
||||||
|
│ └──────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
│ │ Loki │ │ ← Internal only
|
||||||
|
│ └──────────────┘ │
|
||||||
|
└─────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recommended Firewall Rules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Allow only necessary inbound connections
|
||||||
|
sudo firewall-cmd --permanent --zone=public --add-rich-rule='
|
||||||
|
rule family="ipv4"
|
||||||
|
source address="YOUR_NETWORK"
|
||||||
|
port port="3000" protocol="tcp" accept'
|
||||||
|
|
||||||
|
sudo firewall-cmd --permanent --zone=public --add-rich-rule='
|
||||||
|
rule family="ipv4"
|
||||||
|
source address="YOUR_NETWORK"
|
||||||
|
port port="9102" protocol="tcp" accept'
|
||||||
|
|
||||||
|
# Block all other traffic
|
||||||
|
sudo firewall-cmd --permanent --set-default-zone=drop
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
## Incident Response
|
||||||
|
|
||||||
|
### Suspected Breach
|
||||||
|
|
||||||
|
1. **Immediate Actions**
|
||||||
|
2. **Investigation**
|
||||||
|
3. **Recovery**
|
||||||
|
- Rotate all API keys
|
||||||
|
- Stop affected services
|
||||||
|
- Review audit logs
|
||||||
|
|
||||||
|
2. **Investigation**
|
||||||
|
```bash
|
||||||
|
# Check recent logins
|
||||||
|
sudo journalctl -u fetchml-api --since "1 hour ago"
|
||||||
|
|
||||||
|
# Review failed auth attempts
|
||||||
|
grep "authentication failed" /var/log/fetch_ml/*.log
|
||||||
|
|
||||||
|
# Check active connections
|
||||||
|
ss -tnp | grep :9102
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Recovery**
|
||||||
|
- Rotate all passwords and API keys
|
||||||
|
- Update firewall rules
|
||||||
|
- Patch vulnerabilities
|
||||||
|
- Resume services
|
||||||
|
|
||||||
|
### Security Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Monitor failed authentication
|
||||||
|
tail -f /var/log/fetch_ml/api.log | grep "auth.*failed"
|
||||||
|
|
||||||
|
# Monitor unusual activity
|
||||||
|
journalctl -u fetchml-api -f | grep -E "(ERROR|WARN)"
|
||||||
|
|
||||||
|
# Check open ports
|
||||||
|
nmap -p- localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
1. **Principle of Least Privilege**: Grant minimum necessary permissions
|
||||||
|
2. **Defense in Depth**: Multiple security layers (firewall + auth + TLS)
|
||||||
|
3. **Regular Updates**: Keep all components patched
|
||||||
|
4. **Audit Regularly**: Review logs and access patterns
|
||||||
|
5. **Secure Secrets**: Never commit passwords/keys to git
|
||||||
|
6. **Network Segmentation**: Isolate services with internal networks
|
||||||
|
7. **Monitor Everything**: Enable comprehensive logging and alerting
|
||||||
|
8. **Test Security**: Regular penetration testing and vulnerability scans
|
||||||
|
|
||||||
|
## Compliance
|
||||||
|
|
||||||
|
### Data Privacy
|
||||||
|
- Logs are sanitized (no passwords/API keys)
|
||||||
|
- Experiment data is user-isolated
|
||||||
|
- No telemetry or external data sharing
|
||||||
|
|
||||||
|
### Audit Trail
|
||||||
|
All API access is logged with:
|
||||||
|
- Timestamp
|
||||||
|
- User/API key
|
||||||
|
- Action performed
|
||||||
|
- Source IP
|
||||||
|
- Result (success/failure)
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- **Security Issues**: Report privately via email
|
||||||
|
- **Questions**: See documentation or create issue
|
||||||
|
- **Updates**: Monitor releases for security patches
|
||||||
187
docs/src/smart-defaults.md
Normal file
187
docs/src/smart-defaults.md
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
# Smart Defaults
|
||||||
|
|
||||||
|
This document describes Fetch ML's smart defaults system, which automatically adapts configuration based on the runtime environment.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Smart defaults eliminate the need for manual configuration tweaks when running in different environments:
|
||||||
|
|
||||||
|
- **Local Development**: Optimized for developer machines with sensible paths and localhost services
|
||||||
|
- **Container Environments**: Uses container-friendly hostnames and paths
|
||||||
|
- **CI/CD**: Optimized for automated testing with fast polling and minimal resource usage
|
||||||
|
- **Production**: Uses production-ready defaults with proper security and scaling
|
||||||
|
|
||||||
|
## Environment Detection
|
||||||
|
|
||||||
|
The system automatically detects the environment based on:
|
||||||
|
|
||||||
|
1. **CI Detection**: Checks for `CI`, `GITHUB_ACTIONS`, `GITLAB_CI` environment variables
|
||||||
|
2. **Container Detection**: Looks for `/.dockerenv`, `KUBERNETES_SERVICE_HOST`, or `CONTAINER` variables
|
||||||
|
3. **Production Detection**: Checks `FETCH_ML_ENV=production` or `ENV=production`
|
||||||
|
4. **Default**: Falls back to local development
|
||||||
|
|
||||||
|
## Default Values by Environment
|
||||||
|
|
||||||
|
### Host Configuration
|
||||||
|
- **Local**: `localhost`
|
||||||
|
- **Container/CI**: `host.docker.internal` (Docker Desktop/Colima)
|
||||||
|
- **Production**: `0.0.0.0`
|
||||||
|
|
||||||
|
### Base Paths
|
||||||
|
- **Local**: `~/ml-experiments`
|
||||||
|
- **Container/CI**: `/workspace/ml-experiments`
|
||||||
|
- **Production**: `/var/lib/fetch_ml/experiments`
|
||||||
|
|
||||||
|
### Data Directory
|
||||||
|
- **Local**: `~/ml-data`
|
||||||
|
- **Container/CI**: `/workspace/data`
|
||||||
|
- **Production**: `/var/lib/fetch_ml/data`
|
||||||
|
|
||||||
|
### Redis Address
|
||||||
|
- **Local**: `localhost:6379`
|
||||||
|
- **Container/CI**: `redis:6379` (service name)
|
||||||
|
- **Production**: `redis:6379`
|
||||||
|
|
||||||
|
### SSH Configuration
|
||||||
|
- **Local**: `~/.ssh/id_rsa` and `~/.ssh/known_hosts`
|
||||||
|
- **Container/CI**: `/workspace/.ssh/id_rsa` and `/workspace/.ssh/known_hosts`
|
||||||
|
- **Production**: `/etc/fetch_ml/ssh/id_rsa` and `/etc/fetch_ml/ssh/known_hosts`
|
||||||
|
|
||||||
|
### Worker Configuration
|
||||||
|
- **Local**: 2 workers, 5-second poll interval
|
||||||
|
- **CI**: 1 worker, 1-second poll interval (fast testing)
|
||||||
|
- **Production**: CPU core count workers, 10-second poll interval
|
||||||
|
|
||||||
|
### Log Levels
|
||||||
|
- **Local**: `info`
|
||||||
|
- **CI**: `debug` (verbose for debugging)
|
||||||
|
- **Production**: `info`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### In Configuration Loaders
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Get smart defaults for current environment
|
||||||
|
smart := config.GetSmartDefaults()
|
||||||
|
|
||||||
|
// Use smart defaults
|
||||||
|
if cfg.Host == "" {
|
||||||
|
cfg.Host = smart.Host()
|
||||||
|
}
|
||||||
|
if cfg.BasePath == "" {
|
||||||
|
cfg.BasePath = smart.BasePath()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Overrides
|
||||||
|
|
||||||
|
Smart defaults can be overridden with environment variables:
|
||||||
|
|
||||||
|
- `FETCH_ML_HOST` - Override host
|
||||||
|
- `FETCH_ML_BASE_PATH` - Override base path
|
||||||
|
- `FETCH_ML_REDIS_ADDR` - Override Redis address
|
||||||
|
- `FETCH_ML_ENV` - Force environment profile
|
||||||
|
|
||||||
|
### Manual Environment Selection
|
||||||
|
|
||||||
|
You can force a specific environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Force production mode
|
||||||
|
export FETCH_ML_ENV=production
|
||||||
|
|
||||||
|
# Force container mode
|
||||||
|
export CONTAINER=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
The smart defaults system is implemented in `internal/config/smart_defaults.go`:
|
||||||
|
|
||||||
|
- `DetectEnvironment()` - Determines current environment profile
|
||||||
|
- `SmartDefaults` struct - Provides environment-aware defaults
|
||||||
|
- Helper methods for each configuration value
|
||||||
|
|
||||||
|
## Migration Guide
|
||||||
|
|
||||||
|
### For Users
|
||||||
|
|
||||||
|
No changes required - existing configurations continue to work. Smart defaults only apply when values are not explicitly set.
|
||||||
|
|
||||||
|
### For Developers
|
||||||
|
|
||||||
|
When adding new configuration options:
|
||||||
|
|
||||||
|
1. Add a method to `SmartDefaults` struct
|
||||||
|
2. Use the smart default in config loaders
|
||||||
|
3. Document the environment-specific values
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Add to SmartDefaults struct
|
||||||
|
func (s *SmartDefaults) NewFeature() string {
|
||||||
|
switch s.Profile {
|
||||||
|
case ProfileContainer, ProfileCI:
|
||||||
|
return "/workspace/new-feature"
|
||||||
|
case ProfileProduction:
|
||||||
|
return "/var/lib/fetch_ml/new-feature"
|
||||||
|
default:
|
||||||
|
return "./new-feature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use in config loader
|
||||||
|
if cfg.NewFeature == "" {
|
||||||
|
cfg.NewFeature = smart.NewFeature()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
To test different environments:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test local defaults (default)
|
||||||
|
./bin/worker
|
||||||
|
|
||||||
|
# Test container defaults
|
||||||
|
export CONTAINER=true
|
||||||
|
./bin/worker
|
||||||
|
|
||||||
|
# Test CI defaults
|
||||||
|
export CI=true
|
||||||
|
./bin/worker
|
||||||
|
|
||||||
|
# Test production defaults
|
||||||
|
export FETCH_ML_ENV=production
|
||||||
|
./bin/worker
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Wrong Environment Detection
|
||||||
|
|
||||||
|
Check environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "CI: $CI"
|
||||||
|
echo "CONTAINER: $CONTAINER"
|
||||||
|
echo "FETCH_ML_ENV: $FETCH_ML_ENV"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Path Issues
|
||||||
|
|
||||||
|
Smart defaults expand `~` and environment variables automatically. If paths don't work as expected:
|
||||||
|
|
||||||
|
1. Check the detected environment: `config.GetSmartDefaults().GetEnvironmentDescription()`
|
||||||
|
2. Verify the path exists in the target environment
|
||||||
|
3. Override with environment variable if needed
|
||||||
|
|
||||||
|
### Container Networking
|
||||||
|
|
||||||
|
For container environments, ensure:
|
||||||
|
- Redis service is named `redis` in docker-compose
|
||||||
|
- Host networking is configured properly
|
||||||
|
- `host.docker.internal` resolves (Docker Desktop/Colima)
|
||||||
46
docs/src/testing.md
Normal file
46
docs/src/testing.md
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Testing Guide
|
||||||
|
|
||||||
|
How to run and write tests for FetchML.
|
||||||
|
|
||||||
|
## Running Tests
|
||||||
|
|
||||||
|
### Quick Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All tests
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Unit tests only
|
||||||
|
make test-unit
|
||||||
|
|
||||||
|
# Integration tests
|
||||||
|
make test-integration
|
||||||
|
|
||||||
|
# With coverage
|
||||||
|
make test-coverage
|
||||||
|
|
||||||
|
|
||||||
|
## Quick Test
|
||||||
|
```bash
|
||||||
|
make test # All tests
|
||||||
|
make test-unit # Unit only
|
||||||
|
.
|
||||||
|
make test.
|
||||||
|
make test$
|
||||||
|
make test; make test # Coverage
|
||||||
|
# E2E tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Testing
|
||||||
|
```bash
|
||||||
|
docker-compose up -d (testing only)
|
||||||
|
make test
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
## CLI Testing
|
||||||
|
```bash
|
||||||
|
cd cli && zig build dev
|
||||||
|
./cli/zig-out/dev/ml --help
|
||||||
|
zig build test
|
||||||
|
```
|
||||||
94
docs/src/troubleshooting.md
Normal file
94
docs/src/troubleshooting.md
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
Common issues and solutions for Fetch ML.
|
||||||
|
|
||||||
|
## Quick Fixes
|
||||||
|
|
||||||
|
### Services Not Starting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Docker status
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# Restart services
|
||||||
|
docker-compose down && docker-compose up -d (testing only)
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Not Responding
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check health endpoint
|
||||||
|
curl http://localhost:9101/health
|
||||||
|
|
||||||
|
# Check if port is in use
|
||||||
|
lsof -i :9101
|
||||||
|
|
||||||
|
# Kill process on port
|
||||||
|
kill -9 $(lsof -ti :9101)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check database connection
|
||||||
|
docker-compose exec postgres psql -U postgres -d fetch_ml
|
||||||
|
|
||||||
|
# Reset database
|
||||||
|
docker-compose down postgres
|
||||||
|
docker-compose up -d (testing only) postgres
|
||||||
|
|
||||||
|
# Check Redis
|
||||||
|
docker-compose exec redis redis-cli ping
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Errors
|
||||||
|
|
||||||
|
### Authentication Errors
|
||||||
|
|
||||||
|
- **Invalid API key**: Check config and regenerate hash
|
||||||
|
- **JWT expired**: Check `jwt_expiry` setting
|
||||||
|
|
||||||
|
### Database Errors
|
||||||
|
- **Connection failed**: Verify database type and connection params
|
||||||
|
- **No such table**: Run migrations with `--migrate` (see [Development Setup](development-setup.md))
|
||||||
|
|
||||||
|
### Container Errors
|
||||||
|
- **Runtime not found**: Set `runtime: docker (testing only)` in config
|
||||||
|
- **Image pull failed**: Check registry access
|
||||||
|
|
||||||
|
## Performance Issues
|
||||||
|
- **High memory**: Adjust `resources.memory_limit`
|
||||||
|
- **Slow jobs**: Check worker count and queue size
|
||||||
|
|
||||||
|
## Development Issues
|
||||||
|
- **Build fails**: `go mod tidy` and `cd cli && rm -rf zig-out zig-cache`
|
||||||
|
- **Tests fail**: Start test dependencies with `docker-compose -f docker-compose.test.yml up -d`
|
||||||
|
|
||||||
|
## CLI Issues
|
||||||
|
- **Not found**: `cd cli && zig build dev`
|
||||||
|
- **Connection errors**: Check `--server` and `--api-key`
|
||||||
|
|
||||||
|
## Network Issues
|
||||||
|
- **Port conflicts**: `lsof -i :9101` and kill processes
|
||||||
|
- **Firewall**: Allow ports 9101, 6379, 5432
|
||||||
|
|
||||||
|
## Configuration Issues
|
||||||
|
- **Invalid YAML**: `python3 -c "import yaml; yaml.safe_load(open('config.yaml'))"`
|
||||||
|
- **Missing fields**: Run `see [Configuration Schema](configuration-schema.md)`
|
||||||
|
|
||||||
|
## Debug Information
|
||||||
|
```bash
|
||||||
|
./bin/api-server --version
|
||||||
|
docker-compose ps
|
||||||
|
docker-compose logs api-server | grep ERROR
|
||||||
|
```
|
||||||
|
|
||||||
|
## Emergency Reset
|
||||||
|
```bash
|
||||||
|
docker-compose down -v
|
||||||
|
rm -rf data/ results/ *.db
|
||||||
|
docker-compose up -d (testing only)
|
||||||
|
```
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue