Skip to content

What Atlantis gives you for free, and what commercial plugins add

A tour of the Atlantis framework: how it is put together, everything the MIT-licensed core does on its own, how a plugin is built and isolated, what it takes to run it yourself, and why business features are separate commercial plugins.

Atlantis is the application framework I have been building since January 2026. This site runs on it, and so does a client site that takes orders every day. It is written in TypeScript, and it is split along one line that shapes everything else: a kernel that every installation has, and plugins that each add one business domain, such as a shop, invoicing or shipping.

The kernel is open source under the MIT licence. The business plugins are separate commercial products. People who hear that tend to ask the same two questions: how much of the useful part is actually free, and why is the line drawn where it is?

This article answers both in detail. It walks through how Atlantis is put together, everything the free core does without a single plugin installed, how a plugin is built and kept in its box, what it takes to run Atlantis on your own server, and finally why the business plugins are sold separately. It is long, because the honest answer to "what do I get for free" is a long list.

How it is put together

An Atlantis installation has four kinds of parts:

  • The kernel: the core, the API server, the admin console and the frontend that renders sites. It is the same on every installation.
  • Plugins: each one owns a domain, its data, its admin screens and its API routes.
  • Themes: they decide what visitors see. A theme renders pages; the content comes from the admin.
  • Appearances: optional replacements for the whole admin console, so a product built on Atlantis can have its own console rather than a re-skinned generic one.

The kernel itself is a set of packages, each with one job:

packages/
├── core/        # plugin lifecycle, roles and permissions, security, migrations, i18n, versioning
├── api/         # the API server: REST routes, controllers, middleware
├── admin/       # the admin console
├── frontend/    # renders themes for visitors
├── sdk/         # the only thing a plugin or theme may import
├── auth/        # sessions, refresh rotation, two-factor, API keys, single sign-on
├── database/    # PostgreSQL, SQLite and MySQL behind one interface
├── cache/  email/  media/  queue/  scheduler/
├── mcp-server/  # every installation as an MCP server for AI agents
└── cli/         # builds, packs, migrations and the platform gateway

The line about the SDK matters more than it looks. Plugins and themes are not allowed to reach into the kernel’s internals; they get one public surface, and a build check refuses anything that imports past it. That is what lets the kernel change underneath plugins without breaking them.

Atlantis can also run in three shapes: the API alone, the API with the admin, or the full stack with a public frontend. A mobile app’s backend does not need a website; a website does not need to be split across servers.

The path of a request

A request travels through a short, fixed path:

browser or app
    → your reverse proxy (TLS only)
    → platform gateway (routes by host name, from the site table)
    → API server / admin / frontend
    → kernel: plugin registry, security monitor, database layer
    → the plugins that own the data

The gateway is part of the framework. It reads the list of sites and their host names from the database and routes each request accordingly, so creating a new site in the admin makes it reachable within a second, without anyone editing proxy rules or generating configuration files. Your own proxy only has to terminate TLS, and the gateway can do that too.

Identity, access and security

Everything about who someone is and what they may do lives in the kernel, not in a plugin:

  • Users, roles and permissions. Permissions are granular, roles collect them, users hold roles. A role sets the maximum a person can do, and the kernel checks it on every request rather than trusting each feature to remember.
  • Two-factor login with time-based codes, recovery codes and encrypted secrets, working with any authenticator app.
  • Sessions with refresh-token rotation, long-lived API keys for integrations, and single sign-on through the auth extension system.
  • A security monitor that watches for brute-force attempts and unusual spikes.
  • An audit log covering admin changes, plugin database writes, AI tool calls, rate-limit denials and plugins trying to do something they did not declare.

On a multi-site installation, people are members of sites, with roles per site. An administrator of one site never sees another. The platform administrator is the only one who can install plugins or change platform-wide keys.

Records, history and languages

Most business software is records: products, orders, customers, pages. In Atlantis a plugin declares a collection as a list of typed fields, and the kernel builds the rest:

  • the database table, and its changes when fields are added
  • the admin list and edit screens, generated from the fields
  • a REST API for the collection
  • version history: every create and update made through the admin or the API is snapshotted, for every plugin’s collections, with one-click restore

Fields can be marked as localised. Their values are stored per language and collapsed to the active language on every read, with a fallback to a language that has content. Admin labels, navigation and each plugin’s own texts are translatable too, without an outside library.

The services every plugin shares

Plugins do not bring their own cache, queue or e-mail library. The kernel runs one of each, configured once, and every plugin uses it:

  • Cache: Redis, Memcached or in-memory.
  • Queue: BullMQ on Redis for real deployments, a local queue for development.
  • File storage: local disk, S3 or Cloudinary, behind one interface, so moving files to S3 does not touch plugin code.
  • E-mail: SMTP, SendGrid or Mailgun, and a mock provider for development.
  • WebSockets, for real-time events between the server, the admin and themes.
  • Outbound webhooks, with a record of every delivery.
  • Scheduled jobs.

The practical effect is that two plugins never fight over a Redis connection, and switching an e-mail provider is a configuration change, not a code change.

Many sites, one installation

One Atlantis installation can serve many sites. A site is a customer: its own host names, content, people, plugins, theme and settings, on one shared platform.

Sites are kept apart twice. The application adds the site to every query, and, on PostgreSQL, row-level security in the database refuses any row that belongs to another site. The second layer exists for the day the first one has a bug: a query that forgets its filter still cannot return another customer’s data. Unique constraints include the site too, so two customers can both have a page called /pricing. Row-level security does not make your unique constraints tenant-safe is about what happens when that last part is missed.

Each site switches on its own subset of the installed plugins. The code runs once for the whole platform, but data is never shared. A new site is private until it is published: its own people can preview it without an account, everyone else gets nothing. Sites can be exported to a portable archive and imported elsewhere, and an existing single-site installation can be adopted into a multi-site one.

There is also a second kind of site, a workspace, which has no public website at all. Its domain serves an admin console locked to a product’s own appearance. That is how a product built on Atlantis gets its own branded console.

The rest of the free core

A few more things are in the kernel because they should not depend on which plugins a site runs:

  • Personal-data erasure. The kernel keeps a register of where personal data lives. It holds its own datasets, and every plugin declares its own, with the strategies it supports: delete, anonymise or retain, with a stated reason. An invoice is retained because it is a statutory document. A "delete my account" request walks the whole register, so it reaches every plugin’s data even on a site with no compliance product installed.
  • Redirects and canonical addresses. Retired URLs are routing, so they belong to the kernel and work on an installation with no plugins.
  • TLS certificates as records in the admin, with expiry warnings. Private keys are encrypted at rest.
  • System backups, restore, and site-transfer bundles.
  • An MCP server. Every installation can be driven by an AI agent through the Model Context Protocol, with tokens minted in the admin. A token’s scopes limit which tools it can reach, the user’s own permissions are still checked on every call, list views leave out customers’ personal details, invoice tools are read-only, and every call is audited. Remote access is off until an operator switches it on.

How a plugin is built

A plugin is a folder with a manifest, a settings schema and its own code, split the usual way into controllers, services and repositories:

plugins/<name>/
├── index.ts          # exports only
├── manifest.json     # metadata, capabilities, dependencies
├── settings.ts       # configuration schema, rendered as a settings screen
├── src/
│   ├── on-init.ts    # registers routes, hooks and collections
│   ├── controllers/  # requests: validation and orchestration
│   ├── services/     # business logic
│   └── repositories/ # data access only
└── ui/               # admin and storefront components

The rule that shapes everything else: a plugin never imports another plugin. It talks to the rest of the system only through the kernel, in four ways:

  1. HTTP, through a namespaced client, for calls to another plugin’s public API.
  2. Hooks and events, to react to something another plugin did.
  3. Its own database tables, which the kernel prefixes with fcp_<plugin>_ so no two plugins collide.
  4. Settings, read through the kernel.

A plugin never touches the kernel’s own tables either. If it needs to notify administrators or look up who holds a role, it asks the kernel, which checks access on the way.

Plugins can build on each other’s collections without depending on each other. An SEO plugin adds its fields to the blog’s posts like this, and the blog does not need to know the SEO plugin exists:

context.collections.extend('blog', 'posts', {
  fields: [{ name: 'seoTitle', type: 'text', label: 'SEO title' }],
});

If the blog has not registered its collection yet, the extension waits and is applied when it does. And a plugin can react to another plugin’s records through hooks:

context.hooks.on('collection:posts:beforeSave', async (post) => {
  if (!post.seoTitle && post.title) post.seoTitle = post.title;
  return post;
});

Keeping plugins in their box

A plugin you did not write is a risk: it runs inside your application, next to your customers’ data. Atlantis treats that seriously in three ways.

First, signing. A plugin package is checked against its signature before it loads, and an unsigned or altered package is rejected.

Second, declared capabilities. A plugin states in its manifest what it needs, network access for example. If a new version asks for more than the approved one did, it is held until a platform administrator approves the change, instead of quietly gaining access.

Third, process isolation. An isolated plugin runs as a separate operating-system process, under its own unprivileged user, with a memory ceiling, a deadline on every call and a read-only code directory. It talks to the kernel through a message contract. If it crashes or runs away, it takes down only itself.

How the code is written

The codebase has a strong house style, and it is worth knowing about if you intend to build on it. Every layer is class-based: routers, middleware, controllers, services and repositories are classes, and data shapes are classes that carry their own behaviour rather than bare interfaces. On the UI side that is carried by three standalone packages that work in any React project, with no Atlantis dependency: class-based React components, a build-time template compiler, and a TypeScript build tool that also serves as the real type-check gate.

Architecture boundaries are enforced by a checker rather than by convention: which package may import which, and that plugins and themes only use the SDK. A change that crosses a boundary fails the build.

Running it yourself

On a server with Docker, a production install is two compose files and an environment file:

mkdir fromcode && cd fromcode
base=https://raw.githubusercontent.com/fromcode119/framework/main/deploy
curl -fsSLO $base/docker-compose.full-stack.yml
curl -fsSLO $base/docker-compose.images.yml
curl -fsSL  $base/.env.example -o .env
docker compose -f docker-compose.full-stack.yml -f docker-compose.images.yml pull
docker compose -f docker-compose.full-stack.yml -f docker-compose.images.yml up -d

The downloaded .env needs no editing; the secrets are generated on first boot. Then you open the server in a browser, by IP if there is no domain yet. A setup wizard asks for the database first and shows exactly what it will create, then asks for a language, an administrator account, a name for the platform and the domain for the admin. The wizard stays open for fifteen minutes after start and only for whoever reaches it first, so an exposed server cannot be claimed by a passer-by.

The database choice deserves a minute of thought, because it decides how many sites you can run:

  • PostgreSQL: many sites, kept apart by row-level security. The bundled default.
  • SQLite: one site, one file, no database server. Good for evaluating.
  • MySQL: one site, with real roles and backups, but no row-level security.

The choice is effectively permanent. Tenancy in Atlantis is one database with every row tagged with its site, not a database per customer, so a driver without row-level security is not a slower way to run several sites; it simply cannot. The platform refuses to start a second site on it rather than risk showing one customer’s data to another.

On PostgreSQL the wizard creates three database roles, not one, because PostgreSQL skips row-level security for superusers and for the owner of a table. The role that serves requests must be neither, or the separation between sites is silently switched off.

Updating is the same two compose commands with a newer version tag. For development there is also a path with no Docker at all: clone the repository, install, and start the local starter, which runs on SQLite.

How it compares

The framework’s README carries a feature table against WordPress, Strapi, Payload, Ghost, Directus and NestJS. Reduced to what matters when choosing: most content platforms give you a fixed schema or leave infrastructure to you; most application frameworks give you raw materials and no system. Atlantis tries to sit between them, with a kernel that already has identity, multi-site isolation, queues, caching, version history and plugin isolation, and a contract for adding your own domains on top.

Whether that is the right trade depends on what you are building. If you need a blog next week, a hosted blog is simpler. If you are building a business application that will grow for years, the parts in the kernel are the ones you would otherwise build yourself, badly, under deadline.

Why the core is free and the business plugins are not

The kernel is the part you would be most locked into. Data models, permissions, sessions, the admin: if the foundation under your application is closed, moving away from it later means rewriting everything above it. So that part is open. You can run it, change it and build on it, commercial use included, under the MIT licence. A plugin you write for it is your own work, under whatever terms you choose, and it uses exactly the same contract as mine.

A business plugin is a different kind of thing. It carries decisions about a domain: when an order counts as paid, how an invoice is built from an order, what a courier needs on a label, which data a tax authority expects. Those decisions have to be maintained as rules, couriers and payment providers change, and that ongoing work is what makes them products rather than a library.

The framework’s README describes a marketplace for plugins. It is paused at the moment; when it runs again, the free plugins will be available there. The commercial plugins are not sold on their own today: they are used in the projects I build, and there is no public price list.

the honest limits
  • It is young. The first commit is from January 2026. It runs real sites, including one that takes real orders every day, but it has not had a decade of other people’s edge cases.
  • The ecosystem is small. There is no catalogue of thousands of third-party plugins; there is a clear contract for writing your own.
  • Several sites on one installation need PostgreSQL.
  • It has opinions, about classes, boundaries and what a plugin may touch. If you disagree with them, it will feel strict.
  • It is software you, or I, run. It is not a hosted website builder with a sign-up page.

Where to start

The code, the licence, the installation guide, the configuration reference and the plugin development guide are on GitHub. To see what the business plugins do together on a real site, From order to invoice to shipping label walks through a live site’s order flow step by step, and the platform page lists what is available.

Want something built on Atlantis?

Describe what it has to do.

Discuss your project