> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boxd.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Agentic SaaS offerings

> Give every user of your product their own machine, created and managed through the SDK.

Your product gives each customer an agent, a workspace, or a running app instance. Back each one with its own boxd machine. Your backend creates machines through the [TypeScript](/reference/typescript-sdk) or [Python](/reference/python-sdk) SDK, each tenant gets a full Linux VM with a persistent disk and its own HTTPS URL, and hibernation keeps a mostly idle fleet affordable.

## A sandbox per tenant

Three steps make a multi-tenant product.

**1. Build the tenant image once.** Create a machine, install your agent harness on it the way you would on any Linux box, and save it as a [snapshot](/guides/snapshots). [Hermes](https://github.com/NousResearch/hermes-agent) is one example of a harness, and `/boxd-setup-hermes` installs it for you ([see its setup](/use-cases/personal-assistants#put-an-assistant-on-a-machine)):

```bash theme={"theme":"github-dark"}
boxd machine new harness-builder
boxd connect harness-builder            # install your harness, or run /boxd-setup-hermes
boxd snapshots save harness-builder agent-harness-v1
```

**2. Create an isolated sandbox per tenant, and hand it its secrets in code.** Every tenant machine comes from the same snapshot, named after the tenant, with `isolated` set so tenants can never reach each other or the rest of your fleet:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"theme":"github-dark"}
    import { Boxd } from "@boxd-sh/sdk";

    const boxd = new Boxd();                    // reads BOXD_API_KEY

    async function createTenant(tenant: string, secrets: Record<string, string>) {
      const machine = await boxd.machines.create({
        name: `tenant-${tenant}`,
        fromSnapshot: "agent-harness-v1",
        isolated: true,
      });
      await boxd.machines.waitUntilReady(machine.id);

      // Isolated machines receive no account-level env vars or secrets,
      // so the tenant's configuration goes in through code.
      const env = Object.entries(secrets).map(([k, v]) => `${k}=${v}`).join("\n");
      await boxd.machines.files.upload(machine.id, "/home/boxd/agent/.env", env);
      await boxd.machines.exec(machine.id, { command: "sudo systemctl restart agent" });

      return machine.access.url;
    }

    console.log(await createTenant("acme", { OPENAI_API_KEY: "sk-..." }));
    // https://tenant-acme.boxd.sh
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"theme":"github-dark"}
    from boxd import Boxd

    boxd = Boxd()                          # reads BOXD_API_KEY

    def create_tenant(tenant: str, secrets: dict[str, str]) -> str:
        machine = boxd.machines.create(
            f"tenant-{tenant}", from_snapshot="agent-harness-v1", isolated=True
        )
        boxd.machines.wait_until_ready(machine.id)

        # Isolated machines receive no account-level env vars or secrets,
        # so the tenant's configuration goes in through code.
        env = "\n".join(f"{k}={v}" for k, v in secrets.items())
        boxd.machines.files.upload(machine.id, "/home/boxd/agent/.env", env)
        boxd.machines.exec(machine.id, "sudo systemctl restart agent")

        return machine.access.url

    print(create_tenant("acme", {"OPENAI_API_KEY": "sk-..."}))
    # https://tenant-acme.boxd.sh
    ```
  </Tab>
</Tabs>

**3. Ship updates as snapshot versions.** Snapshots are named and versioned, so `agent-harness-v1` means the same environment for every tenant, and rolling out `v2` is a new snapshot rather than a migration. New tenants get it immediately, and existing tenants get it on their next recreate.

<Note>
  Account-level [env vars and secrets](/guides/env-secrets) reach your own machines, and isolated machines deliberately receive none of that account state. For tenant sandboxes, per-tenant secrets therefore go in through code at create time, as above.
</Note>

## A fleet that is mostly asleep

Most tenants are idle most of the time, and boxd's lifecycle model is built for exactly that. A machine with no inbound traffic hibernates automatically, costs effectively nothing while asleep, and wakes in about 85ms when the next request hits its URL. The caller can't tell it was ever off. For tighter windows, enable auto-suspend and the machine resumes in sub-millisecond time:

```typescript theme={"theme":"github-dark"}
await boxd.machines.setAutoSuspendTimeout(machine.id, 60);
```

A tenant who leaves on Friday and comes back on Monday costs you close to nothing over the weekend, and their machine answers Monday's first request as if it never slept. See [Suspend, resume, and hibernate](/guides/suspend-resume).

## Serve it under your domain

Tenant machines can live under your brand instead of `boxd.sh`. Delegate one wildcard subdomain to your org and every machine, current and future, gets a name under it:

```bash theme={"theme":"github-dark"}
boxd manage domain set vms.mysaas.com
```

After that, `tenant-42` answers at `https://tenant-42.vms.mysaas.com` with TLS issued automatically. For a single flagship app you can also bind one specific domain to one machine. See [Custom domains](/guides/custom-domains).

## Keep tenants apart

Every machine is hardware isolated, so one tenant's code can never read another tenant's memory or disk. On top of that, the `isolated` flag in the example above is the strictest network setting: a tenant sandbox reaches none of your other machines, ever, beyond networks you grant it explicitly. See [Sandboxes](/use-cases/sandboxes).

Two softer arrangements exist when tenants should cooperate:

* Machines without the flag share your account's private network by default, which suits a fleet that works together.
* [Network labels](/guides/vm-to-vm#tag-based-networks) partition per tenant, so machines within one tenant reach each other while tenants stay apart. Isolated sandboxes never reach each other even on a shared network, so a tenant that needs several cooperating machines uses labels without the flag.

## Limits

Each machine gets 2 vCPU, 8 GiB RAM, and a 100 GB copy-on-write disk by default, with bigger shapes available on request. Accounts start with a cap of 50 concurrent machines, and raises for real workloads are usually same-day. Email [contact@boxd.sh](mailto:contact@boxd.sh) with what you're building. See [Resources and limits](/guides/resources).
