- Synced 14 capability delta specs to main specs - Created 6 new main specs: api-authentication, error-boundary, input-validation, security-headers, shared-types - Updated 8 existing specs with security, validation, and performance requirements - Archived change to openspec/changes/archive/2026-02-20-code-review-fix/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.6 KiB
ADDED Requirements
Requirement: ML service non-root user
The ML service Dockerfile SHALL create a non-root user and run the application as that user. The Dockerfile SHALL include RUN useradd -m -r appuser and USER appuser directives.
Scenario: Container runs as non-root
- WHEN the ML service container starts
- THEN the application process runs as user
appuser(not root)
Requirement: TA-Lib downloaded over HTTPS with checksum
The ML service Dockerfile SHALL download TA-Lib source over HTTPS (not HTTP). The download SHALL be verified with a SHA256 checksum before extraction.
Scenario: HTTPS download
- WHEN the Dockerfile downloads TA-Lib source
- THEN the URL uses
https://protocol
Scenario: Checksum verification
- WHEN the TA-Lib tarball is downloaded
- THEN a
sha256sum -ccheck runs before extraction, and the build fails if the checksum does not match
Requirement: .dockerignore file exists
The project SHALL include a .dockerignore file at the repository root that excludes .git, .env, .env*, node_modules, .next, data/, *.md, __pycache__/, mlruns/, and models/.
Scenario: Docker context excludes sensitive files
- WHEN
docker buildruns - THEN
.env,.git, andnode_modulesare not included in the build context
MODIFIED Requirements
Requirement: Docker Compose configuration
The project SHALL include docker-compose.yml for simplified deployment orchestration.
Scenario: Service definition
- WHEN docker-compose.yml is parsed
- THEN defines service named 'candle-annotator' using Dockerfile from current directory
Scenario: Port mapping
- WHEN docker-compose up runs
- THEN maps host port 3000 to container port 3000
Scenario: Volume mounting for ML data
- WHEN docker-compose up runs
- THEN mounts named volume 'ml-data' to /app/ml-data in the candle-annotator container
Scenario: Frontend depends on PostgreSQL
- WHEN docker-compose up runs
- THEN the candle-annotator service starts only after the postgres service is healthy (
depends_on: postgres: condition: service_healthy)
Scenario: Frontend DATABASE_URL uses env var interpolation
- WHEN the candle-annotator service starts
- THEN the
DATABASE_URLenvironment variable uses${POSTGRES_PASSWORD}interpolation:postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
Scenario: Restart policy
- WHEN container crashes or stops
- THEN docker-compose automatically restarts container unless explicitly stopped (restart: unless-stopped)
Scenario: No SQLite volume
- WHEN docker-compose.yml is parsed
- THEN there is no
candle-datavolume defined or mounted
Scenario: PostgreSQL port bound to localhost only
- WHEN docker-compose up runs
- THEN the postgres service port mapping is
127.0.0.1:5432:5432(not5432:5432)
Scenario: MLflow port bound to localhost only
- WHEN docker-compose up runs
- THEN the mlflow service port mapping is
127.0.0.1:5000:5000
Scenario: ML service port bound to localhost only
- WHEN docker-compose up runs
- THEN the ml-service port mapping is
127.0.0.1:8001:8001
Scenario: Credentials via env var interpolation
- WHEN docker-compose.yml is parsed
- THEN all database credentials use
${POSTGRES_USER},${POSTGRES_PASSWORD}, and${POSTGRES_DB}variable interpolation from.env
Requirement: Environment variable configuration
The project SHALL use environment variables for runtime configuration.
Scenario: .env.example file with placeholder credentials
- WHEN repository is cloned
- THEN
.env.examplecontainsPOSTGRES_PASSWORD=change_me_to_a_strong_password(not a real password)
Scenario: .env file gitignored
- WHEN
.gitignoreis inspected - THEN it includes
.env(not just.env*.local)
Scenario: DATABASE_URL configuration
- WHEN
DATABASE_URLenvironment variable is set - THEN the Next.js application connects to the PostgreSQL database at the specified URL
Scenario: No DATABASE_PATH variable
- WHEN environment variables are inspected
- THEN there is no
DATABASE_PATHvariable (SQLite path is removed)
Scenario: PORT configuration
- WHEN PORT environment variable is set
- THEN Next.js server listens on specified port (default: 3000)
Scenario: NODE_ENV configuration
- WHEN NODE_ENV environment variable is set to 'production'
- THEN Next.js runs in production mode with optimizations enabled
Scenario: API_KEY configuration
- WHEN
API_KEYenvironment variable is set - THEN both Next.js middleware and FastAPI dependency use this key for authentication
Requirement: Container security
The Docker setup SHALL follow security best practices.
Scenario: Non-root user
- WHEN container runs
- THEN application process runs as non-root user 'appuser' (UID 1000)
Scenario: Read-only filesystem where possible
- WHEN container runs
- THEN only /app/data directory requires write permissions, all other files are read-only to appuser
Scenario: No sensitive data in image
- WHEN Docker image is built
- THEN .env files, secrets, and database files are not included in image layers
Scenario: Minimal attack surface
- WHEN container runs
- THEN only port 3000 is exposed, no SSH, no unnecessary services, alpine base reduces package vulnerabilities
Scenario: No node_modules in production image
- WHEN the Next.js production Docker image is built
- THEN the
COPY --from=builder /app/node_modulesline is removed (standalone output bundles needed deps)
Requirement: Production build optimization
The Docker image SHALL be optimized for production use with minimal size.
Scenario: Use alpine base images
- WHEN Dockerfile specifies base images
- THEN uses node:18-alpine for both build and runtime stages
Scenario: Multi-stage build cleanup
- WHEN Docker image is built
- THEN build artifacts, devDependencies, and source files are not included in final image
Scenario: Layer caching optimization
- WHEN Dockerfile is structured
- THEN package.json and package-lock.json are copied and dependencies installed before source code copy for better layer caching
Scenario: Final image size
- WHEN Docker image build completes
- THEN final image size is under 200MB (excluding data volume)
Scenario: Base images pinned to digest
- WHEN Dockerfiles specify base images
- THEN images use
@sha256:<hash>pinning for reproducible builds