Xedge is a Lua foundation built upon the Barracuda App Server. Xedge provides a rapid development environment for edge devices. With Xedge, developers rapidly test and prototype code, bypassing the need for a full compile and deploy cycle. This accelerates the iteration and development process, enhancing productivity.
Xedge essentially transforms your embedded device into a high level operating system, allowing you to run multiple Lua applications simultaneously. With the Xedge IDE, you can develop not only web applications but also connect to IoT cloud service providers using the many IoT protocols supported by the platform. You can have applications running while developing new ones, all at the same time.
The above video shows how to create a web app, open files, and make them sticky in the editor. The video also shows how to start and stop applications. In the video, we are using the Xedge integrated into our online Lua tutorial, and you may immediately test Xedge by navigating to the Lua tutorial: Xedge. With Xedge, you can create and manage any number of Lua (IoT) and LSP (web) applications. In the above video poster, you can see two applications running, indicated by the green color in the left pane. Note that the 'net' file system is a unique network file system that cannot be used without being initialized. See the tutorial Using the NetIO for details.
When utilizing the Mako Server, Xedge is typically loaded as follows:
mako -l::xedge.zip
To build Xedge for your Mako Server, start by downloading the BAS Resources from the GitHub repository. Once downloaded, go to the 'build' directory. Here, you can run either 'XedgeMako.cmd' for Windows or 'XedgeMako.sh' for Unix-based systems. For an immediate hands-on experience, check out the online Lua tutorial, which features a fully functional Xedge IDE that is ready to go.
When running standalone on an embedded device, the Xedge Lua application and the web UI are integrated into the firmware, along with an Xedge C application. For firmware build instructions, refer to the Xedge C application documentation. This document outlines the usage of Xedge.
Additionally, we offer an advanced Xedge reference version for the ESP32 microcontroller, known as Xedge32:
Xedge securely stores all configuration data, including passwords and other sensitive information, in an encrypted JSON-encoded database file named xcfg.bin. This database is encrypted using a symmetric key protected by White-box Cryptography.
In the standalone version of Xedge - where the server-side application is embedded as a ZIP file within the firmware - the encryption key is securely embedded and safeguarded against any extraction attempts. However, to ensure the Trusted Platform Module (TPM) protects the database effectively, you need to configure specific settings in the C code. For detailed instructions, see the Standalone Xedge Version and Security Settings section.
The Xedge Mako Server version uses a public secret, allowing the database to be decrypted only on the same machine where it was created. This means that copying the xcfg.bin file to another machine will not compromise the data, as decryption won't be possible elsewhere. To enhance the security of the Xedge Mako Server version and strengthen database protection, follow the guidelines provided on the Xedge GitHub page.
Xedge utilizes the Monaco Editor when it has access to the Internet and falls back to a basic notepad-like editor when it cannot download the Monaco Editor via the unpkg.com Content Delivery Network (CDN). The Monaco Editor is a powerful, feature-rich code editor; for example, the core of Visual Studio Code (VS Code) is based on this editor. Due to its relatively large size, Xedge does not embed the editor as part of its own package. If you see the message "cannot load the Monaco Editor," it means your browser cannot access the Internet or the unpkg.com CDN.
Upon starting Xedge, the left pane displays the File Systems set up by the C code, which may vary depending on the platform Xedge is running on. To use Xedge, you must create an application by navigating into one of the file systems and selecting a directory as the root for the application. You can also create new directories using the file browser. Alternatively, you can choose a deployed Lua application packaged as a ZIP file, but note that the app will be read-only in the IDE. For more information on deploying applications, see the tutorial Xedge Application Deployment: From Installation to Creation.
The tutorial Your First Xedge32 Project is indispensable reading, even for those not interested in the ESP32. It provides valuable insights into effectively using the Xedge IDE.
Begin by expanding a file system. Navigate to the destination directory or create a new one using the Context Menu. You can open the Context Menu by right-clicking on a resource or long-pressing using a tablet or phone. Select "New App" in the Context Menu to open the Application Configuration dialog. After creating an application, the left pane tree view refreshes, and your new app appears in the tree view.
.preload script:
Xedge provides the same .preload script as the Mako Server. See the tutorial Lua Environments for more information on how both Xedge and Mako Server sets up the application environments.
An application in Xedge is a directory or a zip file and comprises one or more Lua programs managed by Xedge. An application can include a .preload file containing Lua code and any number of individual Lua files with the .xlua extension. The .preload file and .xlua files automatically execute when an application starts and automatically stops when the application is turned off.
An application in Xedge can also include web applications. This is possible if the application is 'LSP enabled'. To make an application 'LSP enabled', you need to adjust its settings through the 'Application Configuration' menu. In simpler terms, the 'LSP enabled' setting allows your application to include and manage web applications. This is done through the 'Application Configuration' dialog. We'll delve into the specifics of web applications and 'LSP enabled' applications in detail later.
Every .preload file and .xlua file in an Xedge application runs in its own unique Lua environment. This means they operate separately and independently from each other. However, it's important to note that .xlua files inherit elements from the .preload file. This means that any functions, variables, or libraries defined in the .preload file can be used by the .xlua files. Further, each .preload file inherits from the global environment. This means that any elements defined in the global environment are also accessible to the .preload file and the .xlua files. Think of these environments like a stack of individual sheets, where each sheet represents an environment. The global environment is at the top, the .preload file environment is in the middle, and the .xlua file environments are at the bottom. This relationship is illustrated in figure 1.
If you are new to Lua, be sure to check out our online Lua scope tutorial. It provides a concise, comprehensive introduction to Lua environments.
The .xlua files are the cornerstones of the Lua REPL. Each running .xlua file should be considered as an independent program. Saving this file terminates any previous version, and the new version is loaded and executed. By separating Lua into individual programs, developers can work on one program while having separate programs run independently, enabling development in a running system.
An '.xlua' file is typically used to write non visual programs that interact with hardware resources, such as GPIO, or to operate as an MQTT client. However, it's not designed for creating web applications (visual applications).
Web Applications:
If you want to design web applications in Xedge, you'll need to use the 'LSP' (Lua Server Pages) feature. This requires enabling 'LSP' for your application, which you can do via the application configuration dialog.
An 'LSP-enabled' application contains files with the '.lsp' extension. Usually, there will be a file called 'index.lsp' in the root directory of the application, which serves as the entry point for your web application.
While '.lsp' pages can access the environment established by the '.preload' file, they do not inherit any elements from it, unlike '.xlua' files. To better understand how '.lsp' pages operate and interact with the rest of the application, please refer to the Command Environment section."
Xedge has been specifically designed to enable rapid teardown and recreation of Lua programs, facilitating swift testing and development. The Lua language features a garbage collector that, over time, collects (destroys) any non-referenced Lua code. When restarting a program, the old version is dereferenced and replaced with a new one. However, when dealing with hardware resources, relying on the garbage collector may not be practical, as there might only be one or a very limited set of hardware resources. Generally, hardware resources must be released immediately in a REPL. For this reason, Xedge includes logic that can automatically shut down resources when a program is renewed.
For optimal results, hardware resources should not be declared using the Lua keyword "local" when using the REPL. If you do, ensure that you use the unload handler to release the resource. The unload handler is executed when the program is shut down (or replaced with a new version).
ESP32 example 1, not using the "local" keyword:
pin18 = esp32.gpio(18,"OUT")
ESP32 example 2, using the "local" keyword:
local pin18 = esp32.gpio(18,"OUT") function onunload() pin18:close() end
The above considerations apply to any resource, not just hardware resources, when using the REPL.
The Xedge UI is a Single Page Application (SPA) that primarily interacts with the Xedge server-side application through a virtualized file system. You can directly access this file system by navigating to http://device-address/rtl/apps/. figure 2 below shows the Web File Manager and the Windows File Explorer (mapped as a WebDAV drive). When using the Web File Manager or WebDAV, it is not possible to visually distinguish applications from file systems. You can directly access the files using any editor when the device is mapped as a WebDAV drive. For instance, saving an .xlua file using an external editor will trigger the logic for running the file on the server side.
Check the WebDAV product page if you are new to WebDAV.
You can access the configuration menu by clicking the 3 dots located in the top-right corner of the interface.
By enabling the SMTP settings, Xedge gains the ability to send crucial information via email. One notable feature includes reporting Lua exceptions. In case one of your Lua programs crashes, a detailed message will be sent to your email address. SMTP settings also enable the xedge.elog() function, allowing you to send messages through email. Keep in mind that, unless it's an emergency with the flush attribute set, messages are accumulated and sent in bulk either when the maximum size or time limit is reached. Note that email messaging is automatically disabled when the Xedge browser UI is connected to an Xedge device.
See the tutorial How to Send Emails with the Xedge IDE for a step-by-step guide to configuring the SMTP settings.
The automatic X.509 certificate management powered by SharkTrustX addresses the challenges outlined in the tutorial Why You Need Automatic Certificate Management by offering simple configuration options and programmer-friendly APIs for automating X.509 certificate management.
Xedge simplifies installing and updating X.509 certificates, making it an easy-to-use solution for private network (LAN) deployments. This feature ensures that your browser remains warning-free when you access your Xedge device using an HTTPS address.
As shown in the above slideshow video, activating the Automatic Certificate Management Environment (ACME) feature is straightforward. Simply input a valid email address and choose a name for your device. Your email address will be sent to the Let's Encrypt Certificate Authority, while the device name you select becomes part of your fully qualified domain name, following the format: [name-selected].x.realtimelogic.com.
Xedge might recommend a unique name based on its configuration. For instance, a name like '4358f4' could be automatically generated, drawing from the lower 3 bytes of the MAC address. Feel free to choose any name, but be aware that already-used names will result in an error message.
Note that activating the automatic certificate management feature may take up to 3 minutes to become fully operational.
Elliptic Curve Cryptography Keys & soft Trusted Platform Module
When ACME is enabled through the user interface, it utilizes an integrated soft Trusted Platform Module (TPM). This module generates Elliptic Curve Cryptography (ECC) keys, which are never stored in plaintext. Instead, key attributes like the ECC curve type are saved in a key file on the disk. These attributes allow Xedge to regenerate the keys upon each startup. For a more detailed insight into this process, refer to the TPM section in Xedge's C code documentation.
Advanced Configuration with Lua
ACME can be configured programmatically for those who prefer a more hands-on approach using Lua or when the UI has been disabled. The ACME user interface, as shown in the above figure, interacts with two Lua modules: acme/bot and acme/dns. Both Lua modules are integral to the Mako Server and included in Xedge.
The following example shows how to set the required settings programmatically for a server deployed within a private network. The values can be hard coded, or you can ask the user to input data.
local ad=require"acme/dns" if not ad.active() then -- This section will only execute one time -- email and subdomain name comes from a user or hardcoded: local email,subdom="info@realtimelogic.com", "xyz" -- If hardcoded, subdomain is typically derived from MAC address local options={revcon=true,acceptterms=true} ad.auto(email,subdom,options) -- Persistently store the revcon state (required if enabled) xedge.revcon(options.revcon) else -- if previosly activated -- Xedge reactivation is automatic, including revcon state. end
Domain Name and Professional Use
It's important to note that x.realtimelogic.com is a test portal for SharkTrustX, suitable for testing and home use. However, for professional applications, setting up a dedicated portal is recommended. For more information on this, please refer to the SharkTrustX product page.
ACME and SharkTrustX References:
The Reverse Connection feature, included in the Let's Encrypt plugin, allows remote access to your Xedge via the SharkTrustX demo portal. This feature requires activation of the Automatic Certificate Management. You can easily enable or disable the remote connection at any time without the need to click the save button after toggling the Reverse Connection switch. See SharkTrustX for details.
Xedge supports both local user accounts and Single Sign-On (SSO). To open the Authentication Settings, click the 3 dots, followed by clicking on Authentication.
Note that installing an authenticator complicates the use of the Web File Manager and the WebDAV server. When an authenticator is installed, you must first log in to Xedge at the URL http://device-address/rtl/. After that, you can navigate to http://device-address/rtl/apps/. To use WebDAV, log in using your browser, navigate to the Web File Manager, and click the Session URL button, which generates a unique URL with an included token. Use this URL when mapping the WebDAV drive.
You can create multiple local users. To add a new user, input the desired username and password. To remove an existing user, enter the username and leave the password field empty.
SSO is a user authentication process that enables users to access multiple applications with just one set of login credentials.
If you are new to SSO, be sure to check out the Single Sign-On tutorial. It provides an introduction to using SSO with Xedge.
Xedge supports Microsoft Entra ID Single-Sign-On (SSO), a feature that simplifies user access to multiple applications using a single set of credentials. To set up users, they must be part of the organization's Entra ID tenant, with appropriate roles and permissions assigned to access the desired applications. Configuring SSO involves registering the applications, defining user access, and managing authentication settings in the Entra ID portal.
Entra ID offers a free tier with basic features that can support smaller-scale use, such as for personal or family purposes. To get started, you need to sign up for a free Microsoft Azure account and create an Entra ID tenant. Then, you can invite your friends as users in your tenant and configure access to applications using Entra ID and SSO.
After signing up for Entra ID, create the required SSO settings as follows:
http(s)://name/rtl/login/
. For example, if you are setting this up for testing purposes, enter the URLs http://localhost/rtl/login/
and https://localhost/rtl/login/
.Actions to take when the client secret expires:
The client secret can at a maximum be set to two years. When a user attempts to authenticate and the client secret has expired, an HTML form will be presented to the user. This form will ask the user to enter a new client ID secret value. To continue logging in a user with access to the Azure portal must create a new secret value and enter this value in the Xedge form.
After completing the above instructions, you can grant individual users within the organization access to the application by following these steps:
Xedge extends the API provided by the Barracuda App Server with a few additional APIs. Additionally, the Xedge ESP32 reference version Xedge32 extends Xedge with a wealth of HW APIs.
The global xedge object includes many functions and objects. All of them should be considered private, except for the ones listed below.
Initializes a Lua code loader to enable the dynamic loading of Lua modules from within the application, specifically from a ".lua" sub-directory at the application's root directory. This function extends Lua's package loading mechanism by adding a custom loader to the package.cpath array, where Lua stores its module loaders. See Lua modules for details.
To facilitate the loading of Lua script modules from within your application, add the following line at the beginning of your application's .preload script:
xedge.createloader(io) -- io is the application's pre-defined IO interface
The loader is designed to function irrespective of the application's deployment state, supporting both deployed (ZIP) and non-deployed (directory) formats, ensuring flexibility across different deployment scenarios.
Note: Once a module is loaded using the require function, it remains in memory for the duration of the application's runtime. Restarting the application does not reload the module if its code has been modified. The following approach can be used to reload a module manually:
package.loaded["moduleName"] = nil
Replace "moduleName" with the actual name of your module. This command clears the previously loaded module, allowing "require" to reload it upon the following invocation.
Functions elog and eflush are enabled if the SMTP settings have been configured. Function elog either appends to the internal message queue or flushes the queue after 30 seconds if the option table includes the attribute flush set to true. Function eflush sets flush to true and immediately flushes the message queue by sending an email.
The option table op may include:
Note: When a browser is connected to the Xedge IDE, calling xedge.elog() has the same effect as calling xedge.log(). In other words, no email is sent or buffered, thus calling xedge.eflush() has no effect.
Calculates and returns the MD5 HA1 digest. The realm defaults to 'Xedge' if not set.
Send a message to the trace buffer, using priority 5, with the following prepended: 'Xedge:'.
The virtual file system used by Xedge provides the same API as the Barracuda App Server's IO Interface.
Xedge.event() serves as a messaging distribution handler. To subscribe to an events, install a callback function. To remove the callback, call the function with the same parameters and set the third argument to true.
To trigger an event, invoke the global function _XedgeEvent(event, ...). The first parameter must specify the event name. The _XedgeEvent() function is primarily designed for being called from C code, offering an efficient and straightforward approach to transmitting events to Lua code. For more details, consult the Xedge C application documentation. Additionally, see Xedge32 for information on how the C code generates events for the ESP32.
Note: The Xedge event mechanism uses an instance of the EventEmitter, and function _XedgeEvent() sets the retain flag on all events received.
Internally, Xedge creates an event when Lua code crashes, allowing you to implement logic that reboots the system if any of your scripts fail during runtime. The following example demonstrates how to design an event function that restarts the system in case your scripts experience runtime failures.
local function errEvent(emsg) -- arg1 is the error message, but it is already in the log buffer if SMTP is enabled xedge.eflush{subject="The code crashed"} -- Flush email with error message immediately ba.thread.run(function() ba.sleep(5000) -- Give SMTP time to send error message -- Call a Lua binding that reboots the system; the following works for ESP32 esp32.execute"restart" end) end xedge.event("error",errEvent) -- Subscribe
Send email messages using the SMTP client library. The options argument is the same as the options argument used by the function mail:send(options); however, no instance needs to be created and no options need to be set when calling xedge.sendmail() if you have configured the Xedge SMTP settings. Calling xedge.sendmail() with an empty options table sends a test email to the email address configured in the Xedge SMTP settings. You typically call this function with the following options: to, subject, txtbody or htmlbody. The optional callback is called when the email has been sent or sending it failed. The callback receives two arguments (ok, err).
Persistently store the SharkTrust reverse connection feature. This setting is typically used in combination with programmatically setting the ACME settings.
Enables or disables the Xedge Integrated Development Environment (IDE), including the Xedge 404 handler and the TraceLogger. By default, the IDE and its associated components are enabled. This function lets you programmatically insert and remove the IDE from the Virtual File System. The function is typically used in deployed applications to remove the IDE and related components, which are usually not included in the final product. See Creating a COTS Product and Security Considerations for additional information.
In the following app management functions, the name argument is either the name of the application as shown in the Xedge IDE or the path (URL) to the app, e.g., disk/test/myapp.zip.
Returns the configuration table if the zip file includes a .config script. The optional .config script is explained in the tutorial Mastering Xedge Application Deployment.
This function is designed to upgrade a deployed application packaged as a zip file. To use this function, replace the existing zip file with the new zip file before calling this function. The function stops the existing application if it is running and restarts the app using the latest zip file if the app is running. You can set forcerestart to true to force the application to start. Setting this argument to true saves the auto start and running state in the xedge.conf file and make sure the app auto starts at reboot.
This function also executes the upgrade function before starting the new app if the zip file includes a .config script and the table returned by .config includes the upgrade function, as explained in the Mastering Xedge Application Deployment tutorial. All other table fields are ignored.
Stop the application if it is running. Set the second argument to true if you want the app state to be saved in xedge.conf.
Start the application if it is not running. Set the second argument to true if you want the app state to be saved in xedge.conf.
A variable extracted from C code and presented as seconds since January 1, 1970.
Creating a Product Based on Xedge Standalone (Firmware Version):
Xedge, including the default IDE, is designed to facilitate the creation of commercial off-the-shelf (COTS) products directly from the platform, using both Xedge and the IDE "as is". However, you have several options, such as embedding your own application within the Xedge firmware. This section is specifically for those who wish to create a COTS product without modifying the Xedge firmware, including the embedded IDE. For those interested in more advanced options, such as embedding custom applications within the firmware, refer to the Xedge source code page.
When using Xedge "as is", you'll need to assemble a multipart system that includes the Xedge firmware, at least one application packaged as a ZIP file, and the Xedge configuration file. To create a product, follow these steps:
You should now have the Xedge configuration file and one or more applications packaged as ZIP files. To create a COTS product, ensure the device is powered on and runs the Xedge firmware. Then, follow these steps to upload your files:
Note: when creating a COTS product, you should restrict access to the Xedge IDE as detailed below.
If you want to prevent your customers from writing their own custom Lua applications, you can either prevent access to the UI by adding SSO authentication or remove the web-based Xedge UI completely from your application at startup. Here are the details on both methods:
Removing Xedge UI: If you prefer, you can completely remove the Xedge UI from your application. You can achieve this by adding the following code to your application's .preload script. Be sure to understand that this action will remove the UI completely, so it's only reversible with proper reinstallation or software changes.
-- The following code removes the Xedge user interface and the Xedge -- 404 handler. A new 404 handler is installed, redirecting all -- requests to another server. if "zip" == io:resourcetype() then trace"Detecting deployed mode: removing Xedge UI!" -- Remove the Xedge UI's directory object and all sub-directories xedge.ui(false) end
If you do not need a web server, you can close all server connections. A BAS-powered server has at least two connections but up to four on some platforms. The following generic code should work on all platforms.
for _,con in ipairs{ba.lcon,ba.slcon,ba.lcon6,ba.slcon6} do con:close() end