For the past few months, we have been reviewing MantisBase development with a particular focus on the hurdles we have accumulated along the way: requested features that were becoming increasingly difficult to implement cleanly, performance bottlenecks, and a few architectural decisions that had started to outlive their usefulness.
At some point, adding another feature on top stops being progress.
Sometimes you have to go back and fix the foundations.
That is, in large part, what MantisBase v0.4 has become.
A little history
During the v0.2 → v0.3 transition, one of our goals was to standardize the API surface and establish a clearer contract for anyone building against MantisBase.
Part of that plan involved exposing the new APIs to JavaScript through Duktape. The bigger idea was not limited to JavaScript either: once we had a stable internal API, supporting other scripting languages would become considerably easier.
Unfortunately, the JavaScript work planned for v0.3 never really saw the light of day.
Why?
Because the underlying code was still moving too quickly.
We were making many seemingly small changes that affected routes, internal objects, authentication behavior, entity handling, and lifecycle management. Every one of those changes would then have needed to be mirrored in the JavaScript bindings.
That would have meant maintaining effectively two implementations of a rapidly changing API, and potentially repeating the same work again for every additional scripting language.
That did not make much sense.
Rather than continue adding bindings on top of an architecture we already knew needed work, we decided to address the underlying problems first.
What problems were we running into?
A few issues kept showing up during development and testing.
Realtime connections did not scale particularly well
Until v0.4, MantisBase used cpp-httplib extensively for both normal HTTP traffic and Server-Sent Events (SSE).
SSE is intentionally a long-lived HTTP connection. A client connects, and that connection stays open while the server pushes database changes back to it.
The problem with our implementation was that every active SSE connection occupied a server thread.
With a handful of clients, this was not particularly noticeable.
With enough clients, however, those long-lived connections could consume the available worker threads. Normal REST requests would then sit waiting for a worker to become available.
In other words, realtime traffic could eventually prevent the server from serving regular HTTP traffic.
That is not a particularly useful property for something calling itself a backend.
The global singleton had become expensive
MantisBase historically exposed much of the application through a global singleton.
It was convenient.
Until it wasn’t.
Global state made lifecycle management difficult, made ownership less obvious, and caused headaches in the testing harness. Starting and stopping multiple instances within the same process could result in stale state, shutdown-order issues, memory problems, and occasionally spectacular crashes.
It also worked against one of MantisBase’s longer-term goals: being genuinely useful as an embeddable C++ library.
If somebody embeds MantisBase into a desktop application, daemon, embedded device, test environment, or another larger system, they should control the lifetime of the MantisBase instance.
A hidden global object should not control it for them.
The API was becoming harder to extend safely
The scripting work exposed another problem.
An API that changes frequently is expensive to bind.
If adding JavaScript, Python, Lua, or another language requires duplicating large parts of MantisBase internals, every change becomes progressively more expensive.
Before expanding the extension system, we therefore needed to make the C++ side cleaner first.
That became one of the guiding ideas behind v0.4:
Make MantisBase easier to embed, extend, test, and operate before piling more features on top.
So, what have we been cooking?
Quite a bit.
What’s changing in v0.4?
1. Goodbye, global singleton
We have officially dropped the global MantisBase singleton.
This is probably not the flashiest v0.4 feature, but internally it is one of the most important changes.
Instances now have much clearer ownership and lifecycle semantics. That makes testing considerably easier and creates a much better foundation for embedding MantisBase inside other C++ applications.
Instead of assuming that exactly one magical MantisBase instance exists somewhere in global state, the application can explicitly create, configure, use, and destroy an instance.
Boring architectural changes are often the ones that unlock the interesting features later.
This is one of those changes. You can read more details about this change in our previous post here.
2. Authentication is expanding
Authentication has received quite a bit of attention.
The first stage of OAuth support has landed. It is still relatively new and needs considerably more real-world testing, so consider it early support rather than a finished OAuth story.
We have also added API keys.
This is particularly useful for automation, services, scripts, CI jobs, agents, and AI-driven workflows where an interactive username/password login flow does not make much sense.
API keys can be created for administrative accounts as well as regular authentication accounts, allowing machine-to-machine clients to interact with MantisBase without pretending to be an interactive user.
There is more work to do here, but this gives us a much better foundation for non-human clients.
3. A new HTTP backend
This is probably the most consequential runtime change in v0.4.
We have migrated the underlying HTTP server from cpp-httplib to Drogon.
The biggest motivation was concurrency.
As described earlier, our previous SSE implementation tied each long-running realtime connection to a worker thread. At sufficient concurrency, realtime subscriptions could therefore starve ordinary HTTP requests.
Drogon’s asynchronous networking model gives us considerably more room to handle long-lived connections without treating every connection as a permanently occupied worker.
The change also opens another realtime option:
WebSockets.
SSE remains useful, especially because it is simple, works over HTTP, and is perfectly adequate for many database subscription use cases, but WebSockets are now available as an alternative for applications that need bidirectional communication or want to maintain a persistent realtime channel.
This migration was not simply swapping one HTTP library for another. A large amount of route handling, middleware behavior, authentication plumbing, request/response handling, realtime infrastructure, and testing had to move with it.
That is part of why v0.4 has taken the shape it has.
4. ARM Linux builds
Linux ARM support has been added to our builds.
MantisBase has always been particularly interesting to us outside the traditional “deploy another service into Kubernetes” use case.
A relatively small, self-contained backend makes sense on development machines, edge systems, local appliances, desktop applications, Raspberry Pis, and other small Linux devices.
ARM support is therefore an important part of making that story practical rather than theoretical.
There will inevitably be hardware and distro combinations we have not tested, so feedback here is especially welcome.
5. Simpler data types
We have simplified MantisBase’s data-type system.
One particularly noisy area was integer types.
Rather than exposing a collection of conceptually similar integral types as completely independent MantisBase types, integer fields now use a simpler int model with precision describing the actual representation—for example u8, u16, u32, i8, through to i64.
The intention is to keep the schema easier to reason about without losing control over representation when that precision matters.
We have also removed the old xml and blob types.
The general direction here is deliberate: fewer special cases, a smaller schema vocabulary, and clearer behavior.
Every type we expose becomes something that has to be supported consistently across SQLite, PostgreSQL, REST serialization, validation, migrations, scripting, the admin UI, and eventually language bindings.
A smaller, well-defined type system is considerably easier to keep correct.
6. Developer packages
Starting with the v0.4 release line, release artifacts now include developer packages containing both the static and shared MantisBase libraries for the platforms we build.
This matters if you do not want to run MantisBase purely as a standalone server.
You might want to:
embed it into an existing C++ application;
build your own server around the MantisBase core;
add functionality that does not belong upstream;
experiment with alternative scripting integrations; or
use parts of MantisBase as infrastructure within another project.
The standalone binary is still the easiest way to get started.
The developer package is for everyone who eventually asks:
“Okay, but can I use MantisBase inside my application?”
Increasingly, the answer should be yes.
7. Security hardening
As the API surface has grown, we have also spent considerably more time looking at how those APIs behave when clients are actively hostile rather than merely buggy.
Recent security-hardening work has covered access control, OAuth verification, realtime authorization, SQL/schema validation, file serving and uploads, rate limiting, HTTP security headers, error handling, and container deployment.
Some of those changes are intentionally stricter and may expose assumptions in existing deployments.
That is a tradeoff we are willing to make while v0.4 is still in prerelease.
It is much better to discover and fix those assumptions now than to permanently preserve unsafe behavior because somebody might already depend on it.
If you are testing the v0.4 prereleases, this is one area where feedback is especially valuable.
Try the weird things.
Try the things you think should fail.
And if they do not fail, tell us.
8. Documentation rewrite
A lot of the documentation had fallen behind the codebase.
That happens surprisingly quickly during periods of heavy architectural work: endpoints change, CLI commands move, examples refer to previous response formats, and suddenly the docs describe a product that only partially exists anymore.
We have been going through the documentation and bringing it back into parity with the current codebase.
This includes reorganizing documentation, updating API responses and examples, fixing installation instructions, and filling in missing sections.
The v0.4 releases have already included several rounds of documentation fixes and restructuring.
Documentation is still a work in progress, so if you encounter something that disagrees with the actual application, please open an issue.
9. More configuration knobs
We have also expanded runtime configuration.
More behavior can now be controlled using environment variables, making it easier to configure MantisBase in Docker and other deployment environments without maintaining custom configuration files.
Some features and behaviors can be explicitly enabled or disabled depending on how you intend to run the server.
Check the Docker and configuration documentation for the current set of options—the list is still evolving during the v0.4 prerelease cycle.
A few development hacks
If you are building MantisBase yourself, there are a couple of quirks worth knowing about.
Prefer Release-with-Debug-Info for debugging
For development builds, we currently recommend building with release optimizations plus debug information rather than using a full Debug configuration on GCC.
Some Debug builds can run into stack-related problems.
You still get useful symbols for debugging without paying all of the costs associated with the full Debug configuration.
Use dynamic linking while iterating
Static linking is useful for release binaries because it helps us ship a portable package with minimal runtime dependencies.
It is considerably less fun when you are making a tiny source change and rebuilding repeatedly.
For local development, enable dynamic linking where appropriate. Build-test-debug cycles become much faster, and you can switch back to the static configuration when validating a release-style build.
Check the CMake options for the relevant switches.
PostgreSQL on Windows is still missing
PostgreSQL support is not currently shipped on Windows.
Static linking PostgreSQL and all of the associated dependencies into a clean Windows release remains an interesting little adventure.
SQLite works, of course.
PostgreSQL-on-Windows is something we still need to revisit.
No macOS build yet
We are also not shipping a macOS build at the moment.
The main reason is considerably less interesting than some deep technical incompatibility:
I do not currently have a Mac available for properly developing, testing, and maintaining the build.
Rather than publish an artifact we cannot confidently test, macOS will have to wait for now.
Contributions here are very welcome.
So, is v0.4 ready?
Not quite.
And that is why the releases are labelled alpha and beta.
At the time of writing, v0.4.0-beta.2 is available as a GitHub prerelease.
These builds exist specifically so that we can find the remaining rough edges before calling v0.4 stable.
If you have been curious about MantisBase, this is actually a particularly useful time to try it.
Run an application against it.
Embed the library.
Open too many realtime connections.
Try OAuth.
Use API keys from an automation.
Run it on ARM.
Throw malformed requests at it.
Build it using a toolchain we probably forgot existed.
Break it.
That feedback is far more valuable during beta than after a stable release.
For Docker users, the prerelease images are available under the corresponding v0.4 tags.
Where we go from here
v0.4 contains visible features, but the more important changes are underneath them.
Removing global state makes embedding and testing easier.
Changing the HTTP backend gives realtime connections a better foundation.
Simplifying the type system reduces the number of edge cases every future integration must support.
Developer libraries make MantisBase useful as more than a standalone executable.
Authentication and API keys make service-to-service and automation workloads more practical.
And all of that makes the scripting story we originally wanted to build considerably more realistic.
That last part is important.
JavaScript support was not abandoned because scripting stopped being interesting.
We paused it because building language bindings against unstable foundations would have multiplied our maintenance work.
With v0.4, we are trying to make those foundations boring.
Stable objects. Clear ownership. Predictable APIs. Better concurrency. Smaller interfaces.
Once those pieces settle, JavaScript—and potentially other scripting languages—becomes a much more manageable problem.
Help us break it
MantisBase is open source, and v0.4 is still being shaped.
If you try the prerelease, we would love to hear:
what worked well;
what broke;
what was confusing;
what you expected to exist but could not find;
where the documentation disagreed with reality;
and what you think MantisBase should tackle next.
Open an issue or start a GitHub Discussion. We also have a Discord server now for longer-form conversations, questions, experiments, and project updates.
And, of course:
Star it. Fork it. Build it. Break it. Fix it.
Contributions of every size are welcome.
v0.4 has involved considerably more rebuilding than we originally expected.
But sometimes the fastest way forward is to stop adding floors and reinforce the foundation first.
That is what we have been cooking.


