This repository has been archived on 2026-05-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Will Anderson 275742e6da engram: runtime-native rewrite
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.
2026-04-30 13:49:28 -05:00

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:

  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:

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_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:

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.