/* * el_runtime.h — El language C runtime header * * Declares all built-in functions available to compiled El programs. * Include this in every generated .c file. * * Value model: * All El values are represented as el_val_t (= int64_t). * On 64-bit systems a pointer fits in int64_t. * String values are cast: (el_val_t)(uintptr_t)"hello" * Integer values are stored directly. * This lets arithmetic work naturally while still passing strings around. * * Type conventions (El -> C): * String -> el_val_t (holds const char* via uintptr_t cast) * Int -> el_val_t * Bool -> el_val_t (0 = false, nonzero = true) * Any -> el_val_t * Void -> void * * Macros for convenience: * EL_STR(s) cast string literal to el_val_t * EL_CSTR(v) cast el_val_t back to const char* * EL_INT(v) identity — el_val_t is already int64_t * * Link requirements: * -lcurl — required for the HTTP client (http_get, http_post, llm_*). * -lpthread — required for the HTTP server (one detached thread per * connection, capped at 64 concurrent). * -loqs — optional; required only when liboqs is installed and the * pq_* / sha3_256_hex entry points are needed. Detected at * compile time via __has_include(). * -lcrypto — optional; pulled in alongside -loqs. Used for X25519 in * pq_hybrid_* and HKDF-SHA256 derivation. * * Canonical compile command: * cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \ * -o .c el-compiler/runtime/el_runtime.c * * With liboqs (post-quantum stack): * cc -std=c11 -I el-compiler/runtime -lcurl -lpthread -loqs -lcrypto \ * -o .c el-compiler/runtime/el_runtime.c */ #pragma once #include #include typedef int64_t el_val_t; #define EL_STR(s) ((el_val_t)(uintptr_t)(s)) #define EL_CSTR(v) ((const char*)(uintptr_t)(v)) #define EL_INT(v) (v) #define EL_NULL ((el_val_t)0) /* Float values share the el_val_t (int64) slot via a bit-cast. * The codegen emits Float literals as `el_from_float()` so the * underlying bits represent the IEEE 754 double. Float-aware builtins * (math, format, json) round-trip via these helpers. */ static inline double el_to_float(el_val_t v) { union { int64_t i; double f; } u; u.i = (int64_t)v; return u.f; } static inline el_val_t el_from_float(double f) { union { double f; int64_t i; } u; u.f = f; return (el_val_t)u.i; } #ifdef __cplusplus extern "C" { #endif /* ── I/O ──────────────────────────────────────────────────────────────────── */ void println(el_val_t s); void print(el_val_t s); el_val_t readline(void); /* ── String builtins ─────────────────────────────────────────────────────── */ el_val_t el_str_concat(el_val_t a, el_val_t b); el_val_t str_eq(el_val_t a, el_val_t b); el_val_t str_starts_with(el_val_t s, el_val_t prefix); el_val_t str_ends_with(el_val_t s, el_val_t suffix); el_val_t str_len(el_val_t s); el_val_t str_concat(el_val_t a, el_val_t b); el_val_t int_to_str(el_val_t n); el_val_t str_to_int(el_val_t s); el_val_t str_slice(el_val_t s, el_val_t start, el_val_t end); el_val_t str_contains(el_val_t s, el_val_t sub); el_val_t str_replace(el_val_t s, el_val_t from, el_val_t to); el_val_t str_to_upper(el_val_t s); el_val_t str_to_lower(el_val_t s); el_val_t str_trim(el_val_t s); /* ── Math ────────────────────────────────────────────────────────────────── */ el_val_t el_abs(el_val_t n); el_val_t el_max(el_val_t a, el_val_t b); el_val_t el_min(el_val_t a, el_val_t b); /* ── Refcount (ARC) ────────────────────────────────────────────────────────── * Lists and Maps carry a refcount. Strings and ints do not — el_retain and * el_release are safe no-ops on non-refcounted values (they sniff a magic * header at offset 0 and only act if the magic matches). * * Codegen emits these at let-binding shadowing, function entry (params), and * function exit (locals other than the returned value). The refcount lets * el_list_append and el_map_set mutate in place when uniquely owned (cheap) * and copy-on-write when shared (preserves persistent semantics across * accumulator patterns in the compiler itself). */ void el_retain(el_val_t v); void el_release(el_val_t v); /* ── List ────────────────────────────────────────────────────────────────── */ el_val_t el_list_new(el_val_t count, ...); el_val_t el_list_len(el_val_t list); el_val_t el_list_get(el_val_t list, el_val_t index); el_val_t el_list_append(el_val_t list, el_val_t elem); el_val_t el_list_empty(void); el_val_t el_list_clone(el_val_t list); /* ── Map ─────────────────────────────────────────────────────────────────── */ el_val_t el_map_new(el_val_t pair_count, ...); el_val_t el_get_field(el_val_t map, el_val_t key); el_val_t el_map_get(el_val_t map, el_val_t key); el_val_t el_map_set(el_val_t map, el_val_t key, el_val_t value); /* ── HTTP ─────────────────────────────────────────────────────────────────── */ el_val_t http_get(el_val_t url); el_val_t http_post(el_val_t url, el_val_t body); el_val_t http_post_json(el_val_t url, el_val_t json_body); el_val_t http_get_with_headers(el_val_t url, el_val_t headers_map); el_val_t http_post_with_headers(el_val_t url, el_val_t body, el_val_t headers_map); el_val_t http_post_form_auth(el_val_t url, el_val_t form_body, el_val_t auth_header); el_val_t http_delete(el_val_t url); void http_serve(el_val_t port, el_val_t handler); void http_set_handler(el_val_t name); /* HTTP server v2 ───────────────────────────────────────────────────────────── * Same dispatch model as http_serve, but the handler signature is widened: * * el_val_t handler(method, path, headers_map, body) * * `headers_map` is an ElMap from lowercased header name → header value (both * Strings). Repeated headers are joined with ", " per RFC 7230. * * Response value: the handler may return either * (a) a plain body string — same auto-content-type / 200-OK behaviour as * http_serve (3-arg) — or * (b) a response envelope built with `http_response(status, headers_json, * body)`. The runtime detects the envelope discriminator * `"el_http_response":1` at the start of the returned string and * unpacks status / headers / body before sending. * * The 3-arg http_serve(port, handler) remains supported unchanged for * existing handlers (e.g. products/web/server.el): it dispatches with * (method, path, body), hardcodes 200 OK, and auto-detects content type. */ void http_serve_v2(el_val_t port, el_val_t handler); void http_set_handler_v2(el_val_t name); /* Build an HTTP response envelope. `headers_json` should be a JSON object * literal like `{"WWW-Authenticate":"Basic"}` (or "" / "{}" for none). The * returned string carries the discriminator `{"el_http_response":1,...}` * which the runtime's send-path detects and unpacks. Detection happens * uniformly inside http_send_response, so a 3-arg handler may also return * an envelope. The 3-arg variant remains documented as a fixed 200-OK * auto-content-type contract for legacy handlers that return plain bodies. */ el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body); /* HTTP timeout — every libcurl request honors EL_HTTP_TIMEOUT_MS (default * 60000ms). Read lazily on first use, so setting the env var any time before * the first http_* call is sufficient. */ /* Streaming variants — write the response body straight to a file via * libcurl's CURLOPT_WRITEFUNCTION = fwrite. These bypass the el_val_t string * wrapper entirely, so binary payloads (audio/mpeg, image/png, etc.) survive * embedded NUL bytes that would truncate a strlen()-based code path. * * Both honor EL_HTTP_TIMEOUT_MS, follow redirects, and accept the same * `headers_map` shape as http_post_with_headers (ElMap of String→String). * * Return value: 1 on success (file fully written), 0 on any failure * (network, file open, partial write). On failure the output file is removed * so callers cannot mistake a partially-written file for a valid one. */ el_val_t http_post_to_file(el_val_t url, el_val_t body, el_val_t headers_map, el_val_t output_path); el_val_t http_get_to_file(el_val_t url, el_val_t headers_map, el_val_t output_path); /* ── URL encoding ────────────────────────────────────────────────────────── */ el_val_t url_encode(el_val_t s); /* RFC 3986 unreserved set */ el_val_t url_decode(el_val_t s); /* '+' → space, %XX → byte */ /* ── HTML allowlist sanitizer ──────────────────────────────────────────────── * el_html_sanitize(input_html, allowlist_json) — strict allowlist HTML * cleaner. State-machine parser; tag/attribute names compared case- * insensitively against the allowlist; `` / `<… src>` URL schemes * validated (http, https, mailto, fragment-only, or relative); whole- * subtree drop for script / style / iframe / object / embed / form; HTML- * escapes free text outside dropped subtrees. * * The allowlist is JSON of the form * {"p":[],"a":["href","title"],"strong":[],...} * where each value is the array of attribute names allowed for that tag. */ el_val_t el_html_sanitize(el_val_t input_html, el_val_t allowlist_json); /* ── Filesystem ──────────────────────────────────────────────────────────── */ el_val_t fs_read(el_val_t path); el_val_t fs_write(el_val_t path, el_val_t content); el_val_t fs_list(el_val_t path); el_val_t fs_exists(el_val_t path); el_val_t fs_mkdir(el_val_t path); /* mkdir -p, mode 0755 */ /* Length-explicit binary write. `length` is an Int (el_val_t holding the * byte count). The caller knows the length from context — typically because * `bytes` came from base64_decode (which produces a magic-tagged binary * buffer with embedded NULs possible) and the caller already tracks the * decoded length, OR because the bytes came from a fixed-size source * (sha256_bytes = 32, hmac_sha256_bytes = 32). Bypasses strlen entirely. * * Returns 1 on success, 0 on failure (invalid path, can't open, partial * write, negative length). On partial-write failure, the file is removed * so callers cannot read back a truncated artefact. */ el_val_t fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t length); /* ── JSON ────────────────────────────────────────────────────────────────── */ el_val_t json_get(el_val_t json, el_val_t key); el_val_t json_parse(el_val_t s); el_val_t json_stringify(el_val_t v); el_val_t json_get_string(el_val_t json_str, el_val_t key); el_val_t json_get_int(el_val_t json_str, el_val_t key); el_val_t json_get_float(el_val_t json_str, el_val_t key); el_val_t json_get_bool(el_val_t json_str, el_val_t key); el_val_t json_get_raw(el_val_t json_str, el_val_t key); el_val_t json_set(el_val_t json_str, el_val_t key, el_val_t value); el_val_t json_array_len(el_val_t json_str); el_val_t json_array_get(el_val_t json_str, el_val_t index); el_val_t json_array_get_string(el_val_t json_str, el_val_t index); /* ── Time ────────────────────────────────────────────────────────────────── */ el_val_t time_now(void); el_val_t time_now_utc(void); el_val_t sleep_secs(el_val_t secs); el_val_t sleep_ms(el_val_t ms); el_val_t time_format(el_val_t ts, el_val_t fmt); el_val_t time_to_parts(el_val_t ts); el_val_t time_from_parts(el_val_t secs, el_val_t ns, el_val_t tz); el_val_t time_add(el_val_t ts, el_val_t n, el_val_t unit); el_val_t time_diff(el_val_t ts1, el_val_t ts2, el_val_t unit); /* ── Instant + Duration: first-class temporal types ────────────────────────── * Both types share the el_val_t (int64) slot. Instants are nanoseconds * since the Unix epoch; Durations are signed nanoseconds. Type discipline * is enforced at codegen-time: BinOps on names registered as Instant or * Duration route through the typed wrappers below; mismatches like * Instant+Instant become #error at the C compiler. * * Postfix literals — `30.seconds`, `1.hour`, `500.millis`, `30.nanos` — are * recognised by the parser as DurationLit AST nodes and lowered to literal * int64 nanoseconds at codegen time. The runtime never sees the units. */ el_val_t el_now_instant(void); el_val_t now(void); el_val_t unix_seconds(el_val_t n); el_val_t unix_millis(el_val_t n); el_val_t instant_from_iso8601(el_val_t s); el_val_t el_duration_from_nanos(el_val_t ns); el_val_t duration_seconds(el_val_t n); el_val_t duration_millis(el_val_t n); el_val_t duration_nanos(el_val_t n); el_val_t el_instant_add_dur(el_val_t inst, el_val_t dur); el_val_t el_instant_sub_dur(el_val_t inst, el_val_t dur); el_val_t el_instant_diff(el_val_t a, el_val_t b); el_val_t el_duration_add(el_val_t a, el_val_t b); el_val_t el_duration_sub(el_val_t a, el_val_t b); el_val_t el_duration_scale(el_val_t dur, el_val_t scalar); el_val_t el_duration_div(el_val_t dur, el_val_t scalar); el_val_t el_instant_lt(el_val_t a, el_val_t b); el_val_t el_instant_le(el_val_t a, el_val_t b); el_val_t el_instant_gt(el_val_t a, el_val_t b); el_val_t el_instant_ge(el_val_t a, el_val_t b); el_val_t el_instant_eq(el_val_t a, el_val_t b); el_val_t el_instant_ne(el_val_t a, el_val_t b); el_val_t el_duration_lt(el_val_t a, el_val_t b); el_val_t el_duration_le(el_val_t a, el_val_t b); el_val_t el_duration_gt(el_val_t a, el_val_t b); el_val_t el_duration_ge(el_val_t a, el_val_t b); el_val_t el_duration_eq(el_val_t a, el_val_t b); el_val_t el_duration_ne(el_val_t a, el_val_t b); el_val_t instant_to_unix_seconds(el_val_t i); el_val_t instant_to_unix_millis(el_val_t i); el_val_t instant_to_iso8601(el_val_t i); el_val_t duration_to_seconds(el_val_t d); el_val_t duration_to_millis(el_val_t d); el_val_t duration_to_nanos(el_val_t d); el_val_t el_sleep_duration(el_val_t dur); el_val_t unix_timestamp(void); el_val_t ttl_cache_set(el_val_t key, el_val_t value); el_val_t ttl_cache_get(el_val_t key, el_val_t max_age); el_val_t ttl_cache_age(el_val_t key); /* ── Calendar + CalendarTime + Rhythm + LocalDate/Time/DateTime ───────────── * Phase 1.5 of the time system. Calendar is pluggable: EarthCalendar (IANA * zones, Gregorian, DST) is the user-facing default; MarsCalendar, * CycleCalendar(period), NoCycleCalendar, RelativeCalendar handle non-Earth * domains. * * A Calendar interprets an Instant under a particular cycle convention and * produces a CalendarTime. CalendarTime carries the underlying Instant and * a back-pointer to its Calendar; arithmetic and formatting consult the * Calendar to convert ns since epoch into year/month/day/hour/minute/second * (or sol/phase, or cycle/phase, depending on kind). * * Storage convention: Calendar / CalendarTime / Rhythm / LocalDate / * LocalDateTime are heap-allocated structs whose pointers are cast into * el_val_t. A 24-bit magic header at offset 0 lets the runtime identify * the kind safely. LocalTime is small enough to live in the int64 slot * directly (nanos since midnight, signed). */ /* Zone — opaque IANA zone or fixed offset, used by EarthCalendar. * `zone_id` is either an IANA name ("America/New_York", "UTC") or a fixed * offset string ("+05:30", "-08:00"). The runtime resolves it via tzset() * on first use of the owning EarthCalendar. */ el_val_t zone(el_val_t id); el_val_t zone_utc(void); el_val_t zone_local(void); el_val_t zone_offset(el_val_t hours, el_val_t minutes); /* Calendar constructors. Each returns an el_val_t pointer to a heap- * allocated, magic-tagged Calendar struct. Calendars are interned by * (kind, zone_id, period_ns, epoch_ns) so identical constructors return * the same pointer — equality is reference equality. */ el_val_t earth_calendar(el_val_t z); el_val_t earth_calendar_default(void); el_val_t mars_calendar(void); el_val_t cycle_calendar(el_val_t period_dur); el_val_t no_cycle_calendar(void); el_val_t relative_calendar(el_val_t epoch_inst); /* CalendarTime constructors and methods. Returns a heap-allocated struct * whose pointer fits in el_val_t. */ el_val_t now_in(el_val_t cal); el_val_t in_calendar(el_val_t inst, el_val_t cal); el_val_t cal_format(el_val_t ct, el_val_t pattern); el_val_t cal_to_instant(el_val_t ct); el_val_t cal_cycle_phase(el_val_t ct); el_val_t cal_in(el_val_t ct, el_val_t cal); /* LocalDate / LocalTime / LocalDateTime — calendar-agnostic value types. * LocalTime carries nanoseconds since midnight as a signed int64 directly * in the el_val_t slot (no allocation). LocalDate / LocalDateTime are * heap-allocated structs with magic headers. */ el_val_t local_date(el_val_t y, el_val_t m, el_val_t d); el_val_t local_time(el_val_t h, el_val_t m, el_val_t s, el_val_t ns); el_val_t local_datetime(el_val_t date, el_val_t time); el_val_t zoned(el_val_t date, el_val_t time, el_val_t cal); el_val_t local_date_year(el_val_t ld); el_val_t local_date_month(el_val_t ld); el_val_t local_date_day(el_val_t ld); el_val_t local_time_hour(el_val_t lt); el_val_t local_time_minute(el_val_t lt); el_val_t local_time_second(el_val_t lt); el_val_t local_time_nanos(el_val_t lt); el_val_t el_local_date_add_dur(el_val_t ld, el_val_t dur); el_val_t el_local_time_add_dur(el_val_t lt, el_val_t dur); el_val_t el_local_date_lt(el_val_t a, el_val_t b); el_val_t el_local_date_eq(el_val_t a, el_val_t b); /* Rhythm — pluggable recurrence AST. Returns a heap-allocated struct * pointer in el_val_t; rhythms are immutable so callers may share them. */ el_val_t rhythm_cycle_start(void); el_val_t rhythm_cycle_phase(el_val_t phase); el_val_t rhythm_duration(el_val_t d); el_val_t rhythm_session_start(void); el_val_t rhythm_event(el_val_t name); el_val_t rhythm_and(el_val_t a, el_val_t b); el_val_t rhythm_or(el_val_t a, el_val_t b); el_val_t rhythm_weekday(el_val_t day); el_val_t rhythm_weekly_at(el_val_t day, el_val_t hour, el_val_t minute); el_val_t rhythm_next_after(el_val_t r, el_val_t after, el_val_t cal); el_val_t rhythm_matches(el_val_t r, el_val_t ct); /* ── UUID ────────────────────────────────────────────────────────────────── */ el_val_t uuid_new(void); el_val_t uuid_v4(void); /* ── Environment ─────────────────────────────────────────────────────────── */ el_val_t env(el_val_t key); /* ── In-process state K/V ────────────────────────────────────────────────── */ el_val_t state_set(el_val_t key, el_val_t value); el_val_t state_get(el_val_t key); el_val_t state_del(el_val_t key); el_val_t state_keys(void); /* ── Float formatting ────────────────────────────────────────────────────── */ el_val_t float_to_str(el_val_t f); el_val_t int_to_float(el_val_t n); el_val_t float_to_int(el_val_t f); el_val_t format_float(el_val_t f, el_val_t decimals); el_val_t decimal_round(el_val_t f, el_val_t decimals); el_val_t str_to_float(el_val_t s); /* ── Math (Float-aware) ──────────────────────────────────────────────────── */ el_val_t math_sqrt(el_val_t f); el_val_t math_log(el_val_t f); el_val_t math_ln(el_val_t f); el_val_t math_sin(el_val_t f); el_val_t math_cos(el_val_t f); el_val_t math_pi(void); /* ── String additions ────────────────────────────────────────────────────── */ el_val_t str_index_of(el_val_t s, el_val_t sub); el_val_t str_split(el_val_t s, el_val_t sep); el_val_t str_char_at(el_val_t s, el_val_t i); el_val_t str_char_code(el_val_t s, el_val_t i); el_val_t str_pad_left(el_val_t s, el_val_t width, el_val_t pad); el_val_t str_pad_right(el_val_t s, el_val_t width, el_val_t pad); el_val_t str_format(el_val_t fmt, el_val_t data); el_val_t str_lower(el_val_t s); el_val_t str_upper(el_val_t s); /* ── Text-processing primitives (Phase 1: byte/codepoint, ASCII char classes) * Phase 2 (filed): Unicode-grapheme awareness, NFC/NFD normalization, regex. * is_* predicates: empty input returns false; multi-char requires ALL bytes * to match. ASCII ranges only in Phase 1. */ /* Counting */ el_val_t str_count(el_val_t s, el_val_t sub); /* non-overlapping */ el_val_t str_count_chars(el_val_t s); /* codepoint count */ el_val_t str_count_bytes(el_val_t s); /* alias of str_len */ el_val_t str_count_lines(el_val_t s); el_val_t str_count_words(el_val_t s); el_val_t str_count_letters(el_val_t s); /* ASCII [A-Za-z] */ el_val_t str_count_digits(el_val_t s); /* ASCII [0-9] */ /* Find / position */ el_val_t str_index_of_all(el_val_t s, el_val_t sub); /* [Int] of byte offsets */ el_val_t str_last_index_of(el_val_t s, el_val_t sub); el_val_t str_find_chars(el_val_t s, el_val_t any_of); /* first idx of any ch */ /* Transform */ el_val_t str_repeat(el_val_t s, el_val_t n); el_val_t str_reverse(el_val_t s); /* by codepoint */ el_val_t str_strip_prefix(el_val_t s, el_val_t prefix); el_val_t str_strip_suffix(el_val_t s, el_val_t suffix); el_val_t str_strip_chars(el_val_t s, el_val_t chars); el_val_t str_lstrip(el_val_t s); el_val_t str_rstrip(el_val_t s); /* Char classification (Bool) */ el_val_t is_letter(el_val_t s); el_val_t is_digit(el_val_t s); el_val_t is_alphanumeric(el_val_t s); el_val_t is_whitespace(el_val_t s); el_val_t is_punctuation(el_val_t s); el_val_t is_uppercase(el_val_t s); el_val_t is_lowercase(el_val_t s); /* Split / join */ el_val_t str_split_lines(el_val_t s); el_val_t str_split_chars(el_val_t s); /* alias of native_string_chars */ el_val_t str_split_n(el_val_t s, el_val_t sep, el_val_t n); el_val_t str_join(el_val_t list, el_val_t sep); /* alias of list_join */ /* ── List additions ──────────────────────────────────────────────────────── */ el_val_t list_push(el_val_t list, el_val_t elem); el_val_t list_push_front(el_val_t list, el_val_t elem); el_val_t list_join(el_val_t list, el_val_t sep); el_val_t list_range(el_val_t start, el_val_t end); /* ── Bool helpers ────────────────────────────────────────────────────────── */ el_val_t bool_to_str(el_val_t b); /* ── Numeric parsing ─────────────────────────────────────────────────────── */ el_val_t parse_int(el_val_t s, el_val_t default_val); /* ── Process ─────────────────────────────────────────────────────────────── */ void exit_program(el_val_t code); el_val_t getpid_now(void); /* ── CGI identity ───────────────────────────────────────────────────────────── * Called at the start of main() in CGI programs (those with a `cgi {}` block). * Records the program's DHARMA identity before any other code executes. */ void el_cgi_init(el_val_t name, el_val_t dharma_id, el_val_t principal, el_val_t network, el_val_t engram); /* ── DHARMA network builtins ───────────────────────────────────────────────── * Available to CGI programs (declared with a `cgi {}` block). * * Peers are addressed by `dharma_id` of the form * "@" e.g. "ntn-genesis@http://localhost:7770" * If the @ portion is omitted, transport defaults to * "http://localhost:7770" (the local CGI daemon assumption). * * Wire protocol (all peers expose): * POST /dharma/recv { channel, from, content } → response body * POST /dharma/event { type, payload, source, timestamp } * POST /api/activate { query } → list of nodes * * Hosting application's responsibility: an El program with a `cgi {}` block * runs http_serve() with its own request handler; that handler should route * "/dharma/event" requests by calling el_runtime_dharma_event_arrive() so * incoming events feed dharma_field() queues. The runtime itself does not * intercept any /dharma path. */ el_val_t dharma_connect(el_val_t cgi_id); el_val_t dharma_send(el_val_t channel, el_val_t content); el_val_t dharma_activate(el_val_t query); void dharma_emit(el_val_t event_type, el_val_t payload); el_val_t dharma_field(el_val_t event_type); void dharma_strengthen(el_val_t cgi_id, el_val_t weight); el_val_t dharma_relationship(el_val_t cgi_id); el_val_t dharma_peers(void); /* Public C API: called by an El program's HTTP handler when a /dharma/event * request arrives. Pushes onto the per-event-type queue and signals any * pending dharma_field() blockers. All three arguments must be NUL-terminated * C strings (or NULL — then treated as empty). */ void el_runtime_dharma_event_arrive(const char* event_type, const char* payload, const char* source); /* ── Engram local graph primitives ─────────────────────────────────────────── * Operate on the CGI's local Engram knowledge graph. * `engram_activate` queries the local graph only; `dharma_activate` is * network-wide across all connected CGI graphs. */ el_val_t engram_node(el_val_t content, el_val_t node_type, el_val_t salience); el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t importance, el_val_t confidence, el_val_t tier, el_val_t tags); /* Layered consciousness — see el_runtime.c for the layered architecture * design notes (search "Layered consciousness architecture"). The five * canonical layers (safety / core-identity / domain-knowledge / imprint / * suit) are seeded automatically; engram_add_layer extends the registry * with imprint or suit overlays at runtime. Nodes default to layer 1 * (core-identity) when created via engram_node / engram_node_full. */ el_val_t engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t certainty, el_val_t confidence, el_val_t status, el_val_t tags, el_val_t layer_id); el_val_t engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible, el_val_t transparent, el_val_t injectable); el_val_t engram_remove_layer(el_val_t layer_id); el_val_t engram_list_layers(void); el_val_t engram_get_node(el_val_t id); void engram_strengthen(el_val_t node_id); void engram_forget(el_val_t node_id); el_val_t engram_node_count(void); el_val_t engram_search(el_val_t query, el_val_t limit); el_val_t engram_scan_nodes(el_val_t limit, el_val_t offset); void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation); el_val_t engram_edge_between(el_val_t from_id, el_val_t to_id); el_val_t engram_neighbors(el_val_t node_id); el_val_t engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction); el_val_t engram_edge_count(void); /* Three-pass activation: background fan-out → working-memory promotion → * Layer 0 override. See "Three-pass activation" in el_runtime.c. */ el_val_t engram_activate(el_val_t query, el_val_t depth); el_val_t engram_save(el_val_t path); el_val_t engram_load(el_val_t path); /* JSON-string accessors — return pre-serialized JSON so HTTP handlers * can pass results straight through without round-tripping ElList/ElMap * through json_stringify. */ el_val_t engram_get_node_json(el_val_t id); el_val_t engram_search_json(el_val_t query, el_val_t limit); el_val_t engram_scan_nodes_json(el_val_t limit, el_val_t offset); el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset); el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction); el_val_t engram_activate_json(el_val_t query, el_val_t depth); el_val_t engram_stats_json(void); el_val_t engram_list_layers_json(void); /* engram_compile_layered_json — produce a prompt-ready text block split * into "[LAYER 0 — STRUCTURAL]" (non-suppressible layers, sacred fire) * and "[ENGRAM CONTEXT]" (standard suppressible layers). Returns "" if * no nodes promoted to working memory. */ el_val_t engram_compile_layered_json(el_val_t intent, el_val_t depth); /* ── LLM (Anthropic API client) ───────────────────────────────────────────── * All functions call https://api.anthropic.com/v1/messages with the API key * from env ANTHROPIC_API_KEY. Default model when empty: claude-sonnet-4-5. */ el_val_t llm_call(el_val_t model, el_val_t prompt); el_val_t llm_call_system(el_val_t model, el_val_t system_prompt, el_val_t user_prompt); el_val_t llm_call_agentic(el_val_t model, el_val_t system, el_val_t user, el_val_t tools); el_val_t llm_vision(el_val_t model, el_val_t system, el_val_t prompt, el_val_t image_url_or_b64); el_val_t llm_models(void); /* Register a tool handler by name. The handler is looked up via dlsym * (mirroring http_set_handler), so any El `fn (input)` compiles to * a global C symbol that this function can locate at runtime. * Handler signature: `el_val_t handler(el_val_t input_json)` — receives * the tool input as a JSON-string el_val_t and returns a JSON-string * el_val_t result. Used by llm_call_agentic. */ void llm_register_tool(el_val_t name, el_val_t handler_fn_name); /* ── args() ───────────────────────────────────────────────────────────────── * Provides access to command-line arguments passed to the program. * Populated by el_runtime_init_args() before main() runs. */ el_val_t args(void); void el_runtime_init_args(int argc, char** argv); /* ── Crypto primitives ───────────────────────────────────────────────────── * SHA-256, HMAC-SHA-256, and base64 (standard + URL-safe). * Self-contained — no OpenSSL/libcrypto dependency. The implementations are * adapted from public-domain reference code (Brad Conte / RFC 4648). * * Bytes-returning variants (sha256_bytes, hmac_sha256_bytes) return a string * value whose contents are raw binary; callers usually feed these into * base64_encode. Note that el_val_t strings are NUL-terminated by convention, * so the binary payload may contain embedded NULs — pass it directly into * base64_encode (which uses an explicit length) rather than treating it as * a printable C string. * * The "base64" variants emit/accept RFC 4648 standard alphabet with padding. * The "base64url" variants use URL-safe alphabet (`-`/`_`) with no padding, * as used in JWTs. */ el_val_t sha256_hex(el_val_t input); el_val_t sha256_bytes(el_val_t input); el_val_t hmac_sha256_hex(el_val_t key, el_val_t message); el_val_t hmac_sha256_bytes(el_val_t key, el_val_t message); el_val_t base64_encode(el_val_t input); el_val_t base64_decode(el_val_t input); el_val_t base64url_encode(el_val_t input); el_val_t base64url_decode(el_val_t input); /* Length-aware variants (internal — exposed for the rare caller that already * has a known-length binary buffer and doesn't want to round-trip through * a NUL-terminated el_val_t string). Sha256_bytes and hmac_sha256_bytes feed * these implicitly. */ el_val_t el_sha256_bytes_n(const unsigned char* data, size_t len); el_val_t el_base64_encode_n(const unsigned char* data, size_t len, int url_safe); /* ── Post-quantum primitives (liboqs-backed) ──────────────────────────────── * All inputs/outputs hex-encoded. Algorithm choices: * Signature: CRYSTALS-Dilithium-3 (NIST level 3, balanced) * KEM: CRYSTALS-Kyber-768 (NIST level 3) * Hash: SHA3-256 (Keccak) (PQ-aware protocols favour SHA3 over SHA2) * * If liboqs is not linked (detected via __has_include() at compile * time), the pq_* entry points return a JSON-shaped error string so callers * fail loudly rather than silently fall back to classical schemes: * {"error":"liboqs not linked, post-quantum primitives unavailable"} * * The hybrid handshake pairs X25519 with Kyber-768 per NIST PQ guidance and * CNSA 2.0. Combined shared secret is HKDF-SHA256(x25519_ss || kyber_ss). * Even if Kyber falls, X25519 holds; if X25519 falls under quantum attack, * Kyber holds. SHA3-256 also remains usable independent of liboqs (the * Keccak permutation is PQ-OK as a primitive). */ el_val_t pq_keygen_signature(void); el_val_t pq_sign(el_val_t secret_key_hex, el_val_t message); el_val_t pq_verify(el_val_t public_key_hex, el_val_t message, el_val_t signature_hex); el_val_t pq_kem_keygen(void); el_val_t pq_kem_encaps(el_val_t public_key_hex); el_val_t pq_kem_decaps(el_val_t secret_key_hex, el_val_t ciphertext_hex); el_val_t pq_hybrid_keygen(void); el_val_t pq_hybrid_handshake(el_val_t remote_pub_combined); el_val_t sha3_256_hex(el_val_t input); /* ── AEAD: AES-256-GCM (libcrypto-backed) ─────────────────────────────────── * Symmetric authenticated encryption used to wrap envelopes after a KEM * handshake. Caller MUST supply a 32-byte key (64 hex chars) — typically the * Kyber-768 / hybrid shared_secret, optionally normalized via SHA3-256. * * aead_encrypt returns a JSON map {"nonce":"...","ciphertext":"..."} where * ciphertext is the AES-256-GCM output with the 16-byte auth tag appended. * Nonce is a fresh 12-byte CSPRNG draw — callers never pick the nonce, which * structurally rules out the GCM nonce-reuse footgun. * * aead_decrypt returns the plaintext String, or "" on any failure (including * auth-tag mismatch). Callers MUST check for "" before trusting the result. */ el_val_t aead_encrypt(el_val_t key_hex, el_val_t plaintext); el_val_t aead_decrypt(el_val_t key_hex, el_val_t nonce_hex, el_val_t ciphertext_hex); /* ── Native VM builtin aliases (for compiled El source) ───────────────────── * These match the El VM's native_* builtins so that El source compiled * to C can call the same names without modification. */ el_val_t native_list_get(el_val_t list, el_val_t index); el_val_t native_list_len(el_val_t list); el_val_t native_list_append(el_val_t list, el_val_t elem); el_val_t native_list_empty(void); el_val_t native_list_clone(el_val_t list); el_val_t native_string_chars(el_val_t s); el_val_t native_int_to_str(el_val_t n); /* ── Method-call shorthand aliases ────────────────────────────────────────── * The El method-call convention `obj.method(args)` compiles to * `method(obj, args)`. These aliases expose the runtime functions under * the short names that result from method calls in El source. * * Example: `myList.append(x)` → `append(myList, x)` (calls this alias) * `myList.len()` → `len(myList)` (calls this alias) */ el_val_t append(el_val_t list, el_val_t elem); /* el_list_append */ el_val_t len(el_val_t list); /* el_list_len */ el_val_t get(el_val_t list, el_val_t index); /* el_list_get */ el_val_t map_get(el_val_t map, el_val_t key); /* el_map_get */ el_val_t map_set(el_val_t map, el_val_t key, el_val_t value); /* el_map_set */ /* ── OTLP/HTTP Observability ─────────────────────────────────────────────── */ /* See bottom of el_runtime.c for the implementation. * Configured by env vars OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_SERVICE_VERSION. * No-op when OTLP_ENDPOINT is unset. Drop-on-failure semantics. */ /* ── Subprocess execution ────────────────────────────────────────────────── */ el_val_t exec_command(el_val_t cmd); /* run shell command, return exit code */ el_val_t exec_capture(el_val_t cmd); /* run shell command, capture stdout */ el_val_t exec(el_val_t cmd); /* exec(cmd) → stdout String (30s timeout) */ el_val_t exec_bg(el_val_t cmd); /* exec_bg(cmd) → PID String (non-blocking) */ el_val_t emit_log(el_val_t level, el_val_t msg, el_val_t fields_json); el_val_t emit_metric(el_val_t name, el_val_t value, el_val_t tags_json); el_val_t trace_span_start(el_val_t name); el_val_t trace_span_end(el_val_t span_handle); el_val_t emit_event(el_val_t name, el_val_t duration_ms); #ifdef __cplusplus } #endif