Home / TLS / Core API

Core API Reference

Complete reference for all types, methods, status codes, and function pointers available to modules. Include one header: portal/portal.h

Single header. Everything a module needs is exposed through portal/portal.h. The core API is intentionally small — every function pointer in portal_core_t exists because a module cannot do its job without it.

1. Types

portal_header_t — Key-Value Pair

The fundamental unit for message metadata. Headers carry auxiliary information alongside the body.

typedef struct {
    char *key;
    char *value;
} portal_header_t;

portal_labels_t — Label Set

Labels control access to paths. A path can require one or more labels; only authenticated users (or modules) whose token carries matching labels can reach it.

typedef struct {
    char  labels[PORTAL_MAX_LABELS][PORTAL_MAX_LABEL_LEN];
    int   count;
} portal_labels_t;
FunctionSignatureDescription
portal_labels_add(portal_labels_t *set, const char *label)Add a label to the set
portal_labels_remove(portal_labels_t *set, const char *label)Remove a label from the set
portal_labels_has(portal_labels_t *set, const char *label)Check if label exists in set
portal_labels_intersects(portal_labels_t *a, portal_labels_t *b)Check if two sets share any label
portal_labels_clear(portal_labels_t *set)Remove all labels from the set

portal_msg_t — The Universal Message

Every request, command, event, and subscription flows through this single structure. This is Law 2 in code.

typedef struct {
    uint64_t          id;
    char             *path;
    uint8_t           method;
    uint16_t          header_count;
    portal_header_t  *headers;
    void             *body;
    size_t            body_len;
    portal_ctx_t     *ctx;    // auth + trace
} portal_msg_t;
FunctionSignatureDescription
portal_msg_alloc(void)Allocate a new empty message
portal_msg_free(portal_msg_t *msg)Free message and all owned memory
portal_msg_set_path(portal_msg_t *msg, const char *path)Set the target path
portal_msg_set_method(portal_msg_t *msg, uint8_t method)Set the method (GET, SET, CALL...)
portal_msg_set_body(portal_msg_t *msg, const void *data, size_t len)Set the body (copies data)
portal_msg_add_header(portal_msg_t *msg, const char *key, const char *value)Append a key-value header

portal_resp_t — Response

Returned by synchronous send calls. Carries a status code, optional headers, and a body.

typedef struct {
    uint16_t          status;
    uint16_t          header_count;
    portal_header_t  *headers;
    void             *body;
    size_t            body_len;
} portal_resp_t;
FunctionSignatureDescription
portal_resp_alloc(void)Allocate a new empty response
portal_resp_free(portal_resp_t *resp)Free response and all owned memory
portal_resp_set_status(portal_resp_t *resp, uint16_t status)Set the status code
portal_resp_set_body(portal_resp_t *resp, const void *data, size_t len)Set the response body (copies data)

portal_ctx_t — Message Context

Attached to every message. Carries authentication credentials and distributed tracing information. The core populates this automatically — modules read it but never forge it.

typedef struct {
    portal_auth_t   auth;     // user + token + labels
    portal_trace_t  trace;    // trace_id, parent_id, timestamp_us, hops
    char           *source_node;
    char           *source_module;
} portal_ctx_t;

2. Methods

Seven methods cover every possible interaction pattern. Each message carries exactly one.

ConstantValueDescription
PORTAL_METHOD_GET0x01Read a resource, no side effects
PORTAL_METHOD_SET0x02Create or update a resource
PORTAL_METHOD_CALL0x03Execute an action
PORTAL_METHOD_EVENT0x04Fire and forget notification
PORTAL_METHOD_SUB0x05Subscribe to events on a path
PORTAL_METHOD_UNSUB0x06Unsubscribe from events
PORTAL_METHOD_META0x07Query path metadata (access mode, labels, owner)

3. Status Codes

Inspired by HTTP but reduced to the essentials. Every response carries exactly one status code.

CodeConstantMeaning
200PORTAL_OKSuccess
201PORTAL_CREATEDResource created
202PORTAL_ACCEPTEDRequest accepted, processing async
400PORTAL_BAD_REQUESTMalformed message or invalid parameters
401PORTAL_UNAUTHORIZEDAuthentication required or token invalid
403PORTAL_FORBIDDENAuthenticated but insufficient labels
404PORTAL_NOT_FOUNDPath does not exist
409PORTAL_CONFLICTResource locked or version conflict
500PORTAL_INTERNAL_ERRORModule-side failure
503PORTAL_UNAVAILABLEModule not loaded or unreachable node

4. Core API (portal_core_t)

Every module receives a portal_core_t pointer on load. This is the module's only interface to the system. All interaction with the core, other modules, events, storage, and configuration goes through these function pointers.

Law 3 in practice. The core does nothing — but it gives modules everything they need. Every function pointer below exists because a module cannot do its job without it.

Path Management

Function PointerSignatureDescription
path_register(core, const char *path, const char *module_name)Register a path owned by this module
path_unregister(core, const char *path)Release a previously registered path
path_set_access(core, const char *path, uint8_t mode)Set access mode: READ, WRITE, or RW (Law 8)
path_add_label(core, const char *path, const char *label)Require this label to access the path
path_remove_label(core, const char *path, const char *label)Remove a label restriction from the path

Message Routing

Function PointerSignatureDescription
send(core, portal_msg_t *msg, portal_resp_t *resp)Send a message to any path, local or remote. Blocks until response.

Module Queries

Function PointerSignatureDescription
module_loaded(core, const char *name)Check if a module is currently loaded and available

Event Loop

Function PointerSignatureDescription
fd_add(core, int fd, uint32_t events, callback, userdata)Register a file descriptor for async I/O (Law 13)
fd_del(core, int fd)Remove a file descriptor from the event loop
timer_add(core, uint64_t interval, callback, userdata)Register a periodic timer (interval in milliseconds)

Events

Function PointerSignatureDescription
event_register(core, const char *path, const char *description, portal_labels_t *labels)Declare an event that this module can emit
event_unregister(core, const char *path)Remove an event declaration
event_emit(core, const char *path, const void *data, size_t len)Fire an event to all subscribers (Law 10)

Pub/Sub

Function PointerSignatureDescription
subscribe(core, const char *pattern, handler, userdata)Subscribe to events matching a path pattern
unsubscribe(core, const char *pattern, handler)Unsubscribe a previously registered handler

Storage

Function PointerSignatureDescription
storage_register(core, portal_storage_provider_t *provider)Register a storage backend (key-value, SQL, etc.)

Config

Function PointerSignatureDescription
config_get(core, const char *module, const char *key)Read a configuration value for a module (Law 11)

Logging

Function PointerSignatureDescription
log(core, uint8_t level, const char *module, const char *fmt, ...)Write a log entry at the specified level

Log levels:

ConstantValueUse
PORTAL_LOG_ERROR0Failures that require immediate attention
PORTAL_LOG_WARN1Unexpected conditions that are recoverable
PORTAL_LOG_INFO2Normal operational messages
PORTAL_LOG_DEBUG3Detailed diagnostic information
PORTAL_LOG_TRACE4Per-message tracing, high volume

Resource Locking

Physical resources (serial ports, GPIO, IoT devices) require exclusive access. These functions implement Law 14 — auto-lock on first write, keepalive, auto-release after inactivity.

Function PointerSignatureDescription
resource_lock(core, const char *resource, const char *owner)Acquire exclusive lock on a resource
resource_unlock(core, const char *resource, const char *owner)Release a held lock
resource_keepalive(core, const char *resource, const char *owner)Reset the 60-second inactivity timer
resource_locked(core, const char *resource)Check if a resource is currently locked
resource_owner(core, const char *resource)Return the current lock owner (or NULL)

5. Constants

Limits

ConstantValueDescription
PORTAL_MAX_PATH_LEN1024Maximum path length in bytes
PORTAL_MAX_MODULE_NAME64Maximum module name length
PORTAL_MAX_MODULES256Maximum modules loaded simultaneously
PORTAL_MAX_HEADERS32Maximum headers per message
PORTAL_MAX_EVENTS64Maximum events per module
PORTAL_MAX_LABELS32Maximum labels per set
PORTAL_MAX_LABEL_LEN64Maximum label string length

Access Modes

ConstantValueDescription
PORTAL_ACCESS_READ0x01Path accepts GET and META only
PORTAL_ACCESS_WRITE0x02Path accepts SET and CALL only
PORTAL_ACCESS_RW0x03Path accepts all methods

6. Internal Core Paths

The core registers these paths on startup. They are always available, even with no modules loaded.

/core — Core Status

PathMethodDescription
/core/statusGETReturns uptime, version, loaded module count
/core/modulesGETList all loaded modules and their state
/core/modules/{name}GETDetails of a specific module
/core/modules/{name}/loadCALLLoad a module at runtime (Law 5)
/core/modules/{name}/unloadCALLUnload a module at runtime
/core/modules/{name}/reloadCALLReload a module (unload + load)
/core/pathsGETList all registered paths with access modes
/core/config/{module}/{key}GETRead a module configuration value
/core/config/{module}/{key}SETUpdate a module configuration value
/core/log/levelGETCurrent global log level
/core/log/levelSETChange global log level at runtime

/auth — Authentication

PathMethodDescription
/auth/loginCALLAuthenticate with user + password, returns token
/auth/logoutCALLInvalidate current token
/auth/verifyCALLVerify a token and return its labels

/users — User Management

PathMethodDescription
/usersGETList all users
/users/{id}GETGet user details
/users/{id}SETCreate or update a user
/users/{id}/labelsGETGet user label set
/users/{id}/labelsSETReplace user label set

/groups — Group Management

PathMethodDescription
/groupsGETList all groups
/groups/{id}GETGet group details and member list
/groups/{id}SETCreate or update a group
/groups/{id}/membersGETList group members
/groups/{id}/membersSETAdd or remove members

/events — Event Registry

PathMethodDescription
/eventsGETList all registered events
/events/{path}GETGet event description, labels, and subscriber count
/events/{path}SUBSubscribe to an event
/events/{path}UNSUBUnsubscribe from an event

7. Communication Patterns

Request / Response

The standard synchronous pattern. A module sends a message and blocks until the target module responds. Used for GET, SET, CALL, and META.

portal_msg_t  *msg  = portal_msg_alloc();
portal_resp_t *resp = portal_resp_alloc();

portal_msg_set_path(msg, "/local/db/users/1");
portal_msg_set_method(msg, PORTAL_METHOD_GET);

int rc = core->send(core, msg, resp);

if (rc == PORTAL_OK && resp->status == PORTAL_OK) {
    // resp->body contains the user record
    process_user(resp->body, resp->body_len);
}

portal_msg_free(msg);
portal_resp_free(resp);

Fire and Forget (Events)

An event producer emits data to a path. All subscribers receive it asynchronously. The producer does not wait and does not know who (or if anyone) received it. Used with PORTAL_METHOD_EVENT.

// Producer: emit an event
const char *payload = "{\"temp\":22.5,\"unit\":\"C\"}";
core->event_emit(core, "/local/iot/sensor/temperature",
                 payload, strlen(payload));

// Consumer: subscribe to events (registered once, usually in module_load)
static void on_temperature(const char *path,
                            const void *data, size_t len,
                            void *userdata) {
    core->log(core, PORTAL_LOG_INFO, "dashboard",
              "temperature update: %.*s", (int)len, (const char *)data);
}

core->subscribe(core, "/local/iot/sensor/*", on_temperature, NULL);
Pattern or exact match. The subscribe function accepts glob patterns. /local/iot/sensor/* matches any event under that prefix. Exact paths also work.