Skip to content
NOWACRM Docs

Operator documentation

Lifecycle Architecture Document (LIFECYCLE.md)

1. **Hierarchy is Absolute**:

Lifecycle Architecture Document (LIFECYCLE.md)

System Invariants (Architectural Law)

  1. Hierarchy is Absolute:
    • Owner (Platform Admin, singleton) → Director (×∞) → ISP/Tenant (×∞) → Partner (×∞, recursive, no depth limit) → Employee (×∞).
    • No entity can access data outside its subtree (no sibling access, no upstream access).
  2. No RBAC Above ISP:
    • Owner, Directors, and ISPs are organizational entities with hard-coded, hierarchy-based access.
    • RBAC (Roles & Permissions) exists ONLY within the ISP Tenant Zone (for Employees and Partners).
  3. Ownership is Explicit:
    • Every object has a tenant_id (belongs to) and tenants have parent_id (managed by).
  4. Tenant Isolation:
    • Wallets, Subscribers, Plans, and Logs exist strictly within the ISP Tenant boundary.
  5. No Partner Depth Limit:
    • Partners can create sub-partners, who can create sub-sub-partners, infinitely.
    • Each partner can also create their own employees, unlimited.

A. Full Hierarchy

Owner (singleton, platform admin)
 └── Director ×∞  (user_type=1, no tenant_id)
       └── ISP/Tenant ×∞  (type=isp, user_type=2 for ISP admin)
             ├── Employee ×∞  (user_type=2, tenant_id=ISP)
             └── Partner ×∞   (user_type=3, type=partner, recursive)
                   ├── Employee ×∞  (user_type=2, tenant_id=partner's tenant)
                   └── Sub-Partner ×∞  (user_type=3, type=partner)
                         ├── Employee ×∞
                         └── Sub-Sub-Partner ×∞
                               └── ... (no depth limit)

User Types

user_type Role Description
0 / NULL Owner Platform admin (singleton). No tenant_id.
1 Director Regional manager. No tenant_id. Has own tenant (type=director).
2 Employee Staff with RBAC. Has tenant_id (belongs to ISP or Partner).
3 Partner Reseller admin. Has tenant_id (own partner tenant).

Tenant Types

type Description parent_id
root Platform root (NowaCRM) NULL
director Director's org node root tenant ID
isp ISP/Tenant boundary director's tenant ID
partner Partner org node (recursive) ISP or parent partner's tenant ID

B. Domain Routing

Domain Who Purpose
admin.nowacrm.test Owner Create/manage Directors, system settings
app.nowacrm.test/director/* Directors Create/manage ISPs under them
app.nowacrm.test/* ISP + Partners + Employees Unified portal with hierarchical org selector
login.nowacrm.test (future) End users/subscribers Customer self-service portal

Auth Guards

Guard Portal Validates
owner admin.* Owner role via user_roles table
director app.*/director user_type = 1
app app.* user_type in [2, 3]

C. Entity Lifecycle

1. Owner (System Bootstrap)

  • Created by: Database Seeder / CLI Command.
  • Cardinality: Singleton (only one).
  • Scope: Global — all Directors, Audit Logs, System Settings.
  • Cannot be deleted or suspended via UI.

2. Director

  • Created by: Owner.
  • Quantity: Unlimited per platform.
  • What's created:
    • tenants row: type=director, parent_id=root_tenant.
    • users row: user_type=1, tenant_id=NULL (directors don't belong to a tenant scope).
  • Scope: Can only see/manage ISPs they created. Cannot see other Directors.

3. ISP / Tenant (Tenant Boundary)

  • Created by: Director.
  • Quantity: Unlimited per Director.
  • What's created:
    • tenants row: type=isp, parent_id=director's tenant, director_id=director_user_id.
    • users row: ISP Admin with user_type=2, tenant_id=new_isp_tenant_id.
    • isp_profiles row: Business/legal info.
    • Default RBAC roles (Admin, Manager, Support) scoped to this tenant.
    • Wallet initialization.
  • This is the tenant boundary — all data below is scoped by tenant_id.

4. Partner (Recursive, No Depth Limit)

  • Created by: ISP Admin, or any Parent Partner.
  • Quantity: Unlimited. Sub-partners unlimited. No depth limit.
  • What's created:
    • tenants row: type=partner, parent_id=creator's tenant.
    • users row: Partner Admin with user_type=3, tenant_id=new_partner_tenant_id.
    • partners row: Business profile (commission, contact info).
  • Scope: Own subtree only — own employees, own sub-partners, and everything below.
  • Cannot see sibling partners, parent ISP config, or upstream data.

5. Employee

  • Created by: ISP Admin, or Partner Admin.
  • Quantity: Unlimited per ISP or Partner.
  • What's created:
    • users row: user_type=2, tenant_id=the_org_they_belong_to.
  • RBAC: Must be assigned a Role (defined by root ISP).
  • Scope: Determined by their org placement + RBAC permissions.

D. Creation Lifecycle (Who Creates Whom)

Creator Can Create Portal
Owner Director (×∞) admin.nowacrm.test
Director ISP/Tenant (×∞) app.nowacrm.test/director
ISP Admin Partner (×∞), Employee (×∞) app.nowacrm.test
Partner Sub-Partner (×∞, no depth limit), Employee (×∞) app.nowacrm.test
Employee Nothing (consumer of RBAC permissions) app.nowacrm.test

Creation Flow

Owner logs into admin.nowacrm.test
  → Creates Director "Galaxy Telecom"
    → Director user (user_type=1) + Director tenant (type=director)

Director logs into app.nowacrm.test/director
  → Creates ISP "Nova Internet Services"
    → ISP tenant (type=isp, parent=director tenant)
    → ISP Admin user (user_type=2, tenant_id=ISP)
    → ISP Profile, Wallet, Default Roles

ISP Admin logs into app.nowacrm.test
  → Creates Partner "CityNet Resellers"
    → Partner tenant (type=partner, parent=ISP tenant)
    → Partner Admin user (user_type=3, tenant_id=partner)
    → Partner profile (commission, business info)
  → Creates Employee "John Support"
    → User (user_type=2, tenant_id=ISP) + Role assignment

Partner Admin logs into app.nowacrm.test
  → Creates Sub-Partner "LocalNet"
    → Partner tenant (type=partner, parent=CityNet's tenant)
    → Sub-Partner Admin user (user_type=3)
  → Creates Employee "Jane Sales"
    → User (user_type=2, tenant_id=CityNet's tenant)

Sub-Partner creates Sub-Sub-Partner... (infinite depth)

E. Scope Boundaries (Visibility)

Rule Description
Upstream: None Partner cannot see ISP config. Sub-partner cannot see parent partner's data.
Downstream: Full ISP sees everything below. Partner sees their full subtree.
Sibling: Zero Partner A cannot see Partner B. Director A cannot see Director B.

ISP Portal Org Selector

The unified ISP portal includes a hierarchical org selector in the header:

  • ISP Admin sees: All (ISP + all partners + all sub-partners below)
  • Partner Admin sees: Own org + all sub-partners below
  • Employee at leaf org: No selector (only their own org data)

Switching the org selector re-scopes all read queries to WHERE tenant_id IN (selected_subtree). Write operations always use the user's own tenant_id for safety.


F. Tenant Hierarchy (Database)

hierarchy_path Format

Each tenant stores a materialized path: /{root_id}/{parent_id}/.../{self_id}/

Example:

/1/          → Root (NowaCRM Platform)
/1/2/        → Director (Galaxy Telecom)
/1/2/14/     → ISP (Nova Internet) under Galaxy
/1/2/14/20/  → Partner (CityNet) under Nova
/1/2/14/20/25/ → Sub-Partner (LocalNet) under CityNet

Subtree Query

To get all descendants of tenant X:

SELECT id FROM tenants WHERE hierarchy_path LIKE '{X.hierarchy_path}%'

G. Tenant Boundary Data

Global Data (Shared)

  • System updates, platform settings.
  • Global hardware types (ONU Models) defined by Owner.

ISP Tenant Data (Isolated)

  • Plans & Pricing: Unique to each ISP.
  • Subscribers: Strictly siloed per ISP.
  • Wallet Transactions: Never cross ISP boundaries.
  • RBAC Roles: 'Support' in ISP A ≠ 'Support' in ISP B.
  • Audit Logs: Tenant-scoped.

Partner Data (Sub-Tenant)

  • Subset of ISP data.
  • Partners do not define Plans (they sell ISP plans).
  • Partners do not define Roles (they use ISP-defined roles).
  • Partners have their own Wallet (commission tracking).

G2. Plan Sharing Lifecycle

Share Types

  • Commission (%): Partner earns X% of plan price per renewal. ISP revenue = price × (1 - rate/100). Rate: 0.01% – 99.99%.
  • Fixed Price (₹): Partner pays fixed cost to ISP per renewal. Partner earning = price - cost. Cost must be > 0 and ≤ plan price.

Rules & Invariants

  • sell_price is always plan.price (not configurable).
  • One share per plan+partner pair (unique constraint). Soft-deleted shares are restored on re-assignment.
  • commission_rate >= 100 is rejected (ISP must retain revenue).
  • buy_price = 0 is rejected (ISP must earn something).
  • buy_price > plan.price is rejected (partner cannot pay more than MRP).
  • Opposite-type fields are auto-cleared: commission sets buy_price=null, fixed sets commission_rate=null.

Renewal Flow

  1. SubscriberBillingService::resolvePartnerShare() finds active share for plan+partner.
  2. If commission: partner_cost = plan.price × (1 - rate/100).
  3. If fixed: partner_cost = buy_price.
  4. If no share exists: partner pays full MRP (plan.price).
  5. GST applied on partner_cost (always tax-exclusive).
  6. Total debited from partner wallet → invoice generated.

Cascading Effects

  • Deactivating share → next renewal falls back to MRP.
  • Changing commission rate → applies from next renewal (existing invoices unaffected).
  • Plan price change → commission recalculates automatically; fixed amount stays constant.
  • Bulk assign → applies same rate/cost to multiple plans for one partner in a single operation.

H. RBAC Lifecycle (ISP Level Only)

Context: Applies only to Employees of ISPs and Partners.

1. Role Creation

  • Defined by ISP Admin (root tenant of the ISP).
  • Stored with tenant_id = ISP tenant ID.
  • Examples: ISP Admin, Field Technician, L1 Support, Partner Manager.

2. Permission Assignment

  • Granular permissions (e.g., subscriber.create, wallet.topup).
  • Assigned to Roles, not Users directly.

3. Effective Permission Resolution

  • User logs in → System loads Roles via user_roles → loads Permissions via role_permissions.
  • Constraint: Even with subscriber.delete permission, user can ONLY delete subscribers within their org subtree.
  • Algorithm: HasPermission(Action) AND InSubtree(ResourceTenantID)

I. Deletion / Suspension Flow

1. Director Removal

  • Action: Archive / Soft Delete.
  • Impact: Orphans associated ISPs.
  • Resolution: ISPs must be re-assigned to Owner or another Director before deletion.

2. ISP Suspension

  • Action: Status = suspended.
  • Cascade:
    • All ISP Employees locked out.
    • All Partners (and their sub-partners, recursively) locked out.
    • RADIUS/NAS executes CoA to disconnect all subscribers.
    • Billing cycles pause.

3. Partner Suspension

  • Action: Status = suspended.
  • Cascade:
    • All sub-partners (recursively) suspended.
    • All employees under this partner locked out.
    • Subscribers re-assigned or frozen.
    • Wallet balance frozen.

4. Employee Removal

  • Action: Deactivate (status = inactive).
  • Impact: Loses login access immediately. No cascade.

J. Invariants (Must Never Break)

  1. Owner is a singleton, immutable, cannot be deleted.
  2. No Cross-Tenant Data Leakage: Every query for tenant data MUST include tenant scoping.
  3. No Circular Hierarchies: A tenant cannot be its own ancestor (hierarchy_path enforces this).
  4. No Partner Depth Limit: The recursive partner hierarchy has no artificial depth cap.
  5. RBAC Containment: A Role created by ISP A cannot be assigned to a user in ISP B.
  6. Write Safety: Write operations always use the authenticated user's tenant_id, never the "viewing" subtree.
  7. Suspension Cascades Downward: Suspending any node locks out the entire subtree below it.