Object Storage

The Rouic Platform provides S3-compatible object storage powered by MinIO. Store files, images, backups, and any other binary data in buckets with fine-grained access control.

Overview

Object storage uses the S3 protocol, which means you can use any existing S3 SDK, CLI tool, or library to interact with it. The storage is backed by MinIO, which provides a fully S3-compatible API.

Endpoint

https://s3.$rouic.com

Region

us-east-1

(MinIO convention — server is EU-hosted)

Credentials

Access key and secret key are provided when you create a storage user or bucket via the dashboard.

Max Object Size

5 TB

(multipart upload for >5GB)

Creating Buckets

Via the Dashboard

  1. Navigate to Storage in the sidebar
  2. Click "Create Bucket"
  3. Enter a bucket name (lowercase, alphanumeric, hyphens allowed)
  4. Select an access policy (private or public-read)
  5. Click "Create"

Via AWS CLI

bash
# Configure the CLI
aws configure set aws_access_key_id YOUR_ACCESS_KEY
aws configure set aws_secret_access_key YOUR_SECRET_KEY

# Create a bucket
aws s3 mb s3://my-bucket --endpoint-url https://s3.rouic.com

# List buckets
aws s3 ls --endpoint-url https://s3.rouic.com

Via MinIO CLI (mc)

bash
# Add the platform as an alias
mc alias set rouic https://s3.rouic.com ACCESS_KEY SECRET_KEY

# Create a bucket
mc mb rouic/my-bucket

# List buckets
mc ls rouic

Access Policies

privateDefault

All access requires valid credentials. Objects are not publicly accessible. Use presigned URLs to grant temporary access to specific objects.

public-read

Anyone can read objects via their URL. Writing still requires credentials. Use this for public assets, images, or static files.

Public URL format: https://s3.$rouic.com/<bucket>/<key>

Uploading Files

Via the Dashboard

Open a bucket in the dashboard and click "Upload". You can drag and drop files or select them from your file system. Files up to 100 MB can be uploaded through the dashboard.

Via CLI

AWS CLI

bash
# Upload a single file
aws s3 cp myfile.png s3://my-bucket/images/myfile.png \
  --endpoint-url https://s3.rouic.com

# Upload a directory
aws s3 sync ./public s3://my-bucket/public \
  --endpoint-url https://s3.rouic.com

# Download a file
aws s3 cp s3://my-bucket/images/myfile.png ./download.png \
  --endpoint-url https://s3.rouic.com

MinIO CLI

bash
# Upload a file
mc cp myfile.png rouic/my-bucket/images/myfile.png

# Upload a directory recursively
mc cp --recursive ./public rouic/my-bucket/public/

# Download
mc cp rouic/my-bucket/images/myfile.png ./download.png

Code Examples

Node.js (AWS SDK v3)

import {
  S3Client,
  PutObjectCommand,
  GetObjectCommand,
} from '@aws-sdk/client-s3';

const s3 = new S3Client({
  endpoint: 'https://s3.rouic.com',
  region: 'us-east-1',
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY,
    secretAccessKey: process.env.S3_SECRET_KEY,
  },
  forcePathStyle: true, // Required for MinIO
});

// Upload a file
await s3.send(new PutObjectCommand({
  Bucket: 'my-bucket',
  Key: 'images/photo.jpg',
  Body: fileBuffer,
  ContentType: 'image/jpeg',
}));

// Download a file
const response = await s3.send(new GetObjectCommand({
  Bucket: 'my-bucket',
  Key: 'images/photo.jpg',
}));
const data = await response.Body.transformToByteArray();

Python (boto3)

import boto3
import os

s3 = boto3.client(
    's3',
    endpoint_url='https://s3.rouic.com',
    aws_access_key_id=os.environ['S3_ACCESS_KEY'],
    aws_secret_access_key=os.environ['S3_SECRET_KEY'],
    region_name='us-east-1',
)

# Upload a file
s3.upload_file('local-file.jpg', 'my-bucket', 'images/photo.jpg')

# Download a file
s3.download_file('my-bucket', 'images/photo.jpg', 'download.jpg')

# List objects
response = s3.list_objects_v2(Bucket='my-bucket', Prefix='images/')
for obj in response.get('Contents', []):
    print(obj['Key'], obj['Size'])

PHP (AWS SDK)

<?php
use Aws\S3\S3Client;

$s3 = new S3Client([
    'endpoint' => 'https://s3.rouic.com',
    'region' => 'us-east-1',
    'version' => 'latest',
    'use_path_style_endpoint' => true,
    'credentials' => [
        'key' => getenv('S3_ACCESS_KEY'),
        'secret' => getenv('S3_SECRET_KEY'),
    ],
]);

// Upload
$s3->putObject([
    'Bucket' => 'my-bucket',
    'Key' => 'images/photo.jpg',
    'SourceFile' => '/path/to/local/file.jpg',
]);

// Download
$result = $s3->getObject([
    'Bucket' => 'my-bucket',
    'Key' => 'images/photo.jpg',
]);
$body = $result['Body']->getContents();

Go (AWS SDK v2)

package main

import (
    "context"
    "os"
    "strings"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
    cfg, _ := config.LoadDefaultConfig(context.TODO(),
        config.WithRegion("us-east-1"),
        config.WithCredentialsProvider(
            credentials.NewStaticCredentialsProvider(
                os.Getenv("S3_ACCESS_KEY"),
                os.Getenv("S3_SECRET_KEY"),
                "",
            ),
        ),
    )

    client := s3.NewFromConfig(cfg, func(o *s3.Options) {
        o.BaseEndpoint = aws.String("https://s3.rouic.com")
        o.UsePathStyle = true
    })

    // Upload
    client.PutObject(context.TODO(), &s3.PutObjectInput{
        Bucket: aws.String("my-bucket"),
        Key:    aws.String("hello.txt"),
        Body:   strings.NewReader("Hello, World!"),
    })
}

Ruby (aws-sdk-s3)

require 'aws-sdk-s3'

s3 = Aws::S3::Client.new(
  endpoint: 'https://s3.rouic.com',
  region: 'us-east-1',
  access_key_id: ENV['S3_ACCESS_KEY'],
  secret_access_key: ENV['S3_SECRET_KEY'],
  force_path_style: true,
)

# Upload
s3.put_object(
  bucket: 'my-bucket',
  key: 'images/photo.jpg',
  body: File.open('photo.jpg', 'rb'),
)

# Download
resp = s3.get_object(bucket: 'my-bucket', key: 'images/photo.jpg')
File.open('download.jpg', 'wb') { |f| f.write(resp.body.read) }

Presigned URLs

Presigned URLs allow temporary access to private objects without exposing your credentials. They are useful for:

  • Allowing users to download private files
  • Allowing users to upload files directly to storage (bypassing your server)
  • Generating temporary links for email attachments

Generate a presigned download URL (Node.js)

javascript
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { GetObjectCommand } from '@aws-sdk/client-s3';

const url = await getSignedUrl(
  s3,
  new GetObjectCommand({
    Bucket: 'my-bucket',
    Key: 'private/report.pdf',
  }),
  { expiresIn: 3600 } // 1 hour
);

console.log(url);
// https://s3.rouic.com/my-bucket/private/report.pdf?X-Amz-...

Generate a presigned upload URL (Node.js)

javascript
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { PutObjectCommand } from '@aws-sdk/client-s3';

const uploadUrl = await getSignedUrl(
  s3,
  new PutObjectCommand({
    Bucket: 'my-bucket',
    Key: 'uploads/user-avatar.jpg',
    ContentType: 'image/jpeg',
  }),
  { expiresIn: 300 } // 5 minutes
);

// Client can PUT directly to this URL

Generate a presigned URL (Python)

python
url = s3.generate_presigned_url(
    'get_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'private/report.pdf',
    },
    ExpiresIn=3600,  # 1 hour
)
print(url)

CORS Configuration

If your web application needs to upload or download files directly from the browser (e.g., using presigned URLs), you need to configure CORS on the bucket.

Set CORS via AWS CLI

bash
aws s3api put-bucket-cors \
  --endpoint-url https://s3.rouic.com \
  --bucket my-bucket \
  --cors-configuration '{
    "CORSRules": [
      {
        "AllowedOrigins": ["https://myapp.rouic.com"],
        "AllowedMethods": ["GET", "PUT", "POST"],
        "AllowedHeaders": ["*"],
        "MaxAgeSeconds": 3600
      }
    ]
  }'

Set CORS via MinIO CLI

bash
# Create a cors.json file
cat > cors.json << 'EOF'
{
  "CORSRules": [
    {
      "AllowedOrigins": ["https://myapp.rouic.com"],
      "AllowedMethods": ["GET", "PUT", "POST"],
      "AllowedHeaders": ["*"],
      "MaxAgeSeconds": 3600
    }
  ]
}
EOF

mc anonymous set-json cors.json rouic/my-bucket

Tip: For development, you can set AllowedOrigins to ["*"]. For production, always restrict to your actual domain.