1. Summary
skeleton_foundation is a ready-made base for multi-tenant SaaS products that installs on one Linux server with a single script. It provides an operator console, isolated tenant workspaces with schema-per-tenant PostgreSQL and forced row-level security, shared sign-in, encrypted backups and day-to-day operations, but no product features and no customer data. Teams add their product as modules that operators switch on per tenant or plan, and EuclidPRO.ai runs on it.
2. Problem and context
Who has the problem. Small teams building software for several customer organisations from one codebase.
What it costs them. Before a team can ship its first product feature, it has to build the same base every multi-tenant product needs:
- tenant isolation, so one customer can never see another's data;
- sign-in, users, roles and an audit trail;
- an operator console to create tenants, plans and domains;
- backups that are encrypted and actually restore;
- installation, upgrades, health checks and edge configuration.
Assumption: this base takes months to build well, and mistakes in tenant isolation or backups are the most expensive kind a SaaS company can make. skeleton_foundation builds that base once, tests it, and lets teams start from it.
3. Users and personas
| Persona | What they do with it |
|---|---|
| Product engineer | Builds the product as modules: tables, routes and screens |
| Platform operator | Installs the platform, creates tenants, manages plans, domains, operators, backups and upgrades |
| Tenant administrator | Manages their organisation's users, categories, departments, groups and settings |
| Tenant user | Signs in with company, username and password and uses the enabled modules |
| Individual user (B2C) | Uses the individual-user app, with plans |
| Coding agent | Changes the code following the rules, map and recipes in AGENTS.md |
4. Goals and non-goals
Goals
- Install on one server with one script, and re-run safely if it stops.
- Isolate tenants by default: a schema per tenant with forced row-level security.
- Let product features plug in as modules without touching the security code.
- Upgrade in place while keeping data, secrets and tenants.
- Make backups encrypted, scheduled and provably restorable.
- Test everything against a real database, with no skipped tests.
Non-goals
- Product features or customer data. The repository ships none.
- Assumption: multi-server or Kubernetes deployments. The design targets one server; two installations can share a server with different slugs and ports.
- A hosted service. Each team runs its own installation.
5. Requirements
Functional requirements
F-1. Install. As an operator, I want to install the platform by filling in five settings and running one script.
setup.sh checkvalidates the settings and the server and changes nothing.setup.sh installruns 15 numbered steps and can be re-run safely; completed work is detected and skipped.- The first install creates the owner account with a generated password.
F-2. Create a tenant. As an operator, I want to create a tenant from the console.
- Provisioning runs about 20 steps (schema, role, grants, storage, domain, administrator, verification), shown live.
- The tenant gets its own subdomain and its own database schema.
F-3. Add a module. As a product engineer, I want to add a feature as a module.
- A module is a feature switch, off by default, that operators turn on per tenant or plan.
- Its tenant tables and their access are declared in one manifest.
- Its API routes receive the session, the permissions and the tenant's database connection.
- Its screens appear in the tenant workspace.
F-4. Back up and restore. As an operator, I want daily encrypted backups I can trust.
- A timer runs an AES-256 encrypted backup on a schedule, and old backups are removed after the retention period.
- Each backup holds the roles, a full database dump, tenant credential files and an optional copy of tenant storage, with a SHA-256 manifest.
- A drill restores a backup into a throwaway cluster and verifies the tenant schemas and isolation.
F-5. Bring your own storage or database. As a tenant or operator, I want to move a tenant's files to an S3-compatible bucket, or its data to an external PostgreSQL.
- The connection is tested before it is activated, and the worker relocates the data.
F-6. Upgrade. As an operator, I want to upgrade with git pull and setup.sh install.
- Only what changed is rebuilt; new migrations and tenant schema upgrades are applied; services restart.
- Secrets, data, tenants and tenant PgBouncer entries are preserved.
F-7. Remove. As an operator, I want uninstall --purge to return the folder to its original state, keeping the backups.
Non-functional requirements
| Area | Requirement |
|---|---|
| Security | Forced row-level security and a schema per tenant; security-key sign-in for operators; a recent security-key check before changes; Turnstile on sign-in pages; HTTPS mandatory |
| Secrets | Generated on the server, never written into .env/, readable by root only; logs never contain passwords |
| Least privilege | One system user per service; services can't read the operator's credentials; all ports bind to 127.0.0.1 |
| Platform | Ubuntu 22.04/24.04 or Debian 12 with systemd; 2+ vCPUs, 4 GB+ RAM, 20 GB+ disk; Node.js 22+, PostgreSQL 16+ |
| Edge | Three modes: Cloudflare API (automatic tunnel, DNS and Turnstile), Cloudflare token, or your own TLS reverse proxy |
| Testing | Unit, integration and Playwright browser tests against a throwaway cluster with two fixture tenants; CI runs them through a real PgBouncer and fails if any test is skipped |
| Distribution | setup.sh package writes a clean zip with every secret blanked |
6. Solution overview and architecture
| Host | Serves |
|---|---|
platform. subdomain | Operator console |
app. subdomain | Shared sign-in |
user. subdomain | Individual-user app |
| One subdomain per tenant | Tenant workspace |
| The root domain | Landing page |
The repository is organised as setup.sh (installer and control), backend/, frontend/, dependency/ (shared packages), database/ (migrations and seeds), deploy/templates/ (systemd, PgBouncer and PostgreSQL), scripts/ and tests/.
7. AI design
skeleton_foundation has no AI features. It is built to be changed safely by people and coding agents: AGENTS.md gives contributors and agents the rules, a map of the code and step-by-step recipes, including how to add a module.
8. Success metrics
Confirmed
- EuclidPRO.ai runs on skeleton_foundation.
- Published as a public template under the MIT licence.
Metrics to track
- Time from a fresh server to a signed-in operator
- Time to provision a tenant, and provisioning success rate
- Backup restore drill pass rate
- Upgrade success rate across tenant schemas
- Test suite duration (skipped tests are already enforced at zero by CI)
- Number of products built on the foundation
9. Release history and roadmap
| When | Release |
|---|---|
| Up to Oct 2026 | Public template: installer, operator console, tenant workspaces, shared sign-in, B2C app, backups, edge modes, tests and CI |
Known gap (from the README): a backup taken while files are being uploaded can contain a file without its database row, or the reverse. File writes already take the shared side of a database lock; the backup script does not yet hold the exclusive side. Assumption: closing this gap is the next roadmap item.
10. Risks and open questions
Risks
- Lost backup key. Without
backup.key, backups can't be decrypted, and without the integration sealing key stored tenant credentials can't be unsealed. The README tells operators to copy both off the server. - Single server. Assumption: one server limits scale and availability; there is no built-in failover.
- Cloudflare coupling. The fully automatic edge mode depends on Cloudflare's API; the other modes need more manual setup.
- Backup consistency during uploads (the known gap in section 9).
Open questions
- At what tenant count or load should an installation move to a larger server or a managed database?
- Should a high-availability option be on the roadmap?
11. My role
- Author of skeleton_foundation. Assumption: that covers the architecture, the installer, the security model, the operator console, the tenant workspace, the provisioning worker, the backup and restore tooling, the tests and the documentation.
- Use it as the base for EuclidPRO.ai.