The resource model
Thirteen typed resources, one lifecycle, and a plan that is only a bundle of grants.
Everything kstack manages is a typed resource, and every type implements the same lifecycle.
provision / update / suspend / unsuspend / deprovision / observe
That is the whole interface. It is why "sell one mailbox" and "sell a 200-site reseller plan" run the same machinery.
The types
domain, dns_zone, dns_record, website, ssl_cert, database, db_user, mailbox, mail_domain, forwarder, ftp_account, cron_job, backup_policy.
How they are owned
account (parent_account_id -> reseller tree, quota pool)
└── subscription -> plan -> resource_grants[]
└── resource (typed)
An account can have a parent, which is what makes a reseller tree a tree rather than a special case. Quota pools follow the same shape.
A plan is a bundle of grants
A plan does not describe a product in prose. It grants a number of each resource type, and its kind decides which grants are even allowed.
| Kind | May grant |
|---|---|
shared_hosting, reseller_hosting |
anything |
database |
database, backup_policy |
email |
mail_domain, mailbox, dns_zone |
dns |
dns_zone |
vps |
vm, backup_policy, private_network |
A grant the kind does not sell is refused with a 400 naming the grant, rather than accepted and ignored.
Isolation
The website is the isolation unit — not the account. One unprivileged, id-mapped system container per site. A customer with five sites has five boundaries, so a compromise of one does not reach the other four.
Databases are deliberately not per-customer containers. They are shared instances on database nodes, with a per-customer database, user and grants. A container each would hold the same engine in memory once per customer, and at density that cost is not recoverable.

