- Issue #3: err_404/err_405 now emit HTTP 404/405 via __status__ envelope instead of HTTP 200 - Issue #4: add auth_check() function to handle_request; enforces NEURON_TOKEN on all routes except /health and /lineage - Issue #5: missing required params now return HTTP 400 (__status__ envelope) in /api/chat (GET+POST), /imprint/contextual, /imprint/user, and handle_chat - Issue #6: LLM unavailable in handle_chat now returns HTTP 503 instead of HTTP 200 - Issue #7: add 32 KB message size guard on POST /api/chat before engram_compile and LLM - Issue #8: add TODO comment to route_health documenting the live-engram-query problem and the /health/deep split plan - Issue #9: add comment to hist_trim documenting fragile str_index_of parser and silent data corruption risk - Issue #10: add TODO comment in handle_request documenting missing per-IP rate limiting - Issue #11: fix connectd_post temp file collision — add monotonic sequence counter so concurrent requests get unique paths - Issue #12: fix call_mcp_bridge fixed temp file race — add monotonic sequence counter for unique paths under concurrent load - Issues #1/#2: add TODO comment in handle_request documenting EL no-exception limitation and SIGSEGV handler gap
This commit is contained in:
@@ -16,14 +16,24 @@ fn strip_query(path: String) -> String {
|
||||
}
|
||||
|
||||
fn err_404(path: String) -> String {
|
||||
return "{\"error\":\"not found\",\"path\":\"" + path + "\"}"
|
||||
// __status__ envelope — el_runtime reads the first key and emits HTTP 404.
|
||||
// Issue #3: previously returned HTTP 200 with JSON error body.
|
||||
return "{\"__status__\":404,\"error\":\"not found\",\"path\":\"" + path + "\"}"
|
||||
}
|
||||
|
||||
fn err_405(method: String, path: String) -> String {
|
||||
return "{\"error\":\"method not allowed\",\"method\":\"" + method + "\",\"path\":\"" + path + "\"}"
|
||||
// __status__ envelope — emits HTTP 405.
|
||||
// Issue #3: previously returned HTTP 200 with JSON error body.
|
||||
return "{\"__status__\":405,\"error\":\"method not allowed\",\"method\":\"" + method + "\",\"path\":\"" + path + "\"}"
|
||||
}
|
||||
|
||||
fn route_health() -> String {
|
||||
// NOTE (issue #8): This endpoint performs live engram graph queries on every call
|
||||
// (engram_node_count, engram_edge_count) and reads imprint state. High-frequency
|
||||
// load-balancer probes will add non-trivial overhead, and the soul reports "alive"
|
||||
// even when the LLM is unreachable (false positive for LB health).
|
||||
// TODO: split into GET /health (state-only, no graph queries) for LB probes and
|
||||
// retain this full check at GET /health/deep for ops monitoring.
|
||||
let cgi_id: String = state_get("soul_cgi_id")
|
||||
let boot: String = state_get("soul_boot_count")
|
||||
let boot_num: String = if str_eq(boot, "") { "0" } else { boot }
|
||||
@@ -59,7 +69,8 @@ fn route_lineage() -> String {
|
||||
|
||||
fn route_imprint_contextual(body: String) -> String {
|
||||
if str_eq(body, "") {
|
||||
return "{\"ok\":false,\"error\":\"empty body\"}"
|
||||
// Issue #5: empty body is a client error — HTTP 400.
|
||||
return "{\"__status__\":400,\"ok\":false,\"error\":\"empty body\"}"
|
||||
}
|
||||
let tags: String = "[\"imprint\",\"contextual\"]"
|
||||
let id: String = engram_node_full(
|
||||
@@ -81,7 +92,8 @@ fn route_imprint_contextual(body: String) -> String {
|
||||
|
||||
fn route_imprint_user(body: String) -> String {
|
||||
if str_eq(body, "") {
|
||||
return "{\"ok\":false,\"error\":\"empty body\"}"
|
||||
// Issue #5: empty body is a client error — HTTP 400.
|
||||
return "{\"__status__\":400,\"ok\":false,\"error\":\"empty body\"}"
|
||||
}
|
||||
let tags: String = "[\"imprint\",\"user\"]"
|
||||
let id: String = engram_node_full(
|
||||
@@ -219,9 +231,13 @@ fn connectd_get(suffix: String) -> String {
|
||||
// so arbitrary JSON cannot reach the shell as a command-line argument.
|
||||
fn connectd_post(suffix: String, body: String) -> String {
|
||||
let eff: String = if str_eq(body, "") { "{}" } else { body }
|
||||
// Unique temp path per call — prevents collision if concurrency is ever added
|
||||
// or if two soul instances run on the same machine (latent correctness hazard).
|
||||
let tmp: String = "/tmp/neuron-connectors-req-" + int_to_str(time_now()) + ".json"
|
||||
// Issue #11: time_now() has second-granularity; two concurrent requests in the same
|
||||
// second collide on the same temp path. Added a monotonic per-process sequence counter.
|
||||
let connectd_seq_s: String = state_get("connectd_post_seq")
|
||||
let connectd_seq_n: Int = if str_eq(connectd_seq_s, "") { 0 } else { str_to_int(connectd_seq_s) }
|
||||
let connectd_seq_next: Int = connectd_seq_n + 1
|
||||
state_set("connectd_post_seq", int_to_str(connectd_seq_next))
|
||||
let tmp: String = "/tmp/neuron-connectors-req-" + int_to_str(time_now()) + "-" + int_to_str(connectd_seq_next) + ".json"
|
||||
fs_write(tmp, eff)
|
||||
let out: String = exec_capture("curl -s --max-time 20 -X POST http://127.0.0.1:7771" + suffix + " -H 'Content-Type: application/json' -d @" + tmp)
|
||||
if str_eq(out, "") {
|
||||
@@ -256,9 +272,45 @@ fn handle_connectors(method: String, clean: String, body: String) -> String {
|
||||
return "{\"ok\":false,\"error\":\"unknown connectors route\"}"
|
||||
}
|
||||
|
||||
|
||||
// auth_check — validate NEURON_TOKEN bearer auth on every request.
|
||||
// Returns "" when authorized, or a JSON 401 error string when not.
|
||||
// /health and /lineage are public routes — always exempted.
|
||||
// When NEURON_TOKEN is not configured (empty), auth is disabled (dev/local mode).
|
||||
// Issue #4: previously no auth layer existed anywhere in the router.
|
||||
// Clients pass the token in the JSON body as "__auth".
|
||||
// TODO: also check Authorization: Bearer header once el_runtime v2 header-map
|
||||
// path is adopted universally.
|
||||
fn auth_check(clean: String, body: String) -> String {
|
||||
if str_eq(clean, "/health") { return "" }
|
||||
if str_eq(clean, "/lineage") { return "" }
|
||||
let token: String = state_get("soul_token")
|
||||
if str_eq(token, "") { return "" }
|
||||
let auth_field: String = json_get(body, "__auth")
|
||||
if str_eq(auth_field, token) { return "" }
|
||||
return "{\"__status__\":401,\"error\":\"unauthorized\"}"
|
||||
}
|
||||
|
||||
fn handle_request(method: String, path: String, body: String) -> String {
|
||||
let clean: String = strip_query(path)
|
||||
|
||||
// Issue #1/#2: EL has no exception/try-catch mechanism. A C-level crash inside
|
||||
// an http_worker pthread drops the TCP connection (client gets RST) rather than
|
||||
// returning HTTP 500. TODO: register a SIGSEGV/SIGBUS handler in el_runtime.c
|
||||
// that writes a 500 JSON response to the current worker fd before aborting.
|
||||
|
||||
// Issue #10: Rate limiting is not implemented.
|
||||
// TODO: add a per-IP token-bucket counter returning HTTP 429 when exceeded.
|
||||
// Requires a C-level counter in el_runtime.c or a sidecar reverse proxy.
|
||||
|
||||
// Auth — enforced on all routes except /health and /lineage.
|
||||
// Issue #4: previously no auth check existed anywhere in the router.
|
||||
let auth_err: String = auth_check(clean, body)
|
||||
if !str_eq(auth_err, "") {
|
||||
return auth_err
|
||||
}
|
||||
|
||||
|
||||
if str_eq(method, "POST") && str_eq(clean, "/dharma/recv") {
|
||||
return handle_dharma_recv(body)
|
||||
}
|
||||
@@ -286,7 +338,8 @@ fn handle_request(method: String, path: String, body: String) -> String {
|
||||
let raw_msg: String = json_get(body, "message")
|
||||
let eff_msg: String = if str_eq(raw_msg, "") { body } else { raw_msg }
|
||||
if str_eq(eff_msg, "") {
|
||||
return "{\"error\":\"message required\"}"
|
||||
// Issue #5: missing required param — HTTP 400.
|
||||
return "{\"__status__\":400,\"error\":\"message required\"}"
|
||||
}
|
||||
let agentic_flag: Bool = json_get_bool(body, "agentic")
|
||||
let reply: String = if agentic_flag {
|
||||
@@ -426,8 +479,17 @@ fn handle_request(method: String, path: String, body: String) -> String {
|
||||
return handle_elp_chat(body)
|
||||
}
|
||||
if str_eq(clean, "/api/chat") {
|
||||
let agentic_flag: Bool = json_get_bool(body, "agentic")
|
||||
// Issue #5: validate required params — return HTTP 400 when missing.
|
||||
let raw_msg: String = json_get(body, "message")
|
||||
if str_eq(raw_msg, "") {
|
||||
return "{\"__status__\":400,\"error\":\"message is required\",\"response\":\"\"}"
|
||||
}
|
||||
// Issue #7: reject oversized messages before engram_compile and the LLM.
|
||||
// Runtime caps Content-Length at 64 MB but messages pass through unauthenticated.
|
||||
if str_len(raw_msg) > 32768 {
|
||||
return "{\"__status__\":400,\"error\":\"message too large (max 32768 chars)\",\"response\":\"\"}"
|
||||
}
|
||||
let agentic_flag: Bool = json_get_bool(body, "agentic")
|
||||
let reply: String = if agentic_flag {
|
||||
handle_chat_agentic(body)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user