diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8745eaa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,36 @@ +# Keep the image small and reproducible. The 11 GB test corpora, the local +# venv, and the vendored Flipper firmware are NOT needed to serve the app. + +# Heavy / irrelevant +data/rf_test_datasets/ +data/test_db/ +data/test_known_devices/ +data/test_rtl433_real/ +signatures/ +venv/ +logs/ +docs/ +tests/ +test_files/ +scripts/ + +# Benched Phase 3B CNN binaries (not on the serving path) +models/category_cnn.pt +models/category_cnn.onnx + +# VCS / caches / editor +.git/ +.gitignore +**/__pycache__/ +**/*.pyc +**/*.pyo +.pytest_cache/ +.mypy_cache/ +*.egg-info/ +.DS_Store + +# Docs & planning (not needed at runtime) +*.md +CONTEXT.md +FABLE.md +PLAN_TO_PROD.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c7c99c1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,41 @@ +# GigLez — production image for the live "simple mode" API. +# Serves: FastAPI upload/map UI + statistical device-category identification. +FROM python:3.10-slim + +# Faster, quieter, unbuffered logs in containers. +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=1 + +WORKDIR /app + +# Dependencies first for layer caching — only re-runs when reqs change. +COPY requirements-prod.txt ./ +RUN pip install --no-cache-dir -r requirements-prod.txt + +# Application code + assets the live path needs. +COPY src/ ./src/ +COPY static/ ./static/ +COPY templates/ ./templates/ +COPY config/ ./config/ +COPY models/category_classifier.joblib ./models/category_classifier.joblib + +# Seed data: the static rtl_433 protocol table (read-only) and an initial +# capture store. When /app/data is mounted as a *named volume*, Docker seeds +# the empty volume from these baked files on first run, then persists writes +# to captures_simple.json across restarts. +COPY data/rtl_433_protocols.json data/captures_simple.json data/weather_sensors_found.txt ./data/ + +# Run as a non-root user. +RUN useradd --create-home --uid 10001 giglez \ + && chown -R giglez:giglez /app +USER giglez + +EXPOSE 8000 + +# Container-native healthcheck hits the app's /health endpoint. +HEALTHCHECK --interval=30s --timeout=3s --start-period=15s --retries=3 \ + CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/health').status==200 else 1)" + +# Serve with uvicorn (module app object), not the __main__ dev banner. +CMD ["uvicorn", "src.api.main_simple:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..bcceefd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +# GigLez — single-service deployment of the live simple-mode API. +# docker compose up --build → http://localhost:8000 +services: + giglez: + build: . + image: giglez:latest + ports: + - "8000:8000" + volumes: + # Persist the JSON capture store across restarts. Seeded from the + # baked-in data/ on first run (see Dockerfile), then app writes survive. + - giglez_data:/app/data + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/health').status==200 else 1)"] + interval: 30s + timeout: 3s + start_period: 15s + retries: 3 + +volumes: + giglez_data: diff --git a/requirements-prod.txt b/requirements-prod.txt new file mode 100644 index 0000000..9803d32 --- /dev/null +++ b/requirements-prod.txt @@ -0,0 +1,30 @@ +# GigLez production runtime — the LIVE simple-mode path only +# (src/api/main_simple.py → SignatureMatcher → pattern_decoder → statistical ML). +# +# Versions are pinned to what actually trained/serves the model in this env. +# In particular scikit-learn/numpy MUST match the versions the +# models/category_classifier.joblib bundle was built with (1.6.1 / 2.2.x), +# or joblib.load() will warn/break. Do NOT downgrade to the old +# requirements.txt pins — those drive the dormant SQLAlchemy/PostGIS path. +# +# NOTE: torch/onnx are intentionally absent — the Phase 3B CNN is benched +# (loses to the statistical model), so it is not part of the serving path. + +# Web stack +fastapi==0.121.1 +starlette==0.46.0 +uvicorn[standard]==0.31.1 +jinja2==3.1.6 +python-multipart==0.0.22 +pydantic==2.12.4 + +# Numerics + statistical ML (RAW category classifier) +numpy==2.2.6 +scipy==1.15.3 +scikit-learn==1.6.1 +joblib==1.5.3 + +# Support +loguru==0.7.2 +pyyaml==6.0.2 +python-dateutil==2.9.0.post0