Skip to content

Application storage (TenancyEngine)

Manage the object-storage buckets and runtime access patterns your application uses to hold tenant file uploads, public previews, and purchased downloads — without provisioning cloud storage credentials yourself.

Console: Application workspace → Storage (/applications/:id/storage)

Requires Applications configure to create, update, or delete buckets and their access rules. Read-only access shows existing buckets and usage.

How it works

Each bucket is a logical container scoped to your application. Files uploaded by tenants through the portal storage API are tagged with the owning tenant. Files uploaded by your backend through the runtime storage API are app-owned by default and should be exposed only through explicit business-subject grants. Both surfaces are billed against your application's storage entitlement.

Creating a bucket

  1. Click Add bucket.
  2. Give it a name — this is a label shown in the console, not the underlying storage key.
  3. Choose the access mode for this bucket's intended objects:
    • Tenant-private — use the portal storage API with TenancyEngineStorageVisibility.UserPrivate. Only the uploading user, an explicit share, or a role with storage view-all access can read the object.
    • Tenant-shared — use the portal storage API with TenancyEngineStorageVisibility.TenantShared. Every member of that tenant can read the object. Do not use this for paid downloads, support evidence, customer-specific files, or anything that still needs application-level authorization.
    • Public preview — use a deliberately public object for thumbnails, samples, or marketing media that are safe for anonymous inspection. Keep previews separate from the private source file.
    • Purchased download — use the runtime storage API. Upload the paid file as app-owned/private, create a read grant keyed to the purchase/order/work item, and request download URLs only after your app has rechecked its own entitlement or payment state.
  4. Optionally set a maximum object size and a retention policy (auto-delete objects after N days).

Uploading and retrieving objects

Buckets are managed here, but objects themselves are uploaded and retrieved through the platform storage API from your application's backend — this page does not upload tenant files directly.

Safe reference flows

Tenant-private attachment

Use for user-specific attachments such as private forms, imports, or customer evidence.

csharp
await storage.UploadPortalObjectAsync(
    TenancyEngineStoragePatterns.TenantPrivateUpload("intake.pdf", "application/pdf", "intake"),
    content,
    ct);

Tenant-shared workspace file

Use only when every current tenant member should be able to read the file.

csharp
await storage.UploadPortalObjectAsync(
    TenancyEngineStoragePatterns.TenantSharedUpload("team-export.csv", "text/csv", "exports"),
    content,
    ct);

Public preview media

Use for thumbnails, samples, and other inspection-safe assets. Do not point this at the paid/private source file.

csharp
await storage.UploadRuntimeObjectAsync(
    TenancyEngineStoragePatterns.PublicPreviewUpload("assetvault", "preview.png", "image/png"),
    previewContent,
    ct);

Purchased download

Use for commercial files and anything gated by checkout, license, job, ticket, or support workflow. The object stays private; the grant is keyed to the business subject that your app already authorizes.

csharp
var upload = await storage.UploadRuntimeObjectAsync(
    TenancyEngineStoragePatterns.PurchasedDownloadUpload("assetvault", "bundle.zip", "application/zip"),
    fileContent,
    ct);

await storage.CreateRuntimeStorageGrantAsync(
    TenancyEngineStoragePatterns.PurchasedDownloadGrant(
        "assetvault",
        upload.ObjectId,
        purchaseId: purchase.Id.ToString("D"),
        expiresAtUtc: DateTime.UtcNow.AddHours(2)),
    ct);

// Call this only after your app verifies the purchaser, payment/license state, and refund state.
var downloadUrl = await storage.GetRuntimeDownloadUrlAsync(
    TenancyEngineStoragePatterns.PurchasedDownloadUrl(
        "assetvault",
        upload.ObjectId,
        purchaseId: purchase.Id.ToString("D")),
    ct);

Required runtime API-key scopes:

OperationScope
Runtime private/public upload and confirmstorage.write
Runtime grant create/list/revokestorage.grants.write
Runtime grant-checked download URLstorage.read

storage.read, storage.write, and storage.grants.write are not included in broad MCP helper scopes. Grant them deliberately to the application org API key that performs storage operations.

Per-tenant isolation

Portal-uploaded objects are associated with the tenant that uploaded them. Runtime-uploaded objects are app-owned/private unless you explicitly make them public. For purchased downloads, tenant membership alone is not enough: your app should verify the purchase/order/license first, then ask TenancyEngine for a grant-checked download URL.

Usage and limits

The bucket list shows object count and total bytes stored per bucket. Storage entitlement limits are configured under your application's plan/offering — see Offerings.

Deleting a bucket

Deleting a bucket permanently deletes every object inside it. This cannot be undone — the console requires typing the bucket name to confirm.

TenancyEngine platform documentation