// main-user.el — Neuron daemon entry point. User build. // Written in Engram. // // Principal identity is a compile-time constant — NOT read from config. // This file is the user build. The developer build is main.el. // Built with: el build --manifest el-user.toml // // Responsibilities: // 1. Write PID file to ~/.neuron-user/data/daemon.pid // 2. Start API/proxy server on :7750 (blocking) // - Proxies /axon/, /api/ routes to neuronrs at :7770 // - Health check at /health // // The handle_request function is called by http_serve for every request. import "daemon_config.el" import "proxy.el" import "health.el" import "loop.el" import "plugins/host.el" // ── Principal identity (baked in at compile time) ───────────────────────────── // This literal is compiled into the bytecode. For prod builds it is sealed // inside AES-256-GCM — cannot be changed without the deployment key. let principal: String = "user" // ── Load operational config ─────────────────────────────────────────────────── let cfg: String = load_config() let axon_base: String = config_api_url(cfg) let token: String = config_api_token(cfg) let ui_dir: String = config_ui_dir(cfg) let data_dir: String = config_data_dir(cfg) let port: Int = config_port(cfg) state_set("neuron_principal", principal) // ── Write PID file ──────────────────────────────────────────────────────────── let pid: Int = getpid() let pid_str: String = int_to_str(pid) fs_mkdir(data_dir) let pid_path: String = data_dir + "/daemon.pid" fs_write(pid_path, pid_str) println(color_bold("Neuron daemon") + " — pid " + pid_str + " — " + principal) println(" API → http://localhost:" + int_to_str(port)) println(" Axon → " + axon_base) println(" Data → " + data_dir) println("") // ── Request handler (API server on :7750) ───────────────────────────────────── // // Called by http_serve for every incoming request. // Must be named exactly "handle_request" — http_serve looks it up by that name. fn handle_request(method: String, path: String, body: String) -> String { // Health check if str_eq(path, "/health") { return health_response() } // Axon tool dispatch — proxy to neuronrs if str_starts_with(path, "/axon/") { return proxy_request(axon_base, method, path, body, token) } // Intelligence REST API — proxy to neuronrs if str_starts_with(path, "/api/memories") { return proxy_request(axon_base, method, path, body, token) } if str_starts_with(path, "/api/knowledge") { return proxy_request(axon_base, method, path, body, token) } if str_starts_with(path, "/api/backlog") { return proxy_request(axon_base, method, path, body, token) } if str_starts_with(path, "/api/contexts") { return proxy_request(axon_base, method, path, body, token) } if str_starts_with(path, "/api/ise") { return proxy_request(axon_base, method, path, body, token) } // ── Plugin status ───────────────────────────────────────────────────────── if str_eq(path, "/plugin/status") { return host_status() } // ── Runtime loop control ────────────────────────────────────────────────── if str_eq(path, "/loop/status") { return loop_status_json() } if str_eq(path, "/loop/signal") { let sig: String = json_get(body, "signal") if str_eq(sig, "") { return "{\"error\":\"missing signal\"}" } state_set("loop_signal", sig) return "{\"ok\":true,\"signal\":\"" + sig + "\"}" } if str_eq(path, "/loop/tier") { let new_tier: String = json_get(body, "tier") let new_min: String = json_get(body, "min_tier") if !str_eq(new_min, "") { state_set("loop_min_tier", new_min) } if !str_eq(new_tier, "") { state_set("loop_override_tier", new_tier) } return "{\"ok\":true,\"tier\":\"" + new_tier + "\",\"min_tier\":\"" + new_min + "\"}" } return not_found_response(path) } // ── Initialise plugin host ──────────────────────────────────────────────────── host_on_startup() // ── Initialise loop state ───────────────────────────────────────────────────── state_set("loop_tier", "watching") state_set("loop_min_tier", "resting") state_set("loop_ticks", "0") state_set("loop_bell_fires", "0") state_set("loop_tier_changes", "0") state_set("loop_idle_ticks", "0") state_set("loop_signal", "") state_set("loop_override_tier", "") state_set("loop_last_tick_idle", "0") // ── Spawn the heartbeat thread ──────────────────────────────────────────────── spawn_thread("loop_main") // ── Start API server (blocking) ─────────────────────────────────────────────── http_serve(port)