feat: add uConsole CM5 host with Reticulum mesh network support #9
203
assets/ai-optimizer/CRON_EXECUTION_PROMPT.md
Normal file
203
assets/ai-optimizer/CRON_EXECUTION_PROMPT.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# AI Model Optimization Cron Job - EXECUTION PROMPT
|
||||
|
||||
**When this cron runs, follow these instructions exactly:**
|
||||
|
||||
---
|
||||
|
||||
## Your Role
|
||||
|
||||
You are an AI model optimization agent. Your task is to find the best ollama/llama.cpp configuration for maximum context size and hardware utilization.
|
||||
|
||||
**Hardware:**
|
||||
- 2× AMD MI50 GPUs (32GB VRAM each, 64GB total)
|
||||
- 128GB system RAM
|
||||
- ROCm: HSA_OVERRIDE_GFX_VERSION=9.0.6, HIP_VISIBLE_DEVICES=0,1
|
||||
|
||||
---
|
||||
|
||||
## File Locations
|
||||
|
||||
```
|
||||
STATE: /opt/data/infra/assets/ai-optimizer/state.json
|
||||
RESULTS: /opt/data/infra/assets/ai-optimizer/results.csv
|
||||
INFRA_REPO: /opt/data/infra
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Model Queues
|
||||
|
||||
### GPU Track (Coding - prioritize speed + context on GPU)
|
||||
1. `devstral-small-2:24b`
|
||||
2. `qwen2.5-coder:32b`
|
||||
3. `codellama:34b-instruct`
|
||||
|
||||
### RAM Track (Knowledge - prioritize max context)
|
||||
1. `qwen2.5:72b`
|
||||
2. `nemotron-3-nano:30b`
|
||||
3. `mixtral:8x7b-instruct`
|
||||
|
||||
---
|
||||
|
||||
## Context Steps (in order)
|
||||
```
|
||||
[32768, 65536, 98304, 131072, 163840, 200704, 262144, 327680]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Each Run - Step by Step
|
||||
|
||||
### 1. Read State
|
||||
```bash
|
||||
cd /opt/data/infra
|
||||
cat assets/ai-optimizer/state.json
|
||||
```
|
||||
|
||||
### 2. Determine Next Test
|
||||
- Read `track` (gpu or ram)
|
||||
- Read `current_model` from queue at `model_index`
|
||||
- Read `current_config` for parameters to test
|
||||
- Select next context step from `context_steps` based on `phase`
|
||||
|
||||
### 3. Pull Model (if needed)
|
||||
```bash
|
||||
docker exec ollama ollama list | grep -q "<model>" || docker exec ollama ollama pull <model>
|
||||
```
|
||||
|
||||
### 4. Create Test Modelfile
|
||||
```bash
|
||||
docker exec ollama bash -c "cat <<EOF > /root/.ollama/test_${model}.modelfile
|
||||
FROM ${model}
|
||||
PARAMETER num_ctx ${current_config.num_ctx}
|
||||
PARAMETER num_gpu ${current_config.num_gpu}
|
||||
PARAMETER flash_attn ${current_config.flash_attn}
|
||||
PARAMETER num_predict 4096
|
||||
PARAMETER num_keep 1024
|
||||
PARAMETER repeat_penalty 1.1
|
||||
EOF"
|
||||
|
||||
docker exec ollama ollama create test-model -f /root/.ollama/test_${model}.modelfile
|
||||
```
|
||||
|
||||
### 5. Run Benchmark
|
||||
```bash
|
||||
# Warm up
|
||||
docker exec ollama ollama run test-model "Hello" > /dev/null
|
||||
|
||||
# Coding prompt
|
||||
START=$(date +%s%N)
|
||||
docker exec ollama ollama run test-model "Write a Python async context manager that retries a function with exponential backoff, max 5 retries, and logs each attempt using structlog. Include type hints."
|
||||
END=$(date +%s%N)
|
||||
|
||||
# Calculate tokens/sec from output
|
||||
```
|
||||
|
||||
### 6. Measure VRAM (if possible)
|
||||
```bash
|
||||
# Try host first
|
||||
rocm-smi --showmeminfo vram 2>/dev/null || \
|
||||
# Try via docker
|
||||
docker exec --privileged ollama rocm-smi --showmeminfo vram 2>/dev/null || \
|
||||
# Fallback
|
||||
echo "VRAM measurement unavailable"
|
||||
```
|
||||
|
||||
### 7. Record Results
|
||||
- Parse tokens/sec from ollama output
|
||||
- Record VRAM/RAM usage
|
||||
- Determine if this is best config so far for this model
|
||||
- Update `best_configs` if tokens/sec improved or context increased
|
||||
|
||||
### 8. Update State
|
||||
```python
|
||||
# Logic:
|
||||
if test_successful:
|
||||
if context_step < max_reached:
|
||||
phase = "context_scaling"
|
||||
current_config.num_ctx = next_context_step
|
||||
else:
|
||||
# Move to next model
|
||||
model_index += 1
|
||||
phase = "context_scaling"
|
||||
current_config.num_ctx = context_steps[0]
|
||||
else:
|
||||
# OOM or error - record last good as best
|
||||
best_configs[track][current_model] = last_good_config
|
||||
model_index += 1
|
||||
phase = "context_scaling"
|
||||
```
|
||||
|
||||
### 9. Commit to Repo
|
||||
```bash
|
||||
cd /opt/data/infra
|
||||
git add assets/ai-optimizer/
|
||||
git commit -m "ai-optimizer: tested ${model} at ${num_ctx} ctx - ${status}"
|
||||
git push origin master
|
||||
```
|
||||
|
||||
### 10. Matrix Notification (if available)
|
||||
```python
|
||||
import os
|
||||
if os.getenv("MATRIX_HOME_SERVER") and os.getenv("MATRIX_ACCESS_TOKEN"):
|
||||
# Send notification to Matrix room
|
||||
# Room ID from env or config
|
||||
pass
|
||||
# Else: silent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Stop Conditions
|
||||
|
||||
1. All models in both queues have `best_configs` recorded
|
||||
2. Manual intervention needed (error in state.json `error` field)
|
||||
3. No progress for 3 consecutive runs (stuck)
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
If any step fails:
|
||||
1. Log error to state.json: `"error": {"message": "...", "timestamp": "..."}`
|
||||
2. Do NOT increment model_index (retry next run)
|
||||
3. Commit state with error field
|
||||
4. Exit gracefully
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **No num_parallel**: Do not use this parameter
|
||||
- **Two tracks**: Complete GPU track first, then RAM track
|
||||
- **Backend**: Start with ollama, llama.cpp testing is optional (requires uncommenting in compose.yml)
|
||||
- **Host access**: Some commands need host - use docker exec or SSH if available
|
||||
- **Ask before deploy**: If config changes needed in NixOS modules, show diff and wait for user confirmation before `nh os switch`
|
||||
|
||||
---
|
||||
|
||||
## Example State Transitions
|
||||
|
||||
**Start:**
|
||||
```json
|
||||
{"track": "gpu", "model_index": 0, "current_model": "devstral-small-2:24b", "current_config": {"num_ctx": 32768, ...}}
|
||||
```
|
||||
|
||||
**After successful test at 32k:**
|
||||
```json
|
||||
{"track": "gpu", "model_index": 0, "current_model": "devstral-small-2:24b", "current_config": {"num_ctx": 65536, ...}}
|
||||
```
|
||||
|
||||
**After OOM at 131k:**
|
||||
```json
|
||||
{
|
||||
"track": "gpu",
|
||||
"model_index": 1,
|
||||
"current_model": "qwen2.5-coder:32b",
|
||||
"best_configs": {
|
||||
"gpu": {
|
||||
"devstral-small-2:24b": {"num_ctx": 98304, "num_gpu": 99, "tokens_per_sec": 11.2}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
283
assets/ai-optimizer/CRON_JOB_DRAFT.md
Normal file
283
assets/ai-optimizer/CRON_JOB_DRAFT.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# AI Model Optimization Cron Job
|
||||
|
||||
**Goal:** Find optimal configurations for maximum context size with full hardware utilization.
|
||||
|
||||
**Hardware:**
|
||||
- 2× AMD MI50 GPUs (32GB VRAM each, 64GB total)
|
||||
- 128GB system RAM
|
||||
- ROCm: HSA_OVERRIDE_GFX_VERSION=9.0.6, HIP_VISIBLE_DEVICES=0,1
|
||||
|
||||
---
|
||||
|
||||
## Model Queue
|
||||
|
||||
### GPU-Optimized (Coding - prioritize speed + context on GPU)
|
||||
1. `devstral-small-2:24b` - Best coding model
|
||||
2. `qwen2.5-coder:32b` - Strong coder, fits on GPU+offload
|
||||
3. `codellama:34b-instruct` - Legacy but solid
|
||||
|
||||
### RAM-Optimized (Knowledge - prioritize max context, accept slower)
|
||||
1. `qwen2.5:72b` - Best knowledge, needs heavy offload
|
||||
2. `nemotron-3-nano:30b` - Good general knowledge
|
||||
3. `mixtral:8x7b-instruct` - MoE, efficient for knowledge
|
||||
|
||||
---
|
||||
|
||||
## Optimization Strategy
|
||||
|
||||
**Two separate tracks:**
|
||||
|
||||
### Track A: GPU-Focused (Coding)
|
||||
```
|
||||
Baseline: num_ctx=32768, num_gpu=99, flash_attn=true
|
||||
Steps:
|
||||
1. Increase context: 32k → 65k → 98k → 131k → 163k
|
||||
2. At each step, verify VRAM usage < 60GB (leave headroom)
|
||||
3. If OOM: reduce num_gpu until stable, record best
|
||||
4. Measure tokens/sec - if < 5 tok/s, consider context too high
|
||||
```
|
||||
|
||||
### Track B: RAM-Focused (Knowledge)
|
||||
```
|
||||
Baseline: num_ctx=65536, num_gpu=50, flash_attn=true
|
||||
Steps:
|
||||
1. Increase context: 65k → 131k → 200k → 262k → 327k
|
||||
2. Allow heavy RAM offload (system RAM up to 100GB)
|
||||
3. If OOM: reduce context or num_gpu
|
||||
4. Speed less critical - focus on max stable context
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backend-Specific Configs
|
||||
|
||||
### Ollama (Modelfile parameters)
|
||||
```
|
||||
PARAMETER num_ctx <value>
|
||||
PARAMETER num_gpu <layers>
|
||||
PARAMETER flash_attn true/false
|
||||
PARAMETER num_predict 4096
|
||||
PARAMETER num_keep 1024
|
||||
PARAMETER repeat_penalty 1.1
|
||||
```
|
||||
|
||||
### Llama.cpp (CLI flags)
|
||||
```
|
||||
--ctx-size <value>
|
||||
--n-gpu-layers <layers>
|
||||
--flash-attn on/off
|
||||
--n-predict 4096
|
||||
--batch-size 4096
|
||||
--ubatch-size 512
|
||||
--cache-type-k f16
|
||||
--cache-type-v f16
|
||||
--split-mode layer
|
||||
--no-mmap
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Host Test Instructions
|
||||
|
||||
**The cron runs inside the hermes container. Some tests require host access:**
|
||||
|
||||
### 1. VRAM Monitoring (HOST)
|
||||
```bash
|
||||
# Run on host to check VRAM usage during/after benchmark
|
||||
sudo rocm-smi --showmeminfo vram
|
||||
|
||||
# Or via docker exec if rocm-smi available in container
|
||||
docker exec --privileged ollama rocm-smi --showmeminfo vram
|
||||
```
|
||||
|
||||
### 2. Running Ollama Benchmarks (CONTAINER)
|
||||
```bash
|
||||
# Pull model
|
||||
docker exec ollama ollama pull <model>
|
||||
|
||||
# Create custom modelfile
|
||||
docker exec ollama bash -c 'cat <<EOF > /root/.ollama/test.modelfile
|
||||
FROM <model>
|
||||
PARAMETER num_ctx 65536
|
||||
PARAMETER num_gpu 99
|
||||
PARAMETER flash_attn true
|
||||
EOF'
|
||||
|
||||
# Create model from modelfile
|
||||
docker exec ollama ollama create test-model -f /root/.ollama/test.modelfile
|
||||
|
||||
# Run benchmark (warm model first)
|
||||
docker exec ollama ollama run test-model "Write a Python async context manager with exponential backoff"
|
||||
|
||||
# Cleanup
|
||||
docker exec ollama ollama rm test-model
|
||||
```
|
||||
|
||||
### 3. Running Llama.cpp Benchmarks (CONTAINER - needs llama.cpp container)
|
||||
```bash
|
||||
# Uncomment llama_cpp_devstral in compose.yml first
|
||||
# Then rebuild: sudo nh os switch --flake .#lazyworkhorse
|
||||
|
||||
# Test via HTTP API
|
||||
curl http://localhost:8300/v1/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "devstral-2-small-llama_cpp",
|
||||
"prompt": "Write a Python function",
|
||||
"max_tokens": 100
|
||||
}'
|
||||
```
|
||||
|
||||
### 4. Deploying Changes (HOST via ai-worker)
|
||||
```bash
|
||||
# After optimization, commit results
|
||||
cd /home/ai-worker/infra
|
||||
git add assets/ai-optimizer/
|
||||
git commit -m "ai-optimizer: new best config for <model>"
|
||||
git push
|
||||
|
||||
# If config changes needed in ollama_init_custom_models.nix:
|
||||
# 1. Edit the file
|
||||
# 2. nixpkgs-fmt .
|
||||
# 3. Show diff to user
|
||||
# 4. Wait for confirmation
|
||||
# 5. sudo nh os switch --flake .#lazyworkhorse
|
||||
```
|
||||
|
||||
### 5. Accessing Host from Hermes Container
|
||||
```bash
|
||||
# SSH to host as ai-worker (key should be mounted)
|
||||
ssh -i /path/to/key ai-worker@host.docker.internal
|
||||
|
||||
# Or via docker socket if mounted
|
||||
# (not recommended for security)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Benchmark Prompts
|
||||
|
||||
### Coding (Track A)
|
||||
```
|
||||
"Write a Python async context manager that retries a function with exponential backoff, max 5 retries, and logs each attempt using structlog. Include type hints and error handling."
|
||||
```
|
||||
|
||||
### Knowledge (Track B)
|
||||
```
|
||||
"Explain the complete memory hierarchy in modern GPUs, from registers through L1/L2 caches to VRAM, and how data moves between them during matrix multiplication. Include bandwidth considerations for each level."
|
||||
```
|
||||
|
||||
### Measurement
|
||||
- Tokens per second (generation speed)
|
||||
- Time to first token (latency)
|
||||
- VRAM usage (via rocm-smi)
|
||||
- System RAM usage (via free -h)
|
||||
- Context success (did it complete without OOM?)
|
||||
|
||||
---
|
||||
|
||||
## State File Structure
|
||||
|
||||
`/opt/data/infra/assets/ai-optimizer/state.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"track": "gpu",
|
||||
"current_model": "devstral-small-2:24b",
|
||||
"model_index": 0,
|
||||
"phase": "context_scaling",
|
||||
"backend": "ollama",
|
||||
"current_config": {
|
||||
"num_ctx": 65536,
|
||||
"num_gpu": 99,
|
||||
"flash_attn": true
|
||||
},
|
||||
"best_configs": {
|
||||
"gpu": {
|
||||
"devstral-small-2:24b": {
|
||||
"backend": "ollama",
|
||||
"num_ctx": 131072,
|
||||
"num_gpu": 99,
|
||||
"flash_attn": true,
|
||||
"tokens_per_sec": 12.5,
|
||||
"vram_used_gb": 58.2,
|
||||
"tested_at": "2026-04-28T17:00:00Z"
|
||||
}
|
||||
},
|
||||
"ram": {}
|
||||
},
|
||||
"completed_models": [],
|
||||
"gpu_queue": ["devstral-small-2:24b", "qwen2.5-coder:32b", "codellama:34b-instruct"],
|
||||
"ram_queue": ["qwen2.5:72b", "nemotron-3-nano:30b", "mixtral:8x7b-instruct"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Results CSV
|
||||
|
||||
`/opt/data/infra/assets/ai-optimizer/results.csv`
|
||||
|
||||
```csv
|
||||
timestamp,track,model,backend,phase,num_ctx,num_gpu,flash_attn,tokens_per_sec,vram_gb,ram_gb,status,is_best
|
||||
2026-04-28T17:00:00Z,gpu,devstral-small-2:24b,ollama,context_scaling,65536,99,true,15.2,52.1,18.4,success,false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cron Job Flow
|
||||
|
||||
```
|
||||
1. Read state.json
|
||||
2. If both queues empty → STOP (all models tested)
|
||||
3. Select next model from current track queue
|
||||
4. Pull model if needed (docker exec ollama ollama pull)
|
||||
5. Create Modelfile / llama.cpp config with current test params
|
||||
6. Run benchmark (both prompts)
|
||||
7. Measure: tokens/sec, VRAM (rocm-smi), RAM (free -h)
|
||||
8. If successful:
|
||||
- Increase context (next step)
|
||||
- Update current_config in state
|
||||
9. If OOM/error:
|
||||
- Record last good config as best_configs[track][model]
|
||||
- Move to next model in queue
|
||||
10. Update state.json
|
||||
11. Append to results.csv
|
||||
12. Git commit + push to /opt/data/infra
|
||||
13. Send Matrix notification if available, else silent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Matrix Notification (Optional)
|
||||
|
||||
```python
|
||||
# If matrix credentials available in environment
|
||||
if os.getenv("MATRIX_HOME_SERVER") and os.getenv("MATRIX_ACCESS_TOKEN"):
|
||||
# Send completion notification
|
||||
# Room: !ai-optimizer:lazyworkhorse.net (or similar)
|
||||
pass
|
||||
# Else: silent, just commit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files to Create
|
||||
|
||||
```
|
||||
/opt/data/infra/assets/ai-optimizer/
|
||||
├── state.json # Current progress
|
||||
├── results.csv # All test results
|
||||
├── best_configs.json # Final best configs (human-readable)
|
||||
└── CRON_JOB_DRAFT.md # This file
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- **No num_parallel**: Removed to avoid limiting other settings
|
||||
- **Two tracks**: GPU (coding/speed) vs RAM (knowledge/context)
|
||||
- **Both backends**: Test ollama first, then llama.cpp if available
|
||||
- **Host tests**: rocm-smi must run on host or privileged container
|
||||
- **Deploy**: ai-worker has sudo for nh/nixos-rebuild, must ask user first
|
||||
1
assets/ai-optimizer/results.csv
Normal file
1
assets/ai-optimizer/results.csv
Normal file
@@ -0,0 +1 @@
|
||||
timestamp,track,model,backend,phase,num_ctx,num_gpu,flash_attn,tokens_per_sec,vram_gb,ram_gb,status,is_best
|
||||
|
21
assets/ai-optimizer/state.json
Normal file
21
assets/ai-optimizer/state.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"track": "gpu",
|
||||
"current_model": "devstral-small-2:24b",
|
||||
"model_index": 0,
|
||||
"phase": "context_scaling",
|
||||
"backend": "ollama",
|
||||
"current_config": {
|
||||
"num_ctx": 32768,
|
||||
"num_gpu": 99,
|
||||
"flash_attn": true
|
||||
},
|
||||
"best_configs": {
|
||||
"gpu": {},
|
||||
"ram": {}
|
||||
},
|
||||
"completed_models": [],
|
||||
"gpu_queue": ["devstral-small-2:24b", "qwen2.5-coder:32b", "codellama:34b-instruct"],
|
||||
"ram_queue": ["qwen2.5:72b", "nemotron-3-nano:30b", "mixtral:8x7b-instruct"],
|
||||
"context_steps": [32768, 65536, 98304, 131072, 163840, 200704, 262144, 327680],
|
||||
"last_updated": "2026-04-28T17:00:00Z"
|
||||
}
|
||||
202
flake.lock
generated
202
flake.lock
generated
@@ -23,6 +23,38 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"argononed": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1729566243,
|
||||
"narHash": "sha256-DPNI0Dpk5aym3Baf5UbEe5GENDrSmmXVdriRSWE+rgk=",
|
||||
"owner": "nvmd",
|
||||
"repo": "argononed",
|
||||
"rev": "16dbee54d49b66d5654d228d1061246b440ef7cf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nvmd",
|
||||
"repo": "argononed",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"argononed_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1729566243,
|
||||
"narHash": "sha256-DPNI0Dpk5aym3Baf5UbEe5GENDrSmmXVdriRSWE+rgk=",
|
||||
"owner": "nvmd",
|
||||
"repo": "argononed",
|
||||
"rev": "16dbee54d49b66d5654d228d1061246b440ef7cf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nvmd",
|
||||
"repo": "argononed",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -37,6 +69,36 @@
|
||||
"url": "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_3": {
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -70,11 +132,11 @@
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1774721317,
|
||||
"narHash": "sha256-KS0ElyhZKdUFcfaxfwid3yi2Id3EP9i+dGL16/wx1T8=",
|
||||
"lastModified": 1777373577,
|
||||
"narHash": "sha256-K0sXr8tRA9L1FGE8Khl42NR+DmZOY9gNYCP8ljX7TAo=",
|
||||
"ref": "main",
|
||||
"rev": "d0190cff6f2314cc1c727ff113aea20e086f4bcc",
|
||||
"revCount": 19103,
|
||||
"rev": "faaa14a303dabc6309a52cc8e5eba86f9e29ccaf",
|
||||
"revCount": 19152,
|
||||
"type": "git",
|
||||
"url": "https://git.lix.systems/lix-project/lix"
|
||||
},
|
||||
@@ -144,6 +206,130 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-images": {
|
||||
"inputs": {
|
||||
"nixos-stable": [
|
||||
"nixos-raspberrypi",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixos-unstable": [
|
||||
"nixos-raspberrypi",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1747747741,
|
||||
"narHash": "sha256-LUOH27unNWbGTvZFitHonraNx0JF/55h30r9WxqrznM=",
|
||||
"owner": "nvmd",
|
||||
"repo": "nixos-images",
|
||||
"rev": "cbbd6db325775096680b65e2a32fb6187c09bbb4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nvmd",
|
||||
"ref": "sdimage-installer",
|
||||
"repo": "nixos-images",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-images_2": {
|
||||
"inputs": {
|
||||
"nixos-stable": [
|
||||
"nixos-uconsole",
|
||||
"nixos-raspberrypi",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixos-unstable": [
|
||||
"nixos-uconsole",
|
||||
"nixos-raspberrypi",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1747747741,
|
||||
"narHash": "sha256-LUOH27unNWbGTvZFitHonraNx0JF/55h30r9WxqrznM=",
|
||||
"owner": "nvmd",
|
||||
"repo": "nixos-images",
|
||||
"rev": "cbbd6db325775096680b65e2a32fb6187c09bbb4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nvmd",
|
||||
"ref": "sdimage-installer",
|
||||
"repo": "nixos-images",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-raspberrypi": {
|
||||
"inputs": {
|
||||
"argononed": "argononed",
|
||||
"flake-compat": "flake-compat_2",
|
||||
"nixos-images": "nixos-images",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773704510,
|
||||
"narHash": "sha256-Kq0WPitNekYzouyd8ROlZb63cpSg/+Ep2XxkV0YlABU=",
|
||||
"owner": "nvmd",
|
||||
"repo": "nixos-raspberrypi",
|
||||
"rev": "b5c77d506bed55250a4642ce6c8b395dd29ef06b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nvmd",
|
||||
"ref": "v1.20260317.0",
|
||||
"repo": "nixos-raspberrypi",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-raspberrypi_2": {
|
||||
"inputs": {
|
||||
"argononed": "argononed_2",
|
||||
"flake-compat": "flake-compat_3",
|
||||
"nixos-images": "nixos-images_2",
|
||||
"nixpkgs": [
|
||||
"nixos-uconsole",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773704510,
|
||||
"narHash": "sha256-Kq0WPitNekYzouyd8ROlZb63cpSg/+Ep2XxkV0YlABU=",
|
||||
"owner": "nvmd",
|
||||
"repo": "nixos-raspberrypi",
|
||||
"rev": "b5c77d506bed55250a4642ce6c8b395dd29ef06b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nvmd",
|
||||
"ref": "v1.20260317.0",
|
||||
"repo": "nixos-raspberrypi",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-uconsole": {
|
||||
"inputs": {
|
||||
"nixos-raspberrypi": "nixos-raspberrypi_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775854552,
|
||||
"narHash": "sha256-hBlNh2eFWg0qlxM1gFpjp2JBdB82Zw4Y5otd+hwEvpQ=",
|
||||
"owner": "nixos-uconsole",
|
||||
"repo": "nixos-uconsole",
|
||||
"rev": "cf4cb3b7996bd2cbc88964f90af8929e4d76987b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos-uconsole",
|
||||
"repo": "nixos-uconsole",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1705033721,
|
||||
@@ -178,11 +364,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1774386573,
|
||||
"narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=",
|
||||
"lastModified": 1777268161,
|
||||
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9",
|
||||
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -212,6 +398,8 @@
|
||||
"inputs": {
|
||||
"agenix": "agenix",
|
||||
"lix": "lix",
|
||||
"nixos-raspberrypi": "nixos-raspberrypi",
|
||||
"nixos-uconsole": "nixos-uconsole",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
|
||||
31
flake.nix
31
flake.nix
@@ -12,10 +12,20 @@
|
||||
url = "git+https://git.lix.systems/lix-project/lix?ref=main";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
# uConsole CM5 hardware support
|
||||
nixos-uconsole = {
|
||||
url = "github:nixos-uconsole/nixos-uconsole";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
# nixos-raspberrypi provides hardware.raspberry-pi options required by uconsole-cm5
|
||||
nixos-raspberrypi = {
|
||||
url = "github:nvmd/nixos-raspberrypi/v1.20260317.0";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
self.submodules = true;
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, agenix, lix, ... }@inputs:
|
||||
outputs = { self, nixpkgs, agenix, lix, nixos-uconsole, nixos-raspberrypi, ... }@inputs:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
keys = import ./lib/keys.nix;
|
||||
@@ -79,6 +89,25 @@
|
||||
./hosts/cyt-pi/hardware-configuration.nix
|
||||
];
|
||||
};
|
||||
|
||||
uConsole = nixpkgs.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
specialArgs = { inherit self keys paths inputs nixos-raspberrypi; };
|
||||
modules = [
|
||||
{
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
nixpkgs.hostPlatform = "aarch64-linux";
|
||||
nixpkgs.overlays = [ nixos-raspberrypi.overlays.vendor-pkgs ];
|
||||
nix.package = lix.packages."aarch64-linux".default;
|
||||
}
|
||||
# Raspberry Pi 5 base (provides hardware.raspberry-pi options)
|
||||
nixos-raspberrypi.nixosModules.raspberry-pi-5.base
|
||||
# uConsole CM5 hardware support (display, kernel, config)
|
||||
nixos-uconsole.nixosModules.uconsole-cm5
|
||||
./hosts/uconsole/configuration.nix
|
||||
./hosts/uconsole/hardware-configuration.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
devShells.${system}.default = devShell;
|
||||
};
|
||||
|
||||
153
hosts/uconsole/configuration.nix
Normal file
153
hosts/uconsole/configuration.nix
Normal file
@@ -0,0 +1,153 @@
|
||||
{ config, lib, pkgs, paths, self, keys, inputs, nixos-raspberrypi, ... }:
|
||||
|
||||
let
|
||||
# Reticulum Network Stack - build from PyPI
|
||||
reticulum = pkgs.python3Packages.buildPythonPackage {
|
||||
pname = "reticulum";
|
||||
version = "0.7.0";
|
||||
format = "pyproject";
|
||||
src = pkgs.python3Packages.fetchPypi {
|
||||
pname = "reticulum";
|
||||
version = "0.7.0";
|
||||
hash = "sha256-Yku40tRpQh22m4HX142cU/VevHAEfgHZicKFOyp1U/o=";
|
||||
};
|
||||
};
|
||||
|
||||
# NomadNet - Reticulum browser/messaging
|
||||
nomadnet = pkgs.python3Packages.buildPythonPackage {
|
||||
pname = "nomadnet";
|
||||
version = "0.5.2";
|
||||
format = "pyproject";
|
||||
src = pkgs.python3Packages.fetchPypi {
|
||||
pname = "nomadnet";
|
||||
version = "0.5.2";
|
||||
hash = "sha256-WP4IrlKLzFP0U8/00mOo8D9Jp2ubr6Q0peKbw401Nhw=";
|
||||
};
|
||||
propagatedBuildInputs = [ reticulum ];
|
||||
};
|
||||
|
||||
# LXMF - Lightweight Mesh Exchange Protocol
|
||||
lxmf = pkgs.python3Packages.buildPythonPackage {
|
||||
pname = "lxmf";
|
||||
version = "0.5.1";
|
||||
format = "pyproject";
|
||||
src = pkgs.python3Packages.fetchPypi {
|
||||
pname = "lxmf";
|
||||
version = "0.5.1";
|
||||
hash = "sha256-2zwTgG283qx1Bt6TKaGJtcwPr2tCNOOIASu8RXC/QLE=";
|
||||
};
|
||||
propagatedBuildInputs = [ reticulum ];
|
||||
};
|
||||
in
|
||||
{
|
||||
# --- CORE HARDWARE (CM5 / RPi5) ---
|
||||
# nixos-raspberrypi.nixosModules.raspberry-pi-5.base + nixos-uconsole.nixosModules.uconsole-cm5 imported in flake.nix
|
||||
|
||||
# --- BASIC HOST INFO ---
|
||||
networking.hostName = "uConsole";
|
||||
networking.networkmanager.enable = true;
|
||||
time.timeZone = "America/Montreal";
|
||||
i18n.defaultLocale = "en_CA.UTF-8";
|
||||
|
||||
# --- GPS DAEMON ---
|
||||
services.gpsd = {
|
||||
enable = true;
|
||||
devices = [ "/dev/ttyAMA0" ]; # Default port for RPi5/CM5 GPS
|
||||
nowait = true;
|
||||
};
|
||||
|
||||
# --- USER CONFIGURATION ---
|
||||
users.users.thierry = {
|
||||
isNormalUser = true;
|
||||
description = "Thierry";
|
||||
extraGroups = [
|
||||
"wheel" # Sudo
|
||||
"dialout" # Access to serial/HAM rigs
|
||||
"plugdev" # Access to USB SDRs
|
||||
"wireshark" # Packet capture without root
|
||||
"video" # Hardware acceleration access
|
||||
"networkmanager"
|
||||
];
|
||||
openssh.authorizedKeys.keys = [
|
||||
keys.users.gortium.main
|
||||
keys.users.gortium.gitea
|
||||
];
|
||||
};
|
||||
|
||||
# --- INTERFACE (WAYLAND/SWAY) ---
|
||||
# Sway is recommended for the uConsole's low resources
|
||||
programs.sway = {
|
||||
enable = true;
|
||||
extraOptions = [ "--unsupported-gpu" ]; # Often needed for RPi
|
||||
};
|
||||
|
||||
# --- SOFTWARE TOOLKITS ---
|
||||
environment.systemPackages = with pkgs; [
|
||||
# Base Tools (for your Doom Emacs environment)
|
||||
emacs-pgtk # Emacs with Wayland support
|
||||
git # Required for Doom Emacs / Flakes
|
||||
ripgrep # Fast searching for Emacs/CLI
|
||||
fd # Better find for Emacs
|
||||
htop # Resource monitor
|
||||
tmux # Terminal multiplexer
|
||||
neovim # Alternative editor
|
||||
|
||||
# HAM RADIO (Digital Modes)
|
||||
js8call # Weak-signal keyboard messaging
|
||||
wsjtx # FT8, JT65, etc.
|
||||
fldigi # Digital modem (PSK, RTTY)
|
||||
pat # Winlink client (Use 'pat configure' after install)
|
||||
direwolf # Software TNC for APRS
|
||||
chirp # Radio programming
|
||||
hamlib # Rig control (rigctl)
|
||||
trustedqsl # LotW log signing
|
||||
|
||||
# SDR + RF ANALYSIS
|
||||
sdrpp # Modern SDR GUI (Best for uConsole)
|
||||
gqrx # Classic SDR receiver
|
||||
rtl-sdr # Drivers for RTL2832U
|
||||
inspectrum # Offline signal analysis
|
||||
soapysdr-with-plugins # Hardware abstraction layer
|
||||
|
||||
# LORA, MESH & RETICULUM
|
||||
reticulum # The RNS stack (rnsd, rnsh)
|
||||
nomadnet # Reticulum browser/messaging
|
||||
lxmf # Lightweight Mesh Exchange Protocol
|
||||
# sidechannel - not available on PyPI, would need to build from source
|
||||
|
||||
# HACKING & SECURITY (Kali-like suite)
|
||||
nmap # Port scanning
|
||||
metasploit # Exploitation framework
|
||||
aircrack-ng # Wi-Fi auditing
|
||||
kismet # Wireless sniffer (Essential for your Pi Zero project)
|
||||
bettercap # MITM and network attack tool
|
||||
wireshark # Protocol analyzer
|
||||
burpsuite # Web vulnerability scanner
|
||||
hashcat # Password recovery
|
||||
john # John the Ripper (password cracking)
|
||||
sqlmap # Automated SQL injection
|
||||
|
||||
# GPS & OFFLINE MAPPING
|
||||
foxtrotgps # Lightweight map viewer (Perfect for small screens)
|
||||
viking # GPS data editor and map viewer
|
||||
gpsbabel # GPS data conversion
|
||||
# marble - not available in this nixpkgs version
|
||||
];
|
||||
|
||||
# Udev rules for SDR and Radio hardware access
|
||||
services.udev.packages = [
|
||||
pkgs.rtl-sdr
|
||||
];
|
||||
|
||||
# Enable Wireshark privilege separation
|
||||
programs.wireshark.enable = true;
|
||||
|
||||
# Enable OpenSSH
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PermitRootLogin = lib.mkForce "prohibit-password";
|
||||
};
|
||||
|
||||
# System state version
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
30
hosts/uconsole/hardware-configuration.nix
Normal file
30
hosts/uconsole/hardware-configuration.nix
Normal file
@@ -0,0 +1,30 @@
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[ (modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [ "xhci_pci" "usbhid" "sdhci_pci" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
# uConsole CM5 specific filesystem (eMMC boot)
|
||||
fileSystems."/" =
|
||||
{ device = "/dev/disk/by-label/NIXOS_SD";
|
||||
fsType = "ext4";
|
||||
options = [ "noatime" ];
|
||||
};
|
||||
|
||||
fileSystems."/boot" =
|
||||
{ device = "/dev/disk/by-label/FIRMWARE";
|
||||
fsType = "vfat";
|
||||
};
|
||||
|
||||
swapDevices = [ ];
|
||||
|
||||
# uConsole CM5 is ARM64
|
||||
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
}
|
||||
Reference in New Issue
Block a user