이 목록으로 어떤 값이 런타임 환경에 속하고, 어떤 값이 데이터베이스에 생성되며, 어떤 값이 플러그인에 저장되는지 결정하세요. 각 섹션은 교체가 실행 중인 사이트에 미치는 영향을 설명합니다.
Node.js에서는 프로세스 시작 시 process.env에 들어가도록 런타임 비밀을 호스팅 플랫폼의 비밀 관리자에 넣으세요. Worker에서는 wrangler secret put을 사용하세요. 비밀 값을 astro.config.mjs, wrangler.jsonc, import.meta.env에 넣지 마세요. Vite가 빌드 시점 값을 서버 번들에 임베드할 수 있습니다.
개요
| 비밀 | 출처 | 저장 위치 | 키 분실 영향 |
|---|---|---|---|
EMDASH_ENCRYPTION_KEY | 운영자(emdash secrets generate) | 환경 / Worker 비밀만 | 일치하는 키를 복원할 때까지 암호화된 플러그인 설정을 읽을 수 없음 |
| 미리보기 비밀 | 자동 생성(환경 재정의) | options 테이블(emdash:preview_secret) | 미사용 미리보기 링크가 동작하지 않음. 새 링크는 정상 |
| IP 솔트 | 자동 생성(환경 재정의) | options 테이블(emdash:ip_salt) | 댓글 속도 제한 연속성이 재설정됨 |
| 세션 및 API 토큰 | 세션/토큰별 생성 | 세션 저장소 / 데이터베이스(해시만) | 없음 — 평문은 절대 저장되지 않음 |
| OAuth 제공자 자격 증명 | 사용자(Google/GitHub 콘솔) | 환경 | 해당 제공자 로그인이 교체될 때까지 중지됨 |
| Turnstile 비밀 | 사용자(Cloudflare 대시보드) | 환경 | 댓글 CAPTCHA 검증 실패 |
| S3 자격 증명 | 사용자(스토리지 제공자) | 런타임 환경 | 미디어 업로드/다운로드가 교체될 때까지 실패 |
| 플러그인 비밀 | 사용자(관리 설정 UI) | 암호화된 데이터베이스 설정 | 일치하는 암호화 키를 복원하거나 값을 다시 입력 |
| CLI 자격 증명 | emdash login / emdash plugin publish 디바이스 플로우 | ~/.config/emdash/auth.json(모드 0600) | 디바이스 플로우를 다시 실행 |
| 레지스트리 CLI 자격 증명 | emdash-plugin atproto OAuth | ~/.emdash/oauth/, ~/.emdash/credentials.json(모드 0600) | 다시 로그인. 신원은 PDS에 있음 |
암호화 키
EMDASH_ENCRYPTION_KEY는 type: "secret"으로 선언된 플러그인 설정을 암호화합니다. EmDash는 플러그인 ID와 설정 키를 인증 데이터로 하는 AES-GCM을 사용합니다. 잘못된 형식의 값은 운영자용 시작 메시지를 내고, 암호화된 플러그인 설정이 필요한 작업은 fail closed 합니다. 관련 없는 사이트 요청은 계속 동작합니다.
다음 명령은 올바른 형식의 값을 생성합니다. 런타임 환경에 저장하거나, 배포가 이 변수를 쓰면 Worker 비밀로 저장하세요.
npx emdash secrets generate
# emdash_enc_v1_<43 base64url chars>
# Cloudflare:
wrangler secret put EMDASH_ENCRYPTION_KEY
형식은 emdash_enc_v1_ 뒤에 패딩 없는 base64url의 32 랜덤 바이트입니다. 값은 운영자가 제공하며 데이터베이스에 저장되지 않습니다. 비밀 관리자와 별도의 복구 백업에 보관하세요.
키를 교체하려면 새 값을 앞에 두고 쉼표 뒤에 이전 값을 유지하세요.
EMDASH_ENCRYPTION_KEY=emdash_enc_v1_<new-key>,emdash_enc_v1_<old-key>
EmDash는 새 값과 다시 저장한 값을 첫 번째 키로 암호화합니다. 읽기에는 저장된 kid 지문으로 이전 키를 선택합니다. 이전 키를 제거하기 전에 모든 플러그인 비밀을 다시 저장하고, 새 키만 있는 배포에서 해당 연동을 검증하세요. EmDash는 현재 어떤 키 ID가 아직 사용되는지 보고하지 않으므로, 다시 저장한 자격 증명 목록을 유지하고 각 연동이 그 검증을 통과할 때까지 이전 키를 제거하지 마세요.
생성된 사이트 비밀
두 비밀은 첫 사용 시 자동 생성되어 options 테이블에 유지되므로 요청, 배포, isolate를 걸쳐 안정적입니다. 생성은 원자적입니다. 동시 콜드 스타트는 한 값으로 수렴합니다.
미리보기 비밀
미리보기 URL에 서명합니다(HMAC). emdash:preview_secret으로 저장. 32 랜덤 바이트, base64url.
- 재정의: 여러 프로세스에서 같은 비밀이 필요하거나 감사를 위해 고정하려면
EMDASH_PREVIEW_SECRET(레거시 별칭:PREVIEW_SECRET)을 설정하세요. 환경이 항상 저장 값보다 우선합니다. - 교체:
emdash:preview_secret행을 삭제하거나 환경 변수를 바꾼 뒤 재배포하세요. 영향: 이전에 발급된 미리보기 링크가 검증에 실패합니다. 다른 것은 깨지지 않습니다. 다음 미리보기 요청에서 새 비밀이 생성(또는 환경에서 읽힘)됩니다. - 분실 시: 복구 불가능한 것은 없습니다. 미리보기 링크는 설계상 수명이 짧습니다.
미리보기 URL 구성과 검증은 미리보기 가이드를 보세요.
IP 솔트
댓글 속도 제한에 쓰는 댓글 작성자 IP 주소의 SHA-256 해시(ip_hash)에 솔트를 넣습니다. emdash:ip_salt으로 저장. 사이트별이므로 해시는 EmDash 설치 간에 상관할 수 없습니다.
- 재정의:
EMDASH_IP_SALT를 설정하세요. 하위 호환을 위해EMDASH_AUTH_SECRET/AUTH_SECRET도 참조합니다. 과거 그것으로 솔트를 유도한 설치는 안정적인 해시를 유지합니다. - 교체: 환경 변수를 바꾸거나
emdash:ip_salt행을 삭제하세요. 영향: 새 댓글 제출이 다른 값으로 해시되어 모든 사람의 속도 제한 집계가 재시작됩니다. 기존 댓글과 저장된 해시는 건드리지 않습니다. - 분실 시: 데이터 손실 없음. 속도 제한 연속성만 재설정됩니다.
세션 및 API 토큰
- 세션은 Astro 세션 저장소를 사용합니다(Cloudflare에서는 Workers KV, Node에서는 파일시스템). 쿠키는 불투명 세션 ID를 담으며 관리할 서명 비밀이 없습니다. 로그아웃으로 세션을 끝내거나, 세션 저장소(예: KV 네임스페이스)를 비워 모두 다시 로그인하게 하세요.
- API 토큰(접두사
ec_pat_,ec_oat_,ec_ort_)은 불투명 256비트 난수입니다. SHA-256 해시만 저장됩니다. 평문은 생성 시 한 번만 표시됩니다. 관리자에서 철회 후 재생성으로 교체하세요. - 초대, 매직 링크, 복구 토큰은 단일 목적이고
auth_tokens에 SHA-256 해시로 저장되며 시간 제한이 있습니다(초대 7일, 매직 링크 15분).
선제적으로 백업하거나 교체할 것은 없습니다. 데이터베이스 유출은 해시만 노출하며, 모든 토큰은 관리자에서 철회하거나 재발급할 수 있습니다.
사용자가 제공하는 서비스 자격 증명
외부 서비스 자격 증명은 환경에서 읽히며 데이터베이스에 쓰지 않습니다. 제공자에서 교체하고, 변수를 업데이트한 뒤 재배포하세요.
| 서비스 | 변수 |
|---|---|
| Google 로그인 | EMDASH_OAUTH_GOOGLE_CLIENT_ID, EMDASH_OAUTH_GOOGLE_CLIENT_SECRET(또는 접두사 없는 별칭) |
| GitHub 로그인 | EMDASH_OAUTH_GITHUB_CLIENT_ID, EMDASH_OAUTH_GITHUB_CLIENT_SECRET(또는 접두사 없는 별칭) |
| Marketplace 게시(CI) | EMDASH_MARKETPLACE_TOKEN |
| Turnstile(댓글) | EMDASH_TURNSTILE_SECRET_KEY(또는 TURNSTILE_SECRET_KEY) |
| S3 호환 스토리지 | S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, S3_BUCKET, S3_REGION |
Cloudflare에서는 wrangler secret put으로 설정하세요. 로컬 개발에서는 .env에 넣으세요. Wrangler는 .dev.vars 또는 .env 중 하나만 읽으며, 있으면 .dev.vars가 우선합니다. 바인딩을 통한 R2는 바인딩이 런타임 접근을 주므로 액세스 키 변수가 필요 없습니다. 미디어 스토리지를 보세요.
플러그인 비밀
플러그인이 type: "secret"으로 선언하는 설정(이메일 제공자 API 키, 양식 CAPTCHA 등)은 관리 UI에 입력되고 options 테이블의 plugin:<id>:settings:<key> 아래에 암호화됩니다. 일치하는 암호화 키가 없어도 관리자는 비밀이 설정되었는지만 받으므로, 관리자가 읽을 수 없는 자격 증명을 교체할 수 있습니다. 플러그인 코드는 격리된 런타임 안 ctx.settings로 평문을 읽습니다. 선언된 설정 스키마 밖에 저장된 값(임의 플러그인 KV 및 상태 항목 포함)은 이 암호화 경로를 쓰지 않습니다.
- 자격 증명 교체: 제공자에서 자격 증명을 교체하고 새 값을 플러그인 설정 페이지에 붙여넣으세요. 저장은 새 암호화 봉투를 씁니다.
- 평문 마이그레이션: 이전 EmDash 릴리스에 저장된 비밀은 계속 읽을 수 있습니다. 각 값을 다시 저장해 암호화하세요.
- 암호화 키를 잃은 경우: 별도 키 백업에서 일치하는
EMDASH_ENCRYPTION_KEY를 복원하세요. 복사본이 없으면 영향받는 각 자격 증명을 제공자에서 교체하고, 새 암호화 키를 구성한 뒤 대체 값을 입력하세요.
CLI 자격 증명
emdash CLI는 두 종류의 자격 증명을 ~/.config/emdash/auth.json(XDG_CONFIG_HOME 준수)에 소유자 전용 권한(0600)으로 보관합니다.
- 사이트 토큰 —
emdash login은 OAuth 디바이스 플로우로 EmDash 인스턴스에 인증하고, 결과 토큰을 인스턴스 URL로 키를 잡아 저장합니다.emdash logout이 제거합니다. 호출마다--token또는EMDASH_TOKEN이 저장 토큰을 덮어씁니다. - Marketplace 토큰 —
emdash plugin publish는 GitHub 디바이스 플로우로 EmDash Marketplace에 인증하고, 결과 JWT를marketplace:<origin>으로 키를 잡아 저장합니다. CI 게시에서는 대신EMDASH_MARKETPLACE_TOKEN을 설정하세요. 저장된 자격 증명보다 우선합니다.
파일을 잃어도 해롭지 않습니다. emdash login(또는 디바이스 플로우를 다시 실행하는 emdash plugin publish)을 다시 실행하세요.
플러그인 레지스트리 CLI 자격 증명
별도 emdash-plugin CLI(패키지 @emdash-cms/plugin-cli)는 실험적 AT Protocol 레지스트리를 대상으로 합니다. 그곳 게시는 AT Protocol 신원(게시자 DID)에 묶입니다. 사이트 자체는 게시 자격 증명을 갖지 않으며, 설치는 해당 DID에 귀속된 릴리스 레코드의 체크섬으로 아티팩트를 검증합니다.
- atproto OAuth로 인증합니다. OAuth 세션/상태 blob은
~/.emdash/oauth/에 있고, 게시자 신원(DID, 핸들, PDS)은~/.emdash/credentials.json에 캐시됩니다. 둘 다 소유자 전용 권한으로 씁니다. - CI에서는
EMDASH_PUBLISHER_DID,EMDASH_PUBLISHER_HANDLE,EMDASH_PUBLISHER_PDS로 신원을 제공하세요.EMDASH_REGISTRY_URL은 레지스트리 호스트를 재정의합니다. CI에서 자동publish는 러너의~/.emdash/oauth/OAuth 세션 파일이 여전히 필요합니다. 환경 변수만으로는 OAuth 세션을 전달하지 않습니다. - 게시 접근 교체 또는 철회는 AT Protocol 계정(예: 앱 비밀번호)에서 하며 EmDash 안이 아닙니다. Atmosphere 인증을 보세요.
교체 빠른 참조
| 하고 싶은 일 | 이렇게 하세요 |
|---|---|
| 플러그인 설정 암호화 교체 | 새 키를 앞에 두고, 플러그인 비밀을 다시 저장한 뒤 이전 키 제거 |
| 모든 미리보기 링크 무효화 | emdash:preview_secret 옵션 행 삭제(또는 환경 재정의 변경) |
| 댓글 속도 제한 해싱 재설정 | EMDASH_IP_SALT 변경(또는 emdash:ip_salt 옵션 행 삭제) |
| 유출된 API 토큰 철회 | Admin → Users → API tokens → 철회 후 대체 생성 |
| 모든 세션 종료 | 세션 저장소 비우기(Workers KV 네임스페이스 / 세션 디렉터리) |
| 제공자 자격 증명 교체 | 제공자에서 교체, 환경 변수 업데이트, 재배포 |
| 플러그인 API 키 교체 | 제공자에서 교체, 플러그인 관리 설정에 다시 입력 |