Skip to content
AB

Product requirements document

Public template · MIT

skeleton_foundation

An engineering PRD for a self-installing multi-tenant SaaS foundation: one Linux server, one script, isolated tenants, an operator console and encrypted backups.

Last updated

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

PersonaWhat they do with it
Product engineerBuilds the product as modules: tables, routes and screens
Platform operatorInstalls the platform, creates tenants, manages plans, domains, operators, backups and upgrades
Tenant administratorManages their organisation's users, categories, departments, groups and settings
Tenant userSigns in with company, username and password and uses the enabled modules
Individual user (B2C)Uses the individual-user app, with plans
Coding agentChanges 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 check validates the settings and the server and changes nothing.
  • setup.sh install runs 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

AreaRequirement
SecurityForced 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
SecretsGenerated on the server, never written into .env/, readable by root only; logs never contain passwords
Least privilegeOne system user per service; services can't read the operator's credentials; all ports bind to 127.0.0.1
PlatformUbuntu 22.04/24.04 or Debian 12 with systemd; 2+ vCPUs, 4 GB+ RAM, 20 GB+ disk; Node.js 22+, PostgreSQL 16+
EdgeThree modes: Cloudflare API (automatic tunnel, DNS and Turnstile), Cloudflare token, or your own TLS reverse proxy
TestingUnit, 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
Distributionsetup.sh package writes a clean zip with every secret blanked

6. Solution overview and architecture

skeleton_foundation architectureEdge: Cloudflare Tunnel, Turnstile. Then Frontends (React): Landing page, Operator console, Tenant workspaces, Shared sign-in and B2C app. Then Backends (Fastify): Platform server, Public server, Tenant server, Provisioning worker. Then Data and operations: PgBouncer, PostgreSQL 16, Tenant files, Backups and healthEDGECloudflare TunnelOr your own TLS proxyTurnstileOn sign-in pagesFRONTENDS (REACT)Landing pageYour domainOperator consoleplatform. subdomainTenant workspacesOne subdomain pertenantShared sign-in andB2C appapp. and user.BACKENDS (FASTIFY)Platform serverPublic serverTenant serverProvisioning workerDATA AND OPERATIONSPgBouncerThree poolsPostgreSQL 16platform_db + a schemaper tenantTenant filesLocal or bring-your-ownS3Backups and healthAES-256 timer, healthchecks
Everything runs on one Linux server under systemd. Each tenant gets its own subdomain and database schema.
HostServes
platform. subdomainOperator console
app. subdomainShared sign-in
user. subdomainIndividual-user app
One subdomain per tenantTenant workspace
The root domainLanding 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

WhenRelease
Up to Oct 2026Public 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.