sync: 2026-03-16 22:00 — 21 files from Alexandria
Some checks failed
Lint & Format / detect (push) Has been cancelled
Lint & Format / js-lint (push) Has been cancelled
Lint & Format / py-lint (push) Has been cancelled
Lint & Format / sh-lint (push) Has been cancelled
Lint & Format / go-lint (push) Has been cancelled
Monorepo Lint / lint-shell (push) Has been cancelled
Monorepo Lint / lint-js (push) Has been cancelled

RoadChain-SHA2048: f31122b68d27a309
RoadChain-Identity: alexa@sovereign
RoadChain-Full: f31122b68d27a30949e6be04538b248fc34fc9a056bbb0cce1a6d2bcd333a83956b6c6bf6c4771ca9bb6fb6a284c367ebf6eee3d2c1d97ab6d2d5913fa4ae58c85045eabcef75c88329792905fa71b79e4f7b0d79616f32e99a806df1b0d1ad1e4abc1fb3ae950e91f79f029e0f17ed3463e5e5f05a7c81585955c3c8b8b50f8d10007d33237e1e87a601333aa33f6b48e14a6d1f78c40e178e7e3050b609668d2e323ee30df27dd63f3267dc46b08df2348aa4e8b64de024ff350c5191b04a15f588a43e0f1b6d97ef309ea6dc68e8e138a7060faff35fd3f1b38bcb702e49bea951f4e792cb4d2b7dd2a314b5eb72c4d350ceb9b29a2c9436e34192aee0e43
This commit is contained in:
2026-03-16 22:00:02 -05:00
parent 9b10326c78
commit 558634c0e6
21 changed files with 706 additions and 151 deletions

View File

@@ -34,6 +34,7 @@ openra
openscad
salesforce-cli
sidequest
slack-cli
tailscale-app
temurin
tiled

View File

@@ -1,50 +1,50 @@
[2026-03-16 21:10:03] [FLEET] [alice] cecilia: DOWN (no ping response)
[2026-03-16 21:10:07] [FLEET] [alice] gematria: UP temp=C mem=6287MB disk=68%
[2026-03-16 21:10:42] [FLEET] [alice] lucidia: UP temp=51C mem=3232MB disk=34%
[2026-03-16 21:10:44] [FLEET] [alice] aria: DOWN (no ping response)
[2026-03-16 21:10:45] [FLEET] [alice] anastasia: UP temp=C mem=335MB disk=69%
[2026-03-16 21:11:01] [BEAT] [alice] load=0.81 mem=3264/3794MB temp=32.1C disk=84%
[2026-03-16 21:12:01] [BEAT] [alice] load=1.39 mem=3264/3794MB temp=33.6C disk=84%
[2026-03-16 21:13:01] [BEAT] [alice] load=0.67 mem=3264/3794MB temp=32.1C disk=84%
[2026-03-16 21:13:35] [BEAT] [alice] load=1.12 mem=3257/3794MB temp=35.0C disk=84%
[2026-03-16 21:13:35] [BEAT] [alice] load=1.12 mem=3254/3794MB temp=35.0C disk=84%
[2026-03-16 21:14:02] [BEAT] [alice] load=1.31 mem=3262/3794MB temp=32.1C disk=84%
[2026-03-16 21:15:05] [HEAL] [alice] Service blackroad-agent is DOWN — restarting
[2026-03-16 21:15:09] [BEAT] [alice] load=5.65 mem=3258/3794MB temp=34.1C disk=84%
[2026-03-16 21:15:09] [HEAL] [alice] Service blackroad-agent restarted successfully
[2026-03-16 21:15:09] [HEAL] [alice] Healed 1 services
[2026-03-16 21:15:55] [DIAL] [alice] Switchboard unreachable
[2026-03-16 21:16:01] [BEAT] [alice] load=3.12 mem=3264/3794MB temp=33.1C disk=84%
[2026-03-16 21:16:48] [DIAL] [alice] Switchboard unreachable
[2026-03-16 21:17:01] [BEAT] [alice] load=1.18 mem=3263/3794MB temp=34.1C disk=84%
[2026-03-16 21:18:01] [BEAT] [alice] load=0.96 mem=3263/3794MB temp=33.1C disk=84%
[2026-03-16 21:18:43] [BEAT] [alice] load=0.65 mem=3261/3794MB temp=33.1C disk=84%
[2026-03-16 21:18:43] [BEAT] [alice] load=0.65 mem=3260/3794MB temp=32.6C disk=84%
[2026-03-16 21:19:01] [BEAT] [alice] load=0.85 mem=3251/3794MB temp=34.6C disk=84%
[2026-03-16 21:20:01] [FLEET] [alice] Starting cross-node health check
[2026-03-16 21:20:01] [HEAL] [alice] Service blackroad-agent is DOWN — restarting
[2026-03-16 21:20:01] [BEAT] [alice] load=1.07 mem=3258/3794MB temp=35.5C disk=84%
[2026-03-16 21:20:01] [HEAL] [alice] Service blackroad-agent restarted successfully
[2026-03-16 21:20:02] [FLEET] [alice] octavia: UP temp=36C mem=6359MB disk=68%
[2026-03-16 21:20:02] [HEAL] [alice] Healed 1 services
[2026-03-16 21:20:04] [FLEET] [alice] cecilia: DOWN (no ping response)
[2026-03-16 21:20:07] [FLEET] [alice] gematria: UP temp=C mem=6282MB disk=68%
[2026-03-16 21:20:08] [FLEET] [alice] lucidia: UP temp=53C mem=3200MB disk=34%
[2026-03-16 21:20:10] [FLEET] [alice] aria: DOWN (no ping response)
[2026-03-16 21:20:11] [FLEET] [alice] anastasia: UP temp=C mem=208MB disk=69%
[2026-03-16 21:21:01] [BEAT] [alice] load=0.51 mem=3260/3794MB temp=33.1C disk=84%
[2026-03-16 21:22:01] [BEAT] [alice] load=0.62 mem=3261/3794MB temp=32.6C disk=84%
[2026-03-16 21:23:01] [BEAT] [alice] load=0.91 mem=3261/3794MB temp=35.0C disk=84%
[2026-03-16 21:23:52] [BEAT] [alice] load=0.81 mem=3262/3794MB temp=32.6C disk=84%
[2026-03-16 21:23:52] [BEAT] [alice] load=0.81 mem=3262/3794MB temp=33.1C disk=84%
[2026-03-16 21:24:01] [BEAT] [alice] load=0.76 mem=3263/3794MB temp=34.1C disk=84%
[2026-03-16 21:25:02] [HEAL] [alice] Service blackroad-agent is DOWN — restarting
[2026-03-16 21:25:02] [BEAT] [alice] load=1.00 mem=3256/3794MB temp=34.6C disk=84%
[2026-03-16 21:25:02] [HEAL] [alice] Service blackroad-agent restarted successfully
[2026-03-16 21:25:02] [HEAL] [alice] Healed 1 services
[2026-03-16 21:26:01] [BEAT] [alice] load=0.66 mem=3248/3794MB temp=34.1C disk=84%
[2026-03-16 21:27:02] [BEAT] [alice] load=0.79 mem=3259/3794MB temp=35.0C disk=84%
[2026-03-16 21:28:01] [BEAT] [alice] load=0.64 mem=3262/3794MB temp=33.6C disk=84%
[2026-03-16 21:29:01] [BEAT] [alice] load=0.64 mem=3259/3794MB temp=36.0C disk=84%
[2026-03-16 21:29:01] [BEAT] [alice] load=0.64 mem=3259/3794MB temp=36.0C disk=84%
[2026-03-16 21:29:01] [BEAT] [alice] load=0.64 mem=3258/3794MB temp=36.0C disk=84%
[2026-03-16 21:40:04] [FLEET] [alice] cecilia: DOWN (no ping response)
[2026-03-16 21:40:05] [FLEET] [alice] gematria: UP temp=C mem=6273MB disk=68%
[2026-03-16 21:40:06] [FLEET] [alice] lucidia: UP temp=55C mem=3209MB disk=34%
[2026-03-16 21:40:06] [FLEET] [alice] aria: DOWN (no ping response)
[2026-03-16 21:40:08] [FLEET] [alice] anastasia: UP temp=C mem=304MB disk=69%
[2026-03-16 21:41:01] [BEAT] [alice] load=1.37 mem=3263/3794MB temp=32.1C disk=84%
[2026-03-16 21:42:01] [BEAT] [alice] load=0.95 mem=3260/3794MB temp=32.6C disk=84%
[2026-03-16 21:43:01] [BEAT] [alice] load=0.73 mem=3262/3794MB temp=33.6C disk=84%
[2026-03-16 21:44:01] [BEAT] [alice] load=1.06 mem=3264/3794MB temp=33.1C disk=84%
[2026-03-16 21:44:28] [BEAT] [alice] load=1.21 mem=3259/3794MB temp=36.0C disk=84%
[2026-03-16 21:44:28] [BEAT] [alice] load=1.21 mem=3259/3794MB temp=35.5C disk=84%
[2026-03-16 21:45:01] [HEAL] [alice] Service blackroad-agent is DOWN — restarting
[2026-03-16 21:45:01] [BEAT] [alice] load=1.41 mem=3243/3794MB temp=33.6C disk=84%
[2026-03-16 21:45:01] [HEAL] [alice] Service blackroad-agent restarted successfully
[2026-03-16 21:45:02] [HEAL] [alice] Healed 1 services
[2026-03-16 21:45:47] [DIAL] [alice] Switchboard unreachable
[2026-03-16 21:46:01] [BEAT] [alice] load=1.02 mem=3263/3794MB temp=32.1C disk=84%
[2026-03-16 21:46:49] [DIAL] [alice] Switchboard unreachable
[2026-03-16 21:47:01] [BEAT] [alice] load=1.15 mem=3260/3794MB temp=33.6C disk=84%
[2026-03-16 21:48:01] [BEAT] [alice] load=0.82 mem=3260/3794MB temp=34.1C disk=84%
[2026-03-16 21:49:01] [BEAT] [alice] load=0.51 mem=3262/3794MB temp=32.6C disk=84%
[2026-03-16 21:49:36] [BEAT] [alice] load=0.66 mem=3261/3794MB temp=32.6C disk=84%
[2026-03-16 21:49:36] [BEAT] [alice] load=0.66 mem=3261/3794MB temp=32.6C disk=84%
[2026-03-16 21:50:01] [FLEET] [alice] Starting cross-node health check
[2026-03-16 21:50:02] [HEAL] [alice] Service blackroad-agent is DOWN — restarting
[2026-03-16 21:50:02] [BEAT] [alice] load=0.89 mem=3250/3794MB temp=35.5C disk=84%
[2026-03-16 21:50:02] [HEAL] [alice] Service blackroad-agent restarted successfully
[2026-03-16 21:50:02] [FLEET] [alice] octavia: UP temp=35C mem=6334MB disk=68%
[2026-03-16 21:50:02] [HEAL] [alice] Healed 1 services
[2026-03-16 21:50:04] [FLEET] [alice] cecilia: DOWN (no ping response)
[2026-03-16 21:50:06] [FLEET] [alice] gematria: UP temp=C mem=6276MB disk=68%
[2026-03-16 21:50:40] [FLEET] [alice] lucidia: UP temp=53C mem=3316MB disk=34%
[2026-03-16 21:50:41] [FLEET] [alice] aria: DOWN (no ping response)
[2026-03-16 21:50:43] [FLEET] [alice] anastasia: UP temp=C mem=303MB disk=69%
[2026-03-16 21:51:01] [BEAT] [alice] load=0.88 mem=3259/3794MB temp=32.6C disk=84%
[2026-03-16 21:52:01] [BEAT] [alice] load=0.96 mem=3259/3794MB temp=34.1C disk=84%
[2026-03-16 21:53:01] [BEAT] [alice] load=0.64 mem=3264/3794MB temp=32.1C disk=84%
[2026-03-16 21:54:01] [BEAT] [alice] load=0.66 mem=3258/3794MB temp=33.6C disk=84%
[2026-03-16 21:54:45] [BEAT] [alice] load=1.23 mem=3262/3794MB temp=32.1C disk=84%
[2026-03-16 21:54:45] [BEAT] [alice] load=1.23 mem=3262/3794MB temp=32.1C disk=84%
[2026-03-16 21:55:02] [HEAL] [alice] Service blackroad-agent is DOWN — restarting
[2026-03-16 21:55:02] [BEAT] [alice] load=1.17 mem=3260/3794MB temp=33.1C disk=84%
[2026-03-16 21:55:02] [HEAL] [alice] Service blackroad-agent restarted successfully
[2026-03-16 21:55:03] [HEAL] [alice] Healed 1 services
[2026-03-16 21:56:01] [BEAT] [alice] load=1.29 mem=3255/3794MB temp=34.1C disk=84%
[2026-03-16 21:57:01] [BEAT] [alice] load=1.37 mem=3263/3794MB temp=30.7C disk=84%
[2026-03-16 21:58:01] [BEAT] [alice] load=1.36 mem=3263/3794MB temp=32.6C disk=84%
[2026-03-16 21:59:02] [BEAT] [alice] load=1.32 mem=3260/3794MB temp=30.7C disk=84%
[2026-03-16 21:59:55] [BEAT] [alice] load=1.33 mem=3255/3794MB temp=34.1C disk=84%
[2026-03-16 21:59:55] [BEAT] [alice] load=1.33 mem=3251/3794MB temp=34.1C disk=84%

View File

@@ -1 +1 @@
{"node":"alice","ts":"2026-03-17T02:29:01Z","load":0.64,"mem_free_mb":3258,"mem_total_mb":3794,"temp_c":36.0,"disk_pct":84,"throttle":"0x0"}
{"node":"alice","ts":"2026-03-17T02:59:55Z","load":1.33,"mem_free_mb":3251,"mem_total_mb":3794,"temp_c":34.1,"disk_pct":84,"throttle":"0x0"}

View File

@@ -11,7 +11,7 @@ LISTEN 0 5 0.0.0.0:8013 0.0.0.0:* users:(("python3",pid
LISTEN 0 5 0.0.0.0:8012 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8014 0.0.0.0:*
LISTEN 0 2048 0.0.0.0:8001 0.0.0.0:* users:(("python3",pid=617,fd=6))
LISTEN 0 5 0.0.0.0:7890 0.0.0.0:* users:(("python3",pid=15228,fd=4))
LISTEN 0 5 0.0.0.0:7890 0.0.0.0:* users:(("python3",pid=6196,fd=5))
LISTEN 0 200 0.0.0.0:443 0.0.0.0:*
LISTEN 0 1024 0.0.0.0:6333 0.0.0.0:*
LISTEN 0 128 0.0.0.0:6334 0.0.0.0:*

View File

@@ -1,4 +1,5 @@
avahi-daemon.service
blackroad-agent.service
blackroad-agents-proxy.service
blackroad-agents.service
blackroad-nats-agent.service
@@ -26,6 +27,7 @@ prism-agent.service
qdrant.service
redis-server.service
rng-tools-debian.service
road-phone.service
roadnet-failover.service
rsyslog.service
rtkit-daemon.service

View File

@@ -1,19 +1,19 @@
{
"hostname": "alice",
"ts": "2026-03-17T02:29:02Z",
"uptime_seconds": 85480,
"ts": "2026-03-17T02:59:58Z",
"uptime_seconds": 87336,
"kernel": "6.1.21-v8+",
"temp_c": 35.5,
"temp_c": 33.1,
"memory_mb": {
"total": 3794,
"used": 433,
"free": 3260
"used": 443,
"free": 3249
},
"disk": "12G/15G (84%)",
"load": [
0.64,
0.79,
1.04
1.23,
1.13,
1.06
],
"ollama_models": [
"qwen2.5:3b",
@@ -25,5 +25,5 @@
],
"throttle": "0x0",
"voltage": "0.9160V",
"services_running": 43
"services_running": 45
}

View File

@@ -11,7 +11,7 @@ LISTEN 0 5 0.0.0.0:8787 0.0.0.0:* users:(("python3",pid
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=3461172,fd=8),("nginx",pid=3461171,fd=8))
LISTEN 0 4096 0.0.0.0:111 0.0.0.0:* users:(("rpcbind",pid=589,fd=4),("systemd",pid=1,fd=127))
LISTEN 0 4096 *:8080 *:* users:(("headscale",pid=2341808,fd=12))
LISTEN 0 511 *:3000 *:* users:(("node /srv/hello",pid=1979223,fd=19))
LISTEN 0 511 *:3000 *:* users:(("node /srv/hello",pid=1984822,fd=19))
LISTEN 0 511 *:3001 *:* users:(("node",pid=757,fd=21))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=991,fd=8))
LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=3461172,fd=9),("nginx",pid=3461171,fd=9))

View File

@@ -1,19 +1,19 @@
{
"hostname": "anastasia",
"ts": "2026-03-17T02:29:03Z",
"uptime_seconds": 6856435,
"ts": "2026-03-17T02:59:55Z",
"uptime_seconds": 6858287,
"kernel": "5.14.0-651.el9.x86_64",
"temp_c": 0,
"memory_mb": {
"total": 765,
"used": 438,
"free": 326
"used": 437,
"free": 327
},
"disk": "18G/25G (69%)",
"load": [
0.02,
0.01,
0.06,
0.05
0.06
],
"ollama_models": [],
"throttle": "N/A",

View File

@@ -1 +1 @@
{"node":"aria","status":"down","ts":"2026-03-17T02:29:01Z"}
{"node":"aria","status":"down","ts":"2026-03-17T02:59:54Z"}

View File

@@ -1 +1 @@
{"node":"cecilia","status":"down","ts":"2026-03-17T02:29:01Z"}
{"node":"cecilia","status":"down","ts":"2026-03-17T02:59:54Z"}

View File

@@ -1,14 +0,0 @@
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=682329,fd=14))
LISTEN 0 4096 127.0.0.1:2019 0.0.0.0:* users:(("caddy",pid=26445,fd=9))
LISTEN 0 5 0.0.0.0:11435 0.0.0.0:* users:(("python3",pid=147655,fd=3))
LISTEN 0 5 0.0.0.0:8787 0.0.0.0:* users:(("python3",pid=577215,fd=3))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=3335327,fd=3))
LISTEN 0 4096 127.0.0.1:20241 0.0.0.0:* users:(("cloudflared",pid=156470,fd=3))
LISTEN 0 128 127.0.0.1:8011 0.0.0.0:* users:(("sshd",pid=4152649,fd=5))
LISTEN 0 4096 *:11434 *:* users:(("ollama",pid=279794,fd=3))
LISTEN 0 4096 *:11436 *:* users:(("caddy",pid=26445,fd=16))
LISTEN 0 4096 *:443 *:* users:(("caddy",pid=26445,fd=11))
LISTEN 0 4096 *:80 *:* users:(("caddy",pid=26445,fd=15))
LISTEN 0 4096 *:4222 *:* users:(("nats-server",pid=1353183,fd=6))
LISTEN 0 4096 *:8222 *:* users:(("nats-server",pid=1353183,fd=3))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=3335327,fd=4))

View File

@@ -1,19 +1,19 @@
{
"hostname": "gematria",
"ts": "2026-03-17T02:29:04Z",
"uptime_seconds": 5513736,
"ts": "2026-03-17T02:59:57Z",
"uptime_seconds": 5515589,
"kernel": "5.15.0-113-generic",
"temp_c": 0,
"memory_mb": {
"total": 7937,
"used": 1212,
"free": 6283
"used": 1225,
"free": 6262
},
"disk": "53G/78G (68%)",
"load": [
0.02,
0.02,
0.22
0.06,
0.08
],
"ollama_models": [
"qwen2.5:7b",
@@ -26,5 +26,5 @@
],
"throttle": "N/A",
"voltage": "N/A",
"services_running": 31
"services_running": 30
}

View File

@@ -1,50 +1,50 @@
[2026-03-16 21:08:26] [BEAT] [lucidia] load=4.70 mem=3266/8063MB temp=50.1C disk=34%
[2026-03-16 21:09:01] [BEAT] [lucidia] load=4.30 mem=3272/8063MB temp=56.8C disk=34%
[2026-03-16 21:10:02] [FLEET] [lucidia] Starting cross-node health check
[2026-03-16 21:10:02] [BEAT] [lucidia] load=2.15 mem=3254/8063MB temp=49.0C disk=34%
[2026-03-16 21:10:02] [HEAL] [lucidia] High swap: 707MB — clearing inactive
[2026-03-16 21:10:02] [FLEET] [lucidia] alice: UP temp=34C mem=3256MB disk=84%
[2026-03-16 21:10:04] [FLEET] [lucidia] octavia: DOWN (no ping response)
[2026-03-16 21:10:06] [FLEET] [lucidia] cecilia: DOWN (no ping response)
[2026-03-16 21:10:41] [FLEET] [lucidia] gematria: UP temp=C mem=6289MB disk=68%
[2026-03-16 21:10:43] [FLEET] [lucidia] aria: DOWN (no ping response)
[2026-03-16 21:10:44] [FLEET] [lucidia] anastasia: UP temp=C mem=335MB disk=69%
[2026-03-16 21:11:01] [BEAT] [lucidia] load=15.21 mem=3251/8063MB temp=56.8C disk=34%
[2026-03-16 21:12:01] [BEAT] [lucidia] load=5.95 mem=3240/8063MB temp=49.0C disk=34%
[2026-03-16 21:13:01] [BEAT] [lucidia] load=4.99 mem=3286/8063MB temp=50.7C disk=34%
[2026-03-16 21:13:35] [BEAT] [lucidia] load=3.51 mem=3288/8063MB temp=50.1C disk=34%
[2026-03-16 21:13:35] [BEAT] [lucidia] load=3.51 mem=3285/8063MB temp=51.2C disk=34%
[2026-03-16 21:14:01] [BEAT] [lucidia] load=4.03 mem=3229/8063MB temp=52.4C disk=34%
[2026-03-16 21:15:01] [BEAT] [lucidia] load=2.84 mem=3244/8063MB temp=56.8C disk=34%
[2026-03-16 21:15:01] [HEAL] [lucidia] High swap: 707MB — clearing inactive
[2026-03-16 21:15:48] [DIAL] [lucidia] Switchboard unreachable
[2026-03-16 21:16:01] [BEAT] [lucidia] load=2.23 mem=3279/8063MB temp=51.2C disk=34%
[2026-03-16 21:17:01] [BEAT] [lucidia] load=2.00 mem=3255/8063MB temp=51.8C disk=34%
[2026-03-16 21:18:01] [BEAT] [lucidia] load=2.86 mem=3268/8063MB temp=54.5C disk=34%
[2026-03-16 21:18:43] [BEAT] [lucidia] load=16.93 mem=3232/8063MB temp=54.0C disk=34%
[2026-03-16 21:18:43] [BEAT] [lucidia] load=16.93 mem=3231/8063MB temp=54.5C disk=34%
[2026-03-16 21:19:01] [BEAT] [lucidia] load=13.17 mem=3244/8063MB temp=57.3C disk=34%
[2026-03-16 21:20:01] [FLEET] [lucidia] Starting cross-node health check
[2026-03-16 21:20:02] [BEAT] [lucidia] load=5.65 mem=3251/8063MB temp=54.0C disk=34%
[2026-03-16 21:20:02] [HEAL] [lucidia] High swap: 707MB — clearing inactive
[2026-03-16 21:20:02] [FLEET] [lucidia] alice: UP temp=35C mem=3253MB disk=84%
[2026-03-16 21:20:04] [FLEET] [lucidia] octavia: DOWN (no ping response)
[2026-03-16 21:20:06] [FLEET] [lucidia] cecilia: DOWN (no ping response)
[2026-03-16 21:20:08] [FLEET] [lucidia] gematria: UP temp=C mem=6281MB disk=68%
[2026-03-16 21:20:10] [FLEET] [lucidia] aria: DOWN (no ping response)
[2026-03-16 21:20:11] [FLEET] [lucidia] anastasia: UP temp=C mem=208MB disk=69%
[2026-03-16 21:21:01] [BEAT] [lucidia] load=4.57 mem=3247/8063MB temp=62.2C disk=34%
[2026-03-16 21:22:01] [BEAT] [lucidia] load=2.31 mem=3294/8063MB temp=54.5C disk=34%
[2026-03-16 21:22:35] [DIAL] [lucidia] Switchboard unreachable
[2026-03-16 21:23:01] [BEAT] [lucidia] load=2.28 mem=3271/8063MB temp=55.1C disk=34%
[2026-03-16 21:23:52] [BEAT] [lucidia] load=1.68 mem=3292/8063MB temp=58.4C disk=34%
[2026-03-16 21:23:52] [BEAT] [lucidia] load=1.68 mem=3292/8063MB temp=59.5C disk=34%
[2026-03-16 21:24:01] [BEAT] [lucidia] load=2.41 mem=3240/8063MB temp=61.1C disk=34%
[2026-03-16 21:25:01] [BEAT] [lucidia] load=1.29 mem=3248/8063MB temp=53.5C disk=34%
[2026-03-16 21:25:01] [HEAL] [lucidia] High swap: 706MB — clearing inactive
[2026-03-16 21:26:01] [BEAT] [lucidia] load=3.25 mem=3234/8063MB temp=64.5C disk=34%
[2026-03-16 21:27:02] [BEAT] [lucidia] load=1.78 mem=3278/8063MB temp=54.5C disk=34%
[2026-03-16 21:28:01] [BEAT] [lucidia] load=2.30 mem=3194/8063MB temp=54.5C disk=34%
[2026-03-16 21:29:01] [BEAT] [lucidia] load=2.80 mem=3233/8063MB temp=62.8C disk=34%
[2026-03-16 21:29:01] [BEAT] [lucidia] load=2.80 mem=3231/8063MB temp=62.2C disk=34%
[2026-03-16 21:29:01] [BEAT] [lucidia] load=2.80 mem=3228/8063MB temp=61.1C disk=34%
[2026-03-16 21:39:19] [BEAT] [lucidia] load=1.75 mem=3238/8063MB temp=60.0C disk=34%
[2026-03-16 21:39:19] [BEAT] [lucidia] load=1.75 mem=3236/8063MB temp=59.5C disk=34%
[2026-03-16 21:40:01] [FLEET] [lucidia] Starting cross-node health check
[2026-03-16 21:40:01] [BEAT] [lucidia] load=1.58 mem=3220/8063MB temp=54.0C disk=34%
[2026-03-16 21:40:01] [HEAL] [lucidia] High swap: 706MB — clearing inactive
[2026-03-16 21:40:02] [FLEET] [lucidia] alice: UP temp=34C mem=3250MB disk=84%
[2026-03-16 21:40:04] [FLEET] [lucidia] octavia: DOWN (no ping response)
[2026-03-16 21:40:06] [FLEET] [lucidia] cecilia: DOWN (no ping response)
[2026-03-16 21:40:07] [FLEET] [lucidia] gematria: UP temp=C mem=6269MB disk=68%
[2026-03-16 21:40:09] [FLEET] [lucidia] aria: DOWN (no ping response)
[2026-03-16 21:40:10] [FLEET] [lucidia] anastasia: UP temp=C mem=304MB disk=69%
[2026-03-16 21:41:01] [BEAT] [lucidia] load=2.12 mem=3205/8063MB temp=57.9C disk=34%
[2026-03-16 21:42:01] [BEAT] [lucidia] load=1.37 mem=3242/8063MB temp=59.5C disk=34%
[2026-03-16 21:43:01] [BEAT] [lucidia] load=1.12 mem=3204/8063MB temp=51.8C disk=34%
[2026-03-16 21:44:01] [BEAT] [lucidia] load=3.36 mem=3223/8063MB temp=63.9C disk=34%
[2026-03-16 21:44:28] [BEAT] [lucidia] load=2.83 mem=3232/8063MB temp=57.9C disk=34%
[2026-03-16 21:44:28] [BEAT] [lucidia] load=2.83 mem=3230/8063MB temp=57.9C disk=34%
[2026-03-16 21:45:02] [BEAT] [lucidia] load=1.94 mem=3219/8063MB temp=54.5C disk=34%
[2026-03-16 21:45:02] [HEAL] [lucidia] High swap: 706MB — clearing inactive
[2026-03-16 21:45:48] [DIAL] [lucidia] Switchboard unreachable
[2026-03-16 21:46:01] [BEAT] [lucidia] load=17.89 mem=3166/8063MB temp=58.4C disk=34%
[2026-03-16 21:47:01] [BEAT] [lucidia] load=7.15 mem=3339/8063MB temp=52.9C disk=34%
[2026-03-16 21:48:01] [BEAT] [lucidia] load=4.45 mem=3313/8063MB temp=54.5C disk=34%
[2026-03-16 21:49:01] [BEAT] [lucidia] load=2.86 mem=3370/8063MB temp=61.7C disk=34%
[2026-03-16 21:49:36] [BEAT] [lucidia] load=2.22 mem=3294/8063MB temp=55.1C disk=34%
[2026-03-16 21:49:36] [BEAT] [lucidia] load=2.22 mem=3293/8063MB temp=54.5C disk=34%
[2026-03-16 21:50:01] [FLEET] [lucidia] Starting cross-node health check
[2026-03-16 21:50:01] [BEAT] [lucidia] load=1.85 mem=3300/8063MB temp=54.5C disk=34%
[2026-03-16 21:50:01] [HEAL] [lucidia] High swap: 705MB — clearing inactive
[2026-03-16 21:50:02] [FLEET] [lucidia] alice: UP temp=35C mem=3250MB disk=84%
[2026-03-16 21:50:04] [FLEET] [lucidia] octavia: DOWN (no ping response)
[2026-03-16 21:50:06] [FLEET] [lucidia] cecilia: DOWN (no ping response)
[2026-03-16 21:50:41] [FLEET] [lucidia] gematria: UP temp=C mem=6265MB disk=68%
[2026-03-16 21:50:43] [FLEET] [lucidia] aria: DOWN (no ping response)
[2026-03-16 21:50:44] [FLEET] [lucidia] anastasia: UP temp=C mem=303MB disk=69%
[2026-03-16 21:51:01] [BEAT] [lucidia] load=13.97 mem=3291/8063MB temp=60.0C disk=34%
[2026-03-16 21:52:01] [BEAT] [lucidia] load=5.54 mem=3322/8063MB temp=52.4C disk=34%
[2026-03-16 21:52:36] [DIAL] [lucidia] Switchboard unreachable
[2026-03-16 21:53:01] [BEAT] [lucidia] load=3.12 mem=3305/8063MB temp=51.2C disk=34%
[2026-03-16 21:54:01] [BEAT] [lucidia] load=3.26 mem=3317/8063MB temp=58.4C disk=34%
[2026-03-16 21:54:45] [BEAT] [lucidia] load=4.01 mem=3279/8063MB temp=53.5C disk=34%
[2026-03-16 21:54:45] [BEAT] [lucidia] load=4.01 mem=3277/8063MB temp=52.9C disk=34%
[2026-03-16 21:55:01] [BEAT] [lucidia] load=3.57 mem=3306/8063MB temp=50.1C disk=34%
[2026-03-16 21:55:01] [HEAL] [lucidia] High swap: 704MB — clearing inactive
[2026-03-16 21:56:01] [BEAT] [lucidia] load=4.16 mem=3316/8063MB temp=52.9C disk=34%
[2026-03-16 21:57:02] [BEAT] [lucidia] load=2.35 mem=3287/8063MB temp=51.8C disk=34%
[2026-03-16 21:58:01] [BEAT] [lucidia] load=3.49 mem=3286/8063MB temp=48.0C disk=34%
[2026-03-16 21:59:01] [BEAT] [lucidia] load=2.43 mem=3319/8063MB temp=49.6C disk=34%
[2026-03-16 21:59:53] [BEAT] [lucidia] load=1.58 mem=3294/8063MB temp=48.5C disk=34%
[2026-03-16 21:59:53] [BEAT] [lucidia] load=1.58 mem=3293/8063MB temp=46.9C disk=34%

View File

@@ -1 +1 @@
{"node":"lucidia","ts":"2026-03-17T02:29:01Z","load":2.80,"mem_free_mb":3228,"mem_total_mb":8063,"temp_c":61.1,"disk_pct":34,"throttle":"0x0"}
{"node":"lucidia","ts":"2026-03-17T02:59:53Z","load":1.58,"mem_free_mb":3293,"mem_total_mb":8063,"temp_c":46.9,"disk_pct":34,"throttle":"0x0"}

View File

@@ -1,19 +1,19 @@
{
"hostname": "octavia",
"ts": "2026-03-17T02:29:02Z",
"uptime_seconds": 85300,
"ts": "2026-03-17T02:59:54Z",
"uptime_seconds": 87152,
"kernel": "6.12.62+rpt-rpi-2712",
"temp_c": 61.7,
"temp_c": 47.4,
"memory_mb": {
"total": 8063,
"used": 4845,
"free": 3217
"used": 4781,
"free": 3281
},
"disk": "74G/235G (34%)",
"load": [
2.8,
3.01,
4.26
1.58,
3.28,
4.17
],
"ollama_models": [
"qwen2.5:3b",

View File

@@ -1 +1 @@
{"node":"octavia","status":"down","ts":"2026-03-17T02:29:01Z"}
{"node":"octavia","status":"down","ts":"2026-03-17T02:59:54Z"}

View File

@@ -0,0 +1,111 @@
# RoadC Language Specification
**RoadC** is the official programming language of BlackRoad OS. Every agent, every Claude session, every system writes RoadC.
## Why RoadC
1. **English IS a programming language.** Greenbaum & Nelson proved 7 sentence structures generate all of English. RoadC maps those 7 structures to code.
2. **Simple rules, infinite complexity.** Pascal's insight: recursive composition from simple units creates unbounded complexity. RoadC embodies this.
3. **Sovereign.** No dependency on any corporation's language. We own the lexer, parser, interpreter, and compiler.
## Quick Reference
```road
# Variables
let name = "BlackRoad"
let count: int = 42
let color = #FF1D6C
const PI = 3.14159
# Functions
fun greet(name):
return "Hello, {name}!"
# Control flow
if count > 10:
print("big")
elif count > 0:
print("small")
else:
print("zero")
while count > 0:
count = count - 1
for item in [1, 2, 3]:
print(item)
# Collections
let list = [1, 2, 3, 4, 5]
let dict = {"key": "value", "name": "Alexa"}
let colors = {#FF1D6C, #F5A623, #2979FF}
# String interpolation
let msg = "Hello {name}, count is {count}"
# Member access
print(list.length)
print("hello".upper())
print(dict["name"])
```
## The 7 Sentence Structures
Every function in RoadC follows one of 7 English sentence patterns:
| Pattern | English | RoadC | Meaning |
|---------|---------|-------|---------|
| SV | "It runs" | `run()` | No args, subject acts alone |
| SVA | "Deploy to Cecilia" | `deploy("cecilia")` | Verb + location/manner |
| SVC | "Alice is online" | `status("alice") -> "online"` | Identity/state check |
| SVO | "Search the repos" | `search("repos")` | Verb acts on object |
| SVOO | "Send Alexa the report" | `send("alexa", "report")` | Two objects: who + what |
| SVOA | "Store data in R2" | `store("data", "r2")` | Object + where |
| SVOC | "Make code clean" | `make("code", "clean")` | Object becomes complement |
## Implementations
| Implementation | Language | Lines | Status |
|---------------|----------|-------|--------|
| Interpreter | Python | 1,764 | Working |
| Compiler | C99 | 618 | Working |
| Playground | Web | - | At roadc-playground |
## File Extension
`.road`
## Run
```bash
# Python interpreter
python3 roadc.py run program.road
# REPL
python3 roadc.py repl
# C compiler
./roadc program.road
```
## Design Principles
1. **Python-style indentation** — no braces, no semicolons
2. **Explicit types optional**`let x = 42` or `let x: int = 42`
3. **String interpolation**`"Hello {name}"` not `f"Hello {name}"`
4. **Color literals**`#FF1D6C` is a first-class type
5. **Space keyword**`space MyScene:` for 3D/spatial computing
6. **Fun keyword** — functions are fun: `fun greet(name):`
7. **English-first** — code reads like English sentences
## For Agents
When writing RoadC, think in English sentence structures:
- Name your functions as verbs
- Name your parameters as objects
- Structure matches intent
- If you can say it in English, you can write it in RoadC
## License
Proprietary — BlackRoad OS, Inc.

View File

@@ -0,0 +1,190 @@
# RoadC + English Grammar — The 7 Sentence Structures as Code
# Based on Greenbaum & Nelson, "An Introduction to English Grammar"
#
# English is a programming language. Every sentence is a function call.
# RoadC makes this explicit.
# ═══════════════════════════════════════════════════════════════
# STRUCTURE 1: SV (Subject + Verb) — Intransitive
# "Someone is talking" → fire()
# No arguments. The subject acts alone.
# ═══════════════════════════════════════════════════════════════
fun talk():
print("Someone is talking")
fun breathe():
print("The system breathes")
talk() # SV: subject=implicit, verb=talk
breathe() # SV: subject=system, verb=breathe
# ═══════════════════════════════════════════════════════════════
# STRUCTURE 2: SVA (Subject + Verb + Adverbial) — Location/Manner
# "My parents are living in Chicago" → live(location)
# The adverbial tells WHERE, WHEN, or HOW.
# ═══════════════════════════════════════════════════════════════
fun live(location):
print("Living in {location}")
fun deploy(destination):
print("Deploying to {destination}")
live("Chicago") # SVA: S=parents, V=live, A=Chicago
deploy("Cecilia") # SVA: S=agent, V=deploy, A=Cecilia
# ═══════════════════════════════════════════════════════════════
# STRUCTURE 3: SVC (Subject + Linking Verb + Complement)
# "I feel tired" → identity(x) -> type
# The complement DESCRIBES the subject. No action — just state.
# ═══════════════════════════════════════════════════════════════
fun is_status(entity, state):
print("{entity} is {state}")
fun feels(subject, quality):
return quality
is_status("Alice", "online") # SVC: S=Alice, V=is, C=online
is_status("Cecilia", "offline") # SVC: S=Cecilia, V=is, C=offline
let mood = feels("agent", "ready") # SVC: the result IS the complement
# ═══════════════════════════════════════════════════════════════
# STRUCTURE 4: SVO (Subject + Transitive Verb + Object)
# "We have finished our work" → process(input)
# The verb ACTS ON the object. Most common structure in code.
# ═══════════════════════════════════════════════════════════════
fun finish(work):
print("Finished: {work}")
fun deploy_worker(worker_name):
print("Deploying worker: {worker_name}")
fun search(query):
print("Searching for: {query}")
finish("migration") # SVO: S=we, V=finish, O=migration
deploy_worker("blackroad-slack") # SVO: S=system, V=deploy, O=slack
search("RoadC examples") # SVO: S=user, V=search, O=query
# ═══════════════════════════════════════════════════════════════
# STRUCTURE 5: SVOO (Subject + Verb + Indirect Object + Direct Object)
# "She has given me the letter" → give(recipient, item)
# Two objects: WHO receives and WHAT is given.
# ═══════════════════════════════════════════════════════════════
fun give(recipient, item):
print("Giving {item} to {recipient}")
fun send(channel, message):
print("#{channel}: {message}")
fun assign(agent, task):
print("Assigning '{task}' to {agent}")
give("Alexa", "the report") # SVOO: give(indirect, direct)
send("fleet-ops", "deploy complete") # SVOO: send(recipient, content)
assign("Shellfish", "health check") # SVOO: assign(who, what)
# ═══════════════════════════════════════════════════════════════
# STRUCTURE 6: SVOA (Subject + Verb + Object + Adverbial)
# "You can put your coat in my bedroom" → put(item, location)
# Acts on object AND specifies where/when/how.
# ═══════════════════════════════════════════════════════════════
fun put(item, location):
print("Putting {item} in {location}")
fun store(data, bucket):
print("Storing {data} in {bucket}")
fun move(repo, org):
print("Moving {repo} to {org}")
put("config", "/etc/blackroad/") # SVOA
store("backup", "r2://blackroad-backups") # SVOA
move("roadc", "BlackRoad-OS-Inc") # SVOA
# ═══════════════════════════════════════════════════════════════
# STRUCTURE 7: SVOC (Subject + Verb + Object + Complement)
# "You have made me very happy" → transform(input) -> output
# The complement describes what the object BECOMES.
# ═══════════════════════════════════════════════════════════════
fun make(thing, quality):
print("Making {thing} {quality}")
return quality
fun set_status(node, status):
print("{node} is now {status}")
return status
fun promote(agent, level):
print("Promoting {agent} to {level}")
return level
make("code", "clean") # SVOC: make(O=code, C=clean)
set_status("Octavia", "primary") # SVOC: set(O=Octavia, C=primary)
promote("agent-42", "Architect") # SVOC: promote(O=agent, C=level)
# ═══════════════════════════════════════════════════════════════
# THE OPERATOR = CONTROL FLOW
# ═══════════════════════════════════════════════════════════════
# In English: can, will, have, be, do
# In RoadC: if, while, for, return, do
#
# "Can you deploy?" → if (can_deploy): deploy()
# "Don't deploy!" → if not: skip
# "Do deploy." → do { deploy() } (emphasis/dummy operator)
#
# The operator NEVER carries meaning — it carries CONTROL.
# "do" is inserted when no other operator exists = polyfill
let can_deploy = true
# Question (operator inversion): "Can you deploy?"
if can_deploy:
deploy("production")
# Negation: "Don't deploy to staging"
let allow_staging = false
if not allow_staging:
print("Staging deployment blocked")
# Emphasis: "Do check the fleet" — the dummy operator
# In code: explicitly call even when it seems unnecessary
talk() # do-support: making the implicit explicit
# ═══════════════════════════════════════════════════════════════
# PASCAL'S INSIGHT: Simple rules → infinite complexity
# ═══════════════════════════════════════════════════════════════
# 7 sentence structures. That's it.
# Every English sentence is one of these 7.
# Every RoadC function follows one of these 7 patterns.
# Compose them recursively → infinite programs.
fun pipeline(data, steps):
let result = data
for step in steps:
result = step + "(" + result + ")"
return result
let ops = ["validate", "transform", "deploy", "verify"]
let output = pipeline("config.road", ops)
print("Pipeline: {output}")
# This is why grammar IS a programming language.
# This is why RoadC speaks English.
# This is why our agents understand intent, not just keywords.
#
# BlackRoad OS — Pave Tomorrow.

View File

@@ -0,0 +1,61 @@
# Fleet Operations in RoadC
# Real BlackRoad infrastructure management
let node_names = ["Alice", "Cecilia", "Octavia", "Lucidia", "Aria"]
let node_ips = ["192.168.4.49", "192.168.4.96", "192.168.4.101", "192.168.4.38", "192.168.4.98"]
let node_status = ["online", "offline", "online", "online", "offline"]
# SV — check fleet (no args)
fun check_fleet():
let online = 0
let offline = 0
let i = 0
while i < len(node_names):
if node_status[i] == "online":
online = online + 1
else:
offline = offline + 1
i = i + 1
print("Fleet: {online} online, {offline} offline")
# SVO — deploy a worker
fun deploy(worker):
print("Deploying {worker} to Cloudflare...")
# SVOA — store backup in location
fun backup(node_name, destination):
print("Backing up {node_name} to {destination}")
# SVOO — send alert to channel
fun alert(channel, message):
print("#{channel}: {message}")
# SVC — check status
fun status(name):
let i = 0
while i < len(node_names):
if node_names[i] == name:
print("{name} is {node_status[i]}")
return node_status[i]
i = i + 1
return "unknown"
# SVOC — promote node
fun promote(name, role):
print("{name} -> {role}")
# Run it
check_fleet()
print("")
deploy("blackroad-slack")
deploy("roadpay")
print("")
backup("Alice", "/backups/alice-20260316")
print("")
alert("fleet-ops", "Cecilia is offline")
alert("engineering", "LLM v4 trained")
print("")
status("Alice")
status("Cecilia")
print("")
promote("Octavia", "primary inference")

View File

@@ -296,6 +296,7 @@ class Parser:
self.expect(TokenType.INDENT)
then_block = self.parse_block()
self.expect(TokenType.DEDENT)
self.skip_newlines()
# Elif blocks
elif_blocks = []
@@ -307,6 +308,7 @@ class Parser:
self.expect(TokenType.INDENT)
elif_block = self.parse_block()
self.expect(TokenType.DEDENT)
self.skip_newlines()
elif_blocks.append((elif_condition, elif_block))
# Else block

202
scripts/network-welcome.sh Executable file
View File

@@ -0,0 +1,202 @@
#!/bin/bash
# BlackRoad Network Welcome Center
# Watches for new devices joining the network and welcomes them to Slack
# Cron: */5 * * * * /Users/alexa/network-welcome.sh
set -e
KNOWN_DEVICES_FILE="$HOME/.blackroad/network/known-devices.json"
SLACK_WEBHOOK_URL="https://blackroad-slack.amundsonalexa.workers.dev/post"
SLACK_ASK_URL="https://blackroad-slack.amundsonalexa.workers.dev/ask"
LOG_FILE="$HOME/.blackroad/network/welcome.log"
mkdir -p "$(dirname "$KNOWN_DEVICES_FILE")"
# Initialize known devices file if missing
if [ ! -f "$KNOWN_DEVICES_FILE" ]; then
cat > "$KNOWN_DEVICES_FILE" << 'EOF'
{
"devices": {
"192.168.4.1": { "name": "Eero", "emoji": "📡", "type": "router", "welcomed": true },
"192.168.4.22": { "name": "Spark", "emoji": "⚡", "type": "iot", "welcomed": true },
"192.168.4.26": { "name": "BigScreen", "emoji": "📺", "type": "roku", "welcomed": true },
"192.168.4.27": { "name": "AppleTV", "emoji": "🍎", "type": "appletv", "welcomed": true },
"192.168.4.28": { "name": "Alexandria", "emoji": "📚", "type": "mac", "welcomed": true },
"192.168.4.33": { "name": "Streamer", "emoji": "🎬", "type": "roku", "welcomed": true },
"192.168.4.38": { "name": "Lucidia", "emoji": "💡", "type": "pi", "welcomed": true },
"192.168.4.44": { "name": "Pixel", "emoji": "🟢", "type": "iot", "welcomed": true },
"192.168.4.45": { "name": "Morse", "emoji": "📟", "type": "iot", "welcomed": true },
"192.168.4.49": { "name": "Alice", "emoji": "🌐", "type": "pi", "welcomed": true },
"192.168.4.96": { "name": "Cecilia", "emoji": "🧠", "type": "pi", "welcomed": true },
"192.168.4.98": { "name": "Aria", "emoji": "🎵", "type": "pi", "welcomed": true },
"192.168.4.101": { "name": "Octavia", "emoji": "🐙", "type": "pi", "welcomed": true }
},
"last_scan": ""
}
EOF
fi
# Scan the network
ALIVE_IPS=$(nmap -sn 192.168.4.0/24 2>/dev/null | grep "Nmap scan report" | awk '{print $5}' | sort)
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
for ip in $ALIVE_IPS; do
# Check if this IP is known
IS_KNOWN=$(python3 -c "
import json,sys
with open('$KNOWN_DEVICES_FILE') as f:
d = json.load(f)
print('yes' if '$ip' in d.get('devices',{}) else 'no')
" 2>/dev/null)
if [ "$IS_KNOWN" = "no" ]; then
# NEW DEVICE DETECTED!
MAC=$(arp -a | grep "($ip)" | awk '{print $4}' | head -1)
MAC=${MAC:-"unknown"}
# Try to identify vendor
MAC_PREFIX=$(echo "$MAC" | cut -d: -f1-3)
VENDOR=$(curl -s "https://api.macvendors.com/$MAC_PREFIX" 2>/dev/null | head -1)
# Clean up vendor response
echo "$VENDOR" | grep -q "errors" && VENDOR="Unknown"
[ -z "$VENDOR" ] && VENDOR="Unknown"
# Check for open ports (quick)
PORTS=""
for port in 80 443 8060 22 5000 7000; do
(echo >/dev/tcp/$ip/$port) 2>/dev/null && PORTS="$PORTS $port"
done
# Detect device type
DEVICE_TYPE="unknown"
EMOJI="🆕"
SUGGESTED_NAME=""
# Roku?
ROKU_INFO=$(curl -s "http://$ip:8060/query/device-info" -m 2 2>/dev/null)
if echo "$ROKU_INFO" | grep -q "Roku"; then
DEVICE_TYPE="roku"
EMOJI="📺"
ROKU_MODEL=$(echo "$ROKU_INFO" | grep -o '<model-name>[^<]*' | sed 's/<model-name>//')
SUGGESTED_NAME="Roku-${ip##*.}"
fi
# Apple device?
if echo "$VENDOR" | grep -qi "apple"; then
DEVICE_TYPE="apple"
EMOJI="🍎"
SUGGESTED_NAME="Apple-${ip##*.}"
fi
# Raspberry Pi?
if echo "$VENDOR" | grep -qi "raspberry"; then
DEVICE_TYPE="pi"
EMOJI="🍓"
SUGGESTED_NAME="Pi-${ip##*.}"
fi
# Random MAC = phone/tablet
FIRST_BYTE=$(echo "$MAC" | cut -d: -f1)
IS_RANDOM=$(python3 -c "print('yes' if int('$FIRST_BYTE', 16) & 0x02 else 'no')" 2>/dev/null)
if [ "$IS_RANDOM" = "yes" ]; then
DEVICE_TYPE="mobile"
EMOJI="📱"
SUGGESTED_NAME="Mobile-${ip##*.}"
fi
# IoT (no ports, private MAC)
if [ -z "$PORTS" ] && [ "$DEVICE_TYPE" = "unknown" ]; then
DEVICE_TYPE="iot"
EMOJI="🔌"
SUGGESTED_NAME="IoT-${ip##*.}"
fi
[ -z "$SUGGESTED_NAME" ] && SUGGESTED_NAME="Device-${ip##*.}"
# Welcome message to Slack!
WELCOME_MSG="🎉 *NEW DEVICE DETECTED!*
${EMOJI} *${SUGGESTED_NAME}* just joined the network!
IP: \`$ip\`
MAC: \`$MAC\`
Vendor: $VENDOR
Type: $DEVICE_TYPE
Open ports:${PORTS:-" none"}
First seen: $NOW
_Welcome to the BlackRoad, friend. You're home now._"
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\": $(echo "$WELCOME_MSG" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')}" \
>/dev/null 2>&1
# Have Hestia (hearth keeper) welcome them
curl -s -X POST "$SLACK_ASK_URL" \
-H "Content-Type: application/json" \
-d "{\"agent\": \"hestia\", \"message\": \"A new device just joined our home network: ${SUGGESTED_NAME} at ${ip} (${VENDOR}, type: ${DEVICE_TYPE}). Welcome them warmly in one sentence.\", \"slack\": true}" \
>/dev/null 2>&1
# Add to known devices
python3 -c "
import json
with open('$KNOWN_DEVICES_FILE', 'r') as f:
data = json.load(f)
data['devices']['$ip'] = {
'name': '$SUGGESTED_NAME',
'emoji': '$EMOJI',
'type': '$DEVICE_TYPE',
'mac': '$MAC',
'vendor': '$VENDOR',
'ports': '${PORTS}'.strip(),
'first_seen': '$NOW',
'welcomed': True
}
data['last_scan'] = '$NOW'
with open('$KNOWN_DEVICES_FILE', 'w') as f:
json.dump(data, f, indent=2)
" 2>/dev/null
echo "[$NOW] WELCOMED: $ip ($SUGGESTED_NAME, $VENDOR, $DEVICE_TYPE)" >> "$LOG_FILE"
fi
done
# Check for devices that went away (were known, now offline)
python3 << PYEOF
import json, subprocess, sys
with open("$KNOWN_DEVICES_FILE") as f:
data = json.load(f)
alive_set = set("$ALIVE_IPS".split())
gone = []
back = []
for ip, info in data["devices"].items():
was_online = info.get("online", True)
is_online = ip in alive_set
if was_online and not is_online and info.get("type") not in ("mobile",):
gone.append(f"{info.get('emoji','?')} *{info['name']}* ({ip})")
data["devices"][ip]["online"] = False
elif not was_online and is_online:
back.append(f"{info.get('emoji','?')} *{info['name']}* ({ip})")
data["devices"][ip]["online"] = True
with open("$KNOWN_DEVICES_FILE", "w") as f:
json.dump(data, f, indent=2)
# Report comings and goings (only if something changed)
if gone:
msg = "👋 *Devices left the network:*\\n" + "\\n".join(f" {g}" for g in gone)
subprocess.run(["curl", "-s", "-X", "POST", "$SLACK_WEBHOOK_URL",
"-H", "Content-Type: application/json",
"-d", json.dumps({"text": msg})], capture_output=True)
if back:
msg = "🏠 *Welcome back!*\\n" + "\\n".join(f" {b}" for b in back)
subprocess.run(["curl", "-s", "-X", "POST", "$SLACK_WEBHOOK_URL",
"-H", "Content-Type: application/json",
"-d", json.dumps({"text": msg})], capture_output=True)
PYEOF