Archived
275742e6da
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.
285 lines
10 KiB
Markdown
285 lines
10 KiB
Markdown
# 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:
|
|
|
|
1. **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.
|
|
|
|
2. **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.
|
|
|
|
3. **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:
|
|
|
|
```el
|
|
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:
|
|
|
|
```el
|
|
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:
|
|
|
|
```el
|
|
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_format` with `{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:
|
|
|
|
```el
|
|
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:
|
|
|
|
```el
|
|
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
|
|
|
|
```el
|
|
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
|
|
|
|
```el
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
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.
|