Platform → Brand → Tenant → Agent
Aroovo's defensible shape is a four-level hierarchy. Every row in the database belongs to exactly one Tenant; every Tenant belongs to exactly one Brand; every Brand runs on the same single platform deployment. The structure is enforced at the schema level via mandatory foreign keys, not by convention.
Platform (one deployment, one DB)
└── Brand (Aroovo, AcmeAgency, BetaPartner, ...)
└── Tenant (appLockr, AcmeAgency-Customer-X, ...)
└── Agent (sales, marketing, finance, ...)Platform
One deployment, one database, one operator team (Aroovo). We run it. A single platform hosts every Brand and every Tenant; we do not fork the codebase per Brand and we do not stand up a new Postgres per Brand. Isolation between rows is enforced through Brand- and Tenant-scoped queries, an HMAC-chained audit log per Tenant, and an encrypted secrets vault that refuses cross-Brand reads at the lib level.
Brand
A Brand is a reseller, agency, or consultancy. Each Brand has its own slug, display name, logo, palette, hostname, identity provider, LLM provider config, and Bot Framework or Slack app registrations. Brands invoice their downstream customers; Aroovo invoices the Brand. A Brand-Owner role admins one Brand and every Tenant under it — they never see sibling Brands.
The single aroovo Brand is seeded by an idempotent boot hook and is sacred: the platform refuses to suspend, offboard or delete it. The Aroovo Brand owns the appLockr Tenant, which is the platform's own internal usage. Every reseller is a new Brand row alongside aroovo.
Tenant
A Tenant is one customer of a Brand. Their users, their KB, their billing limits, their cron triggers, their conversation history, their audit chain, their rate-limit bucket, their spend cap. A breach of one Tenant's vault cannot bleed into another's — even when two Tenants share the same Brand. The default appLockr Tenant under the Aroovo Brand is the canonical example.
Tenant onboarding is a three-step wizard (/admin/tenants/new): identify, pick a team preset, provision MCP. See Quickstart.
Agent
An Agent is a configured AI persona (sales, support, finance, ops, or a custom department) running on the platform's in-process agent loop. Each agent has its own system prompt, model choice, tool allowlist, approval gates, MCP server set, and Bot Framework or Slack app id. Sub-agents (e.g. sdr under sales) sit one level deeper and are auto-provisioned when a Tenant adopts the parent department.
Why four levels and not three
Three-level models (Platform → Tenant → Agent) force a choice: either every reseller's customer is its own Tenant — fine until you want per-reseller billing and branding — or every reseller is a Tenant and their customers are sub-objects, which collapses the moment the reseller wants to onboard multiple downstream organisations. The Brand layer cleanly separates the reseller's commercial relationship with us from their customers' isolation from each other.