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
one 1

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

    fault ServerFault@1
        offering_unavailable
        quota_exceeded

    operation Servers.CreateServer@1
        accepts CreateServerRequest
        returns GameServer
        faults ServerFault
        effect irreversible
        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

One
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 200GiB
        budget monthly_per_tenant 250EUR

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

Rust
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

Plain text
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

Rust
match ctx.instances().create(intent).await {
    CreateResult::Succeeded(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.