hormigasais-edge-starter 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +4 -0
- package/.hormigasais/node_config.json +27 -0
- package/README.md +66 -0
- package/agents/hormiga_centinela.py +41 -0
- package/generator.js +42 -0
- package/generator.py +84 -0
- package/generators/__init__.py +0 -0
- package/generators/agents_generator.py +68 -0
- package/generators/config_generator.py +65 -0
- package/generators/contracts_generator.py +38 -0
- package/generators/protocol_generator.py +167 -0
- package/generators/scripts_generator.py +283 -0
- package/package.json +41 -0
- package/protocols/__init__.py +9 -0
- package/protocols/constants.py +13 -0
- package/protocols/decoder.py +7 -0
- package/protocols/encoder.py +12 -0
- package/protocols/human_contract.lbh +10 -0
- package/protocols/lbh_core.py +11 -0
- package/protocols/signer.py +32 -0
- package/protocols/verifier.py +25 -0
- package/scripts/verify_sdk.py +84 -0
- package/update_agents_and_contracts.sh +123 -0
- package/utils/env_loader.py +42 -0
package/.env.example
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "LBH_NODE_CONFIG_V1",
|
|
3
|
+
"node_id": "A16-u0_a350",
|
|
4
|
+
"node_name": "mi-colonia",
|
|
5
|
+
"protocol": "LBH_BINARY_V2",
|
|
6
|
+
"version": "2.0.0",
|
|
7
|
+
"owner": {
|
|
8
|
+
"name": "YOUR-NAME",
|
|
9
|
+
"email": "YOUR-EMAIL"
|
|
10
|
+
},
|
|
11
|
+
"location": {
|
|
12
|
+
"country": "SV",
|
|
13
|
+
"city": "San Miguel"
|
|
14
|
+
},
|
|
15
|
+
"master_node": "A16-SanMiguel-SV",
|
|
16
|
+
"created_at": "2026-07-24T02:27:25Z",
|
|
17
|
+
"status": "INITIALIZED",
|
|
18
|
+
"agents": [
|
|
19
|
+
"hormiga_centinela",
|
|
20
|
+
"hormiga_relevo"
|
|
21
|
+
],
|
|
22
|
+
"security": {
|
|
23
|
+
"hmac_required": true,
|
|
24
|
+
"algorithm": "HMAC-SHA256"
|
|
25
|
+
},
|
|
26
|
+
"certificate": "PENDING"
|
|
27
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 🐜 HormigasAIS Edge Starter
|
|
2
|
+
|
|
3
|
+
Starter oficial para desplegar un **nodo soberano** de HormigasAIS con agentes LBH.
|
|
4
|
+
|
|
5
|
+
> "Soberanía digital en el edge"
|
|
6
|
+
|
|
7
|
+
## 🚀 Instalación Rápida
|
|
8
|
+
|
|
9
|
+
### Opción 1: Desde GitHub (Recomendada actualmente)
|
|
10
|
+
|
|
11
|
+
\`\`\`bash
|
|
12
|
+
npx create-hormigas-node@latest
|
|
13
|
+
\`\`\`
|
|
14
|
+
|
|
15
|
+
O manualmente:
|
|
16
|
+
|
|
17
|
+
\`\`\`bash
|
|
18
|
+
git clone https://github.com/Thrumanshow/hormigasais-edge-starter.git
|
|
19
|
+
cd hormigasais-edge-starter
|
|
20
|
+
bash bootstrap.sh
|
|
21
|
+
python3 generator.py
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
### Opción 2: Cuando esté publicado en npm (próximamente)
|
|
25
|
+
|
|
26
|
+
\`\`\`bash
|
|
27
|
+
npx create-hormigas-node
|
|
28
|
+
\`\`\`
|
|
29
|
+
|
|
30
|
+
## ¿Qué incluye?
|
|
31
|
+
|
|
32
|
+
- **Bootstrap automático** del entorno en Termux
|
|
33
|
+
- **SDK LBH completo** (encoder, decoder, signer, verifier)
|
|
34
|
+
- **Agentes autónomos**: \`hormiga_centinela\` y \`hormiga_relevo\`
|
|
35
|
+
- **Generadores modulares** (config, protocols, scripts, agents, contracts)
|
|
36
|
+
- **Verificación POST** del SDK (\`verify_sdk.py\`)
|
|
37
|
+
- Soporte para secretos en \`~/.hormigas_secrets\`
|
|
38
|
+
|
|
39
|
+
## Comandos útiles
|
|
40
|
+
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
./bootstrap.sh # Preparar entorno
|
|
43
|
+
python3 generator.py # Ejecutar generadores
|
|
44
|
+
python3 scripts/verify_sdk.py # Test completo del SDK LBH
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
## Estructura del proyecto
|
|
48
|
+
|
|
49
|
+
\`\`\`
|
|
50
|
+
hormigasais-edge-starter/
|
|
51
|
+
├── bootstrap.sh
|
|
52
|
+
├── generator.py
|
|
53
|
+
├── package.json
|
|
54
|
+
├── protocols/ # SDK LBH
|
|
55
|
+
├── agents/ # Agentes autónomos
|
|
56
|
+
├── scripts/ # Scripts de verificación
|
|
57
|
+
├── utils/ # Utilidades
|
|
58
|
+
├── .hormigasais/ # Configuración del nodo
|
|
59
|
+
└── logs/
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
**Made with ❤️ for digital sovereignty**
|
|
65
|
+
|
|
66
|
+
[hormigasais.com](https://hormigasais.com) | [@Thrumanshow](https://github.com/Thrumanshow)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
hormiga_centinela.py
|
|
4
|
+
"""
|
|
5
|
+
import time
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
11
|
+
sys.path.insert(0, str(ROOT))
|
|
12
|
+
|
|
13
|
+
from utils.env_loader import load_termux_secrets
|
|
14
|
+
load_termux_secrets()
|
|
15
|
+
|
|
16
|
+
from protocols.encoder import make_header, encode_packet
|
|
17
|
+
from protocols.constants import TYPE_CODES
|
|
18
|
+
from protocols.signer import seal_action
|
|
19
|
+
|
|
20
|
+
NODE_ID = "A16"
|
|
21
|
+
OWNER = "Cristhiam Leonardo Hernández Quiñonez"
|
|
22
|
+
INTERVAL = 30
|
|
23
|
+
|
|
24
|
+
def emit_pulse():
|
|
25
|
+
payload = json.dumps({
|
|
26
|
+
"agent": "hormiga_centinela",
|
|
27
|
+
"status": "ACTIVE",
|
|
28
|
+
"node": NODE_ID,
|
|
29
|
+
"ts": int(time.time())
|
|
30
|
+
})
|
|
31
|
+
header = make_header(NODE_ID[:3], "00")
|
|
32
|
+
frame = encode_packet(header, TYPE_CODES["FUEL"], payload)
|
|
33
|
+
seal = seal_action(payload, OWNER)
|
|
34
|
+
print("[CENTINELA] FUEL pulse emitido")
|
|
35
|
+
return frame
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
print(f"[CENTINELA] Iniciando en nodo {NODE_ID}")
|
|
39
|
+
while True:
|
|
40
|
+
emit_pulse()
|
|
41
|
+
time.sleep(INTERVAL)
|
package/generator.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
const targetName = process.argv[2] || 'mi-colonia';
|
|
7
|
+
const targetDir = path.join(process.cwd(), targetName);
|
|
8
|
+
|
|
9
|
+
console.log(`🐜 [HormigasAIS] Desplegando nodo soberano en edge: ${targetName}...`);
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(targetDir)) {
|
|
12
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Estructura de directorios
|
|
16
|
+
const subdirs = ['agents', 'protocols', 'scripts', '.hormigasais'];
|
|
17
|
+
subdirs.forEach(dir => {
|
|
18
|
+
const p = path.join(targetDir, dir);
|
|
19
|
+
if (!fs.existsSync(p)) fs.mkdirSync(p, { recursive: true });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Copiar o generar archivos base en el directorio destino
|
|
23
|
+
const fecha = new Date().toISOString();
|
|
24
|
+
|
|
25
|
+
const config = {
|
|
26
|
+
"node_id": "NODE-" + Math.random().toString(36).substring(2, 8).toUpperCase(),
|
|
27
|
+
"node_name": targetName,
|
|
28
|
+
"protocol": "LBH_BINARY_V1",
|
|
29
|
+
"version": "2.0",
|
|
30
|
+
"owner": "Cristhiam Leonardo Hernández Quiñonez",
|
|
31
|
+
"location": "San Miguel, El Salvador",
|
|
32
|
+
"nodo_maestro": "A16-SanMiguel-SV",
|
|
33
|
+
"created": fecha,
|
|
34
|
+
"agents": ["hormiga_centinela", "hormiga_relevo"],
|
|
35
|
+
"cert": "SELLADO-SOBERANO-LBH"
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
fs.writeFileSync(path.join(targetDir, '.hormigasais', 'node_config.json'), JSON.stringify(config, null, 2));
|
|
39
|
+
|
|
40
|
+
console.log(`✅ Nodo configurado en ${targetDir}`);
|
|
41
|
+
console.log(`
|
|
42
|
+
Siguiente paso:\n cd ${targetName}\n ./scripts/ignicion.sh`);
|
package/generator.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
HormigasAIS Edge Starter
|
|
4
|
+
generator.py
|
|
5
|
+
|
|
6
|
+
Orquestador principal del starter.
|
|
7
|
+
|
|
8
|
+
Responsabilidades:
|
|
9
|
+
- Crear la estructura base.
|
|
10
|
+
- Ejecutar cada generador especializado.
|
|
11
|
+
- No sobrescribir archivos existentes salvo indicación explícita.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
import importlib
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
ROOT = Path(__file__).resolve().parent
|
|
19
|
+
GENERATORS = ROOT / "generators"
|
|
20
|
+
|
|
21
|
+
sys.path.insert(0, str(ROOT))
|
|
22
|
+
|
|
23
|
+
MODULES = [
|
|
24
|
+
"config_generator",
|
|
25
|
+
"readme_generator",
|
|
26
|
+
"protocol_generator",
|
|
27
|
+
"contracts_generator",
|
|
28
|
+
"agents_generator",
|
|
29
|
+
"scripts_generator",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main():
|
|
34
|
+
|
|
35
|
+
GENERATORS.mkdir(exist_ok=True)
|
|
36
|
+
|
|
37
|
+
init = GENERATORS / "__init__.py"
|
|
38
|
+
|
|
39
|
+
if not init.exists():
|
|
40
|
+
init.write_text(
|
|
41
|
+
"# HormigasAIS Generators Package\n",
|
|
42
|
+
encoding="utf-8"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
print("============================================")
|
|
46
|
+
print("🐜 HormigasAIS Generator")
|
|
47
|
+
print("============================================")
|
|
48
|
+
|
|
49
|
+
for module_name in MODULES:
|
|
50
|
+
|
|
51
|
+
print(f"[LOAD] {module_name}")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
|
|
55
|
+
module = importlib.import_module(
|
|
56
|
+
f"generators.{module_name}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if hasattr(module, "generate"):
|
|
60
|
+
|
|
61
|
+
module.generate(ROOT)
|
|
62
|
+
|
|
63
|
+
print(f"[ OK ] {module_name}")
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
|
|
67
|
+
print(f"[SKIP] {module_name} (sin generate())")
|
|
68
|
+
|
|
69
|
+
except ModuleNotFoundError:
|
|
70
|
+
|
|
71
|
+
print(f"[WAIT] {module_name} aún no existe")
|
|
72
|
+
|
|
73
|
+
except Exception as e:
|
|
74
|
+
|
|
75
|
+
print(f"[FAIL] {module_name}")
|
|
76
|
+
print(e)
|
|
77
|
+
|
|
78
|
+
print("")
|
|
79
|
+
print("Proceso terminado.")
|
|
80
|
+
print("============================================")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents_generator.py
|
|
3
|
+
HormigasAIS Edge Starter
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
def write_if_missing(path: Path, content: str):
|
|
9
|
+
if path.exists():
|
|
10
|
+
print(f" └── {path.name} ya existe")
|
|
11
|
+
return
|
|
12
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
13
|
+
path.write_text(content, encoding="utf-8")
|
|
14
|
+
print(f" └── {path.name} creado")
|
|
15
|
+
|
|
16
|
+
def generate(root: Path):
|
|
17
|
+
print("[LOAD] agents_generator")
|
|
18
|
+
agents_dir = root / "agents"
|
|
19
|
+
agents_dir.mkdir(exist_ok=True)
|
|
20
|
+
|
|
21
|
+
centinela_code = '''#!/usr/bin/env python3
|
|
22
|
+
"""
|
|
23
|
+
hormiga_centinela.py
|
|
24
|
+
"""
|
|
25
|
+
import time
|
|
26
|
+
import json
|
|
27
|
+
import sys
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
31
|
+
sys.path.insert(0, str(ROOT))
|
|
32
|
+
|
|
33
|
+
from utils.env_loader import load_termux_secrets
|
|
34
|
+
load_termux_secrets()
|
|
35
|
+
|
|
36
|
+
from protocols.encoder import make_header, encode_packet
|
|
37
|
+
from protocols.constants import TYPE_CODES
|
|
38
|
+
from protocols.signer import seal_action
|
|
39
|
+
|
|
40
|
+
NODE_ID = "A16"
|
|
41
|
+
OWNER = "Cristhiam Leonardo Hernández Quiñonez"
|
|
42
|
+
INTERVAL = 30
|
|
43
|
+
|
|
44
|
+
def emit_pulse():
|
|
45
|
+
payload = json.dumps({
|
|
46
|
+
"agent": "hormiga_centinela",
|
|
47
|
+
"status": "ACTIVE",
|
|
48
|
+
"node": NODE_ID,
|
|
49
|
+
"ts": int(time.time())
|
|
50
|
+
})
|
|
51
|
+
header = make_header(NODE_ID[:3], "00")
|
|
52
|
+
frame = encode_packet(header, TYPE_CODES["FUEL"], payload)
|
|
53
|
+
seal = seal_action(payload, OWNER)
|
|
54
|
+
print("[CENTINELA] FUEL pulse emitido")
|
|
55
|
+
return frame
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
print(f"[CENTINELA] Iniciando en nodo {NODE_ID}")
|
|
59
|
+
while True:
|
|
60
|
+
emit_pulse()
|
|
61
|
+
time.sleep(INTERVAL)
|
|
62
|
+
'''
|
|
63
|
+
|
|
64
|
+
c_path = agents_dir / "hormiga_centinela.py"
|
|
65
|
+
write_if_missing(c_path, centinela_code)
|
|
66
|
+
c_path.chmod(0o755)
|
|
67
|
+
|
|
68
|
+
print("[ OK ] agents_generator")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
config_generator.py
|
|
3
|
+
HormigasAIS Edge Starter
|
|
4
|
+
|
|
5
|
+
Genera la configuración inicial del nodo.
|
|
6
|
+
No sobrescribe configuraciones existentes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import json
|
|
11
|
+
import datetime
|
|
12
|
+
import getpass
|
|
13
|
+
import socket
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def generate(root: Path):
|
|
17
|
+
|
|
18
|
+
config_dir = root / ".hormigasais"
|
|
19
|
+
config_dir.mkdir(exist_ok=True)
|
|
20
|
+
|
|
21
|
+
config_file = config_dir / "node_config.json"
|
|
22
|
+
|
|
23
|
+
if config_file.exists():
|
|
24
|
+
print(" └── node_config.json ya existe")
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
now = datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
28
|
+
|
|
29
|
+
hostname = socket.gethostname()
|
|
30
|
+
|
|
31
|
+
if hostname == "localhost":
|
|
32
|
+
hostname = "A16"
|
|
33
|
+
|
|
34
|
+
config = {
|
|
35
|
+
"schema": "LBH_NODE_CONFIG_V1",
|
|
36
|
+
"node_id": f"{hostname}-{getpass.getuser()}",
|
|
37
|
+
"node_name": "mi-colonia",
|
|
38
|
+
"protocol": "LBH_BINARY_V2",
|
|
39
|
+
"version": "2.0.0",
|
|
40
|
+
"owner": {
|
|
41
|
+
"name": "YOUR-NAME",
|
|
42
|
+
"email": "YOUR-EMAIL"
|
|
43
|
+
},
|
|
44
|
+
"location": {
|
|
45
|
+
"country": "SV",
|
|
46
|
+
"city": "San Miguel"
|
|
47
|
+
},
|
|
48
|
+
"master_node": "A16-SanMiguel-SV",
|
|
49
|
+
"created_at": now,
|
|
50
|
+
"status": "INITIALIZED",
|
|
51
|
+
"agents": [
|
|
52
|
+
"hormiga_centinela",
|
|
53
|
+
"hormiga_relevo"
|
|
54
|
+
],
|
|
55
|
+
"security": {
|
|
56
|
+
"hmac_required": True,
|
|
57
|
+
"algorithm": "HMAC-SHA256"
|
|
58
|
+
},
|
|
59
|
+
"certificate": "PENDING"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
with open(config_file, "w", encoding="utf-8") as f:
|
|
63
|
+
json.dump(config, f, indent=4, ensure_ascii=False)
|
|
64
|
+
|
|
65
|
+
print(" └── node_config.json creado")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
contracts_generator.py
|
|
3
|
+
HormigasAIS Edge Starter
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import datetime
|
|
8
|
+
|
|
9
|
+
def write_if_missing(path: Path, content: str):
|
|
10
|
+
if path.exists():
|
|
11
|
+
print(f" └── {path.name} ya existe")
|
|
12
|
+
return
|
|
13
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
14
|
+
path.write_text(content, encoding="utf-8")
|
|
15
|
+
print(f" └── {path.name} creado")
|
|
16
|
+
|
|
17
|
+
def generate(root: Path):
|
|
18
|
+
print("[LOAD] contracts_generator")
|
|
19
|
+
protocols_dir = root / "protocols"
|
|
20
|
+
protocols_dir.mkdir(exist_ok=True)
|
|
21
|
+
|
|
22
|
+
fecha = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
23
|
+
|
|
24
|
+
contract_content = f"""\
|
|
25
|
+
==============================================================================
|
|
26
|
+
.human GOVERNANCE CONTRACT — HormigasAIS Edge Starter
|
|
27
|
+
==============================================================================
|
|
28
|
+
NODE: A16
|
|
29
|
+
OWNER: Cristhiam Leonardo Hernández Quiñonez
|
|
30
|
+
CREATED: {fecha}
|
|
31
|
+
SOVEREIGNTY DECLARATION:
|
|
32
|
+
This node operates under full digital sovereignty.
|
|
33
|
+
CERT::LBH-HUMAN-STARTER-V1-CLHQ
|
|
34
|
+
==============================================================================
|
|
35
|
+
"""
|
|
36
|
+
contract_path = protocols_dir / "human_contract.lbh"
|
|
37
|
+
write_if_missing(contract_path, contract_content)
|
|
38
|
+
print("[ OK ] contracts_generator")
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
protocol_generator.py
|
|
3
|
+
HormigasAIS Edge Starter
|
|
4
|
+
|
|
5
|
+
Genera el paquete protocols/ modular del SDK LBH.
|
|
6
|
+
No sobrescribe archivos existentes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def write_if_missing(path: Path, content: str):
|
|
13
|
+
if path.exists():
|
|
14
|
+
print(f" └── {path.name} ya existe")
|
|
15
|
+
return
|
|
16
|
+
path.write_text(content, encoding="utf-8")
|
|
17
|
+
print(f" └── {path.name} creado")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate(root: Path):
|
|
21
|
+
|
|
22
|
+
protocols = root / "protocols"
|
|
23
|
+
protocols.mkdir(exist_ok=True)
|
|
24
|
+
|
|
25
|
+
write_if_missing(
|
|
26
|
+
protocols / "__init__.py",
|
|
27
|
+
'''"""
|
|
28
|
+
LBH Protocol SDK
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from .constants import TYPE_CODES
|
|
32
|
+
from .encoder import encode_packet
|
|
33
|
+
from .decoder import decode_packet
|
|
34
|
+
from .signer import seal_action
|
|
35
|
+
from .verifier import verify_signature
|
|
36
|
+
'''
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
write_if_missing(
|
|
40
|
+
protocols / "constants.py",
|
|
41
|
+
'''"""
|
|
42
|
+
Constantes oficiales LBH
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
TYPE_CODES = {
|
|
46
|
+
"SEAL":"5345414c",
|
|
47
|
+
"VERI":"56455249",
|
|
48
|
+
"SYNC":"53594e43",
|
|
49
|
+
"PING":"50494e47",
|
|
50
|
+
"FUEL":"4655454c",
|
|
51
|
+
"ACKK":"41434b4b",
|
|
52
|
+
"ERRR":"45525252",
|
|
53
|
+
}
|
|
54
|
+
'''
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
write_if_missing(
|
|
58
|
+
protocols / "encoder.py",
|
|
59
|
+
'''from .constants import TYPE_CODES
|
|
60
|
+
|
|
61
|
+
def make_header(node_id="A16", version="00"):
|
|
62
|
+
return (
|
|
63
|
+
node_id.encode().hex()[:4].ljust(4,"0") +
|
|
64
|
+
version.encode().hex()[:4].ljust(4,"0")
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def encode_packet(header, type_code, payload):
|
|
68
|
+
payload_hex = payload.encode().hex()
|
|
69
|
+
length = format(len(payload_hex)//2,"08x")
|
|
70
|
+
return f"{header}{type_code}{length}{payload_hex}"
|
|
71
|
+
'''
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
write_if_missing(
|
|
75
|
+
protocols / "decoder.py",
|
|
76
|
+
'''def decode_packet(frame):
|
|
77
|
+
return {
|
|
78
|
+
"header":frame[:8],
|
|
79
|
+
"type_code":frame[8:16],
|
|
80
|
+
"length":int(frame[16:24],16),
|
|
81
|
+
"payload":bytes.fromhex(frame[24:]).decode()
|
|
82
|
+
}
|
|
83
|
+
'''
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
write_if_missing(
|
|
87
|
+
protocols / "signer.py",
|
|
88
|
+
'''import hashlib
|
|
89
|
+
import hmac
|
|
90
|
+
import os
|
|
91
|
+
import time
|
|
92
|
+
|
|
93
|
+
def seal_action(content, owner):
|
|
94
|
+
|
|
95
|
+
secret = os.environ.get("LBH_SECRET")
|
|
96
|
+
|
|
97
|
+
if not secret:
|
|
98
|
+
raise RuntimeError(
|
|
99
|
+
"LBH_SECRET no definido."
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
sha = hashlib.sha256(content.encode()).hexdigest()
|
|
103
|
+
|
|
104
|
+
ts = int(time.time())
|
|
105
|
+
|
|
106
|
+
payload = f"{sha}|{owner}|{ts}"
|
|
107
|
+
|
|
108
|
+
sig = hmac.new(
|
|
109
|
+
secret.encode(),
|
|
110
|
+
payload.encode(),
|
|
111
|
+
hashlib.sha256
|
|
112
|
+
).hexdigest()
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
"sha256":sha,
|
|
116
|
+
"firma":sig,
|
|
117
|
+
"timestamp":ts,
|
|
118
|
+
"owner":owner
|
|
119
|
+
}
|
|
120
|
+
'''
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
write_if_missing(
|
|
124
|
+
protocols / "verifier.py",
|
|
125
|
+
'''import hashlib
|
|
126
|
+
import hmac
|
|
127
|
+
import os
|
|
128
|
+
|
|
129
|
+
def verify_signature(content, signature, owner, timestamp):
|
|
130
|
+
|
|
131
|
+
secret = os.environ.get("LBH_SECRET")
|
|
132
|
+
|
|
133
|
+
if not secret:
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
sha = hashlib.sha256(content.encode()).hexdigest()
|
|
137
|
+
|
|
138
|
+
payload = f"{sha}|{owner}|{timestamp}"
|
|
139
|
+
|
|
140
|
+
expected = hmac.new(
|
|
141
|
+
secret.encode(),
|
|
142
|
+
payload.encode(),
|
|
143
|
+
hashlib.sha256
|
|
144
|
+
).hexdigest()
|
|
145
|
+
|
|
146
|
+
return hmac.compare_digest(
|
|
147
|
+
expected,
|
|
148
|
+
signature
|
|
149
|
+
)
|
|
150
|
+
'''
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
write_if_missing(
|
|
154
|
+
protocols / "lbh_core.py",
|
|
155
|
+
'''"""
|
|
156
|
+
Compatibilidad con versiones anteriores.
|
|
157
|
+
|
|
158
|
+
Importa automáticamente todos los módulos del SDK.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
from .constants import *
|
|
162
|
+
from .encoder import *
|
|
163
|
+
from .decoder import *
|
|
164
|
+
from .signer import *
|
|
165
|
+
from .verifier import *
|
|
166
|
+
'''
|
|
167
|
+
)
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
scripts_generator.py
|
|
3
|
+
HormigasAIS Edge Starter
|
|
4
|
+
|
|
5
|
+
Genera scripts auxiliares del starter.
|
|
6
|
+
|
|
7
|
+
Fase:
|
|
8
|
+
- utils/env_loader.py
|
|
9
|
+
- scripts/verify_sdk.py (POST del SDK LBH)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def write_if_missing(path: Path, content: str):
|
|
16
|
+
if path.exists():
|
|
17
|
+
print(f" └── {path.name} ya existe")
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
path.write_text(content, encoding="utf-8")
|
|
22
|
+
print(f" └── {path.name} creado")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def generate(root: Path):
|
|
26
|
+
|
|
27
|
+
print("[LOAD] scripts_generator")
|
|
28
|
+
|
|
29
|
+
# ============================================================
|
|
30
|
+
# utils/env_loader.py
|
|
31
|
+
# ============================================================
|
|
32
|
+
|
|
33
|
+
utils = root / "utils"
|
|
34
|
+
utils.mkdir(exist_ok=True)
|
|
35
|
+
|
|
36
|
+
env_loader = r'''
|
|
37
|
+
"""
|
|
38
|
+
env_loader.py
|
|
39
|
+
HormigasAIS Secret Loader
|
|
40
|
+
|
|
41
|
+
Carga secretos desde:
|
|
42
|
+
- Variables de entorno
|
|
43
|
+
- ~/.hormigas_secrets
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from pathlib import Path
|
|
47
|
+
import os
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def load_termux_secrets():
|
|
51
|
+
|
|
52
|
+
secret_file = Path.home() / ".hormigas_secrets"
|
|
53
|
+
|
|
54
|
+
if not secret_file.exists():
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
for line in secret_file.read_text(
|
|
58
|
+
encoding="utf-8"
|
|
59
|
+
).splitlines():
|
|
60
|
+
|
|
61
|
+
line = line.strip()
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
not line
|
|
65
|
+
or line.startswith("#")
|
|
66
|
+
or "=" not in line
|
|
67
|
+
):
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
key, value = line.split("=", 1)
|
|
71
|
+
|
|
72
|
+
os.environ.setdefault(
|
|
73
|
+
key.strip(),
|
|
74
|
+
value.strip()
|
|
75
|
+
.strip('"')
|
|
76
|
+
.strip("'")
|
|
77
|
+
)
|
|
78
|
+
'''
|
|
79
|
+
|
|
80
|
+
write_if_missing(
|
|
81
|
+
utils / "env_loader.py",
|
|
82
|
+
env_loader
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ============================================================
|
|
87
|
+
# scripts/verify_sdk.py
|
|
88
|
+
# ============================================================
|
|
89
|
+
|
|
90
|
+
scripts = root / "scripts"
|
|
91
|
+
scripts.mkdir(exist_ok=True)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
verify_sdk = r'''#!/usr/bin/env python3
|
|
95
|
+
|
|
96
|
+
"""
|
|
97
|
+
verify_sdk.py
|
|
98
|
+
HormigasAIS LBH SDK Power-On Self Test (POST)
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
import json
|
|
102
|
+
import os
|
|
103
|
+
import sys
|
|
104
|
+
from pathlib import Path
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
108
|
+
|
|
109
|
+
sys.path.insert(
|
|
110
|
+
0,
|
|
111
|
+
str(ROOT)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
from utils.env_loader import load_termux_secrets
|
|
116
|
+
|
|
117
|
+
load_termux_secrets()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
print("========================================")
|
|
121
|
+
print("🐜 HormigasAIS :: SDK POST")
|
|
122
|
+
print("========================================")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
|
|
127
|
+
from protocols.encoder import (
|
|
128
|
+
make_header,
|
|
129
|
+
encode_packet
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
from protocols.decoder import (
|
|
133
|
+
decode_packet
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
from protocols.constants import (
|
|
137
|
+
TYPE_CODES
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
from protocols.signer import (
|
|
141
|
+
seal_action
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
from protocols.verifier import (
|
|
145
|
+
verify_signature
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
except Exception as e:
|
|
150
|
+
|
|
151
|
+
print("[FAIL] Importación del SDK")
|
|
152
|
+
print(e)
|
|
153
|
+
sys.exit(1)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
print("[ OK ] Importación del SDK")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
secret = os.environ.get(
|
|
160
|
+
"LBH_SECRET"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if not secret:
|
|
165
|
+
|
|
166
|
+
print(
|
|
167
|
+
"[FAIL] LBH_SECRET no definido"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
print(
|
|
171
|
+
"Configure ~/.hormigas_secrets"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
sys.exit(1)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
print(
|
|
178
|
+
"[ OK ] LBH_SECRET disponible"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
payload = json.dumps(
|
|
183
|
+
{
|
|
184
|
+
"action": "sdk_post",
|
|
185
|
+
"node": "SELFTEST"
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
frame = encode_packet(
|
|
191
|
+
make_header(
|
|
192
|
+
"A16",
|
|
193
|
+
"00"
|
|
194
|
+
),
|
|
195
|
+
TYPE_CODES["FUEL"],
|
|
196
|
+
payload
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
print(
|
|
201
|
+
"[ OK ] Codificación LBH"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
decoded = decode_packet(frame)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
if decoded["payload"] != payload:
|
|
209
|
+
|
|
210
|
+
print(
|
|
211
|
+
"[FAIL] Decodificación"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
sys.exit(1)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
print(
|
|
218
|
+
"[ OK ] Decodificación"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
seal = seal_action(
|
|
223
|
+
payload,
|
|
224
|
+
"SELFTEST"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
print(
|
|
229
|
+
"[ OK ] Firma HMAC"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
valid = verify_signature(
|
|
234
|
+
payload,
|
|
235
|
+
seal["firma"],
|
|
236
|
+
seal["owner"],
|
|
237
|
+
seal["timestamp"]
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
if not valid:
|
|
242
|
+
|
|
243
|
+
print(
|
|
244
|
+
"[FAIL] Verificación HMAC"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
sys.exit(1)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
print(
|
|
251
|
+
"[ OK ] Verificación HMAC"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
print("")
|
|
256
|
+
|
|
257
|
+
print(
|
|
258
|
+
"========================================"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
print(
|
|
262
|
+
"SDK LBH OPERATIVO"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
print(
|
|
266
|
+
"========================================"
|
|
267
|
+
)
|
|
268
|
+
'''
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
verify = scripts / "verify_sdk.py"
|
|
272
|
+
|
|
273
|
+
write_if_missing(
|
|
274
|
+
verify,
|
|
275
|
+
verify_sdk
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
verify.chmod(0o755)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
print("[ OK ] scripts_generator")
|
|
282
|
+
|
|
283
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hormigasais-edge-starter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Starter template para desplegar un nodo soberano HormigasAIS con agentes LBH. Incluye SDK de protocolos LBH, generadores automáticos y agentes edge.",
|
|
5
|
+
"main": "generator.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-hormigas-node": "./generator.js"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/Thrumanshow/hormigasais-edge-starter.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"hormigasais",
|
|
15
|
+
"edge-computing",
|
|
16
|
+
"sovereign-node",
|
|
17
|
+
"lbh-protocol",
|
|
18
|
+
"agent-based",
|
|
19
|
+
"termux",
|
|
20
|
+
"offline-first"
|
|
21
|
+
],
|
|
22
|
+
"author": "Cristhiam Leonardo Hernández Quiñonez",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/Thrumanshow/hormigasais-edge-starter/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/Thrumanshow/hormigasais-edge-starter#readme",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"start": "python3 generator.py",
|
|
30
|
+
"bootstrap": "bash bootstrap.sh",
|
|
31
|
+
"verify": "python3 scripts/verify_sdk.py",
|
|
32
|
+
"postinstall": "python3 generator.py --quick || echo 'HormigasAIS Edge Starter instalado'",
|
|
33
|
+
"test": "python3 scripts/verify_sdk.py"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"jspdf": "^2.5.1"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=14.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .constants import TYPE_CODES
|
|
2
|
+
|
|
3
|
+
def make_header(node_id="A16", version="00"):
|
|
4
|
+
return (
|
|
5
|
+
node_id.encode().hex()[:4].ljust(4,"0") +
|
|
6
|
+
version.encode().hex()[:4].ljust(4,"0")
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
def encode_packet(header, type_code, payload):
|
|
10
|
+
payload_hex = payload.encode().hex()
|
|
11
|
+
length = format(len(payload_hex)//2,"08x")
|
|
12
|
+
return f"{header}{type_code}{length}{payload_hex}"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
==============================================================================
|
|
2
|
+
.human GOVERNANCE CONTRACT — HormigasAIS Edge Starter
|
|
3
|
+
==============================================================================
|
|
4
|
+
NODE: A16
|
|
5
|
+
OWNER: Cristhiam Leonardo Hernández Quiñonez
|
|
6
|
+
CREATED: 2026-07-24T05:54:01Z
|
|
7
|
+
SOVEREIGNTY DECLARATION:
|
|
8
|
+
This node operates under full digital sovereignty.
|
|
9
|
+
CERT::LBH-HUMAN-STARTER-V1-CLHQ
|
|
10
|
+
==============================================================================
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import hmac
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
def seal_action(content, owner):
|
|
7
|
+
|
|
8
|
+
secret = os.environ.get("LBH_SECRET")
|
|
9
|
+
|
|
10
|
+
if not secret:
|
|
11
|
+
raise RuntimeError(
|
|
12
|
+
"LBH_SECRET no definido."
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
sha = hashlib.sha256(content.encode()).hexdigest()
|
|
16
|
+
|
|
17
|
+
ts = int(time.time())
|
|
18
|
+
|
|
19
|
+
payload = f"{sha}|{owner}|{ts}"
|
|
20
|
+
|
|
21
|
+
sig = hmac.new(
|
|
22
|
+
secret.encode(),
|
|
23
|
+
payload.encode(),
|
|
24
|
+
hashlib.sha256
|
|
25
|
+
).hexdigest()
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
"sha256":sha,
|
|
29
|
+
"firma":sig,
|
|
30
|
+
"timestamp":ts,
|
|
31
|
+
"owner":owner
|
|
32
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import hmac
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
def verify_signature(content, signature, owner, timestamp):
|
|
6
|
+
|
|
7
|
+
secret = os.environ.get("LBH_SECRET")
|
|
8
|
+
|
|
9
|
+
if not secret:
|
|
10
|
+
return False
|
|
11
|
+
|
|
12
|
+
sha = hashlib.sha256(content.encode()).hexdigest()
|
|
13
|
+
|
|
14
|
+
payload = f"{sha}|{owner}|{timestamp}"
|
|
15
|
+
|
|
16
|
+
expected = hmac.new(
|
|
17
|
+
secret.encode(),
|
|
18
|
+
payload.encode(),
|
|
19
|
+
hashlib.sha256
|
|
20
|
+
).hexdigest()
|
|
21
|
+
|
|
22
|
+
return hmac.compare_digest(
|
|
23
|
+
expected,
|
|
24
|
+
signature
|
|
25
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
verify_sdk.py
|
|
4
|
+
LBH SDK Power-On Self Test (POST)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
13
|
+
sys.path.insert(0, str(ROOT))
|
|
14
|
+
|
|
15
|
+
print("========================================")
|
|
16
|
+
print("🐜 HormigasAIS :: SDK POST")
|
|
17
|
+
print("========================================")
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from protocols.encoder import make_header, encode_packet
|
|
21
|
+
from protocols.decoder import decode_packet
|
|
22
|
+
from protocols.constants import TYPE_CODES
|
|
23
|
+
from protocols.signer import seal_action
|
|
24
|
+
from protocols.verifier import verify_signature
|
|
25
|
+
except Exception as e:
|
|
26
|
+
print("[FAIL] Importación del SDK")
|
|
27
|
+
print(e)
|
|
28
|
+
sys.exit(1)
|
|
29
|
+
|
|
30
|
+
print("[ OK ] Importación del SDK")
|
|
31
|
+
|
|
32
|
+
secret = os.environ.get("LBH_SECRET")
|
|
33
|
+
|
|
34
|
+
if not secret:
|
|
35
|
+
print("[FAIL] LBH_SECRET no definido")
|
|
36
|
+
sys.exit(1)
|
|
37
|
+
|
|
38
|
+
print("[ OK ] LBH_SECRET disponible")
|
|
39
|
+
|
|
40
|
+
payload = json.dumps({
|
|
41
|
+
"action":"sdk_post",
|
|
42
|
+
"node":"SELFTEST"
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
frame = encode_packet(
|
|
46
|
+
make_header("A16","00"),
|
|
47
|
+
TYPE_CODES["FUEL"],
|
|
48
|
+
payload
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
print("[ OK ] Codificación LBH")
|
|
52
|
+
|
|
53
|
+
decoded = decode_packet(frame)
|
|
54
|
+
|
|
55
|
+
if decoded["payload"] != payload:
|
|
56
|
+
print("[FAIL] Decodificación")
|
|
57
|
+
sys.exit(1)
|
|
58
|
+
|
|
59
|
+
print("[ OK ] Decodificación")
|
|
60
|
+
|
|
61
|
+
seal = seal_action(
|
|
62
|
+
payload,
|
|
63
|
+
"SELFTEST"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print("[ OK ] Firma HMAC")
|
|
67
|
+
|
|
68
|
+
valid = verify_signature(
|
|
69
|
+
payload,
|
|
70
|
+
seal["firma"],
|
|
71
|
+
seal["owner"],
|
|
72
|
+
seal["timestamp"]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if not valid:
|
|
76
|
+
print("[FAIL] Verificación HMAC")
|
|
77
|
+
sys.exit(1)
|
|
78
|
+
|
|
79
|
+
print("[ OK ] Verificación HMAC")
|
|
80
|
+
|
|
81
|
+
print("")
|
|
82
|
+
print("========================================")
|
|
83
|
+
print("SDK LBH OPERATIVO")
|
|
84
|
+
print("========================================")
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
cd ~/hormigasais-edge-starter
|
|
3
|
+
|
|
4
|
+
echo "🐜 Actualizando agents_generator.py y contracts_generator.py..."
|
|
5
|
+
|
|
6
|
+
# agents_generator.py
|
|
7
|
+
cat << 'AGENT' > generators/agents_generator.py
|
|
8
|
+
"""
|
|
9
|
+
agents_generator.py
|
|
10
|
+
HormigasAIS Edge Starter
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
def write_if_missing(path: Path, content: str):
|
|
16
|
+
if path.exists():
|
|
17
|
+
print(f" └── {path.name} ya existe")
|
|
18
|
+
return
|
|
19
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
path.write_text(content, encoding="utf-8")
|
|
21
|
+
print(f" └── {path.name} creado")
|
|
22
|
+
|
|
23
|
+
def generate(root: Path):
|
|
24
|
+
print("[LOAD] agents_generator")
|
|
25
|
+
agents_dir = root / "agents"
|
|
26
|
+
agents_dir.mkdir(exist_ok=True)
|
|
27
|
+
|
|
28
|
+
centinela_code = '''#!/usr/bin/env python3
|
|
29
|
+
"""
|
|
30
|
+
hormiga_centinela.py
|
|
31
|
+
"""
|
|
32
|
+
import time
|
|
33
|
+
import json
|
|
34
|
+
import sys
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
|
|
37
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
38
|
+
sys.path.insert(0, str(ROOT))
|
|
39
|
+
|
|
40
|
+
from utils.env_loader import load_termux_secrets
|
|
41
|
+
load_termux_secrets()
|
|
42
|
+
|
|
43
|
+
from protocols.encoder import make_header, encode_packet
|
|
44
|
+
from protocols.constants import TYPE_CODES
|
|
45
|
+
from protocols.signer import seal_action
|
|
46
|
+
|
|
47
|
+
NODE_ID = "A16"
|
|
48
|
+
OWNER = "Cristhiam Leonardo Hernández Quiñonez"
|
|
49
|
+
INTERVAL = 30
|
|
50
|
+
|
|
51
|
+
def emit_pulse():
|
|
52
|
+
payload = json.dumps({
|
|
53
|
+
"agent": "hormiga_centinela",
|
|
54
|
+
"status": "ACTIVE",
|
|
55
|
+
"node": NODE_ID,
|
|
56
|
+
"ts": int(time.time())
|
|
57
|
+
})
|
|
58
|
+
header = make_header(NODE_ID[:3], "00")
|
|
59
|
+
frame = encode_packet(header, TYPE_CODES["FUEL"], payload)
|
|
60
|
+
seal = seal_action(payload, OWNER)
|
|
61
|
+
print("[CENTINELA] FUEL pulse emitido")
|
|
62
|
+
return frame
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
print(f"[CENTINELA] Iniciando en nodo {NODE_ID}")
|
|
66
|
+
while True:
|
|
67
|
+
emit_pulse()
|
|
68
|
+
time.sleep(INTERVAL)
|
|
69
|
+
'''
|
|
70
|
+
|
|
71
|
+
c_path = agents_dir / "hormiga_centinela.py"
|
|
72
|
+
write_if_missing(c_path, centinela_code)
|
|
73
|
+
c_path.chmod(0o755)
|
|
74
|
+
|
|
75
|
+
print("[ OK ] agents_generator")
|
|
76
|
+
AGENT
|
|
77
|
+
|
|
78
|
+
# contracts_generator.py
|
|
79
|
+
cat << 'CONTRACT' > generators/contracts_generator.py
|
|
80
|
+
"""
|
|
81
|
+
contracts_generator.py
|
|
82
|
+
HormigasAIS Edge Starter
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
from pathlib import Path
|
|
86
|
+
import datetime
|
|
87
|
+
|
|
88
|
+
def write_if_missing(path: Path, content: str):
|
|
89
|
+
if path.exists():
|
|
90
|
+
print(f" └── {path.name} ya existe")
|
|
91
|
+
return
|
|
92
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
93
|
+
path.write_text(content, encoding="utf-8")
|
|
94
|
+
print(f" └── {path.name} creado")
|
|
95
|
+
|
|
96
|
+
def generate(root: Path):
|
|
97
|
+
print("[LOAD] contracts_generator")
|
|
98
|
+
protocols_dir = root / "protocols"
|
|
99
|
+
protocols_dir.mkdir(exist_ok=True)
|
|
100
|
+
|
|
101
|
+
fecha = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
102
|
+
|
|
103
|
+
contract_content = f"""\
|
|
104
|
+
==============================================================================
|
|
105
|
+
.human GOVERNANCE CONTRACT — HormigasAIS Edge Starter
|
|
106
|
+
==============================================================================
|
|
107
|
+
NODE: A16
|
|
108
|
+
OWNER: Cristhiam Leonardo Hernández Quiñonez
|
|
109
|
+
CREATED: {fecha}
|
|
110
|
+
SOVEREIGNTY DECLARATION:
|
|
111
|
+
This node operates under full digital sovereignty.
|
|
112
|
+
CERT::LBH-HUMAN-STARTER-V1-CLHQ
|
|
113
|
+
==============================================================================
|
|
114
|
+
"""
|
|
115
|
+
contract_path = protocols_dir / "human_contract.lbh"
|
|
116
|
+
write_if_missing(contract_path, contract_content)
|
|
117
|
+
print("[ OK ] contracts_generator")
|
|
118
|
+
CONTRACT
|
|
119
|
+
|
|
120
|
+
echo "✅ Generadores actualizados"
|
|
121
|
+
|
|
122
|
+
python3 generator.py
|
|
123
|
+
echo "✅ agents y contracts actualizados correctamente."
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
"""
|
|
3
|
+
env_loader.py
|
|
4
|
+
HormigasAIS Secret Loader
|
|
5
|
+
|
|
6
|
+
Carga secretos desde:
|
|
7
|
+
- Variables de entorno
|
|
8
|
+
- ~/.hormigas_secrets
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_termux_secrets():
|
|
16
|
+
|
|
17
|
+
secret_file = Path.home() / ".hormigas_secrets"
|
|
18
|
+
|
|
19
|
+
if not secret_file.exists():
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
for line in secret_file.read_text(
|
|
23
|
+
encoding="utf-8"
|
|
24
|
+
).splitlines():
|
|
25
|
+
|
|
26
|
+
line = line.strip()
|
|
27
|
+
|
|
28
|
+
if (
|
|
29
|
+
not line
|
|
30
|
+
or line.startswith("#")
|
|
31
|
+
or "=" not in line
|
|
32
|
+
):
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
key, value = line.split("=", 1)
|
|
36
|
+
|
|
37
|
+
os.environ.setdefault(
|
|
38
|
+
key.strip(),
|
|
39
|
+
value.strip()
|
|
40
|
+
.strip('"')
|
|
41
|
+
.strip("'")
|
|
42
|
+
)
|