File: README.md
TrailBase
For more context, documentation, and a live demo, check out the website:
trailbase.io.
Questions? Thoughts? - Take a look at the
FAQ or reach out.
If you like TrailBase or want to follow along, consider leaving a โญ๐.
Project Structure & Releases
This repository contains all components that make up TrailBase including the
server, client libraries, tests, documentation and examples.
Only the benchmarks are
kept separately due to their external dependencies.
Pre-built binaries are available as
GitHub releases for
Linux, MacOS and Windows or Docker images.
Client packages for various languages are available via:
Getting Started
TrailBase is a single executable and therefore very easy to
deploy.
You can simply download the appropriate pre-built
GitHub release bundle for
your system (MacOS, Linux or Windows), unpack and run the executable w/o having
to worry about dependencies or shared-library skew.
If you want to get started even quicker, install TrailBase with the following
command:
# Linux & MacOS
curl -sSL https://trailbase.io/install.sh | bash
# Windows
iwr https://trailbase.io/install.ps1 | iexAlternatively, run TrailBase using the Docker image:
alias trail='
mkdir -p traildepot && \
docker run \
-p 4000:4000 \
-e ADDRESS=0.0.0.0:4000 \
--mount type=bind,source="$PWD"/traildepot,target=/app/traildepot \
trailbase/trailbase /app/trail'Then execute trail help to check that it is properly installed and list all
available command line options.
To bring up the server on localhost:4000, run:
trail runOn first start, a ./traildepot folder will be bootstrapped, an admin user
created and their credentials printed to the terminal.
Afterwards open http://localhost:4000/_/admin/
in your browser and use the credentials to log into the admin dashboard.
If you want to install the auth UI, you can simply run:
trail components add trailbase/auth_ui, which will add a WASM component in ./traildepot/wasm exposing additional UI
endpoints, e.g.
http://localhost:4000/_/auth/login.
Building
If you have all the necessary build dependencies (Rust, node.js, geos,
protobuf, pnpm) installed, you can build TrailBase by running:
# Windows only: make sure to enable symlinks (w/o `mklink` permissions for your
# user, git will fall back to copies).
git config core.symlinks true && git reset --hard
# Download necessary git sub-modules.
git submodule update --init --recursive
# Install Javascript dependencies first. Required for the next step.
pnpm install
# Build the executable. Adding `--release` will yield a more optimized binary
# but slow builds significantly.
cargo build --bin trailAlternatively, if you want to build a Docker image or don't want to deal with
build dependencies, you can simply run:
# Download necessary git sub-modules.
git submodule update --init --recursive
# Build the container as defined in `Dockerfile`.
docker build . -t trailbaseContributing
Contributions are very much appreciated ๐. For anything beyond bug fixes,
let's briefly chat to see how a proposal fits into the overall roadmap and
avoid any surprises.
We're not sure yet what the best setup or exact license is for compatibility
between OSL-3.0 and more popular licenses or use as a framework.
So we'd ask you to sign a simple CLA that retains your copyright, ensures that
TrailBase will continue to forever be freely available under an OSI-approved
copyleft license, while allowing for some flexibility and sub-licensing as
established by much larger, successful projects such as Grafana or Element.
License
TrailBase is free software under the terms of the Open Software License 3.0
(OSL-3.0).
Its client libraries are dual licensed under the permissive
Apache-2.0.
We chose OSL-3.0 over other, better known copyleft licenses due to its
narrower definition of "derivative work" that only covers modifications to
TrailBase itself.
This means that your application's original code is not subject to the
OSL-3.0's copyleft provisions. This is true whether you connect over the
network (e.g. web, mobile, other services, etc.), you're serving static assets,
using the runtime to write custom server-side logic or using TrailBase as a
framework.
This limited scope is similar to the GPL's classpath or the LGPL's linking
exception.
The goal is to allow building on top and around of TrailBase without any
provisions rubbing off onto your original work, while making sure that fixes
and improvements find their way back to the community.
These are our intentions - we felt the need to spell them out explicitly
because licensing is tricky and we ain't lawyers.
Graciously, the license's author provides some more
explanations.
If you have any concerns, please reach out.
If you require an
exception, reach out
to [email protected].
Funding
This project receives funding through NGI Zero Core, a
fund established by NLnet with financial support from the
European Commission's Next Generation Internet program.
Learn more on our NLnet project page.
File: deploy/k8s/README.md
Podman/K8s Example Setup
The following command will start TrailBase and bind to 0.0.0.0:4010:
$ podman play kube trailbase-deployment.yml --publish=4010:4000To make sure TrailBase has started successfully, you can run:
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2e36f124950e docker.io/trailbase/trailbase:latest /app/trail --data... 3 seconds ago Up 3 seconds 0.0.0.0:4010->4000/tcp trailbase-deployment-pod-trailbaseTo get the generated login credentials, check the container logs:
$ podman logs trailbase-deployment-pod-trailbaseFinally, you can browse to
localhost:4010/_/admin and sign in using the
generated credentials.
Note that the database and other runtime files are persisted in a local volume,
i.e.:
$ podman volume ls
DRIVER VOLUME NAME
local trailbase-storageTo use bind-mount a local host path instead, check out the comments intrailbase-deployment.yml.
File: docs/src/content/docs/comparison/pocketbase.mdx
title: PocketBase
import { Aside } from "@astrojs/starlight/components";
import { githubPath } from "@/lib/github";
import GettingTrailBase from "../getting-started/_getting_trailbase.mdx";
Firstly, PocketBase is amazing! It paved the way for single-executable
application bases, is incredibly easy-to-use, and a polished
experience. Gani, the person behind it, is a mad scientist ๐.
From a distance, PocketBase and TrailBase are both single-executables providing
similar feature sets: REST APIs, realtime updates, authentication, file
storage, a runtime for custom endpoints, admin dashboard..., all on top of SQLite[^7].
To see the differences both on a technical and philosophical level, we'll have
to dive a little deeper...
Goals
TrailBase was born out of admiration for PocketBase trying to move the needle
in a few areas:
- Be even lighter and faster. SQLite is astonishingly fast, to the point where
even small overheads matter. PocketBase is written in Go, which in itself is
a fast language, however FFI overhead matters.
This is where a language like Rust with native C ABI support can help. - Less abstraction and embracing standards (SQL[^1], a full ES6 runtime through WASM,
JWT, UUID) to avoid lock-in as much as possible, thus simplifying adoption
either fully or as piece-meal as well as as moving on if necessary. - Untethered access to SQLite[^2] including features such as recursive CTEs,
virtual tables and extensions, e.g., providing vector search and geoip out of
the box[^3], unlocking more use-cases and standard solutions to well-known
problems. - Be just as easy to self-host with aspirations to be even easier to manage a
fleet of deployments across integration tests, development, and production.
We'd also like to eventually tackle replication. - Be simple, flexible and portable enough to support data analysis use-cases.
Imagine a self-contained data science project that ships an interactive UI
with vector search and custom JS extensions alongside its data.
Differences
Beyond goals and intentions, let's look at some of the more practical differences:
Language & Performance
PocketBase being written in Go and TrailBase in Rust may be the most instantly
visible difference.
Preference aside, this will likely matter more to folks who want to use
either as a framework rather than the standalone binary or modifying the core.
In practice, both languages are solid, speedy options with rich ecosystems.
Though Rust's lack of a runtime and lower FFI overhead gives it the edge in
terms of performance.
Measuring we found that TrailBase's APIs are roughly 10x faster.
This may sound like a lot but is the result of SQLite itself being extremely
fast meaning that even small overheads weigh heavily.
Independently, TrailBase offers a WebAssembly runtime in contrast to
PocketBase's gotcha goja JavaScript interpreter ๐.
The former can support a wide-range of guest languages - currently JS/TS and
Rust with more to come - and can allow for a wider range of
isolation/performance trade-offs.
In practice, a custom Rust WASM endpoint can be up to 140x faster than a
PocketBase JS one.
Framework Use
Both PocketBase and TrailBase allow customization using their built-in runtimes.
However, some users may want even more control and built their own binaries
using only bits and pieces.
This is what we refer to as library or framework use. For this use-case
language preference and prior experience of you and your team will likely matter
a lot more with PocketBase written in Go and TrailBase in Rust.
Framework use is something PocketBase has allowed for a long time and is really
great at.
TrailBase technically allows it too, but at this point it really feels more
like an afterthought while we focus on the standalone experience.
Expect TrailBase to improve significantly in this area.
Features
Looking more closely at the breadth of similar features, various differences
emerge.
In lieu of trying to enumerating all of these constantly evolving differences,
let's look a some of the nifty tricks that TrailBase has up its sleeve:
- Support for multiple independent SQLite DBs[^6].
- Language independent bindings via JSON-schema with strict type-safety
enforced all the way from the client to the database[^4]. - A WASM runtime for custom endpoints in multiple languages and speed-ups of up
to 140x[^5]. - Untethered access to SQLite with all its features and capabilities[^1].
- First-party client libraries beyond JS/TS and Dart for C#, Kotlin, Swift, Go,
Python and Rust. - Has a ready, public auth UI (login, profile, ...) to get you started. You can fork,
modify or roll your own since it's just a WASM component. - Support for geospatial data and
GeoJSONAPIs. - Support for stable more and more efficient cursor-based pagination in addition to
OFFSET. - Component-provided admin UI extension dashboards.
- Arguably a less polished admin UI but it works on small mobile screens ๐ .
Contributing & Licensing
Both PocketBase and TrailBase are truly open-source: they accept contributions
and are distributed under OSI-approved licenses.
PocketBase is distributed under the permissive MIT license, while TrailBase
uses the OSL-3.0 copyleft license.
We chose this license over more popular, similar copyleft licenses such as
AGPLv3 due to its narrower definition of derivative work that only covers
modifications to TrailBase itself. This is similar to GPL's classpath or LGPL's
linkage exception allowing the use of TrailBase as a framework and JS runtime
without inflicting licensing requirements on your original work.
Final Words
PocketBase is great and both PocketBase and TrailBase are constantly evolving
making it hard to give clear guidance on which to pick when.
If you can afford the luxury, I'd recommend to give them both a quick spin.
After all they're both incredibly quick and easy to deploy.
In the end, if you're looking for mileage or framework use-cases you're likely
better off with PocketBase.
Otherwise it may be worth giving TrailBase a closer look, especially when
flexibility and performance matter.
[^1]:
We believe that SQL a ubiquitous evergreen technology, which in of itself
is already a high-level abstraction for efficient, unified cross-database
access.
ORMs, on the other hand, often look great in examples but many fall apart
for more complex tasks. They're certainly bespoke, non-transferable
knowledge, and lead to vendor lock-in.
UPDATE: PocketBase in v0.39 gained an SQL console but special care is
needed especially for schema manipulations, since PB manges its own
metadata besides the primary schema, which will get out-of-sync.
[^2]:
Maybe more in line with SupaBase's philosophy. We suspect that PocketBase
relies on schema metadata by construction requiring alterations to be
mediated through PocketBase to keep everything in sync.
[^3]:
All extensions can be built into a small, standalone shared library and
imported by vanilla SQLite to avoid vendor lock-in.
[^4]:
Note that SQLite is not strictly typed by default. Instead column types
merely a type affinity for value conversions.
[^5]:
Depends a lot on the guest language and the specific tasks in question. 140x
is what we measured in heavy computational tasks comparing Goja with
Rust->WASM. It's apples to oranges, yet practically applicable.
[^6]:
This can aid with physical separation and offer a path to overcoming potential locking bottlenecks.
[^7]:
TrailBase recently gained support for Postgres. It's still early
days and change subscriptions are not supported but it offers a path for
folks who feel that they may need finer-grained locking in the future.
File: docs/src/content/docs/comparison/supabase.mdx
title: Supabase
Both Supabase and Postgres are amazing. Comparing either to TrailBase and
SQLite, respectively, is challenging given how different they are
architecturally.
For one, both Postgres and Supabase are heck of a lot more modular. "Rule 34" of
the database world: if you can think of it, there's a Postgres extension for it.
And Supabase does an excellent job at making all that flexibility available
without getting in the way and giving you untethered access while further
expanding upon it.
In many ways, TrailBase is trying to eventually do the same for SQLite:
combining PocketBase's simplicity with Supabase's layering.
One foundational difference is that Postgres itself is a multi-user,
client-server architecture already.
Extending it by building a layered services around it, like Supabase did,
feels very natural.
However, SQLite is neither a multi-user system nor a server. Hence, extending
it by embedding it into a monolith, like PocketBase did, feels fairly natural
as well.
There are ups and downs to either approach. The layered service approach, for
example, allows for isolated failure domains and scaling of individual
components [^1]. The monolith, on the other hand, with its lesser need for modularity
can have fewer interaction points, fewer moving parts making it fundamentally
simpler, cheaper, and
lower overhead (10+x performance difference).
Ultimately, the biggest difference is that Supabase is a polished product with
a lot of mileage under its belt. Our simpler architecture will hopefully let us
get there but for now Supabase is our north star.
[^1]:
For example, in our performance testing we've found that PostgREST,
Supabase's RESTful API layer in front of Postgres, is relatively resource
hungry. This might not be an issue since one can simply scale by pointing
many independent instances at the same database instance.