Engram is now a thin HTTP face over the El runtime's in-process graph
store. The C runtime owns the data; engram_*_json builtins serialize
results directly. There is no SQL, no SQLite, no db layer, no state
machine — the runtime IS the database.
src/server.el (348 lines, replacing 5797 lines across 15 legacy files):
GET /health
GET /api/stats
POST /api/nodes (auth required)
GET /api/nodes
GET /api/nodes/:id
DELETE /api/nodes/:id (auth required)
POST /api/edges (auth required)
GET /api/neighbors/:id
POST /api/activate
GET /api/activate
POST /api/search
GET /api/search
POST /api/strengthen (auth required)
POST /api/save (auth required)
POST /api/load (auth required)
Auth: ENGRAM_API_KEY in env. GET routes pass through (read-only).
Mutating routes require {"_auth": "<key>"} in the JSON body until
http_serve surfaces request headers and we can switch to Bearer.
Persistence: engram_save / engram_load via JSON snapshot at
$ENGRAM_DATA_DIR/snapshot.json. Loaded best-effort on startup.
Build: dist/platform/elc src/server.el > dist/engram.c
cc -std=c11 -O2 -I <runtime> -lcurl -lpthread -o dist/engram
dist/engram.c <runtime>/el_runtime.c
Live: native binary at dist/engram (113 KB), running under
~/Library/LaunchAgents/ai.neuron.engram.plist on :8742. Verified:
GET /api/stats returns counts; POST /api/nodes with auth creates
node with UUID; GET /api/search returns full node JSON; spreading
activation returns hop-decayed strengths (0.8 × edge × decay per
hop) with epistemic confidence filtering.
Legacy (5797 lines of SQLite-era src) sealed at
~/Archives/engram-src-legacy-20260430.tar.gz and removed from disk.
10 KiB
engram-el Specification
Version 1.0.0 — April 29, 2026
Overview
engram-el is the El-native interface layer for the Engram graph engine. It is the integration point between El programs and the Engram knowledge substrate — providing a suite of El programs, test suites, and utilities that operate on a live Engram server via its HTTP API using El's native HTTP builtins.
engram-el has three primary components:
-
Studio — A full-featured terminal-based graph explorer written in El (
studio/studio.el). Provides read access to all graph data: statistics, node browsing by type and tier, spreading activation visualization, edge exploration, and text search. -
Test suite — Language feature tests (
test/language_features_test.el,test/field_test.el,test/llm_test.el) that exercise El builtins against a live Engram instance. -
Integration point — The pattern for how El programs use the Engram graph as their knowledge substrate, demonstrating the graph builtin API in practice.
1. Architecture
1.1 Relationship to El and Engram
engram-el is not a library in the conventional sense. It is a collection of El programs that operate on Engram. The integration uses no additional runtime or SDK:
- El builtins provide
http_get,http_post, and JSON parsing natively. - Engram HTTP API is the sole interface — all graph operations are HTTP requests.
- No compilation step beyond standard El compilation is required.
This demonstrates the intended usage pattern for all El programs that incorporate graph knowledge: use the HTTP API via El's native builtins.
1.2 Configuration
All engram-el programs read configuration from environment variables:
| Variable | Default | Description |
|---|---|---|
ENGRAM_URL |
http://localhost:8340 |
Engram server base URL |
ENGRAM_REPORT |
/tmp/engram-studio-report.txt |
Studio report output path |
2. Studio Application
studio/studio.el is a complete data exploration application for the Engram graph, written entirely in El. It demonstrates El as a serious application language — not a scripting language but a capable system for building non-trivial tools.
2.1 Features
The studio renders a full-page terminal UI with box-drawing characters and ANSI color. Sections:
| Section | Description |
|---|---|
| Database Statistics | Node count, edge count, average salience, DB size |
| Recent Nodes | Most recently created nodes with type and salience |
| Top by Salience | Highest-salience nodes with graphical bar display |
| Nodes by Type | Browse Memory, Concept, Event, Entity, Process, InternalState |
| Nodes by Tier | Browse Working, Episodic, Semantic, Procedural tiers |
| Knowledge Browser | Concept nodes as domain knowledge anchors |
| Text Search | Full-text search results with relevance |
| Edge Explorer | Sample of edges with weights and relation types |
| Node Detail | Full node data plus BFS neighbors |
| Spreading Activation | Visual activation surface from a seed node |
| Interactive Mode Preview | Menu of available commands |
| Report Export | Write complete session report to file |
2.2 API Access Pattern
The studio uses a uniform API access pattern:
fn api_get(path: String) -> String {
let url: String = get_base_url() + path
let resp: String = http_get(url)
if str_starts_with(resp, "{\"error\"") {
return ""
}
return resp
}
fn api_post(path: String, body: String) -> String {
let url: String = get_base_url() + path
let resp: String = http_post(url, body)
if str_starts_with(resp, "{\"error\"") {
return ""
}
return resp
}
Error responses (JSON objects beginning with {"error") return empty string. All rendering logic checks for empty string and emits placeholder messages rather than crashing.
2.3 Spreading Activation Visualization
The activation section demonstrates reading live spreading activation results from Engram:
fn show_activation(seed_id: String, limit: Int, report: String) -> String {
let path: String = "/api/activate?seeds=" + seed_id + "&limit=" + int_to_str(limit) + "&depth=3"
let json_str: String = api_get(path)
// ... renders activation strength bars and hop distances
}
This provides visual confirmation that the spreading activation algorithm is operating — showing which nodes activate, at what strength, and at what hop distance from the seed.
2.4 Report Export
The studio accumulates a text report as it renders each section, then writes the complete report to a file:
export_report(report, report_path)
The report captures the full session output in machine-readable format, useful for automation and logging.
3. Test Suite
3.1 Language Features Test
test/language_features_test.el exercises El language primitives including:
- Modulo operator (
%) - Bitwise operators (
&,^,<<,>>) - Math builtins (
math_sin,math_cos,math_pi) - String padding (
str_pad_left,str_pad_right) - String formatting (
str_formatwith{key}template interpolation) - Float formatting (
format_float) - Time operations (
time_now_utc,time_format,time_add,time_diff) - List operations (
list_range,list_join) - Stack and queue builtins (
stack_new,stack_push,stack_pop,stack_peek,queue_enqueue,queue_dequeue) - Decimal rounding (
decimal_round) - Type conversion (
int_to_float,float_to_int) - Nil checks (
is_nil,unwrap_or) - Character operations (
str_char_at,str_char_code,str_from_char_code)
These tests serve as the canonical behavioral specification for El builtins — any correct El implementation must produce the documented output for these inputs.
3.2 Field Test
test/field_test.el exercises struct field access, map indexing, and nested data access patterns.
3.3 LLM Test
test/llm_test.el exercises the LLM inference builtins against a live Engram-connected inference endpoint.
4. Integration Patterns
4.1 Graph Read Pattern
The standard pattern for reading from Engram in an El program:
fn get_nodes_of_type(node_type: String, limit: Int) -> List {
let path: String = "/api/nodes?node_type=" + node_type + "&limit=" + int_to_str(limit)
let json_str: String = http_get(env("ENGRAM_URL") + path)
if json_str == "" {
return list_new()
}
return json_parse(json_str)
}
4.2 Graph Write Pattern
The standard pattern for writing to Engram from an El program:
fn create_node(label: String, content: String, node_type: String, tier: String) -> String {
let body: String = "{\"label\":\"" + label + "\",\"content\":\"" + content + "\",\"node_type\":\"" + node_type + "\",\"tier\":\"" + tier + "\",\"importance\":0.5}"
let resp: String = http_post(env("ENGRAM_URL") + "/api/nodes", body)
return json_get_string(resp, "id")
}
4.3 Search Pattern
fn search_graph(query: String, limit: Int) -> List {
let path: String = "/api/search?q=" + query + "&limit=" + int_to_str(limit)
let json_str: String = http_get(env("ENGRAM_URL") + path)
if json_str == "" {
return list_new()
}
return json_parse(json_str)
}
4.4 Activation Pattern
fn activate_from_node(node_id: String, depth: Int, limit: Int) -> List {
let path: String = "/api/activate?seeds=" + node_id + "&depth=" + int_to_str(depth) + "&limit=" + int_to_str(limit)
let resp_str: String = http_get(env("ENGRAM_URL") + path)
if resp_str == "" {
return list_new()
}
let results_raw: String = json_get_raw(resp_str, "results")
return json_parse(results_raw)
}
5. Builtin Extensions Demonstrated
The engram-el programs demonstrate El builtins that are not in the core language but are implemented by the VM's builtin dispatch layer:
5.1 JSON Builtins
| Builtin | Used for |
|---|---|
json_parse(s) |
Parse Engram API responses |
json_stringify(v) |
Serialize values to JSON for API requests |
json_get_string(json, key) |
Extract string fields from node JSON |
json_get_int(json, key) |
Extract integer fields (counts, timestamps) |
json_get_float(json, key) |
Extract float fields (salience, weights) |
json_get_raw(json, key) |
Extract nested objects as raw JSON strings |
5.2 Color/Terminal Builtins
| Builtin | Used for |
|---|---|
color_bold(s) |
Section headers, labels |
color_dim(s) |
Timestamps, IDs, less important data |
color_green(s) |
Success states, high salience |
color_yellow(s) |
Warnings, medium salience |
color_cyan(s) |
URLs, relation names, special values |
color_red(s) |
Errors, low salience |
5.3 String Formatting Builtins
| Builtin | Signature | Description |
|---|---|---|
str_pad_right(s, width, pad) |
Pad string to width on right | |
str_pad_left(s, width, pad) |
Pad string to width on left | |
format_float(f, decimals) |
Format float to N decimal places | |
str_slice(s, start, end) |
Extract substring by character index | |
str_len(s) |
String length in characters |
6. Deployment
6.1 Running the Studio
# Connect to default local server
el run-file studio/studio.el
# Connect to remote server
ENGRAM_URL=http://engram.example.com el run-file studio/studio.el
# Save report to custom path
ENGRAM_REPORT=/var/log/engram-report.txt el run-file studio/studio.el
6.2 Running Tests
el run-file test/language_features_test.el
el run-file test/field_test.el
ENGRAM_URL=http://localhost:8340 el run-file test/llm_test.el
7. Design Decisions
7.1 Pure HTTP Integration
engram-el uses HTTP exclusively. It does not use the lower-level graph_compile and graph_traverse VM builtins. This is by design: it demonstrates the HTTP API surface as the primary integration mechanism. The VM builtins are for tightly-integrated runtime code (the Neuron daemon); external tools use the HTTP API.
7.2 Stateless Programs
All engram-el programs are stateless — they read state from Engram on each run and write nothing back (the studio is read-only). This is the correct architecture for exploration tools: they observe the graph without mutating it.
7.3 El as Application Language
The studio's 788 lines of El demonstrate that El is a capable application language. It is not a configuration DSL or a scripting language for simple tasks. The studio handles: API communication, JSON parsing, recursive data rendering, ASCII art, ANSI color codes, file I/O, environment variable configuration, and complex string manipulation — all with El's native builtins, without imports.