Install
$ agentstack add skill-flydev-fr-mormot2-superpowers-mormot2-deploy ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
mormot2-deploy
mORMot 2 deployment and hosting layer: bundling C dependencies (SQLite3, OpenSSL, Zstd, libdeflate, QuickJS, libgss) into a single binary via the mormot.lib.static linkage, running the binary as a Windows Service or POSIX daemon through mormot.app.daemon and mormot.core.os (TServiceController, TServiceSingle, TSynDaemon), supervising children with mormot.app.agl (TSynAngelize), and fronting the listener with a reverse proxy (nginx, IIS ARR, HAProxy, Caddy). This skill is authoritative for the mormot.app.* namespace plus the deployment-time aspects of mormot.lib.static. It assumes the conventions of mormot2-core (logging via TSynLog, RawUtf8) and stops where build-time flags begin: those belong in delphi-build / fpc-build. TLS termination has two homes: in-process via mormot2-net (TAcmeLetsEncryptServer + OpenSSL), or at the reverse proxy. Pick one and document it.
When to use
- Bundling SQLite, OpenSSL, Zstd, libdeflate, QuickJS, or libgss into the executable so the binary ships without runtime DLL/.so dependencies (see
mormot.lib.static.pas). - Standing up a Windows Service via
TServiceController.Install/TServiceSingle/ServiceSingleRun, or running the same code as a POSIX daemon viaTSynDaemon(mormot.app.daemon). - Writing a
systemdunit,launchdplist, or Windows Service registration that targets a mORMot binary, with the rightUser=,WorkingDirectory=, and restart policy. - Fronting a
TRestHttpServerwith nginx, IIS (with ARR), HAProxy, Caddy, or Cloudflare Tunnel, including theUpgrade/Connectionheaders needed foruseBidirAsyncWebSocket traffic. - Supervising one or more mORMot processes with
TSynAngelize(mormot.app.agl): start / stop ordering, HTTP health checks, auto-restart with backoff, log redirection. - Packaging for distribution: Inno Setup / WiX /
.deb/.rpm/ Docker layers; choosing betweenmormot2staticarchive download and per-platform package managers.
When NOT to use
- In-process TLS termination, ACME certificate automation, SNI dispatch. Use mormot2-net (
TAcmeLetsEncryptServer,OnNetTlsAcceptServerName). - Selecting a database driver, sizing the connection pool, or wiring
TSqlDBConnectionProperties. Use mormot2-db. - Compiler search paths,
.dproj/.lpiconfiguration,dcc64orfpcflags. Use delphi-build / fpc-build. - Defining REST routes, SOA contracts, or
TRestServerlifecycle inside the daemon'sStart/Stop. Use mormot2-rest-soa. - JWT, AES-GCM, ECC keypair generation. Use mormot2-auth-security.
- Foundational types (
RawUtf8,TSynLogsetup,TDocVariant). Use mormot2-core.
Core idioms
1. Run as Windows Service or POSIX daemon with TSynDaemon
TSynDaemon (unit mormot.app.daemon) is the cross-platform shape: same code installs as a Windows Service and runs as a POSIX daemon. Override Start and Stop; let CommandLine parse /install, /uninstall, /console (Windows) or --run, --fork, --kill (POSIX).
program myserver;
{$I mormot.defines.inc}
uses
{$I mormot.uses.inc}
mormot.core.base,
mormot.core.log,
mormot.app.daemon,
mormot.rest.http.server,
myserver.rest in 'myserver.rest.pas';
type
TMyDaemon = class(TSynDaemon)
protected
fHttp: TRestHttpServer;
fRest: TMyRestServer;
public
procedure Start; override;
procedure Stop; override;
end;
procedure TMyDaemon.Start;
begin
fRest := TMyRestServer.Create;
fHttp := TRestHttpServer.Create('8080', [fRest], '+', useHttpAsync, 32);
TSynLog.Add.Log(sllInfo, 'listening on :8080', self);
end;
procedure TMyDaemon.Stop;
begin
FreeAndNil(fHttp); // free HTTP wrapper before REST server
FreeAndNil(fRest);
end;
begin
with TMyDaemon.Create(TSynDaemonSettings, '', '', '') do
try
CommandLine; // dispatches /install /console /run /kill /state ...
finally
Free;
end;
end.
Stop MUST be safe to call multiple times. Settings live in .settings (JSON, INI fallback): ServiceName, ServiceDisplayName, Log, LogPath, LogRotateFileCount. On POSIX, default LogPath is /var/log; on Windows, it is the executable folder.
2. Install as a Windows Service from your own installer
If you do not want users to type myserver.exe /install, drive TServiceController.Install directly. It is a one-liner that talks to the Service Control Manager.
uses
mormot.core.os;
var
state: TServiceState;
begin
state := TServiceController.Install(
'MyService', // internal name (CreateService lpServiceName)
'My Application Service', // display name
'REST API for MyApp', // description
{AutoStart=}true,
{ExeName=}'', // '' = current executable, ParamStr(0)
{Dependencies=}'Tcpip;'); // ; or # separated, optional
if state = ssErrorRetrievingState then
raise Exception.Create('install failed; check Windows event log');
end;
TServiceController.CurrentState('MyService') returns ssRunning, ssStopped, ssNotInstalled, etc. For a process that registers itself as the running service (rather than installing), use TServiceSingle with ServiceSingleRun. TSynDaemon.CommandLine already does both; reach for TServiceController directly only when you bypass TSynDaemon.
3. Static-link C dependencies for a single-binary deploy
mORMot ships precompiled .o / .obj archives under mORMot2/static/. Download mormot2static.7z (Windows) or .tgz (POSIX) once per checkout. The framework's defaults under mormot.defines.inc already turn on the right conditional per platform; the deployment-time job is to verify the conditional you want is set and that the static archive matches the checkout.
{$I mormot.defines.inc}
// Active static-linkage conditionals on mainstream targets:
// STATICSQLITE (default; undef NOSQLITE3STATIC)
// OPENSSLSTATIC (Windows + Linux x86_64 by default in 2.x)
// LIBDEFLATESTATIC (Intel Linux/Windows)
// LIBQUICKJSSTATIC (Win32, Linux x86_64)
// ZSTDSTATIC (via mormot.lib.zstd; TSynZstdStatic populates Zstd)
// LIBCURLSTATIC (mainly Android; not for desktop)
uses
mormot.lib.static, // GCC/libc shims, FPU exception masking
mormot.db.raw.sqlite3.static, // pulls SQLite .o into the binary
mormot.lib.openssl11; // OPENSSLSTATIC switches this to static linkage
A binary with STATICSQLITE + OPENSSLSTATIC + LIBDEFLATESTATIC ships as a single file with no DLL/.so dependency for SQL, TLS, or HTTP compression. Verify with ldd myserver (POSIX) or dumpbin /dependents (Windows): nothing under /usr/lib/libssl*, no libcrypto-*.dll. If you see them, the conditional did not apply (often because the static archive is missing for that platform; see references/static-libs.md).
4. Reverse-proxy with nginx, including WebSocket upgrade
useHttpAsync and useBidirAsync work fine behind nginx. The two non-obvious points: WebSocket upgrade requires Upgrade / Connection headers to be re-set (nginx strips hop-by-hop headers by default), and you must forward the original client IP, scheme, and host so server-side logging and OAuth callbacks see the right thing.
upstream mormot { server 127.0.0.1:8080; keepalive 32; }
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://mormot;
proxy_http_version 1.1;
# Pass-through identity. mORMot reads X-Forwarded-* via TRestServer.OnIPCheck etc.
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket upgrade for useBidirAsync.
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s; # keep idle WS connections alive
}
}
Pick TLS at exactly one layer. If nginx terminates TLS, run mORMot as plain HTTP on 127.0.0.1 and disable any in-process ACME wiring. If you want certs inside the binary (TAcmeLetsEncryptServer), put nginx in TCP-passthrough mode (stream { listen 443; proxy_pass 127.0.0.1:8443; }) or remove nginx entirely.
5. Supervise with TSynAngelize or systemd
TSynAngelize (unit mormot.app.agl) is mORMot's NSSM equivalent: a JSON-configured supervisor that starts, stops, watches, and restarts child processes with backoff, optionally over HTTP health checks. Use it when you ship a multi-process bundle (web + worker + scheduler) on hosts where you do not want to author per-distro init.
{
"Services": [
{
"Name": "api",
"Run": "/opt/myapp/bin/myserver",
"Start": [ "start:%run% --run" ],
"Stop": [ "stop:%run%" ],
"Watch": [ "http://127.0.0.1:8080/health=200" ],
"WatchDelaySec": 30,
"RetryStableSec": 60,
"AbortExitCodes": [ 2, 3 ],
"RedirectLogFile": "%log%api-console.log",
"RedirectLogRotateFiles": 5,
"RedirectLogRotateBytes": 10485760
}
]
}
If the host is single-process and Linux, prefer systemd: it integrates with journalctl, cgroup limits, and socket activation, and one less mORMot process is one less thing to monitor. See references/services-and-daemons.md for both unit and plist templates.
Common pitfalls
- Forgetting
myserver.exe /install(or drivingTServiceController.Installfrom your installer).TSynDaemonproduces the right binary but does not register itself with the Service Control Manager automatically. Without an install step,net start MyServicefails witherror 1060("the specified service does not exist"). Either run the binary once with/installfrom a privileged shell, or callTServiceController.Installfrom your installer's post-install script. User=on systemd that cannot read its own working files. AUser=mormotservice that runs from/etc/myapp.settingswill silently fail to load settings if that file is owned byroot:rootmode 0600. The daemon proceeds with defaults. Symptom: it serves on the wrong port, logs to/tmpinstead of/var/log/myapp. Fix by settingchown mormot:mormot /etc/myapp.settings && chmod 0640and confirmingLogPathwrites through.- Reverse proxy that drops
UpgradeandConnection. WebSocket clients connect, get HTTP 200 instead of 101, and silently fall back. The mORMot server logs nothing (no upgrade ever reaches it). Always setproxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";for nginx, the equivalentoption http-server-close+http-request set-headerfor HAProxy, and thewebSocketflag on IIS ARR rules. - Static archive arch mismatch. The download is one archive per
-triplet. Building Win64 with the Win32 archive on the search path producesUndefined symbollinker errors deep in_sqlite3_*or_BIO_*. The error message names a C symbol, not a Pascal unit, which makes it look like a framework bug. Verify the archive matches your target before debugging anything else. - SELinux / AppArmor denying outbound connect or port bind. A daemon installed via
dnf installruns under a confined SELinux context that may forbidname_connectto PostgreSQL on a non-default port, orname_bindto ports below 1024 (other than 80/443 by default). Symptom: daemon starts cleanly, then every DB query times out orbind()returns EACCES. Checkjournalctl -t setroubleshootand either label the binary with the right context (semanage fcontext) or run from/opt/myapp(which isunconfined_tby default). - Two TLS terminators. Running
TAcmeLetsEncryptServerand terminating TLS at nginx means cert renewal silently fights for port 80, the binary serves a self-signed or stale cert internally, and nginx's cert is the one users actually see. Pick one. If you keep nginx, drop the in-process ACME (mormot2-netbelongs to a different topology). If you keep in-process ACME, replace nginx with TCP passthrough. - Treating
Stopas a one-shot. Windows SCM may call the stop handler twice during a forced shutdown; systemd may call it once on graceful stop and again on the watchdog timeout.TSynDaemon.Stopoverrides MUST be idempotent: free withFreeAndNil, guard external resource teardown withif Assigned(...). A second-call AV crashes the service stop, leaves the SCM inssStopPending, and forces the next deploy to dotaskkill /F. - Forwarding the wrong client IP.
X-Forwarded-Foris the standard, but only mORMot routes that consult it (e.g.TRestServer.OnIPCheck, custom rate-limit code) will use it. A plainSender.Call.LowLevelRemoteIPreads the TCP peer, which is the proxy. Audit code that throttles or geo-locates by IP and route it through the X-Forwarded-* parser, not the socket peer.
See also
$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-20.md- Hosting (daemon, service, Angelize)references/static-libs.mdreferences/services-and-daemons.mdreferences/reverse-proxy.mdmormot2-netfor in-process TLS / ACME and HTTP engine selectionmormot2-rest-soafor the REST/SOA layer that runs insideStartdelphi-build,fpc-buildfor compiler flags and search paths
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: flydev-fr
- Source: flydev-fr/mormot2-superpowers
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.