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.

References