Architectural security interface
시크릿을 저장하고,
필요한 만큼만 전달하세요.
DevKey는 프로젝트별 시크릿을 암호화해 보관하고, 만료·권한·감사 로그가 있는 Access Key로 애플리케이션에 전달합니다. 이 문서는 관리자 콘솔과 API를 처음부터 운영까지 한 흐름으로 설명합니다.
01 · Start here
처음 5분: 프로젝트에서 API 호출까지
계정과 프로젝트를 만든 뒤, 시크릿 하나에 읽기 권한이 있는 Access Key를 발급하고 첫 호출을 확인합니다.
- 관리자 콘솔에 로그인dev-key.com에서 이메일 인증을 마치고 로그인합니다.
- Project 만들기Projects에서 이름과 URL-safe slug(예:
erp)를 정합니다. API 요청에는 이 slug를 사용합니다. - Secret 저장Secrets → Create에서
DB_PASSWORD같은 키와 값을 입력합니다. 값은 저장 후 암호화되어 관리자 목록에도 평문으로 노출되지 않습니다. - Access Key 발급Access Keys → Create에서 같은 Project를 선택하고, 필요한 Secret에 Read/List 권한만 부여합니다. 만료일은 달력 또는 빠른 기간 버튼으로 지정할 수 있습니다.
- 첫 번째 읽기 요청생성 직후 한 번만 표시되는 원문 키를 안전한 런타임 시크릿에 넣고 아래 API 예제로 확인합니다.
sk_dev_... 또는 sk_live_... 원문이 표시됩니다. 복사하지 못했다면 기존 키를 비활성화하고 새 키를 발급하세요.02 · Mental model
DevKey의 구성 모델
권한 범위가 섞이지 않도록 리소스를 계층으로 나눕니다. 하나의 Access Key는 정확히 하나의 Project에 속하고, 그 Project 안의 Secret Item들에만 권한을 받을 수 있습니다.
사용자 소유의 최상위 경계입니다. Project·Secret·Access Key·감사 로그는 계정 소유권 검사를 통과해야 합니다.
서비스 또는 환경 단위의 격리 경계입니다. API URL에는 Project의 slug가 들어갑니다.
키와 암호화된 값의 쌍입니다. 값 자체는 API 권한을 통과한 요청에서만 복호화됩니다.
애플리케이션이 API를 호출하는 자격 증명입니다. 활성 상태·만료일·Project 범위를 함께 검사합니다.
Access Key와 Secret Item 사이의 연결입니다. canRead, canList 등을 Secret 단위로 지정합니다.
읽기·목록 조회와 성공/실패를 추적합니다. IP, User-Agent, 오류 코드가 함께 기록됩니다.
03 · Admin console
관리자 콘솔 사용법
웹 콘솔은 Better Auth 세션과 계정 소유권으로 보호됩니다. 브라우저에서 값을 직접 다루는 시간을 줄이고, 작업 단위를 Project로 분리하세요.
Secret 만들기·수정·삭제
Secrets → Create에서 Project, key, value를 입력합니다. key는 애플리케이션 환경 변수 이름처럼 대문자와 밑줄 조합을 권장합니다. 설명은 운영자가 목적과 소유 팀을 확인하는 데 사용합니다.
목록에서는 값 대신 메타데이터만 확인할 수 있습니다. 수정 화면에서 값을 비워 둔 채 저장하면 기존 값이 유지되고, 새 값을 넣으면 암호화된 값이 교체됩니다. 삭제 전에는 해당 Secret을 사용하는 Access Key 권한도 함께 점검하세요.
Project 경계
예를 들어 erp-dev, erp-staging, erp-prod를 별도 Project로 만들면 같은 키 이름을 사용해도 API 자격 증명이 서로 섞이지 않습니다. Access Key 생성 후에는 Project를 변경할 수 없습니다.
04 · Access keys
Access Key 발급과 수명 관리
Access Key는 sk_dev_... 또는 sk_live_... 형식입니다. 서버는 원문 대신 HMAC-SHA-256 해시만 저장하고, 요청 때 prefix로 후보를 찾은 뒤 전체 키를 검증합니다.
기간 선택
키는 기한없음, 1개월, 3개월, 6개월, 9개월, 1년 빠른 버튼 또는 정확한 날짜·시간 달력으로 만료를 지정할 수 있습니다. 만료 시각은 저장된 UTC ISO 시각으로 비교되며, 만료된 키는 401을 반환합니다.
생성 후 운영 체크리스트
- 원문 키를 CI/CD 또는 Worker의 시크릿 저장소에 즉시 저장합니다.
- 개발·스테이징·운영 환경마다 Project와 키를 분리합니다.
- 필요한 Secret만 선택하고, 목록 조회가 필요 없으면
canList를 끕니다. - 교체할 때는 새 키를 먼저 배포하고, 트래픽 확인 후 이전 키를 비활성화합니다.
05 · Permissions
권한은 Access Key가 아니라 Secret에 부여합니다
Access Key 설정에서 “Access Key 자체”나 별도 메뉴를 권한 대상으로 고르는 방식이 아닙니다. 선택한 Project의 Secret Item을 행으로 보고, 각 행에 동작을 부여하는 구조입니다.
| 플래그 | 현재 의미 | 권장 사용 |
|---|---|---|
canRead | 해당 Secret의 값을 단건으로 읽을 수 있음 | 필요한 단건 조회에만 켭니다. |
canList | 프로젝트의 .env 목록 응답에 포함할 수 있음. 현재 구현은 Read도 함께 필요 | 부팅 시 여러 값을 한 번에 주입할 때만 켭니다. |
canWrite | 권한 모델에 예약된 쓰기 플래그 | 현재 공개 API에는 Secret 쓰기 엔드포인트가 없으므로 읽기 전용 키에는 끕니다. |
canDelete | 권한 모델에 예약된 삭제 플래그 | 현재 공개 API에는 Secret 삭제 엔드포인트가 없으므로 읽기 전용 키에는 끕니다. |
06 · API
API로 Secret 읽기
운영 API의 기본 주소는 https://dev.dev-key.com입니다. 모든 Secret API 요청은 Authorization: Bearer 헤더에 Access Key를 담아야 합니다. 원문 키를 URL, 로그, 소스 코드에 넣지 마세요.
curl --fail-with-body --silent --show-error https://dev.dev-key.com/api/v1/projects/erp/secrets/DB_PASSWORD -H "Authorization: Bearer $DEVKEY_ACCESS_KEY"
성공 응답은 다음처럼 project, key, 복호화된 value를 반환합니다.
{
"project": "erp",
"key": "DB_PASSWORD",
"value": "example-value"
}
프로젝트의 여러 값 읽기
curl --fail-with-body --silent --show-error https://dev.dev-key.com/api/v1/projects/erp/secrets -H "Authorization: Bearer $DEVKEY_ACCESS_KEY"
목록 응답에는 Access Key에서 canRead와 canList가 모두 켜진 Secret만 포함됩니다.
공개 API 엔드포인트
| 메서드 | 경로 | 인증 | 응답 |
|---|---|---|---|
| GET | /health | 없음 | { "ok": true } |
| GET | /api/v1/projects/:slug/secrets/:key | Bearer Access Key | 단건 Secret JSON |
| GET | /api/v1/projects/:slug/secrets | Bearer Access Key | { "secrets": { ... } } |
| GET | /api/v1/projects/:slug/secrets.env | Bearer Access Key | 환경 변수 형식의 text/plain |
| GET | /openapi.json | 없음 | OpenAPI 3 문서 |
대화형 API Reference는 dev.dev-key.com/docs에서 열 수 있습니다. 문서 호스트에서 /docs로도 같은 Swagger 화면을 사용할 수 있습니다.
07 · Environment injection
.env 형식으로 주입하기
애플리케이션 시작 시 여러 값을 읽어야 한다면 secrets.env를 사용합니다. 응답은 KEY=value 줄 목록이며, 값에 줄바꿈·따옴표가 있을 때는 서버의 dotenv 포맷터 규칙을 따릅니다.
curl --fail-with-body --silent --show-error https://dev.dev-key.com/api/v1/projects/erp/secrets.env -H "Authorization: Bearer $DEVKEY_ACCESS_KEY" -o .env.runtime
.env.runtime는 프로세스 시작 직후 메모리로 읽고, 저장이 필요하면 배포 플랫폼의 시크릿 저장소를 사용합니다. Git에 커밋하지 않도록 .gitignore에 추가하세요.08 · Security model
보안 경계와 감사
관리자 API는 Better Auth 세션과 사용자 소유권을 확인합니다. 세션 쿠키를 API 키처럼 공유하지 마세요.
Secret 값은 Worker의 암호화 키로 AES-GCM 처리되어 D1에 저장됩니다. 목록 DTO에는 평문·암호문을 포함하지 않습니다.
단건 Read와 일괄 List를 분리해 필요한 범위만 허용합니다. 키마다 짧은 만료 기간을 우선하세요.
읽기와 목록 조회의 성공·실패, Project·Access Key, IP, User-Agent, 오류 코드가 감사 로그에 남습니다.
dev-key.com은 관리자 UI, dev.dev-key.com은 API, docs.dev-key.com은 문서 전용입니다.
새 키를 먼저 배포하고 이전 키를 비활성화하는 두 단계 회전으로 다운타임을 피합니다.
운영 전 확인
- 브라우저 개발자 도구·서버 로그·CI 출력에 Access Key가 찍히지 않는지 확인합니다.
- 만료·비활성화 후 같은 요청이 401이 되는지 스테이징에서 테스트합니다.
- 접근하지 않아야 할 Secret은 권한을 끄고, 필요한 경우 Project 자체를 분리합니다.
- 감사 로그에서 비정상 IP·User-Agent·실패 코드가 반복되는지 확인합니다.
09 · Local & deploy
로컬 개발과 배포
DevKey는 Vite로 React 정적 자산을 빌드하고 Hono Worker가 API와 자산을 함께 제공합니다. D1 마이그레이션과 비밀값은 배포 환경별로 분리됩니다.
로컬 시작
npm install
Copy-Item .dev.vars.example .dev.vars # PowerShell
# cp .dev.vars.example .dev.vars # macOS/Linux
npm run db:migrate:local
npm run dev
.dev.vars에는 로컬 암호화 키·Access Key pepper·Better Auth 설정이 필요합니다. 예제 파일을 복사한 뒤 값은 직접 생성하고, 파일 내용은 커밋하거나 공유하지 마세요.
검증과 운영 배포
npm test
npm run build
운영 배포 스크립트는 빌드 → 원격 D1 마이그레이션 → 운영 비밀값 동기화 → wrangler deploy --env production 순서로 실행됩니다.
npm run deploy, npm run db:migrate:prod, npm run secrets:prod는 원격 상태를 변경합니다. 먼저 변경 범위·마이그레이션 SQL·환경 변수를 리뷰하고, 승인된 운영 세션에서만 실행하세요.호스트별 책임
| 호스트 | 역할 | 허용 표면 |
|---|---|---|
dev-key.comwww.dev-key.com | 관리자 웹 | React UI, Better Auth 경로 |
dev.dev-key.com | API | /api/*, /health, OpenAPI, Swagger |
docs.dev-key.com | 문서 | 문서 홈, Swagger, OpenAPI JSON |
| localhost | 개발 편의 | 웹·API 표면을 함께 제공 |
10 · Troubleshooting
문제 해결
| 증상 | 가능한 원인 | 확인할 것 |
|---|---|---|
| 401 Missing or invalid bearer token | Authorization 헤더가 없거나 형식이 다름 | Authorization: Bearer $DEVKEY_ACCESS_KEY와 키의 공백·개행을 확인합니다. |
| 401 Access key has expired | 만료일이 현재 시각보다 과거 | 콘솔에서 만료일을 확인하고 새 키를 발급합니다. |
| 401 Access key is inactive or missing | 키가 비활성화됐거나 prefix를 찾지 못함 | 키 전체를 다시 복사하지 말고, 새 키를 발급해 배포합니다. |
| 403 Access key cannot read this secret | 해당 Secret에 Read 권한이 없음 | Access Key 상세 → 권한에서 Secret 행의 Read를 켭니다. |
| 목록에 Secret이 안 보임 | Read 또는 List 중 하나가 꺼져 있음 | 목록 조회는 두 플래그가 모두 필요합니다. |
| 404 Project/Secret not found | slug·key 오탈자 또는 다른 Project | API 호스트와 Project slug, Secret key의 대소문자를 확인합니다. |
| 관리자 API 401 | Better Auth 세션이 없거나 다른 호스트에서 호출 | 관리자 UI 호스트에서 로그인하고 쿠키 전송 여부를 확인합니다. |
11 · Glossary
용어
Account 사용자 소유 경계. Project 서비스/환경별 격리 단위. Secret Item 암호화되는 key-value 리소스. Access Key API 호출용 Bearer 자격 증명. Permission 키와 Secret 사이의 동작 범위. Audit Log 접근 성공·실패 추적 기록.
문서와 구현의 기준이 다르게 보이면 OpenAPI JSON과 현재 배포된 콘솔의 동작을 우선 확인하세요.