Skip to content

RustFS S3 Storage (NOT IN USE)

Note: RustFS was evaluated as a lower-memory alternative to MinIO in December 2025. However, presigned URLs generated via AWS SDK v3 failed signature validation (HTTP 403) when accessed through Cloudflare Tunnel, despite correct configuration. MinIO is the current recommended solution. See MinIO Storage.

RustFS provides S3-compatible object storage for the Signal bot’s file sharing functionality. It was intended to replace MinIO with a more memory-efficient Rust-based implementation.

ComponentValue
ServiceRustFS (S3-compatible, Rust-based)
Public URLhttps://s3.irregular.chat
Console URLhttps://s3-console.irregular.chat (if enabled)
Bucketirregularchat
Link Expiration24 hours
MetricMinIORustFS
Memory (idle)~16 GiB~74 MiB
Memory %13%0.06%
LanguageGoRust
LicenseAGPL v3Apache 2.0

Key Finding: RustFS uses ~220x less memory than MinIO in idle state.

Note: RustFS is currently alpha software. Monitor stability and have rollback plan ready.

  1. User searches for files with !files <query>
  2. Bot returns search results from local file index
  3. User replies with a file number to get download link
  4. Bot generates a RustFS presigned URL:
    • Constructs S3 path from category + filename
    • Signs URL with AWS4-HMAC-SHA256 algorithm
    • Sets 24-hour expiration
    • Uses public URL for signature so it matches external access
  5. User receives direct download link
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Signal User │─────▶│ Signal Bot │─────▶│ RustFS Server │
│ !files drone │ │ minio-client.ts │ │ irregularchat │
└─────────────────┘ └──────────────────┘ │ bucket │
│ │ └─────────────────┘
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Presigned URL │ │
│ │ https://s3... │◀─────────────┘
│ │ ?X-Amz-... │
│ └──────────────────┘
│ │
◀─────────────────────────┘
Download link (24 hour expiry)

The signal-bot container connects to RustFS via the minio-network:

services:
signal-bot:
networks:
- signal-bot-network
- minio-network # For RustFS access (network name kept for compatibility)
networks:
minio-network:
external: true

Located at /home/minio-irregularchat/docker-compose.yml:

version: '3.8'
services:
rustfs:
image: rustfs/rustfs:latest
container_name: rustfs-production
restart: unless-stopped
ports:
- "0.0.0.0:9002:9000" # S3 API port
- "0.0.0.0:9003:9001" # Console port
environment:
RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY:-rustfs-test-admin}
RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY}
RUSTFS_CONSOLE_ENABLE: "true"
RUSTFS_ADDRESS: ":9000"
RUSTFS_CONSOLE_ADDRESS: ":9001"
# CRITICAL: Required for presigned URLs to work via reverse proxy
RUSTFS_EXTERNAL_ADDRESS: https://s3.irregular.chat
RUSTFS_CORS_ALLOWED_ORIGINS: "*"
RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS: "*"
volumes:
- /datadrive/rustfs-data:/data:rw
- /datadrive/rustfs-logs:/logs:rw # REQUIRED: RustFS needs /logs writable
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 3
networks:
- minio-network
networks:
minio-network:
name: minio-network
FeatureMinIORustFS
CredentialsMINIO_ROOT_USER_FILE (supports _FILE suffix)RUSTFS_ACCESS_KEY (no _FILE suffix)
User IDrootuid 10001 (rustfs user)
Logs VolumeNot requiredRequired - /logs must be writable
ConsoleBuilt-inEnabled via RUSTFS_CONSOLE_ENABLE
File ServingServes mounted files directlyOnly serves files uploaded via S3 API

Add these to the signal-bot .env file:

Terminal window
# RustFS S3 Configuration (uses MINIO_ prefix for code compatibility)
MINIO_ENDPOINT=http://rustfs-production:9000
MINIO_ACCESS_KEY=<your-access-key>
MINIO_SECRET_KEY=<your-secret-key>
MINIO_BUCKET=irregularchat
MINIO_REGION=us-east-1
MINIO_PUBLIC_URL=https://s3.irregular.chat
VariableDescription
MINIO_ENDPOINTInternal Docker network endpoint (used by bot)
MINIO_ACCESS_KEYS3-compatible access key
MINIO_SECRET_KEYS3-compatible secret key
MINIO_BUCKETBucket containing files
MINIO_REGIONAWS region (required for S3 signing)
MINIO_PUBLIC_URLPublic URL for presigned links

Note: Environment variables use MINIO_ prefix for code compatibility with existing minio-client.ts.

RustFS presigned URLs can be very long (~300+ characters) which can cause issues in Signal messages:

  • URLs get truncated by “Read More” in long messages
  • URLs may get cut off when copying
  • Long URLs are hard to share manually

The bot optionally integrates with Shlink (self-hosted URL shortener) to create short, memorable URLs:

ComponentValue
Long URLhttps://s3.irregular.chat/irregularchat/...?X-Amz-... (~300 chars)
Short URLhttps://irregular.chat/abc12 (~27 chars)
ExpirationSame as RustFS presigned URL (24 hours)

Add these to the signal-bot .env file:

Terminal window
# Shlink URL Shortener (optional)
SHLINK_API_URL=http://shlink-api:8080
SHLINK_API_KEY=<your-shlink-api-key>
SHLINK_PUBLIC_URL=https://irregular.chat

The bot maps local file paths to RustFS S3 keys:

Local PathS3 Key
/app/irregularchat/UnmannedSystems/Documents/drone.pdfUnmannedSystems/Documents/drone.pdf
/app/irregularchat/Research/Reports/analysis.pdfResearch/Reports/analysis.pdf

The minio-client.ts utility strips the base path and constructs the S3 key.

Important: RustFS only serves files uploaded via S3 API. Unlike MinIO which can serve mounted files, RustFS requires files to be uploaded.

Files are uploaded using mc mirror:

Terminal window
# Set up alias
RUSTFS_KEY=$(cat /home/minio-irregularchat/secrets/rustfs_access_key.txt)
RUSTFS_SECRET=$(cat /home/minio-irregularchat/secrets/rustfs_secret_key.txt)
mc alias set rustfs http://127.0.0.1:9002 "$RUSTFS_KEY" "$RUSTFS_SECRET"
# Mirror files (initial upload)
mc mirror /datadrive/IrregularChat/ rustfs/irregularchat/

The Signal bot uploads files directly to RustFS when users use !save command. No separate sync cron is needed since:

  • New files uploaded via bot go directly to RustFS
  • rclone bisync with pCloud handles backup

RustFS is exposed via Cloudflare Tunnel (not direct port exposure):

  • API: s3.irregular.chatlocalhost:9002
  • Console: s3-console.irregular.chatlocalhost:9003

This provides:

  • SSL/TLS termination
  • DDoS protection
  • No exposed ports

If RustFS is not configured (missing env vars), the bot falls back to pCloud:

// In file-search.ts
if (isMinioConfigured()) {
// Use RustFS presigned URL
return await getFileLink(s3Key);
} else {
// Fallback to pCloud
return await getPCloudLink(filePath);
}
Terminal window
docker compose exec signal-bot node -e "
const { isMinioConfigured, getMinioConfig } = require('./dist/utils/minio-client.js');
console.log('Configured:', isMinioConfigured());
console.log('Config:', JSON.stringify(getMinioConfig(), null, 2));
"
Terminal window
docker compose exec signal-bot node -e "
const { getFileLink } = require('./dist/utils/minio-client.js');
getFileLink('UnmannedSystems/Documents/test.pdf').then(console.log);
"
Terminal window
curl -f http://localhost:9002/minio/health/live
Terminal window
docker logs rustfs-production --tail 50

If presigned URLs work internally but fail via Cloudflare Tunnel:

  1. Check RUSTFS_EXTERNAL_ADDRESS is set:

    Terminal window
    docker exec rustfs-production env | grep EXTERNAL
    # Should show: RUSTFS_EXTERNAL_ADDRESS=https://s3.irregular.chat
  2. Generate presigned URLs using PUBLIC endpoint:

    Terminal window
    # Use public URL in mc alias for presigned URL generation
    mc alias set rustfs-pub https://s3.irregular.chat "$RUSTFS_KEY" "$RUSTFS_SECRET"
    mc share download --expire=1h "rustfs-pub/irregularchat/path/to/file.pdf"
  3. Why this matters: Presigned URL signatures include the host header. If the URL is signed with 127.0.0.1:9002 but accessed via s3.irregular.chat, the signature won’t match.

  4. Restart RustFS after config change:

    Terminal window
    cd /home/minio-irregularchat
    docker compose down --remove-orphans
    docker compose up -d rustfs
Terminal window
# Set up mc alias
mc alias set rustfs http://127.0.0.1:9002 "$RUSTFS_KEY" "$RUSTFS_SECRET"
# Check bucket size
mc du rustfs/irregularchat/
# List files
mc ls rustfs/irregularchat/ --recursive | head -20
Terminal window
cd /home/minio-irregularchat
docker compose restart rustfs
Terminal window
cd /home/minio-irregularchat
docker compose pull rustfs
docker compose up -d rustfs

RustFS and Signal Bot have restart: unless-stopped, ensuring automatic restart after server reboot.

Credentials are stored in /home/minio-irregularchat/secrets/:

Terminal window
# Access Key
cat /home/minio-irregularchat/secrets/rustfs_access_key.txt
# Secret Key
cat /home/minio-irregularchat/secrets/rustfs_secret_key.txt