Caprese
A front-end web server specialized for real-time message exchange
Install / Use
/learn @zenywallet/CapreseREADME
Caprese
A front-end web server specialized for real-time message exchange
Appetizers
Can I write server-side APIs, back-end services, client-side applications, javascript, wasm, even html, in one language and in one code file, and even in one executable binary and process? 〜a certain geek〜
Yes, you can do it with the Caprese. It is free and flexible. Actually, though, it's thanks to a great Nim and libraries.
Is web3 a web? Are there any web server that can be called web3? 〜a certain tweet〜
Caprese will be the base of that system. It would be a decentralized web server with server-to-server connections that could verify the reliability of contents and applications.
Quick Trial
Install Nim
I heard you like Ubuntu, so the following are required to install Nim.
sudo apt install build-essential curl
Install with choosenim.
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
echo 'export PATH='$HOME'/.nimble/bin:$PATH' >> ~/.bashrc
. ~/.bashrc
See Nim for installation details.
Build Caprese and Launch
you also require the following installation to build the SSL libraries.
sudo apt install automake autoconf libtool cmake
Do you have git installed?
sudo apt install git
Now let's build the Caprese.
git clone https://github.com/zenywallet/caprese
cd caprese
nimble install --depsOnly
nimble depsAll
nim c -r -d:EXAMPLE1 -d:release --opt:speed --threads:on --mm:orc src/caprese.nim
Open https://localhost:8009/ in your browser. You'll probably get a SSL certificate warning, but make sure it's a local server and proceed.
Build Your Custom Web Server
Install Caprese package.
nimble install https://github.com/zenywallet/caprese
It will take quite a while, so make some coffee. The Caprese body is located ~/.nimble/pkgs/caprese-0.1.0/ when installed. The version number may change, though. If you can't find it, try looking for ~/.nimble/pkgs2.
It may be easier to find the path with the following command.
nimble path caprese --verbose
In some directory, create server.nim file with the following code.
import caprese
server(ssl = true, ip = "0.0.0.0", port = 8009):
routes:
get "/":
send("Hello!".addHeader())
send("Not Found".addHeader(Status404))
serverStart()
Build and launch.
nim c -r --threads:on server.nim
Open https://localhost:8009/ in your browser. You'll get a SSL certificate warning, but do something.
Features
- Multi-threaded server processing
- WebSocket support
- TLS/SSL support. BearSSL, OpenSSL, LibreSSL, or BoringSSL can be selected depending on the performance and the future security situation
- SNI support for TLS/SSL. Servers can use multiple certificates of different hostnames with the same IP address
- Support for automatic renewal of Let's Encrypt SSL certificates without application restart
- Web pages are in-memory static files at compile time, dynamic file loading is also available for development
- Reverse proxy for backend and internal services, server load balancing is available
- Messaging functionality to send data from the server to clients individually or in groups
- Dependency-free executable for easy server deployment
- Build on your device from source code, this repository does not include binary modules such as SSL libraries
- Languages - Nim 100.0%
Requirements
Closure Compiler Setup
The closure-compiler is needed to minify javascript. It thoroughly optimizes even somewhat verbose and human understandable javascript generated by Nim into short code.
sudo apt install default-jre-headless maven
Maven is used to download the closure-compiler. Caprese automatically downloads and runs the closure-compiler internally. To download manually,
mvn dependency:get -Ddest=./ -Dartifact=com.google.javascript:closure-compiler:LATEST
You can find closure-compiler-vyyyyMMdd.jar in the current path. Copy the file to ~/.cache/caprese/. If several versions of a closure-compiler are found in the path, the latest version is used.
Use scriptMinifier() to make minified javascript
import caprese
const HelloJs = staticScript:
import std/jsffi
var console {.importc, nodecl.}: JsObject
var helloStr = "Hello,".cstring
var spaceStr = " ".cstring
var worldStr = "world!".cstring
console.log(helloStr & spaceStr & worldStr)
const HelloMinJs = scriptMinifier(code = HelloJs, extern = "")
const HelloHtml = staticHtmlDocument:
buildHtml(html):
head:
meta(charset="utf-8")
body:
tdiv: text "Hello"
script: verbatim HelloMinJs
echo HelloHtml
Output
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body>
<div>Hello</div>
<script>console.log("Hello, world!");</script>
</body>
</html>
It's amazing. HTML is generated from Nim's code, and the wasteful script is simplified. Nothing could be more wonderful. Karax is used to generate HTML. Now that the HTML has been statically generated, all we have to do is return this as a server response with a header.
Register in extern the names of some variables and functions that should not be changed by the closure-compiler. If a string is specified for extern, it will be read directly into --externs option of the closure-compiler via a file. You can also pass extern a list of keywords you want to prevent the closure-compiler from replacing strings in seq[string]. The list of keywords will be converted to a readable format by --externs and passed to the closure-compiler. In addition to them, when the Nim generates javascript, some keywords that should not be changed are automatically added internally to extern.
Server Configuration
The server configuration is written in the config: block. The config: block should be set before the server: block. Below are the default settings. You only need to set the items you want to change or explain explicitly.
config:
sslLib = BearSSL
debugLog = false
sigTermQuit = true
sigPipeIgnore = true
limitOpenFiles = -1
serverWorkerNum = -1
eventsSize = 10
soKeepalive = false
tcpNodelay = true
clientMax = 32000
connectionTimeout = 120
recvBufExpandBreakSize = 131072 * 5
maxFrameSize = 131072 * 5
certsPath = "./certs"
privKeyFile = "privkey.pem"
fullChainFile = "fullchain.pem"
httpVersion = 1.1
serverName = "Caprese"
headerServer = false
headerDate = false
headerContentType = true
errorCloseMode = CloseImmediately
connectionPreferred = ExternalConnection
urlRootSafe = true
postRequestMethod = false
sslRoutesHost = SniAndHeaderHost
- sslLib: None, BearSSL(default), OpenSSL, LibreSSL, BoringSSL
Somewhat surprisingly, Caprese supports 4 different SSL libraries. I would like to keep it a secret that BearSSL is the most extreme, with the smallest binary size and the fastest SSL processing speed. Enjoy the differences.
SSL libraries are built from source code in the deps folder of the repository, so there is no need to install SSL libraries on the OS. Just select the SSL library with this parameter and it will be statically linked.
If SSL is not required, it is recommended set to None. This will enable the experimental implementation of fast dispatch processing based on number of client connections and requests. At this time, it is only available for None. - debugLog: true or false(default). If true, debug messages are output to the console.
- sigTermQuit: true(default) or false. If true, handling SIGTERM at the end of the process. The code in the
onQuit:block is called before the process is terminated. - sigPipeIgnore: Whether to ignore SIGPIPE. Caprese requires SIGPIPE to be ignored, but can be set to false if duplicated in other libraries.
- limitOpenFiles: [Number of open files], -1(default, automatically set the maximum number of open files)
- serverWorkerNum: [Number of processing threads], -1(default, automatically set the number of CPUs in the system)
- connectionTimeout: [Client connection timeout in seconds], -1(disabled). The time to disconnect is not exact. Disconnection occurs between a specified second and twice the time.
- headerServer: true or false(default), If true, include
Server:in the response headers. Common benchmarks require this value to be true. In benchmark competition, even a single byte of copying can feel heavy. - headerDate: true or false(default), If true, include
Date:in the response headers. Common benchmarks require this value to be true. It should not be the essence of benchmarking, but sometimes it is a competition of how to implement the date strings. - headerContentType: true(default) or false, If true, include
Content-Type:in the response headers. It may be possible in some cases to set false according to benchmark requirements. - errorCloseMode: CloseImmediately(default) or UntilConnectionTimeout. Behavior when disconnecting clients on error.
- connectionPreferred: ExternalConnection(default) or InternalConnection. Optimize server processing depending on whether the clients are connected from external or internal network connections. The situation is different when the clients a
Related Skills
canvas
349.2kCanvas Skill Display HTML content on connected OpenClaw nodes (Mac app, iOS, Android). Overview The canvas tool lets you present web content on any connected node's canvas view. Great for: -
atmosphere
3.8kReal-time transport layer for Java AI agents. Build once with @Agent — deliver over WebSocket, SSE, gRPC, MCP, A2A, AG-UI, or any transport. Works with Spring AI, LangChain4j, Google ADK, Embabel, Koog or the built-in OpenAI-compatible client.
agentic-obs
10agentic-obs: A Model Context Protocol (MCP) server that provides AI assistants with programmatic control over OBS Studio via the OBS WebSocket API. This server enables AI agents to manage scenes, sources, recording, streaming, and other OBS functionality through standardized MCP tools.
next
A beautifully designed, floating Pomodoro timer that respects your workspace.
