Embedding

Multi-Tenancy

How to enable multi-tenancy for On-Premise installation or embedded studio

Overview

Whether you need multi-tenant separation in Carbone depends on how you execute Carbone On-Premise. Below is a non-exhaustive list of use cases indicating when multi-tenancy separation may be required:

Execution Mode Template source Tenant boundary Carbone separation needed?
Stateless (Ephemeral templates) The template is sent with every render request (e.g., base64) via POST /render/template. Nothing is stored by the Carbone backend. Your application must enforce tenant separation (auth, access control, routing). Not required in Carbone. Carbone acts as a stateless generator.
Hybrid (Template storage only) Templates are stored as files (by Carbone or by your app), optionally on S3/other storage. Carbone does not manage tenant metadata. Your application maintains the association template ↔ tenantId and enforces tenant access control. Usually not required in Carbone. Optional separation can be done at the storage layer (see below).
Stateful (Template versioning) Carbone enables its SQLite database to support features like template listing, search, versioning, deployment. Carbone manages template storage (disk and/or S3) and stores template metadata in SQLite (deployed version, name, comments, tags, category…). Carbone should enforce tenant separation because Carbone is now the system of record for template metadata and discovery features. Recommended to separate by tenant in Carbone (e.g., to list/search templates per tenant safely).

Set the tenantId

To enable tenant isolation, set req.tenantId in the HTTP request hook middleware plugin.

This is also the place where you can authenticate your users by reading a cookie or relying on an external authentication service such as Google OpenID, Microsoft Azure AD, Okta, Auth0, OneLogin, Keycloak, Ory, and others.

// HTTP middleware called before all routes
function beforeMiddleware(req, res, next) {
  // Provide the tenant id using your own logic:
  // header from your application, a token, external auth service, etc.
  req.tenantId = req.headers['custom-tenant-id'];
  return next();
}

Isolation behavior

Constraints

If tenantId is undefined or null, Carbone automatically disables tenant isolation. In this case, all data in the SQLite database will be stored with tenantId = 0 (see Security below).

Security

When req.tenantId is not set (i.e., it is undefined or null), Carbone will NOT separate data between tenants. This can be a security risk if you expect data isolation.

To force tenant isolation for all API requests and block requests where req.tenantId is missing (for example, due to a client mistake or misconfigured authentication), set the security level configuration:

2 is a bitfield (0b00000010) that specifically enables tenantId isolation enforcement.

This enables an additional security check:
If a request does not include a valid tenantId, Carbone will reject it instead of disabling isolation.