Research-Stack/.devcontainer/init-rclone-s3.sh
Brandon Schneider ac4e23dc9b Expand devcontainer with full Python stack, add MCP servers (Notion/AWS), strengthen Lean theorems
- .devcontainer/Dockerfile: add PostgreSQL client libs, OpenSSL/libffi headers, gfortran/BLAS for scipy, rclone; install full Python dependency set (boto3, psycopg2-binary, fastapi, uvicorn, notion-client, httpx, pytest, numpy, scipy, etc.) in uv-managed venv; add rclone S3 gateway init script as ENTRYPOINT
- .devcontainer/devcontainer.json: switch from build to pre-built image (localhost/research
2026-05-19 01:52:14 -05:00

128 lines
6.4 KiB
Bash
Executable file

#!/usr/bin/env bash
# init-rclone-s3.sh
#
# Starts rclone as an S3-compatible gateway in front of the gdrive remote.
# Called automatically when the dev container launches (via CMD in Dockerfile).
#
# ── Google Drive API quota limits (as of 2026-05-01) ────────────────────────
# Per-minute per-project: 1,000,000 quota units
# Per-minute per-user per-project: 325,000 quota units
# Per-method costs:
# files.list (S3 ListObjects) = 100 units per call
# files.get (S3 HeadObject) = 5 units per call
# files.download (S3 GetObject) = 200 units per call
# → Sustained safe rate: ≤ 10 API calls/second (Google's own guidance)
# → 403/429 on exceed; rclone uses exponential backoff to recover
# Source: https://developers.google.com/drive/api/guides/limits
#
# ── How serve s3 touches the Drive API ──────────────────────────────────────
# Every S3 ListObjects maps to files.list (100 units each).
# The VFS directory cache (--dir-cache-time) is the primary defence:
# while the cache is warm, listings are served locally with ZERO API calls.
# --poll-interval controls how often rclone re-checks Drive for remote
# changes; set long so background polling doesn't burn quota.
#
# ── Client ID note ──────────────────────────────────────────────────────────
# rclone's shared client_id is rate-limited across ALL rclone users.
# For production or sustained use, create your own OAuth client_id:
# https://rclone.org/drive/#making-your-own-client-id
# Set RCLONE_DRIVE_CLIENT_ID + RCLONE_DRIVE_CLIENT_SECRET in the
# environment (or in rclone.conf) to use it.
#
# ── Inside the container ─────────────────────────────────────────────────────
# S3 endpoint: http://localhost:9000
# access_key: gdrive
# secret_key: gdrive
# region: us-east-1 (dummy; required by boto3/AWS CLI)
# Each Drive folder at root level is an S3 bucket, e.g.:
# s3://research-stack/ → gdrive:research-stack/
#
# Environment overrides (set in devcontainer.json or shell):
# RCLONE_S3_ADDR - listen address (default: :9000)
# RCLONE_S3_KEY - "access,secret" pair (default: gdrive,gdrive)
# RCLONE_REMOTE - rclone remote + path (default: gdrive:)
set -euo pipefail
ADDR="${RCLONE_S3_ADDR:-:9000}"
AUTH_KEY="${RCLONE_S3_KEY:-gdrive,gdrive}"
REMOTE="${RCLONE_REMOTE:-gdrive:}"
LOG_FILE="${HOME}/.cache/rclone-s3.log"
mkdir -p "$(dirname "$LOG_FILE")"
# Wait up to 15 s for the rclone.conf bind-mount to appear (container startup race)
for i in $(seq 1 15); do
if [ -f "${HOME}/.config/rclone/rclone.conf" ]; then
break
fi
echo "[init-rclone-s3] waiting for rclone.conf mount ($i/15)..." >&2
sleep 1
done
if [ ! -f "${HOME}/.config/rclone/rclone.conf" ]; then
echo "[init-rclone-s3] WARNING: rclone.conf not found — gdrive S3 gateway will not start." >&2
exec "$@"
fi
echo "[init-rclone-s3] starting rclone serve s3 ${REMOTE} on ${ADDR}" >&2
rclone serve s3 "${REMOTE}" \
--addr "${ADDR}" \
--auth-key "${AUTH_KEY}" \
\
`# ── Rate-limit compliance ─────────────────────────────────────────────` \
`# Stay well under Google's 10 TPS guidance for the shared client_id.` \
`# pacer-min-sleep=200ms + pacer-burst=10 caps sustained rate to ~5 TPS.` \
`# If you use your own client_id (RCLONE_DRIVE_CLIENT_ID) you can relax` \
`# pacer-burst to 50 and min-sleep to 100ms safely.` \
--drive-pacer-min-sleep 200ms \
--drive-pacer-burst 10 \
\
`# ── Directory / VFS cache ─────────────────────────────────────────────` \
`# Warm cache means zero API calls for repeated S3 ListObjects.` \
`# dir-cache-time=10m: listing is served locally for 10 minutes.` \
`# poll-interval=5m: rclone checks Drive for remote changes every 5 min.` \
`# vfs-cache-mode=minimal: only open files are cached locally; reads and` \
`# writes pass through. Avoids bulk-downloading the whole Drive.` \
--dir-cache-time 10m \
--poll-interval 5m \
--vfs-cache-mode minimal \
--vfs-cache-max-size 1G \
--vfs-read-chunk-size 32M \
--vfs-read-chunk-size-limit 256M \
\
`# ── Upload safety ────────────────────────────────────────────────────` \
`# 750 GB/day upload limit per Google Workspace user.` \
`# 8 MiB chunk size (Drive default); larger chunks = fewer API round-trips` \
`# for big files, but each chunk counts as a transaction.` \
--drive-chunk-size 8M \
\
`# ── Reliability ──────────────────────────────────────────────────────` \
`# retries=10: exponential backoff on 403/429 as Google recommends.` \
`# low-level-retries=20: retry transient network errors before giving up.` \
`# retries-sleep=1s: initial backoff delay (doubles each retry).` \
--retries 10 \
--retries-sleep 1s \
--low-level-retries 20 \
\
`# ── Logging ──────────────────────────────────────────────────────────` \
--log-file "${LOG_FILE}" \
--log-level INFO \
\
`# ── Misc ─────────────────────────────────────────────────────────────` \
`# no-checksum: skip MD5 verification on serve (reduces files.get calls).` \
--no-checksum \
&
RCLONE_PID=$!
echo "[init-rclone-s3] rclone serve s3 pid=${RCLONE_PID}" >&2
# Give it a moment to bind before handing off to the main command
sleep 2
if ! kill -0 "${RCLONE_PID}" 2>/dev/null; then
echo "[init-rclone-s3] ERROR: rclone failed to start — check ${LOG_FILE}" >&2
fi
# Hand off to whatever CMD or command was passed (default: bash)
exec "${@:-/bin/bash}"