통합
/integrations는 비대화형 자격증명을 다루는 사용자용 허브입니다. 두 가지 서로 다른 항목을 묶습니다.
- API Key — CI 러너·스크립트·외부 서비스가 포털 API에 인증할 때 사용하는 자격증명.
- Webhook — GitHub와 GitLab이 저장소 이벤트(push, pull request)를 푸시하기 위해 포털이 노출하는 인바운드 URL.
조회와 소속 팀 프로젝트의 프로젝트 범위 API Key 발급은 developer, 팀 범위 Key 발급은 team_admin, 조직 범위 Key 발급은 super_admin. 페이지는 본인이 수행할 수 없는 동작을 숨깁니다.
API Key
/integrations를 열고 API keys 섹션으로 스크롤합니다. 목록은 본인이 관리할 수 있는 모든 Key를 표시합니다 — 라벨, prefix, scope, 만료, 마지막 사용 메타데이터. 마지막 사용 시각은 정 확한 요청 시각이 아니라 15분 단위로 반올림됩니다. 이유는 Key 목록을 참고하세요.

Key 생성
-
Create API key를 클릭합니다. 다이얼로그에서 이름과 scope를 입력하고 제출하면 Key가 발급됩니다.

-
폼을 채웁니다.
- Name — Key 용도를 떠올리게 하는 자유 텍스트(예:
github-action-checkout-service). - Scope — 드롭다운에는
org,team,project가운데 본인이 발급할 수 있는 scope만 나열됩니다. 낮은 scope가 더 엄격합니다. 필요한 호출을 커버하는 가장 작은 scope를 선택하세요. 폼에는team_id(scope=team일 때 필수)와project_id(scope=project일 때 필수)를 위한 평문 UUID 입력란이 있습니다. 해당 admin 페이지에서 ID를 복사해 넣으세요. - [이 키가 할 수 있는 일] - 기본값인 [읽기 전용] 또는 [읽기·쓰기]입니다. 아래 읽기 전용 키를 참고하세요.
- Expiration — Never expires 또는 30 / 90 / 180 / 365일 프리셋 가운데 하나입니다. 선택한 기간이 지나면 Key는 인증되지 않고, 그 Key를 쓰는 CI 호출은 실패합니다. 파이프라인 로그로 유출된 Key도 누군가 폐기할 때까지 살아 있지 않고 스스로 만료되므로, CI용 Key에는 TTL을 지정하고 주기적으로 교체하세요.
각 scope 발급 권한:
Scope 발급 가능 주체 orgsuper-admin 만 teamsuper-admin, team-admin projectsuper-admin, team-admin, developer (소속 팀의 프로젝트 한정) - Name — Key 용도를 떠올리게 하는 자유 텍스트(예:
-
Create를 클릭합니다.
폼의 기본값은 Never expires이고, 그렇게 발급한 Key는 누군가 Revoke를 누를 때까지 유효합니다. 다른 장기 시크릿과 동일하게 취급하세요. CI 시크릿 매니저에 보관하고 소스 컨트롤에는 절대 두지 마세요. 프리셋을 고르면 Key가 유출됐을 때 피해가 이어지는 기간에 상한이 생깁니다.
읽기 전용 키
scope는 키가 어디에 닿을 수 있는지를 정하고, 권한 폭은 거기에 무엇을 할 수 있는지를 정합니다. 서로 다른 질문입니다. 스캔 결과만 읽는 파이프라인이 스캔을 시작할 수 있어야 할 이유는 없습니다.
읽기 전용 키는 무언가를 바꾸는 요청을 모두 거절합니다. 엔드포인트마다가 아니라 인증하는 지점에서 막습니다. 실제로는 스캔 조회, 출처 조회, 적합성 보고서 조회는 되고 스캔 실행과 SBOM 업로드는 되지 않습니다. 거절은 키가 읽기 전용이라고 밝히는 403이므로, 파이프라인 담당자가 권한 문제와 구분할 수 있습니다.
새로 만드는 키는 읽기 전용이 기본입니다. 파이프라인이 실제로 쓰기를 할 때만 [읽기·쓰기]를 고르세요. 스캔 실행 액션과 SBOM 업로드가 여기에 해당합니다.
이 기능이 생기기 전에 발급한 키는 읽기·쓰기이고 그대로 유지됩니다. 그때는 그것이 유일한 종류였고, 업그레이드하면서 좁혔다면 그 키를 쓰던 것이 멈추면서 포털에는 이유가 아무것도 남지 않았을 것입니다. 키 목록에 각 키의 권한 폭이 표시되므로 좁힐 만한 키를 찾을 수 있습니다.
좁히기는 한 방향입니다. 키 목록에서 읽기·쓰기 키를 읽기 전용으로 바꿀 수 있지만 되돌릴 수는 없습니다. 몇 달째 CI 로그에 남아 있던 키에 발급 당시보다 더 큰 권한을 주지 않기 위해서이고, 넓히려면 새 비밀값으로 키를 새로 발급합니다.
서비스 계정
키는 발급한 사람이 비활성화되면 멈춥니다. 개인 키에는 맞는 규칙이지만 파이프라인 키에는 맞지 않습니다. 1년째 매일 밤 돌던 빌드가 만든 사람이 떠난 날 멈추고, 그 사실을 빨간 파이프라인으로 처음 알게 됩니다.
서비스 계정은 자동화를 위한 신원입니다. 서비스 계정으로 발급한 키는 그 계정이 살아 있는 동안 동작하므로 사람이 오가는 것과 무관해집니다.
이 페이지의 키 목록 위에 있는 [서비스 계정] 패널에서 만듭니다. 만든 사람이 담당자가 됩니다. 담당자는 인증에 관여하지 않습니다. 관리하는 사람 없는 자격증명에도 이름 하나는 남아 있게 하려고 두는 자리입니다.
담당자는 그 계정이 속한 팀의 구성원이어야 합니다. 팀 밖의 사람은 자격증명에 대해 물어볼 수 있는 사람이 아니라 검사를 통과시키는 이름일 뿐이므로 거절합니다. 발급할 때 다시 확인하므로, 나중에 팀을 떠난 담당자는 더 이상 담당자로 세지 않습니다.
담당자가 퇴사하면 기존 키는 계속 동작합니다. 그것이 이 기능의 목적입니다. 대신 그 계정으로 새 키를 발급하는 것은 막힙니다. 누군가 넘겨받을 때까지 거절되고, 패널에 담당자 없음으로 표시되며 [담당 맡기] 버튼이 함께 나옵니다. 문제를 보여 준 자리에서 바로 해결할 수 있습니다.
서비스 계정을 정지하면 그 계정이 가진 모든 키가 한 번에 멈춥니다. 키가 사람과 함께 죽지 않게 만든 것에 대한 대가입니다. 끝내는 방법이 분명히 있어야 하고, 그것이 키 목록을 뒤지는 일이어서는 안 됩니다. 감사 기록의 행위자가 남도록 계정 기록 자체는 지우지 않습니다.
서비스 계정이 일부러 할 수 없는 일이 몇 가지 있습니다.
- 로그인. 쓸 수 있는 비밀번호가 없고, 그와 별개로 로그인 화면에서 거절됩니다.
- 비밀번호 재설정 수신. 보낼 사람이 없고, 엔드포인트는 모르는 주소와 똑같이 답합니다.
- 외부 신원 연결. OAuth 계정 대조에서 제외되므로 사람이 대화형 경로를 얻을 수 없습니다.
- 관리자 사용자 목록 노출. 그 목록이 제공하는 동작이 서비스 계정에는 맞지 않고, 퇴사자 정리 화면에서 비활성화하면 정리처럼 보이는 파이프라인 장애가 됩니다.
- 담당자 되기, 다른 서비스 계정 만들기. 같은 팀의 활성 상태인 사람만 담당자가 될 수 있으므로, 서비스 계정끼리 서로를 보증하며 끝에 사람이 없는 사슬이 생기지 않습니다.
- 배포 관리자 되기. 만드는 코드뿐 아니라 데이터베이스가 막습니다. 그런 계정이 발급한 키는 그것을 만든 모든 세션보다 오래 살아남기 때문입니다.
- 팀원 화면에서 팀에 추가되거나 제거되기. 서비스 계정의 접근 범위는 만든 자리에서 정합니다. 그 화면에서 소속을 지우면 살아 있는 키를 든 채 멈출 방법이 없어집니다.
서비스 계정이 가진 주소(<이름>@svc.trusca.internal)는 합성한 것이고 메일이 가지 않습니다. 감사 기록이 주소를 출력하기 때문에 두는 값이고, 비어 있는 것보다 알아볼 수 있는 편이 낫습니다.
포털은 전체 Key가 담긴 1회 노출 모달을 엽니다.
tos_a1b2c3d4_eaff8b91d36c5e0a2f1c4d7e8a9b0c2d
전체 Key는 단 한 번만 표시됩니다. 모달을 닫으면 prefix만 보입니다. 지금 복사해서 CI의 시크릿 저장소에 붙여 넣은 다음 Done을 클릭하세요.
모달에는 Copy 버튼과 명시적 경고가 있습니다 — "This is the only time you will see the full key. If you lose it, you must create a new one."
Key 사용
모든 요청의 Authorization 헤더에 Key를 Bearer 스킴으로 전달하세요. API Key는 CI 영역 — 스캔 트리거와 상태 폴링 — 을 인증합니다. 대화형 읽기 엔드포인트에서는 받지 않습니다(예: GET /v1/projects는 JWT 전용이며 Key에는 401을 반환합니다).
curl -sS -X POST \
-H "Authorization: Bearer ${TRUSTEDOSS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"kind":"source"}' \
https://trustedoss.example.com/v1/projects/<project-id>/scans
GitHub Actions에서는 Key를 저장소 또는 조직 시크릿에 보관하고 환경 변수로 노출합니다.
- name: Trigger TRUSCA scan
env:
TRUSTEDOSS_API_KEY: ${{ secrets.TRUSTEDOSS_API_KEY }}
run: curl -sS -H "Authorization: Bearer $TRUSTEDOSS_API_KEY" ...
Jenkins에서는 Credentials 플러그인(Secret text)을 사용해 stage 안에서 바인딩합니다.
stage('Scan') {
withCredentials([string(credentialsId: 'trustedoss-api-key', variable: 'TRUSTEDOSS_API_KEY')]) {
sh 'curl -sS -H "Authorization: Bearer $TRUSTEDOSS_API_KEY" ...'
}
}
Key 폐기
API Key 목록에서 행에 마우스를 올리고 Revoke를 클릭한 뒤 다이얼로그에서 확인합니다. 폐기는 즉시 적용되며(인증 캐시 TTL ~5초) 되돌릴 수 없습니다.
Webhook
Webhooks 섹션으로 스크롤합니다. API Key와 달리 Webhook URL은 고정입니다 — 포털이 well-known 경로로 노출하며, 공급자(GitHub / GitLab)에서 그 URL로 요 청을 보내도록 연결합니다.
GitHub
GitHub에 등록할 URL — https://<your-host>/v1/webhooks/github.
- Content-Type:
application/json. - Signature:
X-Hub-Signature-256. 프로젝트별webhook_secret을 키로 raw body에 대해 HMAC-SHA256. - Events:
push와pull_request가 지원되는 트리거.
포털은 인바운드 전송을 검증하기 위해 프로젝트별 webhook_secret 필드를 저장합니다. 해당 시크릿을 생성·회전하는 UI는 현재 릴리스에서 노출되지 않습니다 — 로드맵 참고. 현재는 운영자가 서버 측에서 시크릿을 부트스트랩합니다.
GitLab
GitLab에 등록할 URL — https://<your-host>/v1/webhooks/gitlab.
- Content-Type:
application/json. - Token:
X-Gitlab-Token헤더로 전송. 값을 프로젝트의webhook_secret으로 설정. - Events: Push events와 Merge request events.
정상 동작 확인
- Key 생성 후 그 Key로 스캔을 트리거하세요 —
curl -sS -X POST -H "Authorization: Bearer <key>" -H "Content-Type: application/json" -d '{"kind":"source"}' .../v1/projects/<project-id>/scans— 새 스캔과 함께200응답이 오는지 확인합니다. 이어서 같은 Key로GET .../v1/scans/<scan-id>를 폴링합니다. (GET /v1/projects는 JWT 전용이라 Key에는401을 반환합니다 — 설정 오류가 아니라 정상 동작입니다.)
- GitHub에 Webhook 등록 후 커밋을 푸시하고 GitHub의 Webhook deliveries 뷰에서 HTTP 200 성공 전송을 확인하세요.
- super-admin이
/admin/audit에서target_table=api_keys&action=create와target_table=webhook_deliveries&action=create이벤트를 확인할 수 있습니다. 현재 감사 로그는 super-admin 전용입니다 — 로드맵 참고.
트러블슈팅
- API에서 HTTP 401 — 자격증명 문제(헤더 없음, 잘못된 Bearer 형식, 알 수 없는 prefix, 서명 불일치, 폐기됨, 만료됨).
- API에서 HTTP 403 — 자격증명은 유효하지만 Key의 scope가 리소스를 커버하지 않음(예:
team-scope Key가org전용 엔드포인트 호출). 더 넓은 scope의 Key를 새로 발급하거나 다른 엔드포인트를 호출하세요. - API에서 HTTP 429 — Key별 레이트 리밋에 도달.
Retry-After헤더가 대기 시간을 알려줍니다. 백오프 후 재시도. - GitHub Webhook이 401 —
X-Hub-Signature-256검증 실패. 시크릿 일치 여부와 GitHub가 raw body 기준으로 HMAC을 계산하는지(재직렬화된 JSON이 아님) 확인. - GitLab Webhook이 401 —
X-Gitlab-Token헤더 값이 프로젝트webhook_secret과 일치하지 않음.
로드맵
매뉴얼이 이전에 약속했으나 v0.10.0에 포함되지 않은 항목.
- Key 생성 폼의 임의 만료 값 입력. 예정입니다. 현재 폼은 Never와 30 / 90 / 180 / 365일 프리셋을 제공하고, 그 밖의 TTL은 API의
expires_in_days(1~1825일)로 직접 발급해야 합니다. - Key 생성 폼의 팀/프로젝트 선택기 — 예정. 현재 폼은 평문 UUID 입력을 받습니다.
- Project Settings → CI/CD 서브탭과 Rotate webhook secret 동작 — 예정. 현재 프로젝트별
webhook_secret은 서버 측에서 부트스트랩됩니다. team_admin을 위한 팀 범위 감사 로그(/audit) — 예정. 현재 감사 로그는 super-admin 전용 (/admin/audit).
함께 보기
- 인증과 프로필 — 사람을 위한 대화형 자격증명.
- GitHub Actions — 종단 간 CI 통합.
- Webhooks (admin reference) — 페이로드 스키마와 admin 측 구성.
- API keys (admin reference) — 백엔드 동작·해싱·감사 로그.