Skip to content

Development Guide

Set up the ZERG development environment, build all components, and understand the codebase structure.

Prerequisites

DependencyVersionPurpose
Erlang/OTP28Sol server
rebar3LatestErlang build tool
LuaJIT2.1+Luna agent, CLI
Python3.11+Mango auth service
Node.js18+Limon, docs-site
PostgreSQL16+Memory/embeddings (optional)

Building the Server

bash
cd server
rebar3 compile

For a release build:

bash
rebar3 as prod release

Run in development mode:

bash
rebar3 shell

The development shell starts the Sol application with default configuration. The HTTP API is available at http://127.0.0.1:21434.

Building the Luna Client

bash
cd client
bash build/build.sh

The build script produces a stripped binary at client/build/luna (approximately 994KB). The build pipeline compiles Lua to bytecode, applies XOR obfuscation, and appends CRC32 checksums.

Building the CLI

bash
cd zerg-cli
bash build/build.sh

Produces a standalone binary at zerg-cli/build/zerg (321-377KB). Built with luastatic, XOR, and UPX compression.

Running Tests

Server Tests (Erlang eunit)

bash
cd server
rebar3 eunit

4,600+ test functions across 433 test files.

Luna Tests (LuaJIT)

bash
cd client
luajit tests/test_redaction.lua
luajit tests/test_shell_quoting.lua
luajit tests/test_permission_checker.lua

Run all tests:

bash
for f in client/tests/test_*.lua; do luajit "$f"; done

Mango Tests (Python)

bash
cd mango
python3 -m pytest tests/ -v

348 tests, 0 expected failures.

CLI Tests

bash
cd zerg-cli
bash tests/run.sh

487 assertions, 0 expected failures.

Limon Tests

bash
cd limon
npm test

379 tests, 0 failures (11 pre-existing known).

Code Structure

Server (server/src/)

The server follows OTP conventions with 289 modules:

Module PrefixCountPurpose
sol_http_~15HTTP request handlers
sol_zmq_~8ZMQ gateway and protocol
sol_plugin_~12Plugin system (Luerl sandbox)
sol_worker_~5Worker process management
sol_infra_~5Infrastructure (health, metrics, SLO)
sol_memory_~5Memory and embedding storage
sol_sched_~3Scheduler (cron/at/every)

Client (client/)

DirectoryPurpose
core/Core agent logic, tools, providers
ui/TUI rendering (LTUI/ncurses)
ffi/Foreign function interface bindings
vendor/Third-party libraries (middleclass, etc.)

Naming Conventions

Erlang modules: sol_<domain>_<component>.erl

  • sol_http_conversations.erl -- HTTP handler for conversations
  • sol_zmq_gateway.erl -- ZMQ gateway gen_server
  • sol_scheduler.erl -- Scheduler gen_server
  • sol_plugin_manager.erl -- Plugin manager gen_server

Lua modules: <domain>_<component>.lua or <domain>/<component>.lua

  • core/redaction.lua -- Secret redaction engine
  • core/permission_checker.lua -- Permission evaluation
  • ffi/zmq.lua -- ZMQ FFI bindings

Adding New HTTP Endpoints

1. Create the Handler Module

Create server/src/sol_http_mymodule.erl:

erlang
-module(sol_http_mymodule).
-export([handle/2]).

handle(mymodule_list, Req) ->
    Items = [],
    {200, sol_http_json:json_response(#{items => Items})};

handle(mymodule_detail, Req) ->
    Id = cowboy_req:binding(id, Req),
    {200, sol_http_json:json_response(#{id => Id})}.

2. Register the Route

Add the route in sol_http.erl to the Cowboy dispatch list:

erlang
{"/api/v1/mymodule", ?MODULE, [mymodule_list]},
{"/api/v1/mymodule/:id", ?MODULE, [mymodule_detail]}

3. Wire the Handler

Map the route atom to the handler module:

erlang
mymodule_list => {sol_http_mymodule, handle},
mymodule_detail => {sol_http_mymodule, handle}

4. Add Ownership Check (if needed)

For user-scoped resources, use sol_identity:check_resource_owner/3.

Adding New Luna Tools

1. Create the Tool Module

Create client/core/tools/my_tool.lua:

lua
local MyClass = require("cel.class")("MyTool")

function MyClass:initialize(config)
    self.config = config
end

function MyClass:execute(args)
    return {output = "result"}
end

return MyClass

2. Register in Tool Registry

Add the tool definition following the builtin__ prefix convention.

3. Follow Conventions

  • Use cel.json for all JSON operations (no direct dkjson imports)
  • Use cel.class for OOP (middleclass v4.1.1)
  • Use cel.shell_quote for all shell arguments
  • Return nil, err on errors

Development Configuration

The default sys.config runs with authentication disabled for local development:

erlang
[
  {sol, [
    {http_port, 21434},
    {http_ip, "127.0.0.1"},
    {mango_auth_enabled, false},
    {memory_pg_enabled, false},
    {pgvector_enabled, false}
  ]}
].

Enable individual features as needed for testing. Authentication, memory, and pgvector are all optional for development.

Released under the MIT License.