Debug infrastructure. Not dashboards. Not tickets.
Friday is a production-grade, single-executable CLI tool for network engineers. It combines a fine-tuned large language model (DocLM), a fully local RAG pipeline, and an atomic transaction engine to diagnose complex network failures and apply fixes — with guaranteed rollback on failure. All inference runs locally. Not one byte of diagnostic data ever leaves your infrastructure.
- Overview
- Key Features
- Architecture
- How It Works
- The Four Validation Gates
- DocLM — The Fine-Tuned Model
- RAG Pipeline
- Variable Resolution and Output Chaining
- Execution Strategies
- Function Registry
- Technology Stack
- Prerequisites
- Installation
- First-Run Sequence
- Usage
- Configuration
- Project Structure
- Performance Targets
- Error Handling and Recovery
- Security Model
- Contributing
- License
Friday accepts natural language queries from network engineers and orchestrates a sequence of pre-defined Go functions to diagnose and remediate issues. DocLM does not generate shell commands. It does not execute arbitrary code. It selects from a fixed, whitelisted registry of 15 functions, specifies their parameters, and chains their outputs together using a typed variable resolution system.
Every destructive operation passes through four validation gates, a dry-run check, an explicit user confirmation prompt showing before/after values, and an automatic LIFO rollback mechanism if anything fails mid-transaction.
Example session:
> My gRPC stream is dropping packets, diagnose and fix
[1/4] check_tcp_health (interface=eth0, port=50051)
✓ ESTABLISHED — retransmits:47 — rec_buffer:6291456
[2/4] analyze_grpc_stream (port=${previous.port}, duration=10)
✓ drop_rate:4.6% — flow_control_events:23
[3/4] inspect_network_buffers
✓ rmem_max:212992 — recommended:6291456 — ⚠ too small
⚠ DESTRUCTIVE OPERATION — user confirmation required
execute_sysctl_command
Parameter : net.core.rmem_max
Current : 212992
New : 6291456
Reversible: Yes (automatic rollback if failure)
Proceed? [y/N]: y
[4/4] execute_sysctl_command (snapshot saved → rollback ready)
✓ committed — 11.5s total
Root Cause : TCP receive buffer too small (212 KB vs recommended 6 MB)
Action Taken: Increased net.core.rmem_max to 6 MB
Monitor with: ss -ti | grep 50051
>
- DocLM — a fine-tuned Qwen2.5-Coder-3B model with a LoRA adapter, served locally via vLLM, trained specifically on network telemetry debugging scenarios, gRPC failure patterns, TCP tuning runbooks, YANG model structures, and kernel parameter documentation
- Local RAG pipeline using a MiniLM-L6-v2 ONNX model compiled into the binary and Qdrant for vector search — no HTTP service, no startup latency
- Conversation history retained across queries for multi-turn debugging sessions (last 10 messages, 4,000-token window)
- Three-phase execution engine: Read → Analyze → Modify
- State snapshots captured before every destructive operation
- LIFO rollback stack that restores the system to its exact prior state on any failure
- Dry-run validation against the live system before any change is committed
- DocLM-generated function calls reference outputs from prior functions using a typed variable syntax —
${previous.port},${func[2].recommended_rmem_max},${func.check_tcp_health.interface} - Nested field access and array indexing supported
- Smart fallback auto-injects missing parameters when a unique type-matching value exists in the execution context
- On invalid variable references, Friday sends the error and the complete list of available fields back to DocLM for one corrective retry
- Typo in a field name (
${previous.reccomendation}) gets auto-corrected if a unique match exists - If the retry also fails, the transaction is aborted with a precise, actionable error message
- Gate 1: Input sanitization — length bounds, UTF-8 validation, injection pattern detection
- Gate 2: RAG retrieval quality — score threshold ≥0.7, max 5 chunks, diversity enforced
- Gate 3: Response validation — JSON schema, function whitelist check, parameter type validation, dependency graph analysis, anti-hallucination grounding score ≥0.6
- Gate 4: Pre-modify dry-run — all variables resolved, dry-run executed against the live system, full before/after preview, explicit user confirmation
- No external API calls at any point in the pipeline
- MiniLM-L6-v2 ONNX embedding model embedded directly in the binary via
go:embed - Qdrant and vLLM run as local Docker containers
- Your network telemetry, routing topology, and internal addressing never leave your perimeter
User Query
│
▼
Input Validation (Gate 1)
Length bounds, UTF-8, injection pattern detection
│
▼
RAG Pipeline
Query Embedding → MiniLM-L6-v2 ONNX (embedded in binary)
Vector Search → Qdrant (local Docker container)
Quality Filter → Score ≥0.7, max 5 chunks (Gate 2)
│
▼
Prompt Construction
System prompt + Function registry (with output schemas)
+ Variable resolution rules + RAG context
+ Conversation history + Current query
│
▼
DocLM Inference (vLLM + Qwen2.5-Coder-3B + LoRA Adapter, T=0.1)
Output: JSON with reasoning, execution_strategy, function list
│
▼
Response Validation (Gate 3)
JSON schema · Function whitelist · Parameter types
Dependency graph · Grounding score ≥0.6
│ │
│ [fixable error] → DocLM retry (1x) → re-validate
│
▼
Transaction Executor
│
├─ PHASE 1 — READ : Non-destructive queries
│ check_tcp_health, analyze_grpc_stream, capture_packets …
│ Failure here → stop cleanly, no state altered
│
├─ PHASE 2 — ANALYZE : Safe CPU-bound analysis
│ analyze_memory_leak, parse_yang_model, analyze_core_dump …
│ Failure here → stop cleanly, no state altered
│
└─ PHASE 3 — MODIFY : Destructive operations
For each function:
1. Dry-run validation (Gate 4)
2. User confirmation (before/after preview)
3. State snapshot → rollback stack push
4. Execute with timeout + retry (max 2)
5. SUCCESS → continue │ FAILURE → LIFO rollback
│
▼
Result Aggregation + Conversation Context Update
│
▼
Response Formatter
Execution timeline · Success/warning/error indicators
Rollback notifications · Root cause summary
All non-destructive operations execute first: TCP health checks, gRPC stream analysis, packet captures, network buffer reads. Zero mutations occur in this phase. If any read-phase function fails, the session stops cleanly with no system state altered and no cleanup required.
Analysis functions run on the data collected in Phase 1: memory leak detection, core dump analysis, YANG model parsing, telemetry correlation. These are safe operations that only consume CPU. Failure stops the session cleanly.
Before execution, every modify-phase function goes through:
- Variable resolution — all
${...}references resolved to actual values from the execution context - Dry-run validation — executed against the live system to check permissions, parameter validity, and resource availability without making changes
- User confirmation — full before/after preview shown, explicit
[y/N]prompt required - State snapshot — current system state captured and pushed to the rollback stack
- Execution — function runs with timeout enforcement and up to 2 retries on transient errors
On any failure in Phase 3, the rollback stack unwinds in LIFO order:
Execution order: Rollback order:
1. Function A → 4. Undo Function C
2. Function B → 3. Undo Function B
3. Function C → 2. Undo Function A
4. Function D → (failed — triggers rollback)
No LLM output reaches the network without passing every gate. Hallucinations are caught. Type mismatches are caught. Circular dependencies are caught.
| Gate | Name | What It Checks |
|---|---|---|
| Gate 1 | Input Validation | Length 5–2,000 chars · UTF-8 validity · Injection pattern detection · Sanitization |
| Gate 2 | Retrieval Quality | Similarity score ≥0.7 · Max 5 chunks · Diversity filter |
| Gate 3 | Response Validation | JSON schema · Function existence (whitelist) · Parameter types · Variable reference pre-check · Dependency graph · Circular dependency detection · Grounding score ≥0.6 · Safety blacklist |
| Gate 4 | Pre-Modify | Variable resolution · Dry-run against live system · Permission check · Resource availability · User confirmation with before/after preview |
If Gate 3 detects a fixable error (e.g. a typo in a variable reference), the error and the list of available fields are sent back to DocLM for one retry. If the retry also fails, the transaction is aborted and a precise error is returned to the user.
DocLM is a LoRA adapter trained on top of Qwen2.5-Coder-3B, specifically fine-tuned for network telemetry debugging. It was trained on:
- Network troubleshooting runbooks (TCP, gRPC, gNMI, YANG)
- Kernel parameter documentation and tuning guides
- gRPC and OpenConfig protocol specifications
- Packet drop and flow control failure scenarios
- Multi-step diagnostic reasoning chains
DocLM outputs strict JSON with a reasoning block, an execution_strategy field, and a functions array where each entry references the whitelisted function registry. It does not generate shell commands. It does not reference functions outside the registry. Anti-hallucination grounding checks at Gate 3 enforce this at runtime.
Inference configuration:
| Setting | Value |
|---|---|
| Base model | Qwen2.5-Coder-3B |
| Adapter | LoRA (fine-tuned) |
| Inference server | vLLM |
| Temperature | 0.1 (deterministic) |
| Max output tokens | 2,048 |
The RAG pipeline operates entirely offline. No component makes an external network call.
Embedding: MiniLM-L6-v2 compiled into the binary as an ONNX model via go:embed. Embeddings are generated in-process with zero startup latency and no HTTP service dependency.
Vector search: Qdrant running as a local Docker container with a pre-indexed collection covering gRPC, TCP, gNMI, YANG, and kernel tuning documentation. Top-5 retrieval with a similarity score threshold of 0.7 and diversity filtering to prevent redundant chunks.
Prompt assembly order:
- Master system prompt
- Full function registry with parameter schemas and output schemas
- Variable resolution rules
- Retrieved RAG context (up to 5 chunks)
- Conversation history (last 10 messages with execution results)
- Current user query
Total prompt budget: ~4,000 tokens
Fallback chain:
- Qdrant unavailable → cached recent chunks used for retrieval
- ONNX model error → keyword search with degraded (dummy) embeddings
- Neither failure terminates the session
DocLM can reference the output of earlier functions when constructing parameters for later ones. The resolver supports three syntax forms:
| Syntax | Resolves to |
|---|---|
${previous.field} |
Specified field from the last function's output |
${func[N].field} |
Specified field from the function at index N |
${func.function_name.field} |
Specified field from the named function |
${previous.nested.deep.field} |
Nested object field access |
${previous.array[0]} |
Array element access |
Restrictions: Variable references are simple field access paths only. Arithmetic, conditionals, and method calls are explicitly not permitted. If computation is needed, DocLM performs it in its reasoning block and passes the resolved constant value directly in the function parameters.
Smart fallback: If DocLM omits a required parameter but exactly one prior function output contains a field of the matching name and type, Friday auto-injects the value and logs a warning. Ambiguous matches (multiple candidates) are never auto-injected.
Self-healing example:
DocLM output:
"value": "${previous.reccomendation}" ← typo
Gate 3 / Dry-run detects:
Field 'reccomendation' not found in previous function output.
Available fields: port (integer), recommendation (string), drop_rate (float)
Sent back to DocLM for retry:
DocLM corrects to: "${previous.recommendation}"
Retry validates → execution continues.
DocLM selects one strategy for each function sequence. The strategy governs how failures in the sequence are handled.
| Strategy | Behaviour |
|---|---|
stop_on_error |
Abort immediately on any failure. Default for critical diagnostic chains. |
skip_on_error |
Skip the failed function and all functions that declare depends_on it. Continue independent branches. |
retry_with_llm |
On failure, send the error to DocLM and request an alternative approach. |
ask_user |
On each failure, prompt the user: stop, skip, or retry. |
Functions marked "critical": true always trigger immediate rollback on failure, regardless of the selected strategy.
Dependency declaration:
{
"functions": [
{
"name": "check_tcp_health",
"params": {"interface": "eth0", "port": 50051},
"critical": false,
"depends_on": []
},
{
"name": "execute_sysctl_command",
"params": {"parameter": "net.core.rmem_max", "value": "${func[2].recommended_rmem_max}"},
"critical": true,
"depends_on": [2]
}
]
}All 15 functions are declared in functions.yaml. Each entry specifies: phase (read / analyze / modify), parameter schema with types, output schema (used by the variable resolver), timeout, reversibility, and the rollback function to call on failure.
| Function | Phase | Destructive | Description |
|---|---|---|---|
check_tcp_health |
read | No | TCP connection state, retransmit count, queue sizes, recommended buffer sizes |
analyze_grpc_stream |
read | No | gRPC stream monitoring — drop rate via sequence gaps, flow control event count |
check_grpc_health |
read | No | gRPC health check RPC with round-trip latency measurement |
capture_packets |
read | No | Read-only packet capture on a specified interface |
inspect_network_buffers |
read | No | Kernel network buffer settings from /proc/sys/, compared against recommended values |
trace_gnmi_subscription |
read | No | gNMI path subscription — streams structured telemetry updates |
check_interface_stats |
read | No | Interface error counters, drop counts, utilisation from /proc/net/dev |
analyze_memory_leak |
analyze | No | RSS growth tracking over a sampling window — identifies leak locations |
parse_yang_model |
analyze | No | Parses and validates a YANG data model against OpenConfig standards |
validate_yang_data |
analyze | No | Validates a gNMI update payload against a parsed YANG schema |
analyze_core_dump |
analyze | No | GDB batch-mode analysis — crash signal, backtrace, thread state |
correlate_telemetry |
analyze | No | Cross-references multiple execution context outputs to identify causal chains |
execute_sysctl_command |
modify | Yes | Kernel parameter modification via sysctl -w — snapshot captured, user confirmation required, auto-reversed on failure |
restart_service |
modify | Yes | System service restart — prior state captured, reversible via rollback stack |
restore_sysctl_value |
modify | Yes | Restores a kernel parameter to its snapshot-captured prior value — called exclusively by the rollback engine |
To add a new function: declare it in functions.yaml, implement its handler in internal/functions/, and register it in the dispatcher switch in internal/executor/executor.go. If it is a modify-phase reversible function, implement its rollback function and reference it in the atomicity.rollback_function field.
| Component | Technology | Purpose |
|---|---|---|
| CLI | Go 1.24.2 + Cobra | User interface and interactive loop |
| Fine-tuned LLM | DocLM (Qwen2.5-Coder-3B + LoRA) | Function call generation |
| Inference server | vLLM | Local LLM serving |
| Embedding model | ONNX Runtime + MiniLM-L6-v2 | Local embeddings, compiled into binary |
| Vector database | Qdrant (local Docker) | Semantic search over documentation |
| Transaction engine | Custom Go | Three-phase atomicity and rollback |
| Variable resolver | Custom Go | Typed output chaining between functions |
| Snapshot manager | Custom Go | State capture and restoration |
| Configuration | Viper | User-editable config |
| Logging | Zap | Structured, leveled audit logs |
| Function registry | YAML | Declarative function definitions |
- Docker Desktop (or Docker Engine + Docker Compose) installed and running
- Go 1.24.2 or later (only needed when building from source)
- Linux host required for full function support (
ss,sysctl,/procfilesystem) - Root or
sudoaccess required for destructive system functions (sysctl modification, service restarts) - A trained LoRA adapter placed in
models/lora_adapter/before first run
Option 1 — Pre-built binary (recommended)
tar -xzf friday-<version>-linux-amd64.tar.gz
cd friday-<version>
./fridayOption 2 — Build from source
git clone https://github.com/<your-org>/friday.git
cd friday
go build -o friday ./cmd/fridayOn first launch, the binary performs the following automatically:
- Verifies Docker is available and running
- Pulls and starts the vLLM container with the Qwen2.5-Coder-3B base model and LoRA adapter
- Pulls and starts the Qdrant container with the pre-indexed vector database
- Loads the embedded ONNX embedding model from the binary
- Validates all 15 entries in
functions.yamlagainst their Go implementations - Opens the interactive prompt
The system is ready when the > prompt appears. First-run startup typically takes 60–120 seconds depending on hardware while the vLLM container loads the model weights.
Start an interactive session:
./fridayBuilt-in commands:
| Command | Description |
|---|---|
help |
Show available built-in commands and example queries |
clear |
Clear conversation history |
dry-run |
Validate the next query plan without executing it |
exit |
Quit the session |
Example queries:
> Check TCP health on eth0 port 50051
> Analyze gRPC stream on port 50052 for 30 seconds
> My gRPC stream is dropping packets, diagnose and fix
> Inspect current kernel network buffer settings
> What was the recommended buffer size from the last check?
> Trace gNMI subscription on path /interfaces/interface/state/counters
Conversation history is preserved within a session. You can reference results from prior queries in follow-up questions and DocLM will chain outputs accordingly using the variable resolution system.
config.yaml is the primary user-editable configuration file:
llm:
endpoint: "http://localhost:8000"
model: "qwen2.5-coder-3b"
lora_adapter: "models/lora_adapter"
temperature: 0.1
max_tokens: 2048
rag:
qdrant_endpoint: "http://localhost:6333"
collection: "telemetry-docs"
top_k: 5
score_threshold: 0.7
conversation:
max_messages: 10
max_tokens: 4000
execution:
default_timeout_seconds: 30
max_retries: 2The LoRA adapter weights must be placed in models/lora_adapter/ before first run. The pre-indexed Qdrant vector database is included in data/vector_db/ in the distribution package.
friday/
├── friday # Single Go binary
├── docker-compose.yml # vLLM and Qdrant service definitions
├── config.yaml # User-editable configuration
├── functions.yaml # Function registry (15 functions)
├── internal/
│ ├── executor/
│ │ ├── executor.go # Function dispatcher
│ │ ├── transaction.go # Three-phase transaction engine
│ │ ├── variables.go # Variable resolution and output chaining
│ │ └── snapshot.go # State snapshot and rollback manager
│ ├── functions/
│ │ ├── network/
│ │ │ ├── tcp.go # TCP health check
│ │ │ └── grpc.go # gRPC health check and stream analysis
│ │ ├── system/
│ │ │ ├── buffers.go # Network buffer inspection
│ │ │ └── sysctl.go # Sysctl modification and restoration
│ │ └── debugging/
│ │ └── core.go # Core dump analysis
│ ├── rag/
│ │ ├── embedder.go # ONNX-based embedding via MiniLM-L6-v2
│ │ ├── retriever.go # Qdrant vector search
│ │ └── models/
│ │ ├── minilm-l6-v2.onnx # Embedded model (go:embed)
│ │ └── vocab.json
│ ├── llm/
│ │ ├── client.go # vLLM HTTP client
│ │ └── validator.go # Response validation and grounding check
│ └── cli/
│ └── session.go # Interactive loop and conversation manager
├── data/
│ └── vector_db/ # Pre-indexed Qdrant storage
└── models/
└── lora_adapter/ # DocLM LoRA weights (not included in repo)
| Metric | Target |
|---|---|
| End-to-end latency | < 15 seconds (simple 3-function query) |
| DocLM inference | < 3 seconds (2K output tokens) |
| Vector search | < 200 ms (top-5 retrieval) |
| Variable resolution | < 50 ms per function |
| Dry-run validation | < 2 seconds (entire modify phase) |
| State snapshot | < 500 ms per function |
| Full rollback | < 5 seconds |
| Scenario | Detection | Recovery |
|---|---|---|
| Invalid variable reference | Dry-run validation | DocLM retry once → fail with clear error to user |
| Function execution timeout | Timeout enforcement | Retry up to 2 times → rollback if in modify phase |
| Transient network error | Error pattern matching | Auto-retry with exponential backoff |
| Permission denied | Execution error | Fail immediately, no retry |
| Modify phase failure | Any error in modify phase | Immediate LIFO rollback, all snapshots restored |
| Rollback failure | Rollback execution error | Log all errors, warn user, request manual intervention |
| Circular dependency | Dependency graph analysis | Reject at Gate 3, send back to DocLM to fix |
| LLM hallucination | Grounding check ≥0.6 | Reject response, request regeneration |
| Qdrant unavailable | Connection error | Fall back to cached recent chunks |
| ONNX model error | Runtime error | Fall back to keyword search with degraded embeddings |
- No arbitrary code execution. DocLM selects only from the whitelisted function registry. The runtime cannot invoke anything outside it.
- Whitelist enforcement at Gate 3. Unknown function names in DocLM output are rejected before any execution attempt.
- Input sanitization at Gate 1. Injection patterns are detected and rejected before the query reaches DocLM.
- User confirmation for every destructive operation. No sysctl value is written, no service is restarted, without an explicit
yfrom the user after reviewing the before/after preview. - Structured audit logging. Every function execution — successful or failed — is written to a structured Zap log.
- State snapshots as forensic artifacts. Snapshots are retained for the duration of the session, enabling post-incident review of exactly what was changed and when.
- Complete local operation. No data leaves the host. All inference, retrieval, and execution is on-prem.
Contributions are welcome. Please read this section before opening a pull request.
Getting started:
- Fork the repository and clone your fork
- Create a feature branch from
main:git checkout -b feature/your-feature-name - Make your changes
- Run all existing tests:
go test ./... - Add tests for any new behaviour
- Open a pull request against
mainwith a clear description of what was changed and why
Code standards:
- All code must be written in Go 1.24.2 or later
- Follow standard Go formatting (
gofmt) — CI enforces this - All exported functions and types must have godoc comments
- Error messages must be descriptive and actionable
- New functions added to
functions.yamlmust have a corresponding implementation ininternal/functions/and a case in the dispatcher ininternal/executor/executor.go
Adding a new network function:
- Declare the function in
functions.yamlwith its phase, parameter schema, output schema, timeout, and atomicity metadata - Implement the handler in the appropriate package under
internal/functions/ - Register it in the dispatcher switch in
internal/executor/executor.go - If the function is
modify-phase and reversible, implement its rollback function and set theatomicity.rollback_functionfield accordingly - Add integration tests under
internal/functions/<package>/<function>_test.go
Please open a GitHub Issue before starting work on a significant change to avoid duplicate effort and get early feedback on direction.
This project is licensed under the MIT License.
