Architecture¶
Ndakita Build is a whitelabeled layer on ERPNext v15, delivered as a multi-tenant service. Each tenant is a construction small or medium enterprise running several concurrent projects.
Components¶
┌──────────────────────────┐
Browser ───▶ │ ndakita_group_site │ Marketing website and sign-up
│ (public web application) │
└────────────┬─────────────┘
│ HTTPS, public endpoints (no secret)
▼
┌──────────────────────────┐
│ ndakita_build_platform │ Provisioning, lifecycle, billing
│ (platform service) │ Metadata database only
└────────────┬─────────────┘
│ bench CLI (subprocess) │ Frappe REST (per tenant)
▼ ▼
┌──────────────────────────────────────────────────┐
│ Frappe Bench (one site + dedicated database each) │
│ {slug}.<base-domain> for every tenant │
│ each site runs the ndakita_build app + ERPNext │
└──────────────────────────────────────────────────┘
- ndakita_group_site — the public website and the sign-up form. It calls only the platform's public endpoints and holds no secret.
- ndakita_build_platform — orchestrates the system: creates tenant sites, tracks lifecycle and billing, and serves payment webhooks. Its database stores only tenant metadata, never ERP business data.
- ndakita_build — the construction ERP itself (DocTypes, custom fields, reports, labour and payment workflows). Installed on every tenant site.
Tenancy model¶
Each tenant is provisioned as a dedicated Frappe site with its own database. This provides database-level isolation rather than row-level filtering, so no query can span tenants. Tenants are not represented as companies inside a shared site.
Branding follows a product-brand model: every tenant's ERP interface displays the Ndakita Build identity (login page, application header, favicon, loading screen). A tenant's own logo appears only on generated documents. Branding is applied automatically during provisioning.
Platform API surface¶
| Endpoint | Authentication | Purpose |
|---|---|---|
POST /public/signup |
none (rate limited) | Create a tenant record and start provisioning |
GET /public/slug-availability |
none (rate limited) | Live availability check in the sign-up form |
GET /public/tenants/{id}/status |
none | Poll provisioning status; reveals the site URL when ready |
GET/POST /tenants/* |
API key | Administrative lifecycle: list, suspend, restore, reprovision |
POST /webhooks/{mpesa,stripe} |
signature | Payment callbacks |
Sign-up to login flow¶
- A visitor completes the sign-up form on the marketing website.
POST /public/signupcreates a tenant record with statusprovisioningand starts provisioning asynchronously so the request returns immediately.- Provisioning runs the standard sequence: create the site, install ERPNext,
the Ndakita Build application, and the whitelabel layer; run the setup wizard
with the correct localisation; migrate (which seeds construction defaults);
verify the installation; and configure web routing. On success the tenant
moves to
trial. - The form polls the status endpoint until
trial, then presents the tenant'shttps://{slug}.<base-domain>address. - The tenant opens the address and signs in as their administrator.
Tenant lifecycle¶
provisioning → trial → pending → active → grace → suspended → cancelled
A separate provisioning_failed state captures build failures. The states are
defined in the platform models and mirrored in the marketing site's types.
Suspension blocks login while preserving data; cancellation exports data before
removing the site.
Design principles¶
- ERPNext and Frappe core are never modified; the system extends them through hooks, fixtures, and overrides.
- The platform never accesses tenant databases directly; it uses the bench CLI for provisioning and the Frappe REST API for tenant data.
- Provisioning is asynchronous and idempotent; a failed tenant can be re-provisioned safely.
- Tenant-facing interfaces contain no references to the underlying frameworks.