The Mako Server is a ready-to-run Lua application server built on the Barracuda App Server C Library. It packages BAS, Lua, Lua Server Pages, SQLite support, TLS, WebSockets, and server-side APIs into a single executable for high-level operating systems such as Linux, Windows, macOS, QNX, and VxWorks.
This page explains how to use and configure the Mako Server runtime. For downloads, getting-started material, tutorials, and the broader product overview, see makoserver.net. For source-level build instructions, see Building the Mako Server with BAS. See Xedge if you need an RTOS-based Lua application environment.
If you are working with Embedded Linux, check out the Embedded Linux Web Interface Design Tutorial. It covers practical examples for controlling GPIOs and provides useful insights to streamline web interface development in embedded systems.
| Goal | Section |
|---|---|
| Use a precompiled server | Precompiled Mako Server Versions |
Understand mako.zip | Mako Server's resource file mako.zip |
| Start the server and load applications | Command-Line Options |
| Configure the server | Configuration File (mako.conf) |
| Use the browser-based IDE | The Mako Server's Integrated Development Environment |
| Use Mako runtime APIs | Functions, IO interfaces, and Modules |
Most users should start with a precompiled Mako Server from makoserver.net. The site also provides tutorials, getting-started material, and ready-to-use examples. Build from BAS only when you need to customize the executable, add C modules, change compile-time options, or port Mako to a specific platform.
The precompiled versions include the following additional modules:
Pre-integrated DNS zone key for domain local.makoserver.net.
The precompiled Mako Server includes an embedded zone key for Real Time Logic's DNS testing service. See the Let's Encrypt configuration options and module acme/dns for more information.
🚫 Warning - Do Not Use the Included Key in Production
The key embedded in the tokengen.c file is intended only for easy testing of SharkTrustX features with the local.makoserver.net domain. For deployment, you must run your own SharkTrustX service with a securely generated key.
The Mako Server's core runtime logic is implemented in Lua and stored in mako.zip. A normal deployment consists of two files: the Mako executable and mako.zip. If the executable cannot find the external mako.zip, it falls back to the embedded ZIP compiled into the executable. The embedded ZIP contains simplified fallback logic and should not be treated as a replacement for the full external resource file.
If you are new to the Mako Server or using it for development, we recommend using the mako.zip Developer Edition.
The Mako Server can load applications from command-line arguments or from instructions in mako.conf. When distributing a finished product, however, it is often more convenient to package the applications directly inside mako.zip so the deployment remains self-contained.
To embed applications in mako.zip, create the following directory:
mako.zip/apps/
Each application must be placed in its own subdirectory. For example, an application named myapp should be installed as follows:
mako.zip/apps/myapp/
By default, the application's base URL path is derived from the directory name. In this example, the application is available under the URL path /myapp/.
You can override the default base path by creating a configuration file named .config in the application's root directory. This Lua-parsed configuration file supports the following settings:
{
name = "basepath", -- Set to "" to install as the root application.
prio = 0 -- Application directory priority.
}
All embedded applications are installed as LSP applications. If an application should be loaded for internal use only and should not be accessible through a URL, create a .preload script in the application's root directory that executes the following statement:
dir:unlink()
This removes the application's URL mapping while still allowing the application to be loaded and used internally.
To modify the Lua logic in mako.zip, run the Mako Server in developer mode. The following example sets the environment variable MAKO_ZIP to a directory containing a copy of the mako.zip content:
$ mkdir makozip $ cd makozip/ $ unzip ../mako.zip $ cd .. $ export MAKO_ZIP=makozip $ mako Mako Server. Version 3.0 BAS lib 4396. Build date: Jun 18 2019 Copyright (c) Real Time Logic. Mounting makozip Server listening on IPv6 port 9357 Server listening on IPv4 port 9357 Loading certificate MakoServer SharkSSL server listening on IPv6 port 9443 SharkSSL server listening on IPv4 port 9443
The output shows that the server mounts the directory makozip instead of mako.zip.
The Mako Server is easy to deploy because only two files are normally required: the mako executable and the mako.zip resource file. The server does not need to be installed and can run directly in a console window.
For a complete beginner's tutorial, see Getting Started with the Mako Server.
On Windows, the server can optionally be installed as a Windows service. On all other platforms, the server can optionally be installed as a daemon (background) process. See the following tutorials for details:
The Mako Server starts as a foreground process if no arguments are provided. You can request the server to list all commands and then exit as follows:
mako -?
You may specify the location of an optional configuration file.
Example:mako -c mytestdir/mako.conf
See the Configuration File documentation for more information on how to use the configuration file.
-d - Run in daemon mode by detaching from the console -s - Run in daemon mode without detaching from the console -u username - Username to run as
sudo mako -l::myapp.zip -u `whoami`
Note: The server will open alternate ports above 1024 if it is unable to open the default ports.
Instead of using this command, you can add the CAP_NET_BIND_SERVICE capability, which allows an executable to bind to ports below 1024 even when run as a non-root Linux user.
sudo setcap cap_net_bind_service=+ep /path/2/mako
-install - Installs the service -installauto - Installs the service for autostart -remove - Removes the service -start - Starts the service -stop - Stops the service -restart - Stops and starts the service -autostart - Changes the service to automatic start -manual - Changes the service to manual start -minimize - Minimize the console window -elevate - Request elevated privileges
The first eight commands are for installing and managing the server as a Windows service. For example, the following command installs the server as a service, enables auto-start, and starts the service:
mako -l::myapp.zip -installauto
On Windows, administrative privileges are required to install a service. The Mako Server will request elevated permissions if the process or command window from which the service is installed is not already elevated.
The two command line options -elevate and -minimize are designed for creating web-based dashboard-like applications for Windows when not running as a service. When using both options, use the command line sequence -elevate -minimize to leave the current command line window (if any) "as is" and to only minimize the new command window that opens when running in elevated mode.
This section provides a quick introduction to running the Mako Server as a Windows service. For a more detailed explanation, see the tutorial Installing the Mako Server as a Windows Service.
Use a Mako Server Configuration File when starting the Mako Server as a Windows service. The following example uses the directory c:\wwwapps as the base directory and the subdirectory mywww for storing the web application (HTML, LSP, etc.).
The configuration file "mymako.conf" must use absolute paths:
apps={
{ name="", path="/c/wwwapps/mywww"}
}
homeio="/c/wwwapps"
Initially, test the configuration file without installing the server as a service:
mako -c c:\wwwapps\mymako.conf
Make sure you can navigate to your 'mywww' app. When it works, install the server as a service as follows:
mako -c c:\wwwapps\mymako.conf -installauto
In the Barracuda App Server, an application is comparable to the 'www' directory used by other servers. Multiple web applications can be loaded simultaneously, and each application can be assigned a base URL. This URL serves as a unique identifier, helping to distinguish it from other loaded applications and prevent URL conflicts.
If two applications use the same name, the server uses each application's priority setting to determine which application takes precedence.
Applications are created by the Lua code in the Mako Server's .config script, using LSP-enabled Resource Reader instances. For more detailed information on this process, refer to the introduction to the virtual file system.
Applications are loaded with command-line options, the configuration file, or both.
Loading apps via the command line:
-l[name]:[priority]:path - Load one or multiple applications
The following example loads three applications:
mako -lmyapp:1:path/2/myapp -lmyapp:0:myapp.zip -l::/path/2/my-root-app
The two myapp applications have the same base URI, but the non-deployed application has a higher priority than the deployed application myapp.zip. Resources are first searched for in the non-deployed application and then in the deployed application if not found. The last application loaded is a non-deployed root application.
script - Execute the specified script and exit.
Description:
This option allows you to run the Mako Server in a special mode that closely resembles the behavior of the standard Lua interpreter. When you provide a script name as the only argument and without a preceding hyphen, the server will execute the specified script immediately upon starting.
During the execution of the script, the server operates as usual: it opens any configured ports and runs any necessary services. However, once the script completes its execution, the server shuts down completely, closing all ports and stopping all services.
Use Case:
This mode is useful for running initialization scripts, one-time setup tasks, or quick automated processes that require the extended Lua APIs provided by the Mako Server but do not need the server to continue running afterward.
Example Usage:
mako myscript.lua
In this example, the server loads and executes myscript.lua. After the script finishes, the server terminates automatically.
See also Using The Mako Server Instead of Bash For Scripting and XLua.
The configuration file lets you customize the server's initialization of the Barracuda App Server and perform other initialization tasks. You can also use the configuration file for loading applications and setting your own configuration parameters.
If you do not specify a configuration file with the "-c" command line option, the Mako Server looks for a file named mako.conf in the following directories:
The configuration file uses Lua syntax. The Mako Server loads the configuration file as a Lua table; thus all configuration options must be provided as key/value pairs. The following key/value pairs are the available configuration options:
When the Mako Server creates a SharkSSL object using ba.create.sharkssl() for its secure server connection object, the associated certificate store is set to nil unless 'certstore' is set. In most typical web server operations, a certificate store is not required. However, if there is a requirement to authenticate HTTP clients, including web browsers, through client-side certificates, then it becomes necessary to establish a certificate store. To request a client certificate from server-side code, use the request:clientcert() method. The client's certificate can be verified using request:certificate(). For this verification to succeed and return true, the certificate store must contain the certificate of the authority that signed the client's certificate. To facilitate the creation of a certificate store for the SharkSSL object, you can specify the necessary configuration in mako.conf. This configuration can be a single string (for one certificate) or a table (for multiple certificates).
certstore = string | table
Examples:
certstore="my-CA-cert.pem"
certstore={"my-CA-cert1.pem", "my-CA-cert2.pem"}
Set your own parameters in the configuration file and then access the configuration parameters from the loaded LSP applications. The configuration parameters are made available as a Lua module.
Example: Configuration File:myparam="This is my custom parameter"In your Lua code, load the configuration table as follows:
local conf=require"loadconf" print(conf.myparam) -- prints "This is my custom parameter"
Enable function mako.log(), which sends log and status information by email. Log data is accumulated internally in the log module, and an email is sent when the accumulated data is larger than 'maxsize' or data has been accumulated for longer than 'sdelay' seconds. Emails may also be sent immediately if the flush option is set when calling mako.log().
Table log:Download: ready-to-run example code from GitHub.
log = {
smtp = {
from = "john.doe@gmail.com",
to = "john.doe@gmail.com",
subject="My Server's Log",
server = "smtp.googlemail.com",
port = 465,
useauth = true,
consec = "tls",
user = "john.doe@gmail.com",
password = "qwerty"
},
logerr = true, -- Send Lua exceptions by email
signature = "Sent from my Mako Server"
}
The Mako Server creates several Barracuda IO instances, and one of them is the home IO, the IO object returned when calling ba.openio("home"). Many applications and modules rely on a working home directory. The home directory is by default set to the execution directory. This works when the server runs in console mode and when the server runs in daemon mode on Linux since a startup script can change to a suitable home directory prior to starting the server in daemon mode. However, this does not work when run as a service on Windows.
The Mako Server provides a platform-neutral way of setting the home directory by using the "homeio" option.
The following mako.conf example sets the home directory to c:\mako-home on a Windows computer:homeio="/c/mako-home" -- Must use POSIX path
If you are compiling the server's C code for an operating system that lacks the "executable directory" concept, such as VxWorks, you may want to provide a hard-coded default value in the C code. The hard-coded value is used if "homeio" is not set. See the C macro MAKO_HOME_DIR for details.
Let's Encrypt is a free Certificate Authority (CA), and its root certificate is installed in most browsers and secure client devices. Mako Server includes support for the Automated Certificate Management Environment (ACME) protocol used by the Let's Encrypt service.
The Mako Server's ACME plugin enables fully automated certificate installation and renewal with no manual user intervention. You can activate the plugin through the mako.conf configuration file or programmatically by using the specialized modules acme/bot and acme/dns. Use the programmatic option if users must be able to enable or disable the feature, or if you want to fully automate the process without user interaction.
Tutorials:
acme/bot module for public servers and the acme/dns module for private on-premises LAN deployments. For private servers: covers both semi-automatic with manual DNS settings and fully automated certificate issuance with a SharkTrustX portal instance.Note: The Let's Encrypt plugin creates a "cert" subdirectory in the home directory when activated. Certificates and Let's Encrypt configuration data are persistently stored in this directory. You must move this subdirectory if you move the home directory.
Note: Any error is printed to the console when the Mako Server runs in console mode. Errors are also sent to the trace. We recommend enabling the log option when the server runs in daemon mode to receive errors and status information by email.
Proxy: All HTTP requests are routed via the configured proxy if the proxy configuration options are set.
Mako Server automatically loads module acme/bot when the following options are set. The server also loads module acme/dns if the DNS-01 challenge option is set.
Table acme:Note: When using the automatic dns-01 challenge option, servername, key, and secret are required; however, these settings can be set automatically by a compiled-in security module. See acmebot.configure() for details.
Examples:The following example shows how to activate the automatic http-01 challenge.
acme={
acceptterms=true,
email="john.doe@company.com",
domains={"company.com"}
}
This example requires that the server has a publicly available IP address or that you have set up port forwarding to port 80. The HTTP challenge option cannot be used if the server cannot be publicly accessed on HTTP port 80. Use the automatic or manual dns-01 challenge options if the server is not publicly available.
The following two examples show how to activate the automatic dns-01 challenge:
1) Server name, key, and secret in cleartext (not recommended):
acme={
acceptterms=true,
email="john.doe@company.com",
domains={"mydevice"}, -- Include only subdomain name
challenge={
type="dns-01",
servername="my-domain-name",
key="64-byte-key",
secret="64-byte-secret"
}
}
2) Server name, key, and secret provided by integrated security module:
acme={
acceptterms=true,
email="john.doe@company.com",
domains={"mydevice"}, -- Include only subdomain name
challenge={type="dns-01"}
}
See function acmedns.auto() for more information on how to use the automatic dns-01 challenge option.
The following example shows how to activate the manual dns-01 challenge.
acme={
acceptterms=true,
email="john.doe@company.com",
domains={"mydevice.company.com"},
challenge={type="dns-01", servername="manual"}
}
These configuration options activate module acme/dns in automatic renewal mode, but with manual DNS configuration. Module acme/dns prints a message in the console and sends you an email (if the log options are enabled) when it is time to renew the certificate. After setting the DNS TXT record as instructed in the email or console message, run the following LSP script:
<?lsp
local ok,err = require("acme/dns").recordset()
if ok then
print"Resuming renewal process..."
else
-- Renewal process most likely not active
print("Resuming renewal process failed:", err)
end
?>
See the acmedns example for additional information on using function acmedns.recordset()
The Let's Encrypt plugin creates a 'cert' subdirectory in the home directory for certificate storage and JSON configuration files. See the Module acme/bot for details on these files. Module acme/bot is used when enabling the Let's Encrypt options in mako.conf.
The Mako Server can load multiple applications. An application can be a directory, ZIP file, or network path. Applications can be loaded with the command line option -l, by creating an 'apps' table in the configuration file, or both.
Table 'apps' is a list of one or several of the following:The following example loads three applications: the first application is provided as a string, and the other two applications are provided as a Lua table. Providing a Lua table allows for more configuration options. The last application is loaded as a root application since we set the name to an empty string. The first application's name is set to the name of the directory 'app1'.
apps={
"/path/2/app1", -- path to 'www' directory. Base URL is: http://server/app1
{
name="myapp", -- Base URL is: http://server/myapp
prio=1, -- Higher priority (for directory/resource name conflict resolution).
path="/path/2/app2" -- path to 'www' directory
dav=true, -- WebDAV at /myapp/dav/
},
{
name="", -- Root app. -- Base URL is: http://server/
path="/path/2/app3"
dav="fs", -- WebDAV at /fs/
auth=true, -- Enable WebDAV authentication
}
}
host="localhost". Default binds to all interfaces.host="192.168.1.100". Default binds to all interfaces.The proxy configuration options are required if the server runs in an environment that requires a proxy for connecting to the Internet.
Table proxy:If you cannot use our recommended Let’s Encrypt option, you can still load your own SSL certificates. For instance, you could create your own public key infrastructure using our free Certificate Management Tool. Note that the SSL Certificate option cannot be used in combination with the Let's Encrypt option.
Mako Server can load multiple certificates for various domains, but Mako Server also lets you load multiple certificates for the same domain name. SharkSSL then selects the certificate with the strongest cipher selection accepted by the client. Elliptic Curve Cryptography (ECC) certificates typically have a higher ranking than RSA certificates. The favorRSA option enables you to still favor RSA for clients that support both EC and RSA certificates. This option makes it possible to have a self signed ECC Certificate Authority (CA) and ECC chain for devices and serve RSA certificates for browsers. See the Certificate Management for IoT tutorial for details.
Enable the TraceLogger by setting one of the following two options:
When the TraceLogger is enabled, navigate to http://your-server/rtl/tracelogger/. The second option requires a user database. The second option also calls the TraceLogger's dir:configure() method with the provided table, enabling additional TraceLogger configuration.
Example:
tracelogger={
auth=true,
priority= 1 -- Filter out lower priority messages
}
users={
root="qwerty",
}
You can setup a static user database that can be used by the WebDAV server(s), the TraceLogger, and custom authenticators. The user database is a key-value pair, where the key is the username and the value is the password. The password can be cleartext or HA1.
Example:
users={
joe="querty", -- Joe's password is 'querty'
alice={'ef8fef1da0258aea0d0d234ab05debaf'} -- Alice's password is a HA1 hash
}
Function mako.udb() returns the static user database as an Authenticator User Database Object.
Realm name and HA1The realm is used by the Mako Server authenticators and the default realm name is "Barracuda Server". You may change the realm name as follows:
realm="My realm name"
Create a system wide Web File Server (WFS) at startup.
Table fileserver:Note: Unless noauth is set, the "fileserver" requires a user database and the fileserver parameter is ignored if no user database is configured.
MS Windows Example:
fileserver={
fsname="fs",
ioname="disk",
path="/C",
lockdir="temp/.LOCK"
}
The Barracuda App Server powering the Mako Server is designed for deep embedded systems where one may have requirements on resource allocation. The server is therefore designed such that one must pre-allocate the number of resources required. The following allows you to modify these options. See the C reference manual for detailed information.
You may hide the creation and modification of mako.conf from the end user; however, any changes require the Mako Server to be restarted before they take effect. As mentioned earlier, mako.conf uses Lua syntax and is loaded as a Lua table by module loadconf.
Converting the loaded Lua table back to a configuration file can be done by using a library such as inspect.lua.
The Mako Server includes all Barracuda App Server APIs and features. The Mako Server also includes the following extended features:
The Mako Server has the concept of an application environment. The environment for an application is a location where you can keep commonly used Lua code, i.e., library functions. This environment is available to LSP pages in the application, but not to LSP pages in other applications. The application environment is referred to as the application table in our documentation. The Mako Server provides a number of environments that can be used by LSP programmers.
The Mako Server creates an application table/environment per loaded application. This table is known as the "app" table and is explained in section Command (Request/Response) Environment. See the Mako Server tutorial Lua Environments and the .preload Script for a more in-depth explanation on how to use the .preload script.
The Mako Server looks for the .preload file in the application's root directory when starting an application. This file is loaded and executed as a Lua script, if found.
The preload script runs in the 'app' table environment (_ENV) and comes with the following pre-set values:
io object shadows Lua's standard io object. You may access Lua's io by prefixing it with _G, e.g., _G.io.
trace("Starting my application")
trace("Path = "..io:realpath(""))
trace("Base URL = "..dir:baseuri())
function onunload()
trace("Stopping my application")
end
The optional function 'onunload' is run when the application stops either when the server terminates or if the application is stopped by calling mako.stopapp or mako.reloadapp.
Functions and data in the .preload script are added to the application table. You can access this table from an LSP page as "app". For example, a function such as myfunc() can be accessed from an LSP page as app.myfunc().
For more information, see the tutorial Lua Environments and the .preload Script.
You can use any source code editor to develop Lua applications for the Mako Server. For a fully integrated browser-based development workflow, load the Xedge IDE as a Mako Server application. This is the high-level operating system version of Xedge described in The Two Xedge Versions.
build directory.
XedgeMako.cmd. XedgeMako.shThe build process generates a file named Xedge.zip.
Xedge.zip to a convenient location, for example the same
directory where the Mako Server executable is located.
mako -l::Xedge.zip
The Xedge IDE will now be available in your browser, allowing you to edit, manage, and deploy Lua applications directly from the Mako Server runtime. For detailed information about using the IDE and its additional APIs, see Using Xedge.
A function on the Windows version that translates a Windows path to a path that can be used by the IO interface.
Mako Server's require() function searches for Lua files in the same locations where the standalone Lua interpreter searches. Mako Server may be set to extend require()'s search path and enable loading of Lua scripts from within the application. To allow the loading of Lua script modules from your application, add the following line to the beginning of the application's .preload script.
mako.createloader(io) -- io is the application's pre-defined IO interface
The loader enables "require" to search for files in ".lua," a subdirectory in your Lua application. The loader works for both deployed and non-deployed Lua applications. In other words, the loader is effective for both deployed (ZIP) and non-deployed (directory) application formats, ensuring flexibility across different deployment scenarios.
The example application require-test (GitHub) shows how to use function "require" with Lua modules in a Mako application.
Immediately terminates the Mako Server process with an optional exit code.
This function is only available when the server is running in the foreground. Internally, it calls the standard C exit() function and does not perform a graceful shutdown - open socket connections and other resources are not explicitly closed. Use this function for development or controlled shutdown scenarios where cleanup is not required.
Parameters:The function re-loads the certificates specified in mako.conf if no arguments are provided. The function enables an application to install new SSL certificates and then load the new certificates without having to restart the server. The Mako function loadcerts() is internally using the BAS function ba.create.servcon in the mode that enables it to recycle an existing server listening connection.
You may also dynamically load new certificates by providing the two arguments. The keys and certs arguments must be arrays of identical size, with the keys and certificates in the same order. Each array element is a string containing the pathname.
Note: Do not use this function if the Let's Encrypt plugin is enabled since use of this function would conflict with the Let's Encrypt plugin's use of this function.
Sends the message by email if data logging has been enabled in the configuration file. The function returns true if data logging is enabled and false otherwise.
Log data is accumulated internally in the log module, and an email is sent when the accumulated data is larger than 'maxsize' or data has been accumulated for longer than 'sdelay' seconds. Emails may also be sent immediately if the flush option is set.
Parameters:You may optionally set or override the following configuration options when 'flush' is set to true: 'from', 'to', and 'subject'. You may also add 'cc' and 'bcc'. See the SMTP library for details.
Deprecated. Use function ba.sharkclient().
Change the trace file name or location. This function enables you to dynamically change the trace file in a running system. The argument "name" is the full trace file name, including the path, which can be relative or absolute. The function returns true on success. The trace file is used by function trace(), Lua exceptions, and any traces enabled by the TraceLogger.
This function is created by mako.zip:/.config if a static user database has been set up in mako.conf.
The function returns the static user database as an Authenticator User Database Object.
Example usage:local auth = ba.create.authenticator(mako.udb())
You may control the applications loaded through command line parameters and the applications loaded through the configuration file. You may also dynamically create new applications.
Dynamically create and load a new application.
Parameters:Returns true on success and nil, err if the .preload script fails.
Returns a table with all apps loaded by the Mako Server. The following example restarts all applications:
for ix,path in ipairs(mako.getapps()) do
print("Reloading:",path)
mako.reloadapp(ix)
end
Reloads the .preload script for an app. Parameter x is either the application's environment or an index position returned by function mako.getapps(). The following code snippet can be used by an LSP page to reload the .preload script for the app:
mako.reloadapp(app) -- reload 'self'
Stops a registered application. Parameter x is either the application's environment (_ENV: app tab) or an index position returned by function mako.getapps().
Returns true on success and false, err if the .preload script has an unload function that failed. The function throws an error if parameter x is invalid.
Starts a registered application. Parameter x is either the application's environment or an index position returned by function mako.getapps().
Removes an app from the internal app database stored in mako.zip:/.config. Note: this function does not change static options such as mako.conf. Parameter x is either the application's environment or an index position returned by function mako.getapps().
The Barracuda App Server provides an IO interface in addition to the standard Lua input/output operations. These IO interfaces must be created by C code, and the Mako Server's C startup code creates the following IO interfaces:
The following example shows how to list all available IO interfaces:
for name,io in pairs(ba.io()) do print(name, ":", io:realpath"", " : ", io:resourcetype()) end
This example produces the following output when run on Linux:
disk : / : disk POSIX home : ./ : disk POSIX net : nil : net vm : nil : zip zip
See io:realpath() and io:resourcetype() for details on the printouts.
The server makes the following global variables accessible to the loaded applications:
Mako Server includes all Barracuda App Server modules and protocols in addition to the following Mako Server specific modules:
Module acmebot is automatically loaded by mako.zip/.config if mako.conf includes the Let's Encrypt settings.
Note: The module creates a "cert" subdirectory in the home directory when activated. Certificates and Let's Encrypt configuration data are persistently stored in this directory. You must move this subdirectory if you move the home directory.
When activated, module acmebot runs in the background and uses module acme engine for the Let's Encrypt communication. The module returns a table with functions, where the following functions may be used by your programs:
The 'configure' function, combined with acmedns.auto(), can be used as an alternative to the Let's Encrypt configuration option in mako.conf. The function configures acmebot and prepares the module for automatic certificate renewal.
This function is automatically called by Mako Server's .config script if the Let's Encrypt configuration options are set in mako.conf. Calling the function when the acme options are set re-programs acmebot with new settings and the previous settings are discarded.
Parameters:Returns a table with the domains saved in cert/domains.json, where each key is a domain name and each value is the certificate expiration time in the format 'YYMMDDHHMMSSZ'. The certificate has not been issued if the expiration time string is empty. The domain names may differ from the names set when calling acmebot.set() or from domains set through the mako.conf configuration file. Domain names may change when using the dns-01 challenge with the DNS service.
Example:
for domain,exp in pairs(acmebot.getdomains()) do
if #exp > 0 then
local y,m,d=exp:match"(%d%d)(%d%d)(%d%d)"
print(string.format("%s expires year %s, month %s, day %s",domain,y,m,d))
else
print(string.format("Certificate not issued for %s",domain))
end
end
This function returns jobs [, domain [,error]
Module acme/bot creates a 'cert' subdirectory in the home directory for certificate storage and JSON configuration files.
Files created and maintained:The purpose of this module is to make it possible to install Let's Encrypt signed certificates for servers running on private networks.
See the list of ACME tutorials for an introduction to this module.
Module acme/dns extends module acme/bot and implements the dns-01 challenge option. This module can be used in automatic mode together with a DNS service, or in manual DNS mode where the server maintainer must manually set a DNS TXT record when the renewal process starts.
When using automatic mode, the only function your program must call is acmedns.auto(), which should typically be called at system startup. The function may also be called by an LSP page when a user initially configures the system by providing an email address and accepting the Let's Encrypt terms.
Note: the functions acmedns.auto() or acmedns.manual() are automatically called by Mako Server's .config script if the Let's Encrypt configuration options are set in mako.conf.
When not using mako.conf, the module is typically activated and reactivated programmatically as follows:
local options={revcon=true,acceptterms=true}
local ad=require"acme/dns"
ad.loadcert() -- Optional; expedite loading
ad.isreg(function(name)
if name then
ad.auto(options) -- reactivate
else -- first time
-- email and subdomain name provided by user or hardcoded:
local email,subdom="info@realtimelogic.com", "xyz"
-- If hardcoded, subdomain is typically derived from MAC address
ad.auto(email,subdom,options)
end
end)
The module returns a table with functions, where the following functions may be used by your programs:
Returns false if the module is inactive, the string "auto" if acme.auto() has been called, or the string "manual" if acme.manual() has been called.
Prepare acmedns for automatic DNS mode by setting the zone data. Function acmedns.configure() must be called prior to calling the functions: acmedns.auto(), acmedns.available(), and acmedns.isreg() if a security module is not installed.
Table op:Activate automatic certificate and DNS management by using an online instance of the SharkTrustX DNS service. Note that the certificate renewal process takes more than two minutes to complete.
The function must be called each time the server starts; however, arguments email and subdomain are ignored if the server is already registered with the online DNS service. You can check whether a certificate is installed by calling acmedns.auto{}. The return value is nil, error if the function must be called with the email and subdomain.
Parameters:
Returns:
Throws:
The function throws an error if a security module is not installed and acmedns.configure() has not been called. This is treated as a program design error.
Example:The following example shows how the module can be initialized at system startup.
local acmedns=require"acme/dns"
-- Call acmedns.configure(secrets) if a security module is not installed
local op={acceptterms=true}
local email,domain=acmedns.auto(op)
if email then
print("Your fully qualified domain name is:", domain)
else
print"Must register"
acmedns.auto("john.doe@company.com","device",op)
end
Check whether a subdomain name is already in use by the zone. A user interface can call this function before calling acmedns.auto to present a name conflict warning.
Parameters:Check if the server is registered with the online DNS service. The arguments to acmedns.auto() are ignored when this function returns state "Registered".
Parameters:Updates the IP address associated with this device's DNS A record. This function is automatically invoked by acmedns.auto() when automatic certificate management is enabled. However, if the device's IP address changes during operation (e.g., due to a DHCP lease renewal or expiration), you must manually call this function at runtime to update the DNS record.
Enable the manual dns-01 challenge option.
Important: function acmedns.recordset() must be called after acmebot.manual() has been called and after the server maintainer has set the DNS record for the domain. The renewal process is paused until this function is called, and no new activation may be initiated as long as the current renewal job is paused. When using manual mode, function acmedns.loadcert() must be called when the device starts.
Parameters:company.com or device.company.com.The manual DNS challenge option must be used with the following support functions:
This function must be called at system startup when using acmedns.manual() but can also be used in automatic mode to expedite certificate loading for already registered devices (without waiting for the acmedns.auto() function to complete). If a certificate is successfully loaded, the function returns {key}, {certificate}. Otherwise, it returns false, indicating that the device has either no certificate or that the certificate has already been loaded.
Call this function if the dns-01 challenge option mode is set to "manual" and after setting the DNS TXT record for your domain name.
Note:
The function returns a table with the following {record=string, data=string, msg=string} if the certificate renewal job is active and if the job is waiting for the server maintainer to set the DNS TXT record. The 'record' is the DNS record value to set and 'data' is the DNS data value to set. The string 'msg' is a preformatted version of 'record' and 'data'.
local t=require"acme/dns".status()
if t then
print("record:",t.record)
print("data:",t.data)
else
print"Not active. Activate by calling acmedns.manual()"
end
<?lsp
local acmedns=require"acme/dns"
local acmebot=require"acme/bot"
if request:method() == "GET" then
local sT,jobs,domain,err
local op={production=false,acceptterms=true}
local ok,err=acmedns.manual("john.doe@company.com","device.company.com",op)
if ok then
repeat
ba.sleep(200) -- wait
sT=acmedns.status()
jobs,domain,err=acmebot.status()
until sT or err
if not sT then ok=false end
end
if ok then
print'<p>Set the following DNS TXT record:</p>'
print('<pre>record:',sT.record)
print('data:',sT.data,'</pre>')
print'<p>Click button two minutes after setting TXT record</p>'
print'<form method="post"><input type="submit"></form>'
else
print('<p>Renewing',domain,'failed, err:',err,'</p>')
end
else -- POST
acmedns.recordset() -- Continue with renewal (resume background job)
local jobs,domain,err
repeat
ba.sleep(200) -- wait
jobs,domain,err=acmebot.status()
until jobs == 0
if err then
print('<p>Renewing',domain,'failed, err:',err,'</p>')
else
print('<p>Domain',domain,'renewed</p>')
end
end
?>
This complete example shows the full process: activating manual mode, presenting the DNS TXT settings to the user, and continuing the renewal process when the user clicks the submit button. However, the example is for illustration only. Production code should use a more advanced state machine. For example, function acmedns.manual() should only be called at startup or when the user changes the domain or email address. Blocking an LSP page by calling ba.sleep, as done here, is not recommended. Instead, use JavaScript and AJAX to poll for server status updates.
When the server restarts, the certificate must be loaded. To do so, include code similar to the following when the server starts. Notice the 'auto' attribute.
local op = {acceptterms = true, auto = true}
acmedns.manual("john.doe@company.com", "device.company.com", op)
When using the automatic dns-01 challenge option, servername, key, and secret can be provided in cleartext. You may use the cleartext option for testing purposes, but these parameters should be protected in a production environment. An installed SharkTrustX server can automatically create a security module (C code) that may be compiled into the firmware or dynamically loaded as a DLL/shared library.
A security module is associated with a zone registered with the online SharkTrustX server. The online server generates the C code automatically when you navigate to https://registered-zone-name/cgen. You must first login as an administrator and then navigate to this URL.
The precompiled Mako Server delivered by Real Time Logic includes an embedded security module for the zone (domain name) https://local.makoserver.net/. This zone is registered with Real Time Logic's DNS testing service. The fully qualified domain name will be subdomain.local.makoserver.net, where subdomain is the name set when calling acmedns.auto() or set through the configuration options. The integrated security module is used if you enable the automatic dns-01 challenge option without providing your own settings through the configuration file or by calling acmebot.configure().
See the Mako Server tutorial Let's Encrypt for more information on using the security module.
Returns all options in mako.conf, including custom options, as a Lua table.
Example:
for key,val in pairs(require"loadconf") do
print("Key=",key, ", val=", val)
end
Module log is automatically loaded by mako.zip/.config if mako.conf includes the data logging option. Module log activates function mako.log(). The module returns a table with functions, where the following function may be used by your programs:
Send an email using the log module's pre-configured SMTP object.
Argument op is a Lua table. You may optionally set or override the following log configuration options: ('body' or htmlbody'), 'from', 'to', and 'subject'. You may also add 'cc' and 'bcc'. See the SMTP library for details.
Note that the sendmail function may also route outgoing SMTP requests via a proxy if the proxy options are set in mako.conf.
The precompiled Mako Server includes LPeg (Lua Parsing Expression Grammars). LPeg brings PEG to Lua. See the following for details:
Example:
-- Using the LPeg module
local lpeg=require"lpeg"
print(lpeg.match(lpeg.P'a','aaa')) -- prints: 2
-- Using the re module
local re = require"re"
-- find the position of the first numeral in a string
print(re.find("the number 423 is odd", "[0-9]+")) -- prints: 12 14
The sqlutil library streamlines common and repetitive tasks when working with the LuaSQL SQLite module. It provides a structured, opinionated layer on top of LuaSQL to simplify database setup, connection management, and query execution.
To understand how this module is intended to be used, first read the Lua SQLite Tutorial. The tutorial demonstrates the recommended usage patterns and serves as the primary guide for working with sqlutil.
For a real-world reference, you can also examine the SharkTrustX database module, which uses sqlutil for all database read operations.
The library is loaded as follows:
local su=require"sqlutil"
The sqlutil functions:
Opens (or creates) an SQLite database connection using LuaSQL, with a consistent, opinionated database file layout. sqlutil.open resolves the database filename from the configured database base directory and then opens the database using env:connect() from LuaSQL's SQLite driver.
Parameters
dbBaseDir .. name .. ".sqlite.db". For example, if name is "users", the file becomes users.sqlite.db in the database base directory.Returns
Errors
Raises a Lua error if the database cannot be opened or created, with a message like:
Cannot open <resolved path>: <driver error>
If env was created internally and connect fails, the environment is closed before raising the error.
Database location
When the module is loaded, it determines a database base directory and automatically appends a data/ subdirectory to it. All SQLite database files are created or opened inside this data/ directory.
The base directory is selected using the following priority order:
After selecting the base directory, the module appends:
<dataBaseDir>/data/
You can override the base directory at runtime with:
sqlutil.dir("/path/to/dbroot")
This sets the database base directory to "/path/to/dbroot/", and all database files will be created or opened inside it.
Usage examples
Simplest form (let open create the environment):
local sqlutil = require "sqlutil"
local env, conn = sqlutil.open("users")
-- ...
sqlutil.close(env, conn)
Reuse a shared environment:
local sqlutil = require "sqlutil" local luasql = require "luasql.sqlite3" -- or your luasql sqlite module local env = luasql.sqlite() local env2, conn = sqlutil.open(env, "users") assert(env2 == env) -- ... sqlutil.close(env, conn)
Get the database directory (if no argument) or set the database directory, thus overriding the path as explained in su.open().
Returns true if the database file exists, otherwise false is returned.
Search the database using the provided SQL query and return the first entry found. The first argument is either a connection object or a function that returns env, conn. The database is also automatically closed if the argument is a function.
Example:local a,b,c = su.find(conn,"a,b,c FROM table WHERE a='10' LIMIT 1")
Function su.findt is similar to su.find, except that the return values are saved in the provided table instead of being returned.
Simplifies iterating the results returned from an SQL SELECT query. When argument 'tab' is set, the response is a table with key/value pairs matching the query.
local function sqlIter(sql)
local env,conn=su.open"mydb"
local next = su.iter(conn, sql, true)
return function()
local t, err = next()
if t then return t end
if err then trace("Err:", err, sql) end
conn:close()
env:close()
end
end
for tab in sqlIter("* FROM MyTable") do -- "SELECT is automatically prepended
trace(ba.json.encode(tab))
end
Executes the provided SQL query and calls the callback function func. The first argument is either a connection object or a function that returns env, conn. The database is also automatically closed if the argument is a function.