springform
Open source · MIT · Python and TypeScript

Small-business software, minus the plumbing.

Login, people, files, notes, todos, reminders, backups and an assistant are already built. You write the app that is actually about the business. A client gets a private fork that pulls platform updates and never edits core.

Fork it. Bake it. Release it.  A springform is the pan a cheesecake comes out of whole. The form stays on the foundation; what you made lifts off it.

1SQLite file, on one volume
1container, one process
2roles: admin and member
0AI keys needed to run it

What you get out of the box

Every record in every app gets the same core: a timeline, attachments, custom fields, saved views, reminders and notifications. Apps add the parts that are about the business.

Core

Login that is not a project

Password plus an authenticator app, or sign in with Google. Invite-only. Admins and members, nothing more.

Core

One directory of people

People and organizations, linked with a role, shared by every app. Duplicate finding and merge built in.

Core

Timelines and files on any record

Notes, comments, @-mentions and field history on everything. Attachments live on the volume, deduplicated by hash.

Core

Fields, views and reports

Custom fields of eight types, saved views per screen, and CSV reports an app registers in one line.

Core

Reminders and notifications

In-app, with web push. A reminder re-checks that you may still see the record before it fires.

Core

Backups you can restore

A nightly snapshot plus the files and the encryption key, with a restore script and a restore test in CI.

Core

Whisk, the assistant

Knows the installed apps and their records. Proposes a write before making it. Runs with zero AI keys; chat waits for one.

Core

MCP, both ways

Each user can mint a link that gives Claude the same tools with the same permissions. Admins can connect Whisk to other MCP servers.

Core

Jobs, Sheets, Guide, Requests

A scheduler with a Jobs page, connected Google Sheets, an in-app guide built from the docs, and a request board that files GitHub issues.

Built-in app

CRM

Pipeline, deals, activities, scoring, dashboards. Ported from a CRM that runs a real company.

Built-in app

Todos

Private by default, team todos anyone can pick up, attached to any record. Today, review, recurring.

Yours

The app about the business

A folder with an app.json. Routes, screens, migrations, tools, jobs and docs are discovered at boot and land on the launcher.

How it's put together

Deliberately small. If you can run a Docker container, you can run this.

Launcher
CRMTodosYour appAnother+ a folder
Core
login · people & organizations · timelines · files · fields · views · reports · reminders · notifications · scheduler · backups · Sheets · Whisk · MCP
One volume
/data/springform.db/data/files/data/backups

FastAPI and Pydantic behind

Python 3.12. One process, one writer, SQLite with WAL and FTS5. No queue, no cache server, no Postgres.

React and TypeScript in front

Vite, Tailwind, React Router. Types are generated from the API, and CI fails when they drift.

Apps are folders

Tables are prefixed with the app's slug. Apps talk to each other only through published functions. A contract test enforces both.

Tested like it matters

Every pull request runs the backend, frontend, image and browser suites, plus a secret scan. Forks run the same workflow.

Fork it. Bake it. Release it.

A client's software is a private repository that starts from a tagged release. Core stays byte-identical to that tag; the apps are theirs.

Fork it

One script makes the private repo, pins the release, seeds labels and waits for the first green CI run.

scripts/new-fork.sh acme --org acme-co

Bake it

Scaffold an app, then build. CI's Fork scope job fails any change outside the app folders, so core never drifts.

scripts/new-app.sh invoices "Invoices"

Release it

Pull the next platform release as a pull request. Tag the fork and it builds its own image, apps included.

scripts/update-from-upstream.sh v0.2.0
git tag v0.2.0-acme.1

Why not a GitHub button fork? A fork of a public repo cannot be private. This is a clone with a fetch-only upstream, which can.

Run it

On first boot the log prints a one-time setup token for creating the first admin. Nothing is created by whoever visits first.

On your laptop

The published image, data in a named volume.

docker run -p 8000:8000 \
  -v springform-data:/data \
  ghcr.io/wwilson1017/springform:latest

On a server

A fresh Ubuntu 24.04 machine whose DNS name already points at it. Docker, Caddy with automatic HTTPS, a firewall, security updates and an off-site backup cron.

git clone \
  https://github.com/wwilson1017/springform.git
sudo DOMAIN=app.example.com \
  springform/deploy/setup.sh

On Railway

The repo carries a railway.json: Dockerfile build, health check on /up, a volume at /data.

Set SECRET_KEY, PUBLIC_URL,
TRUST_PROXY=1, and attach a volume.

Deploys are stop-before-start. SQLite has one writer, and the app holds a lock on the volume so a second copy refuses to start. Expect a few seconds of downtime per deploy, and never more than one worker.

Why this exists

Every time a small business gets its own software, the same first month gets rebuilt: login, users, a contacts list, files, notes on records, to-dos, a scheduler, backups, a helper, and a way to file the next change. Springform is that month, done once, with the extension points a real client needed and did not have.

It is built in the open by Will Wilson, who runs a cheesecake bakery in Tennessee and writes the software that runs it. The CRM and the todo app are ports of tools that already run that business.

What it is not: a no-code builder, a multi-tenant SaaS, or a framework that hides the database. It is a repository you read, a container you run, and a folder you add.

Where it stands: first releases in October 2026, first production install in November. Sharp edges remain. File what you hit.