본문으로 건너뛰기

환경 변수

포털은 설정을 .env에서 읽습니다. 번들된 .env.example이 지원되는 모든 키를 열거합니다. 설치 마법사(scripts/install.sh)가 필수 키를 강한 기본값으로 채워 주고 나머지는 필요에 따라 설정합니다.

대상 독자

배포를 튜닝하는 운영자. .env 파일과 Docker Compose의 변수 치환에 익숙해야 합니다.

읽기 순서

  1. 레포 루트의 .envdocker-compose가 자동 로드합니다.
  2. 백엔드 코드는 os.getenv()런타임에 호출합니다 — 모듈 import 시점이 아닙니다. 이는 CLAUDE.md 규칙 #11. 컨테이너 재시작만으로 변경된 값을 픽업하며 재빌드는 필요 없습니다.
  3. Compose는 docker-compose.yml${VAR} 참조를 docker-compose up 시점에 .env에서 치환합니다.

아래 모든 키는 apps/backend/core/config.py, docker-compose.yml, scripts/* 중 한 곳에서 읽습니다 — 읽는 위치 컬럼에 표기되어 있습니다.

필수 키

다음 네 개는 반드시 존재해야 하며 비어 있어선 안 됩니다. 마법사가 설정합니다.

설정자읽는 위치비고
SECRET_KEY마법사(openssl rand -hex 32)config.pyJWT 서명 키 (HS256). 비-dev에서 최소 32자. 회전 시 모든 refresh token 무효.
DATABASE_URL마법사config.py, docker-compose.ymlpostgresql+asyncpg://user:pass@postgres:5432/trustedoss. compose 서비스명 postgres 호스트 사용.
CORS_ALLOWED_ORIGINS마법사config.py콤마 분리. 프로덕션은 origin을 명시적으로 열거해야 하며 allow_credentials=true 와 함께 * 사용 시 부팅에서 거부됩니다.
DOMAIN마법사docker-compose.ymlTraefik의 host-rule이 사용하는 호스트명. scheme과 path는 제거.

애플리케이션

기본값읽는 위치설명
APP_ENVdevconfig.pydev, staging, 또는 prod. 일부 CORS / 로그 기본값에 영향.
LOG_LEVELINFOconfig.pyDEBUG, INFO, WARNING, ERROR.
IMAGE_TAG0.11.0docker-compose.ymlghcr.io/trustedoss/trusca-backend, …/trusca-backend-worker, …/trusca-frontend의 핀 태그.

데이터베이스

DATABASE_URL(위 표)이 표준 설정입니다. 아래 합성 대안은 GCP Cloud Run 모듈이 Secret Manager에서 DB_PASSWORD를 마운트할 때 DSN을 Terraform state에 굽지 않도록 제공됩니다. DATABASE_URL 또는 네 개의 DB_* 키 중 하나만 설정하세요 — 둘 다 설정 금지.

기본값읽는 위치설명
DATABASE_URLconfig.py, docker-compose.yml위 참고.
DB_USERconfig.py합성 DSN: 사용자명. 결과 DSN에서 URL 인코딩됨.
DB_PASSWORDconfig.py합성 DSN: 비밀번호. URL 인코딩으로 @, :, /, #, % 가 파싱을 통과합니다.
DB_HOSTconfig.py합성 DSN: 호스트. Cloud SQL Auth Proxy 유닉스 소켓 경로(/cloudsql/...)도 가능.
DB_PORT5432config.py합성 DSN: 포트.
DB_NAMEconfig.py합성 DSN: 데이터베이스명.
POSTGRES_USERtrustedossdocker-compose.ymlpostgres 컨테이너 init이 사용. DATABASE_URL과 일치해야 함.
POSTGRES_PASSWORDdocker-compose.yml마법사가 생성.
POSTGRES_DBtrustedossdocker-compose.yml데이터베이스명.

DB_* 네 키 중 하나라도 설정되면 모두 설정해야 합니다 (그렇지 않으면 합성 분기에서 부팅 시 raise). 포털은 async SQLAlchemy + asyncpg를 사용합니다. 커넥션 풀 기본값은 FastAPI worker 수에 맞춰 튜닝되어 있습니다(uvicorn 워커 4 × 각 5 커넥션 = 20).

Redis & Celery

기본값읽는 위치설명
REDIS_URLredis://redis:6379/0config.py브로커 + 결과 백엔드.
CELERY_CONCURRENCY2docker-compose.ymlworker 프로세스 수. 슬롯당 피크 시 ~2 GB RAM 필요.

인증

기본값읽는 위치설명
SECRET_KEYconfig.py필수 키 참고. HS256 서명.
ACCESS_TOKEN_EXPIRE_MINUTES30config.pyJWT access token 수명.
REFRESH_TOKEN_EXPIRE_DAYS7config.pyRefresh token 수명. 회전 + 재사용 탐지 활성화.

취약점 데이터

포털은 SBOM을 로컬 Trivy DB(NVD + OSV + GHSA + EPSS + KEV 통합 번들)에 대조합니다. 라이프사이클은 취약점 데이터 (Trivy DB) 참조.

기본값읽는 위치설명
TRIVY_DB_REPOSITORYghcr.io/aquasecurity/trivy-dbconfig.pyTrivy DB를 받아오는 OCI 저장소. air-gapped 사내 미러로 오버라이드 — Air-gapped 운영 참조.
TRIVY_DB_REFRESH_HOURS168 (주간)config.pytrivy_db_refresh 태스크의 Celery Beat 주기. 낮추면 신선도↑, 높이면 egress↓.
TRIVY_CACHE_DIR/var/lib/trivyintegrations/trivy.pyDB가 풀리는 디렉터리. 공유 trivy-cache 볼륨이 뒷받침 — 워커(rw)와 backend(ro)가 함께 마운트해 관리자 health/disk 패널이 DB 상태를 읽을 수 있다.
TRIVY_TIMEOUT_SECONDS300config.pytrivy sbom 스캔별 타임아웃. 매우 큰 모노레포는 600~900으로 상향.

빌드 / 정책 게이트

CI 빌드 게이트는 기본적으로 Critical CVE와 금지 라이선스에서 빌드를 실패시키며, 이 조건들은 env로 구동되지 않습니다. 아래 단일 env 노브는 선택적 EPSS 차원을 더합니다.

기본값읽는 위치설명
GATE_EPSS_THRESHOLD(미설정)config.py선택적 EPSS 게이트. 0~1 값. 설정 시 미해결 결과 중 epss_score >= GATE_EPSS_THRESHOLD인 것이 있으면 빌드 게이트도 실패하며, 게이트 결과에 epss_gate_count + epss_threshold가 실립니다. 미설정(기본)이면 EPSS 게이트는 비활성 — 기존 Critical-CVE / 금지-라이선스 조건만 적용됩니다. EPSS 값이 없는 결과는 게이트를 트리거하지 않습니다. EPSS 데이터는 Trivy DB에서 옵니다 — Trivy가 값을 제공하는 CVE만 대상입니다.

게이트 모델은 빌드 게이트, CI 워크스루는 EPSS로 빌드 게이팅 참고.

스캔 파이프라인

기본값읽는 위치설명
TRUSTEDOSS_SCAN_BACKENDrealconfig.pyreal(서브프로세스 cdxgen / scancode / Trivy) 또는 mock(픽스쳐 JSON). mock은 테스트 하네스의 dev / CI 기본값입니다. 프로덕션은 real 유지.
SCANCODE_TIMEOUT_SECONDS600config.pyscancode first-party 라이선스 단계의 hard wall-clock 한도. 타임아웃 시 declared 라이선스만으로 스캔을 계속합니다(best-effort).
SCANCODE_MAX_FILES20000config.py적격 first-party 파일(제외 필터 적용 후) 상한. 초과 시 scancode 를 건너뛰고 declared 라이선스만 유지합니다.
SCANCODE_MAX_DETECTIONS5000config.py스캔당 저장되는 detected 라이선스 결과 수 상한.
SCANCODE_MAX_RESULT_BYTES268435456 (256 MB)config.py파싱 전 scancode JSON 아티팩트 상한 — 악의적 트리의 OOM 가드.
WORKSPACE_HOST_PATH/tmp/trustedossconfig.py, docker-compose.ymlworker에 /workspace로 마운트되는 호스트 디렉터리. 레포 클론 + 스캔 아티팩트(cdxgen SBOM, scancode 출력) 보관. compose 스택은 컨테이너 내에서 /workspace로 오버라이드합니다.
ORT_RULES_PATH/opt/trustedoss/ort/rules.ktsdocker-compose.ymlworker 내부 레거시 경로로, ORT 단계 제거 후 잔재입니다. 파일은 placeholder 이며 v0.10.0 에서는 효과가 없습니다 — 라이선스 단계 분류는 apps/backend/tasks/scan_source.py_LICENSE_CATEGORY_DEFAULTS 에서 옵니다.
JSONB_ROW_SIZE_LIMIT_BYTES262144 (256 KB)config.pywriter가 truncate + warn하기 전 행당 JSON 바이트 상한. I-1 무한 페이로드 클래스 가드.

스캔 보존(retention)

superseded·노후 스캔 스냅샷을 회수하는 자동 보존 sweep을 조정하는 키입니다. sweep은 6시간 주기 Celery beat 태스크로 실행됩니다. 전체 모델은 스캔 보존을 참고하십시오.

기본값읽는 위치설명
SCAN_RETENTION_SUPERSEDED_GRACE_DAYS7config.pysuperseded 스냅샷이 sweep에 회수되기 전 보존되는 일수입니다. 동일한 (project, 정규화된 ref) 타겟에 더 새로운 성공 스캔이 도착하면 기존 스냅샷이 superseded 됩니다. 타겟별 롤백 이력을 더 길게 유지하려면 값을 높이십시오.
SCAN_RETENTION_KEEP_LAST30config.py나이와 무관하게 프로젝트당 보존되는 ref-less·실패 스캔의 최소 개수입니다. sweep은 이 하한 아래로 트림하지 않습니다 — ref 타겟이 없는 ad-hoc·진단 스캔을 보호합니다.
SCAN_RETENTION_MAX_AGE_DAYS180config.pyhard age 상한. release가 아닌 스캔이 이보다 오래되면 해당 타겟의 live 스냅샷이라도 sweep이 회수합니다. metadata.release 라벨이 붙은 스캔은 예외이며 영구 보존됩니다.

WebSocket 게이트웨이

기본값읽는 위치설명
WEBSOCKET_MAX_CONNECTIONS_PER_USER3config.py사용자당 동시 커넥션 상한. 같은 사용자의 4번째 커넥션이 가장 오래된 것을 close code 1001(reason="newer_connection")로 evict합니다. 워커 프로세스별 적용 — 멀티 워커 배포는 N × worker-count 까지 허용.
WEBSOCKET_AUTH_TIMEOUT_SECONDS1.0config.py{"type":"auth"} 프레임을 기다리는 시간. 윈도우 내 미수신 시 1008 / reason="auth_timeout"으로 닫힘.

알림

기본값읽는 위치설명
SMTP_HOST(비어있음)config.pySMTP 서버. 없으면 이메일 알림이 NotificationDisabled를 raise하고 채널은 건너뜁니다.
SMTP_PORT587config.pySMTP 포트. 587에서 STARTTLS 기대.
SMTP_USER(비어있음)config.pySMTP 사용자명.
SMTP_PASSWORD(비어있음)config.pySMTP 비밀번호.
SMTP_USE_STARTTLStrueconfig.py465에서 implicit TLS를 요구하는 SMTP 서버 또는 25 테스트 시에만 false.
SMTP_FROMno-reply@trustedoss.localconfig.py아웃고잉 알림의 From: 헤더. 환경별 오버라이드 권장.
SMTP_TIMEOUT_SECONDS10config.py호출당 SMTP 소켓 타임아웃.
SLACK_WEBHOOK_URL(비어있음)config.pysuper_admin 알림용 조직 단위 Slack Webhook. 팀별 Webhook은 UI에서 구성.
TEAMS_WEBHOOK_URL(비어있음)config.py조직 단위 MS Teams Webhook.
NOTIFICATION_HTTP_TIMEOUT_SECONDS10config.pySlack / Teams Webhook 아웃바운드 HTTP 타임아웃.

비밀번호 재설정

기본값읽는 위치설명
PASSWORD_RESET_BASE_URLhttp://localhost:5173config.py재설정 이메일에 임베드되는 프론트엔드 base URL. 링크 템플릿: {base}/reset-password?token={token}.
PASSWORD_RESET_RATE_LIMIT5/minuteconfig.pyPOST /auth/forgot-password에 대한 IP별 slowapi 한도.
PASSWORD_RESET_EMAIL_COOLDOWN_SECONDS300config.py같은 주소로 두 번째 재설정 이메일 발송까지 최소 초 수. 쿨다운 시 Retry-After로 반환.

OAuth (데모 SaaS 전용)

데모 SaaS 배포에 적용. 자체 호스팅 설치는 비워 둡니다(이 경우 /auth/oauth/{provider}/authorize 엔드포인트가 503과 oauth_provider_disabled = true를 반환).

기본값읽는 위치설명
GITHUB_CLIENT_ID(비어있음)config.pyGitHub OAuth App client ID.
GITHUB_CLIENT_SECRET(비어있음)config.pyGitHub OAuth App client secret.
GOOGLE_CLIENT_ID(비어있음)config.pyGoogle OAuth client ID.
GOOGLE_CLIENT_SECRET(비어있음)config.pyGoogle OAuth client secret.
OAUTH_STATE_TTL_SECONDS300config.py서명된 state JWT 수명(CSRF 가드). RFC 6749 §10.12.
OAUTH_HTTP_TIMEOUT_SECONDS10config.pyOAuth 공급자 API로의 아웃바운드 HTTP 타임아웃.
OAUTH_LOGIN_REDIRECT_DEFAULThttp://localhost:5173/config.pyOAuth 콜백 성공 후 SPA가 도착하는 곳.
OAUTH_LOGIN_REDIRECT_FAILUREhttp://localhost:5173/loginconfig.py콜백 실패 시 SPA가 도착하는 곳. ?error=oauth_failed 수신.

백업

기본값읽는 위치설명
BACKUP_RETENTION_DAYS7scripts/backup.shscripts/backup.sh --no-prune로 실행별 오버라이드.
BACKUP_DIR<repo>/backupsscripts/backup.sh백업 스크립트가 쓰는 위치.

디스크 가드

기본값읽는 위치설명
DISK_HARD_LIMIT_PCT95.0apps/backend/services/scan_service.py빨간 게이지 + 새 스캔 차단 + admin 알림.

Traefik / TLS

기본값읽는 위치설명
DOMAINdocker-compose.yml필수 키 참고.
TLS_EMAILdocker-compose.ymlLet's Encrypt HTTP-01 챌린지가 사용하는 이메일. 인증서 발급에 필수.
TRAEFIK_LOG_LEVELINFOdocker-compose.yml라우팅 이슈 추적 시 DEBUG가 유용.

선택적 통합

기본값읽는 위치설명
JIRA_ENABLEDfalse(없음)스텁 — 현재 릴리스의 어떤 코드 경로에서도 소비되지 않음. Phase B Jira 통합용 예약. 기능 도착 시 기존 배포가 깨지지 않도록 .env.example에 포함.
JIRA_URL(비어있음)(없음)스텁. 위 참고.
JIRA_TOKEN(비어있음)(없음)스텁. 위 참고.
HTTP_PROXY / HTTPS_PROXY / NO_PROXY(비어있음)서브프로세스 envgit clone, cdxgen, trivy --download-db-only 부팅 / refresh 경로가 존중.

부트스트랩 / 스크립트

다음 키는 부트스트랩과 데모 시드 스크립트만 읽습니다. 동작 중인 백엔드가 소비하지는 않지만 설치·데모 시점에 설정합니다.

기본값읽는 위치설명
ADMIN_EMAILapps/backend/scripts/create_super_admin.py스크립트 실행 시 프로비저닝할 첫 super-admin의 이메일. 읽을 때 소문자화·trim.
ADMIN_PASSWORDapps/backend/scripts/create_super_admin.py부트스트랩 super-admin의 비밀번호. 12자 이상 필수 — 그렇지 않으면 스크립트가 중단됩니다.
DEMO_SUPER_ADMIN_PASSWORD(자동 생성)apps/backend/scripts/seed_demo.py데모 시드의 super-admin 비밀번호 오버라이드. APP_ENVstaging 또는 prod일 때 필수이며 설정 시 12자 이상이어야 합니다.

검증

백엔드는 시작 시 설정을 검증합니다(apps/backend/main.py lifespan).

  • 비-dev APP_ENV에서 SECRET_KEY가 32자 미만이면 시작 거부.
  • CORS_ALLOWED_ORIGINS*가 포함되고 credentials 허용 시 거부.
  • APP_ENV=prod에서 origin이 평문 http://이면 거부.
  • DB_* 키가 부분 설정이면 거부(합성 DSN 경로는 all-or-nothing).

실패 시 구조화 로그 라인을 emit하고 프로세스가 크래시 — 관대한 fallback은 없습니다.

정상 동작 확인

.env 편집 후:

docker-compose -f docker-compose.yml restart backend worker beat
docker-compose -f docker-compose.yml logs --tail=50 backend | grep backend_starting

시작 로그가 app_env 필드를 담은 단일 backend_starting 이벤트를 emit해야 합니다. 시크릿은 결코 로그에 남지 않습니다.

함께 보기