Skip to content

Storage

Totis uses S3-compatible object storage for build artifacts. Each workspace can use either the default shared storage or configure custom storage.

Storage Architecture

S3 Bucket
 └── {workspace-slug}/
      └── {project-slug}/
           └── {build-id}/
                ├── app.ipa
                ├── app.apk
                └── app.dSYM.zip

Storage Options

Default Storage

By default, workspaces use Totis's managed MinIO storage:

  • Fully managed by Totis
  • Included in subscription
  • Subject to plan storage limits

Custom Storage

Workspaces can configure their own S3-compatible storage:

  • Full control over your data
  • No storage limits from Totis
  • Requires S3-compatible service (AWS S3, MinIO, DigitalOcean Spaces, etc.)

Configuring Custom Storage

At Workspace Creation

{
  "workspaceName": "My Company",
  "storageConfig": {
    "storageType": "CUSTOM",
    "accessKey": "your-access-key",
    "secretKey": "your-secret-key",
    "bucket": "your-bucket-name",
    "endpoint": "https://s3.amazonaws.com",
    "region": "us-east-1"
  }
}

Configuration Fields

Field Description
storageType DEFAULT or CUSTOM
accessKey S3 access key ID
secretKey S3 secret access key
bucket Target bucket name
endpoint S3-compatible endpoint URL
region AWS region (for AWS S3)

Supported Providers

Provider Endpoint Example
AWS S3 https://s3.amazonaws.com
MinIO https://minio.example.com
DigitalOcean Spaces https://nyc3.digitaloceanspaces.com
Backblaze B2 https://s3.us-west-002.backblazeb2.com
Cloudflare R2 https://accountid.r2.cloudflarestorage.com

Storage Quotas

Each subscription plan includes a storage quota:

Plan Storage Limit
FREE 5 GB
PRO 50 GB
TEAM 200 GB
ENTERPRISE Custom

Tracking Usage

Current storage usage is tracked per workspace:

{
  "workspaceId": "ws123",
  "maxStorage": 53687091200,
  "storageUsed": 2147483648
}

Storage Enforcement

  • File uploads check available storage before accepting
  • Exceeding quota returns STORAGE_LIMIT_EXCEEDED error
  • Deleting files decreases usage immediately

File Upload Flow

1. Request Signed URL

curl -X POST ".../build/{buildId}/sign" \
  -d '{"filename": "app.ipa"}'

Response:

{
  "signedUrl": "https://s3.example.com/...?X-Amz-Signature=...",
  "signedToken": "eyJhbGciOi..."
}

2. Upload to S3

Upload directly to the signed URL:

curl -X PUT "$SIGNED_URL" \
  -H "Content-Type: application/octet-stream" \
  --data-binary "@app.ipa"

3. Confirm Upload

curl -X POST ".../build/{buildId}/upload" \
  -d '"SIGNED_TOKEN"'

This step: - Verifies the file exists in S3 - Records file size and metadata - Updates workspace storage usage - Returns the file record

File Downloads

Authenticated Downloads

Request a signed download URL:

curl -X GET ".../build/file/{fileId}"

Returns a time-limited signed URL for download.

Public Downloads

Public links redirect to signed URLs:

GET /api/v1/public/file/{publicLinkId}/{fileName}

Storage Security

Signed URLs

All S3 operations use pre-signed URLs:

  • Upload URLs: 1-hour expiration
  • Download URLs: 1-hour expiration
  • Public link URLs: Same expiration

Access Control

  • Files are never publicly accessible directly
  • All access goes through signed URLs
  • Signed tokens validate upload completions

Data Isolation

Custom storage credentials are:

  • Stored encrypted in the database
  • Never exposed in API responses
  • Used only for that workspace's operations

Best Practices

For Custom Storage

  1. Use dedicated credentials: Create IAM users/keys specific to Totis
  2. Limit bucket permissions: Only grant required S3 permissions
  3. Enable versioning: Protect against accidental deletions
  4. Configure lifecycle rules: Automatically clean up old builds
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::your-bucket",
        "arn:aws:s3:::your-bucket/*"
      ]
    }
  ]
}

Connection Verification

When configuring custom storage, Totis verifies the connection:

  1. Attempts to list bucket contents
  2. Checks write permissions
  3. Returns error if connection fails
{
  "error": "S3_CONNECTION_FAILED",
  "message": "Unable to connect to S3 storage"
}

Storage Migration

Not Currently Supported

Migrating from default to custom storage (or vice versa) is not currently supported. Plan your storage configuration before uploading builds.