Skip to content
DocumentationBuild a multi-tenant platform
On this page

Page resources

Open Markdownllms.txtView source

Last updated

This recipe releases a bounded game-server control plane whose customers can create instances only inside the capabilities and artifacts admitted by that release.

The .one and Rust APIs below are illustrative end-product syntax.

1. Separate product choice from infrastructure

one 1

semantic games.product
    domain one.contracts@1
    struct ServerOffering@1
        distribution: DistributionId
        version: Version
        plan: ServerPlan
        extensions: Set<ExtensionId>(maximum: 20)

    operation CreateServer@1
        input CreateServerRequest
        output GameServer
        faults [OfferingUnavailable, QuotaExceeded]
        effect create(game_server)
        authority tenant_server:create

Minecraft, a mod set, or another distribution is product/runtime meaning. An OCI or Kubernetes host is a physical realization. Rust or TypeScript is an implementation-language choice. The contract does not collapse those axes.

2. Bound the released catalog

semantic games.control_plane
    domain one.components@1
    application GameControlPlane@1
        root api
        operation CreateServer

        catalog offerings
            allow Vanilla version "1.22" plans [Small, Medium]
            allow Paper version "1.22" plans [Medium, Large]
        quota servers_per_tenant 10
        quota storage_per_tenant gib(200)
        budget monthly_per_tenant eur(250)

The released closure contains every distribution artifact, extension family, host capability, grant shape, and bound required by those choices. Package installation outside that closure does not expand the customer catalog.

3. Create an instance inside the envelope

pub async fn create_server(
    ctx: CreateServer,
    request: CreateServerRequest,
) -> Result<GameServer, CreateServerFault> {
    let offering = ctx.catalog().require(request.offering)?;
    ctx.quotas().reserve(request.tenant_id, offering.resources()).await?;

    ctx.instances().create(InstanceIntent {
        tenant: request.tenant_id,
        offering,
        name: request.name,
    }).await
}

The customer request creates a managed instance through an already selected binding. It cannot discover a provider, invoke the One CLI, select an unplanned artifact, replace an Authority epoch, or widen the release envelope.

4. Isolate every tenant dimension

tenant identity
  -> names and instance identities
  -> data partition and encryption context
  -> secrets and runtime grants
  -> quotas, budgets, and rate limits
  -> logs, observations, backups, and erasure

A tenant label is not used as an ambient authorization key. Every lookup resolves an owner-qualified instance identity and checks current authority.

5. Reconcile customer-visible operations

match ctx.instances().create(intent).await {
    CreateResult::Ready(instance) => Ok(instance),
    CreateResult::Rejected(reason) => Err(reason.into()),
    CreateResult::OutcomeUnknown(operation) => {
        Ok(ctx.instances().reconcile(operation).await?)
    }
}

Provisioning, restart, backup, restore, upgrade, and deletion retain durable intent, attempts, provider observations, receipts, and tenant-visible recovery state.

See multi-tenant platforms and deployment.