Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
67d2a18
feat: Enhance authentication and file upload capabilities
usnavy13 Apr 28, 2026
9bf6479
feat: Introduce sandbox network access for skill installations
usnavy13 May 5, 2026
3b5794b
feat(files): Update file upload restrictions and session limits
usnavy13 May 5, 2026
f9ae8bc
feat: Enhance tool name normalization and file handling in execution
usnavy13 May 6, 2026
d9199ad
fix: Resolve 4 CI failures — test mocks, assertions, and bandit suppr…
usnavy13 May 6, 2026
48dee34
Merge pull request #88 from usnavy13/skills-bash-batch-improvements
usnavy13 May 6, 2026
458d9a7
chore: Remove outdated repository guidelines and add CLAUDE.md reference
usnavy13 May 6, 2026
34f4f91
feat: Add original filename support in file handling
usnavy13 May 6, 2026
e85e9e8
fix: Match LibreChat's Unicode sanitization — add emoji, NFC, and two…
usnavy13 May 6, 2026
836a352
fix: Add original_filename param to batch upload test mock
usnavy13 May 6, 2026
64b4494
refactor: Replace MinIO with S3-compatible storage
usnavy13 May 6, 2026
7631955
chore: Update S3 configuration and health check commands
usnavy13 May 6, 2026
ff69603
Merge pull request #89 from usnavy13/filename-sani-fix
usnavy13 May 6, 2026
25e8fed
refactor: Update mounted file edit tests for new output handling
usnavy13 May 6, 2026
16807f0
chore: Update tmpfs configuration and directory structure in Docker s…
usnavy13 May 6, 2026
76987db
chore: Enhance tmpfs security settings in Docker and service files
usnavy13 May 7, 2026
0fc9cc3
Merge pull request #90 from usnavy13/feat/migrate-minio-to-garage-s3
usnavy13 May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,42 @@
API_KEY=your-secure-api-key-here-change-this-in-production
# API_KEYS=key1,key2,key3 # Additional API keys (comma-separated)
# MASTER_API_KEY=your-secure-master-key # Required for admin dashboard CLI
#
# AUTH_ENABLED=true # Set to false to disable x-api-key/Basic auth checks
# # on user endpoints. Use only when running behind a
# # trusted network boundary. /api/v1/admin/* still
# # requires MASTER_API_KEY regardless.
#
# Three ways clients can authenticate when AUTH_ENABLED=true:
# 1. x-api-key: <key> (recommended for proxies)
# 2. Authorization: Basic base64("<key>:") (LibreChat URL credentials)
# e.g. LIBRECHAT_CODE_BASEURL=https://<key>@your-api/v1
# 3. (none, when AUTH_ENABLED=false)

# ── Sandbox network access (skill installs) ───────────────────
# When ENABLE_SANDBOX_NETWORK=true, sandboxes can reach the internet but only
# through an inline allowlist proxy that permits PyPI, npm, Go modules, and
# crates.io. Required for skills that pip/npm/go install dependencies at
# runtime. Off by default (sandboxes are isolated).
#
# ENABLE_SANDBOX_NETWORK=false
# SANDBOX_EGRESS_PORT=18443 # local-only, sandbox -> proxy
# SANDBOX_EGRESS_ALLOWLIST= # comma-separated extra hosts
# SKILL_DEPS_PATH=/opt/skill-deps # backing volume mount

# ── Redis ───────────────────────────────────────────────────────
REDIS_HOST=localhost
REDIS_PORT=6379
# REDIS_PASSWORD=
# REDIS_URL=redis://localhost:6379/0 # Alternative to individual settings

# ── MinIO / S3 ─────────────────────────────────────────────────
MINIO_ENDPOINT=localhost:9000
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
# MINIO_SECURE=false
# MINIO_BUCKET=code-interpreter-files
# ── S3 Storage (Garage) ────────────────────────────────────────
S3_ENDPOINT=localhost:3900
S3_ACCESS_KEY=GKminioadmin0000
S3_SECRET_KEY=minioadminsecret
# S3_SECURE=false
# S3_BUCKET=code-interpreter-files
# S3_REGION=garage

# ── Execution Limits ───────────────────────────────────────────
# MAX_EXECUTION_TIME=30 # Seconds (default: 30)
Expand Down
44 changes: 1 addition & 43 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,43 +1 @@
# Repository Guidelines

## Project Structure & Module Organization
Core application code lives in `src/`. Use `src/api/` for FastAPI routes, `src/services/` for orchestration and business logic, `src/services/sandbox/` and `src/services/container/` for execution backends, `src/models/` for request/response models, and `src/config/` for environment-driven settings. Supporting docs are in `docs/`, dashboard assets in `dashboard/`, container/runtime files in `docker/`, and helper scripts in `scripts/`.

Tests are split by scope: `tests/unit/` for isolated service logic, `tests/integration/` for API and dependency-backed flows, `tests/functional/` for live endpoint testing, and `tests/snapshots/` for stored response fixtures.

## Build, Test, and Development Commands
Set up a local environment with:

```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
```

Run locally with `uvicorn src.main:app --reload`. Start required services with `docker compose up -d`, and build the sandbox image with `docker build -t code-interpreter:nsjail .`.

Key verification commands:

```bash
pytest tests/unit/
pytest tests/integration/
pytest tests/functional/ -v
pytest --cov=src tests/
black src/ --check
flake8 src/
mypy src/
bandit -r src/ -s B104,B108 --severity-level high
```

## Coding Style & Naming Conventions
Target Python 3.11+ with 4-space indentation, explicit type hints, and small async-friendly service boundaries. Follow Black formatting and keep code Flake8- and MyPy-clean. Use `snake_case` for modules, functions, and variables; `PascalCase` for classes and Pydantic models; and `UPPER_SNAKE_CASE` for constants and env names.

## Testing Guidelines
Pytest, `pytest-asyncio`, and `pytest-cov` are the standard tools. Name files `test_*.py` and mirror the component under test where practical, for example `tests/unit/test_session_service.py`. Add unit coverage for new logic first, then integration coverage for endpoint or storage changes. Functional tests use `API_BASE`, `API_KEY`, and `API_TIMEOUT`; keep them stable against a real running API.

## Commit & Pull Request Guidelines
Recent history uses short imperative subjects with prefixes such as `fix:`, `docs:`, `chore(...)`, and `feat:`. Keep the first line under 72 characters and reference issues in the body when relevant. Pull requests should explain behavior changes, note config or API contract impacts, and include the commands you ran. Add screenshots when changing the admin dashboard or other visible UI.

## Security & Configuration Tips
Never commit populated `.env` files, API keys, or storage credentials. Use `.env.example` as the template, and review `docs/CONFIGURATION.md` and `docs/SECURITY.md` before changing auth, sandboxing, Redis, or MinIO behavior.
Read CLAUDE.md
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
flex bison \
curl wget ca-certificates gnupg software-properties-common \
libssl-dev libffi-dev libxml2-dev libxslt-dev zlib1g-dev \
jq iptables \
&& rm -rf /var/lib/apt/lists/*

RUN git clone https://github.com/google/nsjail.git /tmp/nsjail && \
Expand All @@ -51,6 +52,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
portaudio19-dev flac ffmpeg \
libpulse-dev libsdl2-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-ttf-dev \
antiword unrtf \
libreoffice-impress libreoffice-writer libreoffice-calc libreoffice-common \
&& rm -rf /var/lib/apt/lists/*

COPY docker/requirements/python-core.txt /tmp/python-core.txt
Expand Down Expand Up @@ -231,7 +233,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# ============================================
RUN mkdir -p /var/lib/code-interpreter/sandboxes && \
mkdir -p /mnt/data && \
mkdir -p /tmp/empty_proc
mkdir -p /var/lib/code-interpreter/empty_proc

RUN groupadd -g 1001 codeuser && \
useradd -u 1001 -g codeuser -m codeuser && \
Expand Down Expand Up @@ -276,8 +278,9 @@ WORKDIR /app
# Keep the application layer thin so app-only changes do not invalidate runtime stages.
COPY docker/repl_server.py /opt/repl_server.py
COPY docker/ptc_server.py /opt/ptc_server.py
COPY docker/ptc_bash_server.py /opt/ptc_bash_server.py
COPY docker/entrypoint.sh /opt/entrypoint.sh
RUN chmod +x /opt/repl_server.py /opt/ptc_server.py /opt/entrypoint.sh
RUN chmod +x /opt/repl_server.py /opt/ptc_server.py /opt/ptc_bash_server.py /opt/entrypoint.sh

COPY requirements.txt /tmp/requirements.txt
RUN --mount=type=cache,target=/root/.cache/pip \
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.local-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
api:
image: code-interpreter:nsjail-librechat-compat
pull_policy: never
58 changes: 28 additions & 30 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ services:
init: true
cap_add:
- SYS_ADMIN
# NET_ADMIN required to install iptables egress rules for sandbox uid
# when ENABLE_SANDBOX_NETWORK=true. Restricts sandbox traffic to the
# inline allowlist proxy and prevents SSRF to Redis/S3/etc.
- NET_ADMIN
security_opt:
- apparmor:unconfined
ports:
Expand All @@ -15,19 +19,24 @@ services:
- .env
environment:
- REDIS_HOST=redis
- MINIO_ENDPOINT=minio:9000
- S3_ENDPOINT=garage:3900
volumes:
- sandbox-data:/var/lib/code-interpreter/sandboxes
# Persistent skill-deps cache: pip/npm/go/cargo install here when
# ENABLE_SANDBOX_NETWORK=true so future executions reuse the install.
# Survives container restarts; purge with POST /api/v1/admin/skill-deps/purge.
- skill-deps:/opt/skill-deps
# SSL_CERTS_PATH is a host path; SSL_CERT_FILE and SSL_KEY_FILE must point
# to the mounted files inside the container under /app/ssl.
- ${SSL_CERTS_PATH:-./ssl}:/app/ssl:ro
tmpfs:
- /tmp:size=512m,mode=1777,noexec,nosuid,nodev
- /app/data:size=100m
depends_on:
redis:
condition: service_healthy
minio-init:
condition: service_completed_successfully
garage:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -fs http://localhost:8000/health || curl -fsk https://localhost:8000/health"]
interval: 30s
Expand Down Expand Up @@ -55,42 +64,31 @@ services:
timeout: 5s
retries: 5

minio:
image: minio/minio:latest
container_name: code-interpreter-minio
# Garage S3-compatible object storage (replaces MinIO)
garage:
image: dxflrs/garage:v2.3.0
container_name: code-interpreter-garage
restart: unless-stopped
command: /garage server --single-node --default-bucket
ports:
- "127.0.0.1:${MINIO_PORT:-9000}:9000"
- "127.0.0.1:${MINIO_CONSOLE_PORT:-9001}:9001"
- "127.0.0.1:${S3_PORT:-3900}:3900"
- "127.0.0.1:${GARAGE_ADMIN_PORT:-3903}:3903"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
command: server /data --console-address ":9001"
GARAGE_DEFAULT_ACCESS_KEY: ${S3_ACCESS_KEY:-GKminioadmin0000}
GARAGE_DEFAULT_SECRET_KEY: ${S3_SECRET_KEY:-minioadminsecret}
GARAGE_DEFAULT_BUCKET: ${S3_BUCKET:-code-interpreter-files}
volumes:
- minio-data:/data
- garage-data:/var/lib/garage
- ./garage.toml:/etc/garage.toml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
test: ["CMD", "/garage", "status"]
interval: 10s
timeout: 5s
retries: 5

minio-init:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set myminio http://minio:9000 $${MINIO_ACCESS_KEY:-minioadmin} $${MINIO_SECRET_KEY:-minioadmin};
mc mb --ignore-existing myminio/$${MINIO_BUCKET:-code-interpreter-files};
exit 0;
"
environment:
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
MINIO_BUCKET: ${MINIO_BUCKET:-code-interpreter-files}
start_period: 10s

volumes:
sandbox-data:
skill-deps:
redis-data:
minio-data:
garage-data:
52 changes: 20 additions & 32 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ services:
# nsjail requires these capabilities to create namespaces and cgroups
cap_add:
- SYS_ADMIN
- NET_ADMIN
security_opt:
- apparmor:unconfined
ports:
Expand All @@ -22,19 +23,20 @@ services:
environment:
# Container-specific overrides (service discovery within compose network)
- REDIS_HOST=redis
- MINIO_ENDPOINT=minio:9000
- S3_ENDPOINT=garage:3900
volumes:
- sandbox-data:/var/lib/code-interpreter/sandboxes
# SSL_CERTS_PATH is a host path; SSL_CERT_FILE and SSL_KEY_FILE must point
# to the mounted files inside the container under /app/ssl.
- ${SSL_CERTS_PATH:-./ssl}:/app/ssl:ro
tmpfs:
- /tmp:size=512m,mode=1777,noexec,nosuid,nodev
- /app/data:size=100m
depends_on:
redis:
condition: service_healthy
minio-init:
condition: service_completed_successfully
garage:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -fs http://localhost:8000/health || curl -fsk https://localhost:8000/health"]
interval: 30s
Expand Down Expand Up @@ -64,44 +66,30 @@ services:
timeout: 5s
retries: 5

# MinIO for file storage
minio:
image: minio/minio:latest
container_name: code-interpreter-minio
# Garage S3-compatible object storage (replaces MinIO)
garage:
image: dxflrs/garage:v2.3.0
container_name: code-interpreter-garage
restart: unless-stopped
command: /garage server --single-node --default-bucket
ports:
- "127.0.0.1:${MINIO_PORT:-9000}:9000"
- "127.0.0.1:${MINIO_CONSOLE_PORT:-9001}:9001"
- "127.0.0.1:${S3_PORT:-3900}:3900"
- "127.0.0.1:${GARAGE_ADMIN_PORT:-3903}:3903"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
command: server /data --console-address ":9001"
GARAGE_DEFAULT_ACCESS_KEY: ${S3_ACCESS_KEY:-GKminioadmin0000}
GARAGE_DEFAULT_SECRET_KEY: ${S3_SECRET_KEY:-minioadminsecret}
GARAGE_DEFAULT_BUCKET: ${S3_BUCKET:-code-interpreter-files}
volumes:
- minio-data:/data
- garage-data:/var/lib/garage
- ./garage.toml:/etc/garage.toml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
test: ["CMD", "/garage", "status"]
interval: 10s
timeout: 5s
retries: 5

# MinIO bucket initialization
minio-init:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set myminio http://minio:9000 $${MINIO_ACCESS_KEY:-minioadmin} $${MINIO_SECRET_KEY:-minioadmin};
mc mb --ignore-existing myminio/$${MINIO_BUCKET:-code-interpreter-files};
exit 0;
"
environment:
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
MINIO_BUCKET: ${MINIO_BUCKET:-code-interpreter-files}
start_period: 10s

volumes:
sandbox-data:
redis-data:
minio-data:
garage-data:
Loading
Loading