Bluehair Today 유지·보수 인덱스#
범위: Better Auth 기반 다중 사용자 운영 버전과 2026-07-24에 배포·실기기 검증한 Android 위젯 기기 인증. 비밀값·세션·OAuth 코드·D1 export 파일을 issue, PR, 로그, 문서, 채팅에 넣지 마세요.
목차#
- 구성도
- 작업공간 격리와 데이터베이스
- 인증과 비밀값
- 기존 개인 시간표 소유권 이전
- 배포 순서
- 롤백과 복구
- ChatGPT MCP OAuth
- Android 위젯 인증과 동기화
- 디버깅과 QA
- 용량·확장·보안
- 확장 지점
구성도#
Browser / Windows / Android
│ Better Auth session cookie
▼
today.bluehair.blue (primary Worker)
├─ Better Auth + Passkey + Turnstile
├─ D1: account + workspace + timetable rows
└─ Durable Object: one WebSocket hub per workspace
ChatGPT ── OAuth ──► mcp.bluehair.blue (MCP Worker)
│ private Service Binding + signed actor assertion
▼
primary Worker internal endpoint
The primary Worker is the only writer for timetable tables, revisions, and Durable Object invalidation. The MCP Worker has no D1 binding; it cannot bypass the primary Worker’s validation or workspace isolation.
작업공간 격리와 데이터베이스#
migrations/0002_multitenant_auth.sql adds the Better Auth tables and changes
the former single-owner timetable into tenant-scoped rows.
| 영역 | 주요 테이블 / 규칙 |
|---|---|
| 계정 | user, session, account, verification, passkey, rateLimit |
| 작업공간 | workspaces, workspace_members; 신규 계정은 personal-<userId> 소유 작업공간을 자동 생성 |
| 시간표 | schedule_items, one_off_tasks, checklist_items, checklist_completions, sync_meta 모두 workspace_id 사용 |
| 실시간 | 작업공간 ID별 Durable Object hub. 한 작업공간의 invalidate는 다른 작업공간으로 전송되지 않음 |
| 이전 데이터 | 예전 default 행은 legacy-workspace로 옮겨지고, 소유권을 받을 때까지 로그인하지 않은 방문자에게도 보이지 않음 |
서버는 세션 또는 검증된 내부 actor assertion에서만 userId와 workspaceId를
정합니다. API body, query, localStorage, WebSocket URL로 작업공간을 지정하도록
바꾸지 마세요. 모든 SQL에는 workspace_id = ?가 있어야 하며, 업데이트·삭제 전
ensureExists도 같은 작업공간으로 조회해야 합니다.
기본 D1 quota trigger는 작업공간당 반복 일정 500개, 일회성 할 일 5,000개,
체크리스트 500개입니다. 초과 시 API는 409 QUOTA_EXCEEDED를 반환합니다.
인증과 비밀값#
| 값 | 위치 | 용도 | 교체 시 주의 |
|---|---|---|---|
AUTH_SECRET |
primary Worker secret | Better Auth 세션·서명 | 교체하면 기존 세션 영향 여부를 QA하고 사용자 재로그인을 안내 |
MCP_SERVICE_SECRET |
두 Worker 모두 동일한 secret | private ticket consume 및 actor assertion HMAC | 두 Worker를 함께 교체하고 MCP 연결을 재검증 |
OWNER_BOOTSTRAP_TOKEN |
primary Worker secret | 한 번의 기존 데이터 소유권 이전 | 사용자에게 일회성 안전 채널로만 전달, claim 후 교체/폐기 |
OAUTH_COOKIE_SECRET |
MCP Worker secret | OAuth 승인 보류 쿠키 서명 | MCP 로그인 진행 중인 세션이 무효화될 수 있음 |
TURNSTILE_SECRET_KEY |
primary Worker secret | 공개 가입/비밀번호 로그인 bot 검증 | TURNSTILE_SITE_KEY와 짝으로 설정 |
TURNSTILE_SITE_KEY |
primary Worker var | 브라우저 Turnstile 위젯 site key | 공개값이지만 올바른 사이트용 key인지 확인 |
관련 비밀값은 Wrangler에서 write-only입니다. 값을 "확인"하려고 새 값으로 덮어쓰지
마세요. 새 Worker 또는 계획된 회전에서만 wrangler secret put <NAME>으로 설정하고,
두 Worker의 공유 secret은 같은 변경 창에서 갱신합니다.
중요한 vars는 다음과 같습니다.
AUTH_BASE_URL=https://today.bluehair.bluePASSKEY_RP_ID=today.bluehair.blueMCP_ORIGIN=https://mcp.bluehair.blueREGISTRATION_MODE=bootstrap | public | closedENVIRONMENT=production
APP_TOKEN과 ACCESS_MODE는 이 설계에서 사용자 인증 수단이 아닙니다. 클라이언트
설정, 문서, Android/Windows provisioning, OAuth 페이지에 추가하지 마세요.
Android 위젯 인증과 동기화#
현재 상태:
0003_widget_devices.sql, 호환 primary Worker, Android0.3.0(code 2)을 함께 배포했고 SM-S928N/Android 16에서 로그인 보존, 기기 등록, 일정 추가·수정·삭제, Battery Saver 상태의 수동 갱신을 검증했습니다. 등록이 없거나 거부되면앱에서 확인이 필요합니다를 표시하는 fail-closed 동작은 계속 정상입니다.
위협 모델과 인증 경로#
Android WebView의 Better Auth 세션 쿠키는 HttpOnly입니다. 이를 SharedPreferences,
위젯 RemoteViews, JavaScript bridge 또는 별도 bearer token으로 복사하면 앱 백업·로그·다른
프로세스 노출 시 브라우저 세션까지 탈취될 수 있습니다. 그러므로 위젯은 WebView 쿠키를
읽거나 재사용하지 않습니다.
인증된 앱을 한 번 열면 위젯을 아직 추가하지 않았더라도, 앱은 Android Keystore 안에서만 보관되는
RSA 개인 키를 만들고 공개 키(SPKI)와 서명된 등록 증명만 primary Worker에 보냅니다.
private key, browser cookie, 비밀번호, Worker secret은 서버·D1·로그에 저장하지 않습니다.
서버는 기기 ID, 사용자·작업공간 소속, 공개 키 지문, 만료 시각, 마지막 사용 시각,
해제 상태 및 발급 당시 authGeneration만 저장합니다.
각 snapshot 요청은 기기 ID만으로 승인되지 않습니다. 앱은 요청의 버전·기기 ID·HTTP
method/path·timestamp·무작위 nonce를 포함한 canonical 문자열에 RSA 서명합니다. Worker는
공개 키 서명, 짧은 시간 창, 미사용 nonce, 기기 해제/만료, 계정 상태, 현재
authGeneration, workspace membership를 모두 확인한 뒤 그 기기의 사용자 작업공간만
조회합니다. nonce 재사용은 D1의 만료 레코드로 거부합니다.
- 기기 등록은 기본 90일 후 만료합니다. 앱을 다시 열어 로그인·등록하면 새 키/등록으로 갱신할 수 있습니다.
- 한 계정의 활성 Android 위젯 기기는 최대 8대로 제한합니다. 한도에 걸리면 계정 설정에서 사용하지 않는 기기를 해제한 뒤 등록합니다.
/account의 Android 위젯 기기 목록에서 한 기기를 해제하면 해당 기기만 즉시 거부됩니다. 계정 연동 전체 해제로authGeneration이 바뀌면 모든 이전 기기 등록도 서버에서 거부됩니다.deviceId는 조회 키일 뿐 bearer credential이 아닙니다. 공개 키·서명·nonce 없이 snapshot을 읽을 수 없어야 합니다.
기기 등록/관리에는 인증된 same-origin 브라우저 세션이 필요합니다. 구현된 API 경로는 다음과 같으며, 외부 공개 API나 서드파티 앱용 인증 수단으로 노출하지 않습니다.
| 경로 | 인증 | 운영 용도 |
|---|---|---|
POST /api/widget/devices |
Better Auth 세션 + 등록 증명 서명 | 현재 Android 기기 공개 키 등록 |
GET /api/widget/devices |
Better Auth 세션 | 계정의 활성 위젯 기기 목록 |
DELETE /api/widget/devices/:id |
Better Auth 세션 + 소유자 범위 | 특정 분실·교체 기기 해제 |
GET /api/widget/snapshot?date=YYYY-MM-DD |
기기 ID + RSA 서명 + timestamp + nonce | 그 기기의 오늘 위젯 데이터만 반환 |
서명 헤더, nonce, 공개 키, device ID 전체값, schedule 내용은 로그·analytics 차원·오류 메시지에 남기지 않습니다. 관찰에는 요청 결과, 안전하게 축약/해시한 상관 ID, 지연 시간, 거부 사유의 범주만 사용합니다.
동기화 기대치#
앱 로그인·앱 전면 복귀는 즉시 signed snapshot을 요청합니다. 위젯의 새로고침 아이콘은 홈 화면 위에 투명한 사용자 실행 activity를 잠깐 열어 Battery Saver가 일반 백그라운드 네트워크를 막아도 최신 revision을 가져온 뒤 자동으로 닫힙니다. 마지막 정상 화면은 앱 전용·백업 제외 캐시에 그 기기 등록 ID와 함께 최소 범위로 저장되며, 일시적 네트워크 오류나 더 늦게 도착한 낮은 revision으로 덮어쓰지 않습니다. 로그아웃·계정 전환 중이던 이전 요청도 시작 시 기기 ID가 더 이상 현재 ID와 같지 않으면 렌더링/캐시 저장을 건너뜁니다. PC, Android 앱, ChatGPT에서 변경된 일정은 서버 revision에 반영되지만, 아무 조작 없이 닫힌 Android 위젯은 WorkManager의 최소 약 15분 주기로 이를 확인합니다. Android Doze, 절전, 데이터 절약, 네트워크 없음, 제조사 백그라운드 제한에서는 더 늦을 수 있습니다.
따라서 FCM 없이 "다른 기기에서 수정한 모든 변경을 닫힌 위젯에 즉시 표시"한다고 문서·마케팅·UI에서 약속하지 마세요. 그 요구가 필요하면 사용자별 FCM registration token, 서버 발송 권한, 알림 수신·재동기화, 토큰 회전/해제, 전송 실패 관찰을 별도 기능으로 설계하고 QA합니다.
기존 개인 시간표 소유권 이전#
bootstrap은 최초 소유자만 안전하게 기존 legacy-workspace를 가져오는 모드입니다.
- 원격 DB 백업을 먼저 만듭니다.
OWNER_BOOTSTRAP_TOKEN을 생성해 primary Worker secret으로 설정합니다.REGISTRATION_MODE=bootstrap으로 primary Worker를 배포합니다.- 소유자가 가입 화면에서 그 일회성 코드를 입력해 계정을 만듭니다. 생성 hook이 개인 작업공간을 만든 뒤 legacy 행을 그 작업공간으로 원자적으로 이동합니다.
- 로그인 후 snapshot의 항목 수와 과거 revision을 확인합니다.
- legacy가 삭제되어 claim이 불가능해진 것을 확인하고 bootstrap secret을 회전하거나
폐기합니다. 그 뒤에만
public또는closed모드를 선택합니다.
/api/bootstrap/claim을 손으로 반복 호출하거나 legacy 행을 SQL로 다른 사용자에게
복사하지 마세요. 이미 claim된 상태에서 재시도하면 데이터를 섞을 위험이 있습니다.
배포 순서#
로컬/사전 검증#
pnpm install
pnpm web:build
pnpm db:migrate:local
pnpm types
pnpm typecheck
pnpm test
pnpm docs:check
pnpm check
Set-Location integrations/chatgpt-mcp
pnpm install
pnpm types
pnpm typecheck
pnpm test
pnpm check
seed.sql은 이전 단일 작업공간 baseline fixture입니다. migration 0002가 적용된
공유 원격 DB에 실행하지 마세요. 새 사용자 데이터는 가입 및 앱 API로 생성하고,
대량 import는 별도 workspace-scoped migration/관리 도구를 검토해 추가합니다.
원격 전환#
유지보수 창을 잡고, 현재 Worker 버전 ID와 DB migration 상태를 기록합니다.
원격 D1을 export해 접근 제한된 위치에 보관합니다.
pnpm wrangler d1 export timetable --remote --output .\backup-before-0002.sqlprimary와 MCP의 필요한 secrets를 먼저 설정합니다. shared
MCP_SERVICE_SECRET은 두 Worker에서 동일해야 합니다.staging 또는 빈 DB에서
0001→0002migration과 두 계정 격리 QA를 끝낸 뒤, 원격 migration을 적용합니다.pnpm wrangler d1 migrations apply timetable --remote즉시 primary Worker를
bootstrap모드로 배포합니다. migration0002뒤에는 pre-0002 Worker를 계속 서비스하면 안 됩니다.pnpm deploy:dry-run pnpm wrangler deploy소유자 가입·legacy claim·세션·Passkey·두 계정 격리를 QA합니다.
기존 익명 MCP OAuth grant를 무효화/정리한 후 MCP Worker를 배포합니다.
Set-Location integrations/chatgpt-mcp pnpm wrangler deploy --dry-run pnpm wrangler deploy공개 가입을 열 때만 Turnstile secret/site key를 실제 도메인용으로 모두 설정하고
REGISTRATION_MODE=public으로 다시 배포합니다. 둘 중 하나라도 없으면 primary는 fail-closed로 가입을 거부해야 합니다.QA와 production smoke test를 통과한 뒤 유지보수 창을 종료합니다.
Android 위젯 기기 인증 릴리스#
기기 인증은 사용자 데이터가 있는 D1에 새 행을 추가합니다. Worker 코드와 앱만 먼저 배포해 기존 WebView 쿠키를 위젯으로 전달하는 우회책을 만들지 마세요. 다음 순서를 사용합니다.
로컬에서 migration, Worker typecheck/test, Android unit/build를 먼저 통과시킵니다. 새 기기 등록·정상 snapshot·잘못된 서명·nonce 재사용·만료·다른 사용자 접근·개별 해제·
authGeneration무효화를 포함해야 합니다.현재 Worker version, 적용 migration 목록, 배포할 commit을 기록하고 접근 제한된 경로에 원격 D1 export를 만듭니다. export 파일은 민감한 계정·시간표 데이터이므로 저장소, PR, 첨부파일에 넣지 않습니다.
$backupDir = Join-Path $env:LOCALAPPDATA 'BluehairToday\Backups' New-Item -ItemType Directory -Force -Path $backupDir | Out-Null $backupPath = Join-Path $backupDir 'timetable-before-widget-migration.sql' pnpm wrangler d1 export timetable --remote --output $backupPath Get-FileHash -LiteralPath $backupPath -Algorithm SHA256pnpm wrangler d1 migrations list timetable --remote로 적용 상태를 먼저 확인합니다.0003_widget_devices.sql은 2026-07-24에 이미 적용됐으므로 재실행하거나 수동 SQL로 흉내 내지 않습니다. 새 migration이 실제로 pending일 때만 remote D1에 적용합니다.pnpm wrangler d1 migrations apply timetable --remotemigration schema와 호환되는 primary Worker를 배포하고
/api/widget/*의 인증 거부 경로부터 smoke test 합니다. 그런 뒤 Android 앱을 빌드·설치합니다. 앱 업데이트에는 기존 WebView 로그인 세션을 지우는adb uninstall대신 우선adb install -r을 사용합니다.실제 로그인 기기에서 등록과 위젯 표시를 확인하고, 두 번째 사용자 또는 disposable 검증 계정으로 데이터 격리/기기 해제를 확인합니다. 기존 사용자에게 기능을 안내하기 전에는 수동 새로고침·백그라운드 지연·계정 전체 연동 해제까지 기록합니다.
이 순서는 migration의 자동 Cloudflare backup을 대체하지 않습니다. 명시적 export와 hash, Cloudflare backup, 적용 전 Worker version을 모두 보관합니다.
최초 0003 적용 전 export는
%LOCALAPPDATA%\BluehairToday\Backups\timetable-before-0003-20260724-094137.sql,
SHA-256은
88B957C323D56DEFE9B047A320CE67F5A42EE6498E0B2067FE1D8BEDA8FB2C24입니다.
롤백과 복구#
0002는 스키마와 데이터 위치를 바꾸므로, migration 전 코드로의 즉시 Worker
rollback은 안전하지 않습니다. wrangler rollback은 현재 DB와 호환되는 Worker
version일 때만 사용합니다.
장애 시 우선 순서는 다음과 같습니다.
- 새 사용자 가입/쓰기와 MCP 연결을 중지하거나
REGISTRATION_MODE=closed로 제한합니다. - 원인 로그, Worker version, 적용된 migration, 영향 작업공간을 보존합니다.
- 현재 schema와 호환되는 직전 Worker version으로만 rollback합니다.
- DB 복원은 export와 복원 절차를 staging에서 먼저 검증하고, 소유자 승인 아래에서만 수행합니다. "테이블 하나만" 즉흥 수정하지 마세요.
적용 전 export와 migration의 Cloudflare 자동 backup은 모두 보관합니다. 비밀값 유출은 DB rollback 문제가 아니므로 해당 secret을 회전하고 세션/OAuth grant를 무효화합니다.
Android 위젯 migration은 테이블/인덱스를 추가하는 방향이어야 합니다. 위젯 장애가 나도
운영 DB에서 기기 테이블을 즉시 drop하거나 nonce 기록을 수동 일괄 삭제하지 마세요.
현재 schema와 호환되는 Worker로 되돌리거나 문제 endpoint를 fail-closed로 막고, 계정별
기기 해제 또는 authGeneration 갱신으로 접근을 끊습니다. 데이터 복원은 export를 staging에
복원해 검증하고 소유자 승인 아래에서만 수행합니다.
ChatGPT MCP OAuth#
- ChatGPT가 MCP
/authorize를 시작하면 MCP Worker는 scope와 PKCE OAuth 요청을 검증하고 primary/connect/chatgpt로 보냅니다. - 사용자는 primary Worker에서 자기 계정으로 로그인합니다.
- primary Worker는 hash만 저장한 짧은 수명의 1회용 ticket을 만든 뒤, private Service Binding으로만 consume하게 합니다.
- MCP Worker는
version: 2,userId,workspaceId,authGeneration, granted scopes를 OAuth grant props에 암호화해 보관합니다. - 각 tool call은 짧은 수명의 HMAC actor assertion으로 primary Worker에 전달됩니다. MCP Worker는 granted scope를 확인하고, primary는 signature, method/path/body hash, expiry, replay nonce, 사용자 status, auth generation, workspace membership를 다시 검증합니다.
OAuth grant나 refresh token을 로그에 남기지 마세요. 비밀번호 변경, 계정 중지,
연결 해제 정책을 추가할 때는 authGeneration을 올리고 해당 사용자의 OAuth grant를
무효화하는 경로를 함께 테스트합니다. mcp_login_tickets와
internal_request_nonces의 expiry 정리는 유지하되, 정상적인 재시도 ticket을 너무
일찍 삭제하지 않도록 관찰합니다.
디버깅과 QA#
필수 기능 QA#
- 익명
GET /은 소개/로그인 화면을, 익명/app과/api/snapshot은 로그인 흐름 또는401을 반환한다. - 사용자 A와 B를 생성한다. 각각 같은 제목의 항목을 만들어도 snapshot과 revision이
섞이지 않아야 하며 A가 B의 ID를 수정/삭제하면
404여야 한다. - A의 두 브라우저에서 한쪽을 수정해 다른 쪽이 workspace별 WebSocket invalidation과 catch-up으로 갱신되는지 확인한다.
- 비밀번호 로그인, Passkey 등록, Passkey 로그인, 로그아웃 후 세션 거부를 확인한다.
- ChatGPT OAuth에서 account ticket 재사용·변조, 만료 actor assertion, 예전
authGenerationgrant가 거부되는지 확인한다. - quota 직전/초과에서
409 QUOTA_EXCEEDED가 나오고 다른 작업공간에는 영향이 없는지 확인한다. - Android 위젯은 로그인 뒤 기기 등록을 한 번만 만들고, 동일 기기에서 새로고침할 때 올바른 사용자 작업공간의 현재/다음 일정만 표시하는지 확인한다.
- 동일한 signed request의 nonce 재사용, 수정된 timestamp/path/signature, 만료된 기기, 다른 사용자의 device ID, session cookie만 가진 unsigned 요청이 모두 거부되는지 확인한다.
- 계정 설정에서 한 기기를 해제하면 그 기기만 실패하고 다른 등록 기기는 계속 동작하는지,
전체 연동 해제 후 이전
authGeneration의 모든 위젯 요청이 실패하는지 확인한다. - PC 또는 ChatGPT에서 수정한 뒤 위젯 수동 새로고침은 즉시 revision을 가져오는지, 닫힌 앱은 약 15분 주기와 Doze 지연이라는 안내와 일치하는지 확인한다.
관찰과 빠른 진단#
- Cloudflare Workers Logs/Observability에서 request ID, status, 오류 code만 중심으로 본다. email, password, cookie, ticket, assertion, OAuth code는 기록하지 않는다.
401: 세션 만료/도메인 cookie 문제/계정 status를 확인한다.403 CSRF_REJECTED: origin이today.bluehair.blue와 일치하는지 확인한다.503 REGISTRATION_NOT_READY: production Turnstile secret/site key가 모두 있는지 확인한다.INVALID_MCP_TICKET: ticket 만료·재사용·handoff ID 불일치 또는 두 Worker의MCP_SERVICE_SECRET불일치를 확인한다.QUOTA_EXCEEDED: 해당 작업공간 항목 수를 확인하고, 무제한 상향 대신 요금제·보존 정책·인덱스 영향부터 검토한다.- Android 위젯
401/403은 앱 로그인 쿠키를 위젯에 복사하라는 신호가 아니다. 등록 해제·90일 만료·authGeneration변경·서명 실패·기기 시간 오차를 분리해 확인하고, 필요한 경우 앱을 열어 재로그인/재등록한다. - Android 위젯
429은 반복 새로고침을 중단하고 다음 백그라운드 주기를 기다린다. 요청 제한을 높이기 전에는 정상 재시도, launcher 중복 broadcast, 서비스 장애를 먼저 확인한다.
Android 위젯 현장 진단#
다음 명령은 연결된 Android 기기에서 상태를 읽거나 위젯의 정상 새로고침 broadcast를 요청합니다. 로그인 cookie, private key, public key, signature, nonce, schedule JSON을 복사하거나 출력하지 마세요.
adb devices -l
adb shell dumpsys appwidget | Select-String 'blue.bluehair.today|Bluehair Today'
adb shell am broadcast -n blue.bluehair.today/.TimetableWidgetProvider `
-a blue.bluehair.today.action.REFRESH_WIDGET
adb logcat -d -v threadtime | Select-String 'blue.bluehair.today|Widget|WorkManager'
# Worker의 상태 코드·지연만 관찰한다. 인증 헤더를 넣어 재현하지 않는다.
pnpm wrangler tail timetable --format pretty
진단 순서는 (1) 앱 버전과 기기 시간이 자동 설정인지, (2) 앱에서 동일 계정으로 로그인했는지,
(3) 홈 화면에 실제 위젯 인스턴스가 있는지, (4) 네트워크·배터리 절약·Doze 제한, (5) 계정
설정의 기기 해제/90일 만료, (6) Worker Logs의 HTTP status와 latency입니다. am broadcast는
등록이나 서명을 대신하지 않습니다. 기기를 분실했거나 공개된 로그에서 인증 재료가 의심되면
먼저 계정 설정에서 기기를 해제하고, 필요하면 전체 연동 해제로 authGeneration을 변경합니다.
용량·확장·보안#
- D1에는 workspace-first indexes가 있고 revision/실시간 hub도 workspace 단위다. 사용자가 늘어도 전역 broadcast나 전역 snapshot scan을 추가하지 않는다.
- 정적 asset은 캐시할 수 있지만
/,/app,/login,/signup,/connect/chatgpt,/api/*,/ws는 개인화되어no-store여야 한다. Service Worker도 navigation/API를 캐시하면 안 된다. - Better Auth DB rate limit과 Turnstile은 가입/비밀번호 로그인 방어층이다. 공개 전에는 Cloudflare WAF/managed rules, rate limit rules, abuse alert와 account suspension 운영 절차를 추가로 검토한다.
- CSP, HSTS, HttpOnly/Secure/SameSite cookie, same-origin mutation 검사, 요청 본문 크기 제한을 약화시키지 않는다. Passkey RP ID와 origin은 production domain에 고정한다.
- Android widget snapshot은 일반 browser session API와 분리된 기기 서명 경로다. D1 조회는 등록된 기기의 사용자·workspace로만 범위를 정하고, 요청 query의 workspace ID나 device ID만 신뢰하지 않는다. 공개 키를 bearer token처럼 취급하거나 WebView cookie를 bridge로 넘기지 않는다.
- 기기별 rate limit, nonce 만료 정리, 활성 기기 수 상한(기본 8), 90일 만료, 개별 해제와
authGeneration전체 무효화는 함께 유지한다. 위젯 요청이 늘어도 전 계정 snapshot scan, 전역 push, 무제한 nonce 보관을 추가하지 않는다. - 대량 이메일 복구/알림은 아직 연결하지 않았다. 이메일 공급자, verification, recovery-rate limit, audit log, privacy policy가 준비되기 전에는 "비밀번호를 잊음" 버튼만 추가하지 않는다.
확장 지점#
| 목표 | 시작 지점 | 반드시 함께 바꿀 것 |
|---|---|---|
| 팀 공유 작업공간 | workspaces, workspace_members |
role model, invite/accept flow, 모든 SQL·MCP principal의 선택 workspace 검증 |
| 계정 설정/연결 해제 UI | /api/account/revoke-integrations |
authGeneration, OAuth KV grant revoke, 회귀 테스트 |
| 이메일 복구 | Better Auth email flow + provider | verification table, anti-enumeration, Turnstile/rate limit, session revoke, 개인정보 고지 |
| Android widget 인증 | widget_devices, nonce replay table, Android Keystore signer |
cookie bridge 금지, 서명/nonce/90일 만료/8대 한도/개별 해제/authGeneration 회귀 QA |
| 닫힌 위젯의 즉시 반영 | FCM + Android background sync | 사용자별 push token, 재동기화 서명, token 회전·해제, Doze/실패 관찰, 개인정보 고지 |
| Windows 배포 | windows/ |
/app 로그인 flow, WebView cookie lifecycle, secret-free packaging |
| 대량 import/export | 새 관리용 Worker/queue | workspace authorization, schema validation, audit trail, backpressure |
| 관찰성 | Workers Logs/Analytics Engine | PII redaction, request correlation, retention, alert runbook |
세부 파일 소유 관계는 Architecture, 다음 작업 인수인계는 Handoff, 최종 사용자 흐름은 사용 가이드를 참조하세요.