-
Notifications
You must be signed in to change notification settings - Fork 0
Philosophy
These are non-negotiable design rules. Every line of code, every module, every decision must obey them. They are not guidelines — they are laws.
Every resource, service, device, and configuration is addressed by a path. No exceptions. No special APIs. No hidden channels. If it exists in the system, it has a path.
/health → system health check
/iot/devices → list IoT devices
/node/peer1/health → health check on remote peer
/cache/session:abc → cached session data
/logic/apps → list running applications
All communication uses one structure: portal_msg_t. No direct function calls between modules. No shared memory tricks. No callbacks. One message in, one response out.
typedef struct {
char path[512]; // target path
int method; // GET, SET, CREATE, DELETE, ACTION, EVENT, SUBSCRIBE
char headers[4096]; // key:value pairs
char body[65536]; // payload
portal_context_t context; // auth + trace
} portal_msg_t;Each module is an expert at exactly one domain. mod_cache caches. mod_dns resolves names. mod_iot controls devices. No module does two things. No god-modules. If you need combined behavior, compose via messages.
The core is a router, not a doer. It receives messages, finds the owning module via path lookup, checks ACL, delivers the message. The core never implements business logic. Zero. It routes.
No role-based access. No permission matrices. Users have groups. Paths have labels. Access = intersection of user groups and path labels. If the intersection is non-empty, access is granted.
User "operator" groups: ["ops", "monitoring"]
Path "/metrics" labels: ["monitoring"]
→ intersection: ["monitoring"] → ACCESS GRANTED
Path "/admin/users" labels: ["admin"]
→ intersection: [] → ACCESS DENIED
Modules load, unload, and reload at runtime. No restart required. The core tracks reference counts — unload waits for active handlers to finish. Config changes: reload. Bug fix: reload. New feature: load.
portal:/> module load mod_iot # load at runtime
portal:/> module reload mod_web # zero-downtime update
portal:/> module unload mod_hello # clean removal
A module crash (segfault, SIGFPE, stack overflow) must never bring down the core. The core wraps every handler call in setjmp/longjmp. If a module crashes, the core logs the fault, returns 500 to the caller, increments the crash counter, and continues serving.
if (setjmp(crash_buf) == 0) {
rc = handler->handle(core, msg, resp); // module code
} else {
resp->status = 500; // module crashed
snprintf(resp->body, sizeof(resp->body),
"Module %s crashed (signal %d)", mod->name, crash_signal);
mod->stats.crashes++;
}Every message carries a trace context: trace_id (UUID), timestamp (µs), hops counter. When a message crosses a federation link, hops increment. When it passes through modules, the trace follows. Full distributed tracing with zero configuration.
Configuration and data persist through three simultaneous backends: file (always), SQLite (optional), PostgreSQL (optional). The core writes to all enabled backends. Reads try PostgreSQL first, then SQLite, then file. Modules never know which backend served the data.
Remote nodes appear as local paths. /node/peer1/health routes to the health path on peer1 transparently. The wire protocol (PORTAL02) handles TLS encryption, key authentication, and message serialization. Modules don't know (or care) if a message is local or remote.
Modules declare events. Other modules subscribe. The event system is ACL-controlled — you can only subscribe to events whose labels intersect with your groups. Pattern matching supports exact (/events/iot/device_added), wildcard (/events/iot/*), and global (*).
The core and every module compile with standard C libraries only. Embedded: libev (event loop), sha256, cJSON. No boost, no glib, no external frameworks. Optional: libsqlite3, libpq, liblzma, libz, libssh, libldap — but the core runs without them.
Lua (embedded, in-process), Python (forked subprocess, JSON pipe), C (gcc compile + dlopen), Pascal (fpc compile + dlopen). All four expose the same API: portal.get(), portal.set(), portal.call(), portal.route(), portal.on(), portal.log().
All configuration is accessible via /config/* paths. Read config with GET, modify with SET. Changes persist to all storage backends. Modules watch for config changes via events. No config files are read directly after startup — everything goes through the path system.
| # | Law | Core Principle |
|---|---|---|
| 1 | Everything is a Path | Universal addressing |
| 2 | Every Interaction is a Message | Single communication primitive |
| 3 | Modules Do One Thing | Single responsibility |
| 4 | The Core Routes, Modules Act | Separation of concerns |
| 5 | Labels Control Access | Set-intersection ACL |
| 6 | Hot-Load Everything | Zero-downtime operations |
| 7 | Crash Isolation | Fault tolerance |
| 8 | Trace Everything | Observability built-in |
| 9 | Three Storage Backends | Redundant persistence |
| 10 | Federation is Transparent | Location-transparent networking |
| 11 | Events are First-Class | Reactive architecture |
| 12 | No External Dependencies | Self-contained core |
| 13 | Four Scripting Engines | Polyglot with unified API |
| 14 | Configuration is a Path | Config as data |
Top Level System — GPL-2.0 | Website | Repository
mod_cli · mod_web · mod_node · mod_ssh · mod_config_sqlite · mod_config_psql
mod_cache · mod_kv · mod_shm · mod_queue · mod_websocket · mod_mqtt · mod_email · mod_file
mod_logic · mod_logic_lua · mod_logic_python · mod_logic_c · mod_logic_pascal
mod_metrics · mod_health · mod_sysinfo · mod_process · mod_log · mod_audit · mod_cron · mod_scheduler · mod_worker · mod_backup
mod_proxy · mod_dns · mod_http_client · mod_webhook · mod_api_gateway · mod_tunnel · mod_acme
mod_firewall · mod_crypto · mod_ldap · mod_validator
mod_iot · mod_gpio · mod_serial
mod_xz · mod_gzip · mod_template · mod_admin