@sena-ai/platform-core 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sena-ai/platform-core",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
package/specs/auth.md ADDED
@@ -0,0 +1,84 @@
1
+ # Auth & Session
2
+
3
+ ## 한 줄 요약
4
+
5
+ 관리 웹 UI 접근을 Slack OpenID Connect 로그인과 Vault 암호화 세션 쿠키로 보호한다.
6
+
7
+ ## 상위 스펙 연결
8
+
9
+ - Related Requirements: `PLATFORM-FR-001`, `PLATFORM-FR-004`, `PLATFORM-FR-005`
10
+ - Related AC: `PLATFORM-AC-001`, `PLATFORM-AC-004`, `PLATFORM-AC-005`
11
+
12
+ ## Behavior
13
+
14
+ ### `PLATFORM-AUTH-01` 초기 설정과 접근 제어
15
+
16
+ - Trigger: `/setup` 또는 보호된 라우트 접근
17
+ - Main Flow:
18
+ - 로그인 앱 설정이 없으면 setup 흐름으로 유도한다.
19
+ - 설정이 이미 있으면 허용된 워크스페이스 세션이 있는 사용자만 setup에 접근할 수 있다.
20
+
21
+ ### `PLATFORM-AUTH-02` Slack OpenID Connect 로그인
22
+
23
+ - Trigger: `/auth/login` -> `/auth/callback`
24
+ - Main Flow:
25
+ - authorize URL로 리다이렉트한다.
26
+ - state를 저장소에 만들고 10분 제한을 둔다.
27
+ - callback에서 state를 소비하고 token/userInfo를 조회한다.
28
+ - 허용 워크스페이스를 확인하고 세션 쿠키를 발급한다.
29
+
30
+ ### `PLATFORM-AUTH-03` 세션 검증과 로그아웃
31
+
32
+ - Trigger: 보호된 웹/API 라우트 접근 또는 `/auth/logout`
33
+ - Main Flow:
34
+ - 세션 쿠키를 Vault로 복호화해 구조와 만료를 검증한다.
35
+ - 사용자 workspace가 허용 목록에 속하면 컨텍스트에 user/session을 주입한다.
36
+ - 로그아웃 시 쿠키를 제거하고 로그인 페이지로 보낸다.
37
+
38
+ ## Constraints
39
+
40
+ - `PLATFORM-AUTH-C-001`: 세션 쿠키는 `httpOnly`, `secure`, `sameSite=Lax`, `path=/`를 유지해야 한다.
41
+ - `PLATFORM-AUTH-C-002`: state는 일회용이어야 하며 만료된 값은 허용하면 안 된다.
42
+ - `PLATFORM-AUTH-C-003`: 보호된 API 경로와 웹 경로는 실패 응답 형식이 구분돼야 한다.
43
+
44
+ ## Interface
45
+
46
+ - Routes:
47
+ - `GET /auth/login`
48
+ - `GET /auth/callback`
49
+ - `POST /auth/logout`
50
+ - `GET/POST /setup`, `/api/setup`
51
+ - Session Types:
52
+ - `AuthSession`
53
+ - `AuthSessionUser`
54
+ - Helper/API:
55
+ - `createAuthHandler(...)`
56
+ - `createAuthMiddleware(...)`
57
+ - `createSessionCookieValue(...)`
58
+ - `parseSessionCookieValue(...)`
59
+
60
+ ## Realization
61
+
62
+ - 모듈 경계:
63
+ - `auth/handler.ts`가 라우트와 middleware를 담당하고 `auth/session.ts`가 쿠키 직렬화를 맡는다.
64
+ - 상태 모델:
65
+ - OAuth state는 저장소에, 세션은 Vault 암호화 쿠키에 저장한다.
66
+ - 실패 처리:
67
+ - 인증 실패는 API JSON 또는 웹 redirect/error page로 분리해 응답한다.
68
+
69
+ ## Dependencies
70
+
71
+ - Depends On: `vault.md`, `database.md`, `runtime.md`
72
+ - Blocks: `web-ui.md`, 보호된 API
73
+ - Parallelizable With: `slack-integration.md`
74
+
75
+ ## AC
76
+
77
+ - Given 로그인 앱 설정이 없을 때 When 관리 UI에 접근하면 Then setup 페이지로 이동한다.
78
+ - Given 유효한 Slack 로그인 callback이 올 때 When state와 workspace 검증이 통과하면 Then 세션 쿠키가 발급된다.
79
+ - Given 보호된 API 경로 접근 시 세션이 없을 때 When 요청하면 Then JSON 에러와 redirect 정보가 반환된다.
80
+ ## 개편 메모
81
+
82
+ - AGENTS.md 가이드에 맞춰 상위/상세 스펙 섹션과 traceability를 정규화했다.
83
+ - 기존 구현 계약을 바꾸지 않고 문서 구조와 검증 기준을 명확히 재배치했다.
84
+
@@ -0,0 +1,72 @@
1
+ # Database
2
+
3
+ ## 한 줄 요약
4
+
5
+ 플랫폼 코어는 봇, 토큰, OAuth state, 워크스페이스 설정을 공통 Repository 계약으로 저장한다.
6
+
7
+ ## 상위 스펙 연결
8
+
9
+ - Related Requirements: `PLATFORM-FR-005`, `PLATFORM-FR-006`
10
+ - Related AC: `PLATFORM-AC-005`
11
+
12
+ ## Behavior
13
+
14
+ ### `PLATFORM-DB-01` 봇/토큰/설정 저장
15
+
16
+ - `bots`: 봇 메타데이터와 암호화된 Slack 자격 증명 저장
17
+ - `config_tokens`: 워크스페이스별 Config Token 저장
18
+ - `oauth_states`: OAuth state의 생성/소비/만료 정리
19
+ - `workspace_admin_config`: Slack 로그인 앱과 웹 API 설정 저장
20
+
21
+ ### `PLATFORM-DB-02` 공통 Repository 계약
22
+
23
+ - MySQL, PostgreSQL, D1 구현은 동일한 Repository 메서드 집합을 제공한다.
24
+
25
+ ### `PLATFORM-DB-03` 런타임별 DB 초기화
26
+
27
+ - Node.js는 MySQL을 기본 경로로 사용한다.
28
+ - PostgreSQL은 동일 계약의 확장 경로다.
29
+ - CF Workers는 D1 구현을 사용한다.
30
+
31
+ ## Constraints
32
+
33
+ - `PLATFORM-DB-C-001`: 민감 컬럼은 평문이 아닌 암호문 저장을 전제로 한다.
34
+ - `PLATFORM-DB-C-002`: state consume는 읽기 후 삭제 semantics를 가져야 한다.
35
+ - `PLATFORM-DB-C-003`: workspace별 설정은 workspace id 기준으로 격리되어야 한다.
36
+
37
+ ## Interface
38
+
39
+ - Repository Contracts:
40
+ - `BotRepository`
41
+ - `ConfigTokenRepository`
42
+ - `OAuthStateRepository`
43
+ - `WorkspaceAdminConfigRepository`
44
+ - DB Factories:
45
+ - `initMySQLDb`, `createMySQLRepositories`
46
+ - `initPostgreSQLDb`, `createPostgreSQLRepositories`
47
+ - `initD1`, `createD1Repositories`
48
+
49
+ ## Realization
50
+
51
+ - 모듈 경계:
52
+ - DB별 schema/index 구현이 동일한 Repository 인터페이스를 만족한다.
53
+ - 상태 모델:
54
+ - DB별 timestamp/enum/upsert 차이를 각 adapter 내부에 캡슐화한다.
55
+ - 마이그레이션:
56
+ - Node.js는 drizzle-kit, CF Workers는 SQL migration 파일을 사용한다.
57
+
58
+ ## Dependencies
59
+
60
+ - Depends On: Drizzle ORM, `vault.md`
61
+ - Blocks: `auth.md`, `slack-integration.md`, `web-ui.md`
62
+ - Parallelizable With: `runtime.md`
63
+
64
+ ## AC
65
+
66
+ - Given 봇 또는 토큰 데이터를 저장할 때 When Repository를 사용하면 Then 각 DB 구현이 동일한 메서드 계약을 제공한다.
67
+ - Given OAuth state를 소비할 때 When 동일 state를 다시 읽으려 하면 Then 일회용 semantics 때문에 실패한다.
68
+ ## 개편 메모
69
+
70
+ - AGENTS.md 가이드에 맞춰 상위/상세 스펙 섹션과 traceability를 정규화했다.
71
+ - 기존 구현 계약을 바꾸지 않고 문서 구조와 검증 기준을 명확히 재배치했다.
72
+
package/specs/index.md ADDED
@@ -0,0 +1,109 @@
1
+ # @sena-ai/platform-core
2
+
3
+ ## 한 줄 요약
4
+
5
+ 멀티테넌트 sena-ai 플랫폼의 웹 앱, Slack 프로비저닝, 인증, 릴레이, 비밀 저장, DB, 런타임 추상화를 하나의 코어 라이브러리로 제공한다.
6
+
7
+ ## 문제 정의
8
+
9
+ - 로컬 봇 런타임이 Slack 토큰 없이 플랫폼을 경유하도록 만들려면 릴레이/API 프록시, Vault, 인증, 프로비저닝이 함께 설계돼야 한다.
10
+ - Node.js와 Cloudflare Workers라는 서로 다른 런타임에서 동일한 플랫폼 계약을 유지하지 않으면 운영 경로가 분기된다.
11
+ - 관리자 UI, Slack OAuth, 이벤트 릴레이, DB 저장소가 따로 정의되면 워크스페이스 격리와 토큰 보안이 쉽게 깨질 수 있다.
12
+
13
+ ## 목표 & 성공 지표
14
+
15
+ - 플랫폼 핵심 로직을 `Platform` 인터페이스와 `createApp()` 조합으로 런타임 독립적으로 제공한다.
16
+ - Slack 봇 생성/설치/이벤트 수신/릴레이/API 프록시/관리 UI/인증이 하나의 패키지에서 추적 가능해야 한다.
17
+ - 완료 기준:
18
+ - 상위 스펙이 플랫폼의 목표, 경계, 보안 원칙, 런타임 독립성을 명시한다.
19
+ - 각 도메인 스펙이 상위 FR/NFR/AC를 참조한다.
20
+ - 실제 소스의 라우트, 저장소, 암호화, 릴레이 의미와 문서가 일치한다.
21
+
22
+ ## 스펙 안정성 분류
23
+
24
+ - `Stable`
25
+ - Zero Token Exposure 원칙
26
+ - Slack OAuth/Events/Relay/API Proxy의 외부 계약
27
+ - Vault 암호화 포맷과 워크스페이스 격리 규칙
28
+ - `Flexible`
29
+ - 관리 UI 렌더링 세부 구조, 로그 문구, 부트스트랩 스크립트 UX
30
+ - `Experimental`
31
+ - PostgreSQL 사용 확장, 추가 운영 API, 배포 토폴로지 확장
32
+
33
+ ## 용어 정의
34
+
35
+ - `Platform`: 런타임 독립 서비스 조합 인터페이스.
36
+ - `RelayHub`: 봇 런타임 연결 관리와 이벤트 디스패치를 담당하는 계층.
37
+ - `connect_key`: 로컬 봇 런타임이 플랫폼에 인증할 때 쓰는 봇별 연결 키.
38
+ - `Config Token`: Slack App Manifest API를 호출하기 위한 워크스페이스 단위 토큰.
39
+ - `Workspace Admin Config`: 관리 UI 로그인 앱과 Slack 웹 API 접근 설정.
40
+
41
+ ## 요구사항
42
+
43
+ - `PLATFORM-FR-001 [Committed][Stable]`: `createApp()`은 인증, Slack, 릴레이, 웹 UI, API를 하나의 Hono 앱으로 조립해야 한다.
44
+ - `PLATFORM-FR-002 [Committed][Stable]`: 플랫폼은 로컬 봇 런타임에 이벤트를 전달하고 Slack API를 프록시해야 한다.
45
+ - `PLATFORM-FR-003 [Committed][Stable]`: Slack 앱 생성/설치/삭제와 이벤트 수신을 지원해야 한다.
46
+ - `PLATFORM-FR-004 [Committed][Stable]`: 관리 UI 접근은 Slack OpenID Connect와 세션 쿠키로 보호되어야 한다.
47
+ - `PLATFORM-FR-005 [Committed][Stable]`: 민감 정보는 Vault로 암호화되어 저장돼야 한다.
48
+ - `PLATFORM-FR-006 [Committed][Stable]`: MySQL, PostgreSQL, D1에 대해 동일한 Repository 계약을 제공해야 한다.
49
+ - `PLATFORM-FR-007 [Committed][Stable]`: Node.js와 Cloudflare Workers 런타임 구현을 동일한 플랫폼 계약으로 조합할 수 있어야 한다.
50
+ - `PLATFORM-FR-008 [Committed][Flexible]`: 웹 UI와 API는 봇 생성, 상태 확인, 삭제, 부트스트랩 스크립트 제공을 지원해야 한다.
51
+ - `PLATFORM-NFR-001 [Committed][Stable]`: 봇 런타임은 Slack 토큰을 직접 보유하면 안 된다.
52
+ - `PLATFORM-NFR-002 [Committed][Stable]`: 워크스페이스별 설정과 세션은 교차 접근되지 않아야 한다.
53
+ - `PLATFORM-NFR-003 [Committed][Stable]`: Slack Events API 응답은 3초 제한을 넘기지 않도록 즉시 응답 구조를 유지해야 한다.
54
+ - `PLATFORM-NFR-004 [Planned][Experimental]`: 플랫폼 코어는 추가 런타임/DB 조합에도 동일 계약을 유지할 수 있어야 한다.
55
+
56
+ ## 수용 기준 (AC)
57
+
58
+ - `PLATFORM-AC-001`: Given 런타임 서비스와 저장소가 조합될 때 When `createApp()`을 호출하면 Then 플랫폼 라우트가 구성된 앱이 생성된다.
59
+ - `PLATFORM-AC-002`: Given 유효한 `connect_key`를 가진 봇 런타임이 연결될 때 When Slack 이벤트가 들어오면 Then 이벤트는 릴레이를 통해 해당 봇으로 전달된다.
60
+ - `PLATFORM-AC-003`: Given Config Token과 봇 정보가 있을 때 When 프로비저닝을 실행하면 Then Slack 앱 생성/OAuth/삭제 흐름이 수행된다.
61
+ - `PLATFORM-AC-004`: Given 관리 UI 접근 시 When 로그인 앱 설정과 세션 검증이 통과하면 Then 보호된 페이지/API에 접근할 수 있다.
62
+ - `PLATFORM-AC-005`: Given 토큰/시크릿/세션 값이 저장될 때 When DB 또는 쿠키에 기록되면 Then Vault 암호문 형태로 저장된다.
63
+ - `PLATFORM-AC-006`: Given Node.js 또는 CF Workers 환경일 때 When 각 런타임 팩토리를 사용하면 Then 같은 `Platform` 계약으로 앱을 실행할 수 있다.
64
+
65
+ ## 의존관계 맵
66
+
67
+ - Depends On: Hono, Drizzle ORM, Slack HTTP API, 런타임별 crypto/relay 구현
68
+ - Blocks: `platform-node`, `platform-worker`, `platform-connector`
69
+ - Parallelizable With: 개별 통합/관리 UI 개선
70
+
71
+ ## 범위 경계 (Non-goals)
72
+
73
+ - Slack 외 타 플랫폼 연동은 이번 범위 밖이다.
74
+ - 장기 데이터 분석, billing, 조직 관리 기능은 포함하지 않는다.
75
+ - 로컬 봇 런타임 자체 구현은 `platform-connector`와 코어 외 패키지 책임이다.
76
+
77
+ ## 제약 & 가정
78
+
79
+ - Slack App Manifest API와 OpenID Connect/OAuth 2.0 가용성을 전제로 한다.
80
+ - Vault master key는 런타임별 비밀 환경 변수로 제공된다.
81
+ - Node.js는 MySQL을 기본 경로로 사용하고 PostgreSQL은 확장용 구현 상태다.
82
+
83
+ ## 리스크 & 완화책
84
+
85
+ - `Risk`: 토큰이 클라이언트나 로컬 봇에 노출되면 플랫폼 보안 모델이 무너진다.
86
+ - `완화`: API proxy와 connect_key 인증을 Stable 계약으로 명시한다.
87
+ - `Risk`: 런타임별 relay/vault 구현 차이로 동작이 갈라질 수 있다.
88
+ - `완화`: 공통 인터페이스와 교차 런타임 호환 포맷을 문서화한다.
89
+ - `Risk`: 워크스페이스 설정 누수가 발생할 수 있다.
90
+ - `완화`: auth/database/web-ui에서 workspace scoping을 명시한다.
91
+
92
+ ## 검증 계획
93
+
94
+ - 단위 테스트 및 수동 smoke test로 OAuth, Slack Events, relay/api, 세션 쿠키, Vault 암호화, 저장소 CRUD 검증
95
+ - Node.js/CF Workers 조립 경로를 각각 배포 패키지 수준에서 검증
96
+
97
+ ## 상세 스펙 맵
98
+
99
+ - [auth.md](/Users/channy/workspace/sena-ai/packages/platform/core/specs/auth.md)
100
+ - [database.md](/Users/channy/workspace/sena-ai/packages/platform/core/specs/database.md)
101
+ - [relay.md](/Users/channy/workspace/sena-ai/packages/platform/core/specs/relay.md)
102
+ - [runtime.md](/Users/channy/workspace/sena-ai/packages/platform/core/specs/runtime.md)
103
+ - [slack-integration.md](/Users/channy/workspace/sena-ai/packages/platform/core/specs/slack-integration.md)
104
+ - [vault.md](/Users/channy/workspace/sena-ai/packages/platform/core/specs/vault.md)
105
+ - [web-ui.md](/Users/channy/workspace/sena-ai/packages/platform/core/specs/web-ui.md)
106
+ ## 개편 메모
107
+
108
+ - AGENTS.md 가이드에 맞춰 상위/상세 스펙 섹션과 traceability를 정규화했다.
109
+ - 기존 구현 계약을 바꾸지 않고 문서 구조와 검증 기준을 명확히 재배치했다.
package/specs/relay.md ADDED
@@ -0,0 +1,78 @@
1
+ # Relay & API Proxy
2
+
3
+ ## 한 줄 요약
4
+
5
+ 릴레이는 로컬 봇 런타임과 플랫폼을 연결하고, Slack 이벤트 전달과 Slack API 프록시를 담당한다.
6
+
7
+ ## 상위 스펙 연결
8
+
9
+ - Related Requirements: `PLATFORM-FR-001`, `PLATFORM-FR-002`, `PLATFORM-FR-005`
10
+ - Related AC: `PLATFORM-AC-001`, `PLATFORM-AC-002`, `PLATFORM-AC-005`
11
+
12
+ ## Behavior
13
+
14
+ ### `PLATFORM-RELAY-01` 봇 연결 수립
15
+
16
+ - Trigger: `GET /relay/stream?connect_key=...`
17
+ - Main Flow:
18
+ - connect key로 active 봇을 찾는다.
19
+ - 인증이 성공하면 런타임별 relay 구현이 스트리밍 연결을 수립한다.
20
+ - 연결 완료 이벤트를 보낸다.
21
+
22
+ ### `PLATFORM-RELAY-02` Slack 이벤트 전달
23
+
24
+ - Trigger: Slack event callback
25
+ - Main Flow:
26
+ - 봇 ID 대상 relay에 이벤트를 dispatch 한다.
27
+ - 연결이 없으면 드롭하되 경고를 남긴다.
28
+
29
+ ### `PLATFORM-RELAY-03` Slack API 프록시
30
+
31
+ - Trigger: `POST /relay/api`
32
+ - Main Flow:
33
+ - `x-connect-key`로 봇을 인증한다.
34
+ - Vault로 봇 토큰을 복호화한다.
35
+ - Slack API 호출을 대행하고 응답을 돌려준다.
36
+
37
+ ## Constraints
38
+
39
+ - `PLATFORM-RELAY-C-001`: 봇 런타임은 Slack 토큰을 직접 보유하지 않아야 한다.
40
+ - `PLATFORM-RELAY-C-002`: 비활성 봇은 스트림 연결과 API 호출이 모두 거부돼야 한다.
41
+ - `PLATFORM-RELAY-C-003`: 런타임별 relay 구현 차이가 있어도 `RelayHub` 외부 계약은 동일해야 한다.
42
+
43
+ ## Interface
44
+
45
+ - `RelayHub`
46
+ - `handleStream(c, botId, connectKey)`
47
+ - `dispatch(botId, event)`
48
+ - `isConnected(botId)`
49
+ - `connectedBots()`
50
+ - Routes:
51
+ - `GET /relay/stream`
52
+ - `POST /relay/api`
53
+
54
+ ## Realization
55
+
56
+ - 모듈 경계:
57
+ - `relay/api-proxy.ts`는 Slack API proxy만, 런타임별 `runtime/*/relay.ts`는 연결 유지와 dispatch만 담당한다.
58
+ - 상태 모델:
59
+ - Node.js는 봇당 SSE 연결, CF는 봇당 Durable Object/WebSocket 연결을 사용한다.
60
+ - 실패 처리:
61
+ - 연결 없음은 이벤트 드롭으로 처리하되 시스템 전체 실패로 보지 않는다.
62
+
63
+ ## Dependencies
64
+
65
+ - Depends On: `vault.md`, `database.md`, `runtime.md`
66
+ - Blocks: `platform-connector`
67
+ - Parallelizable With: `slack-integration.md`
68
+
69
+ ## AC
70
+
71
+ - Given 유효한 connect key가 있을 때 When 봇 런타임이 `/relay/stream`에 연결하면 Then relay 연결이 수립된다.
72
+ - Given Slack 이벤트가 들어올 때 When 대상 봇이 연결돼 있으면 Then 이벤트가 해당 봇에 전달된다.
73
+ - Given 봇 런타임이 `/relay/api`를 호출할 때 When 인증이 통과하면 Then 플랫폼이 Slack API를 대신 호출한다.
74
+ ## 개편 메모
75
+
76
+ - AGENTS.md 가이드에 맞춰 상위/상세 스펙 섹션과 traceability를 정규화했다.
77
+ - 기존 구현 계약을 바꾸지 않고 문서 구조와 검증 기준을 명확히 재배치했다.
78
+
@@ -0,0 +1,68 @@
1
+ # Runtime Implementations
2
+
3
+ ## 한 줄 요약
4
+
5
+ 플랫폼 코어는 Node.js와 Cloudflare Workers에서 동일한 `Platform` 계약을 조립할 수 있도록 런타임별 Vault, Relay, Crypto 구현을 제공한다.
6
+
7
+ ## 상위 스펙 연결
8
+
9
+ - Related Requirements: `PLATFORM-FR-006`, `PLATFORM-FR-007`
10
+ - Related AC: `PLATFORM-AC-006`
11
+
12
+ ## Behavior
13
+
14
+ ### `PLATFORM-RUNTIME-01` Node.js 런타임 조립
15
+
16
+ - Trigger: `createNodeRuntime(config)`
17
+ - Main Flow:
18
+ - Node Vault, SSE Relay, Node Crypto를 조립해 반환한다.
19
+
20
+ ### `PLATFORM-RUNTIME-02` Cloudflare Workers 런타임 조립
21
+
22
+ - Trigger: `createCfRuntime(env)`
23
+ - Main Flow:
24
+ - CF Vault, DO Relay, CF Crypto를 비동기로 초기화해 반환한다.
25
+
26
+ ### `PLATFORM-RUNTIME-03` 공통 Crypto 계약
27
+
28
+ - `randomHex`, `uuid`, `hmacSha256`, `timingSafeEqual`를 Promise 기반 공통 인터페이스로 제공한다.
29
+
30
+ ## Constraints
31
+
32
+ - `PLATFORM-RUNTIME-C-001`: 런타임별 구현 차이가 있어도 `Vault`, `RelayHub`, `CryptoProvider` 외부 계약은 동일해야 한다.
33
+ - `PLATFORM-RUNTIME-C-002`: CF와 Node의 Vault 포맷은 상호 호환 가능해야 한다.
34
+ - `PLATFORM-RUNTIME-C-003`: timing-safe 비교는 각 런타임의 보안 특성에 맞게 구현해야 한다.
35
+
36
+ ## Interface
37
+
38
+ - Node:
39
+ - `createNodeRuntime(config: NodeRuntimeConfig): NodeRuntime`
40
+ - CF:
41
+ - `createCfRuntime(env: CfEnv): Promise<CfRuntime>`
42
+ - Shared:
43
+ - `CryptoProvider`
44
+ - `Vault`
45
+ - `RelayHub`
46
+
47
+ ## Realization
48
+
49
+ - 모듈 경계:
50
+ - `runtime/node/*`, `runtime/cf/*`가 각 책임을 나눈다.
51
+ - 상태 모델:
52
+ - Node는 동기 초기화, CF는 Web Crypto import와 Durable Object binding 때문에 비동기 초기화가 필요하다.
53
+
54
+ ## Dependencies
55
+
56
+ - Depends On: `vault.md`, `relay.md`
57
+ - Blocks: `platform-node`, `platform-worker`
58
+ - Parallelizable With: `database.md`
59
+
60
+ ## AC
61
+
62
+ - Given Node.js 환경일 때 When `createNodeRuntime()`을 호출하면 Then Node용 Vault/Relay/Crypto 조합을 얻는다.
63
+ - Given CF Workers 환경일 때 When `createCfRuntime()`을 호출하면 Then CF용 Vault/Relay/Crypto 조합을 얻는다.
64
+ ## 개편 메모
65
+
66
+ - AGENTS.md 가이드에 맞춰 상위/상세 스펙 섹션과 traceability를 정규화했다.
67
+ - 기존 구현 계약을 바꾸지 않고 문서 구조와 검증 기준을 명확히 재배치했다.
68
+
@@ -0,0 +1,85 @@
1
+ # Slack Integration
2
+
3
+ ## 한 줄 요약
4
+
5
+ Slack 앱 프로비저닝, OAuth 설치, Events API 수신을 플랫폼 도메인 규칙에 맞게 처리한다.
6
+
7
+ ## 상위 스펙 연결
8
+
9
+ - Related Requirements: `PLATFORM-FR-003`, `PLATFORM-FR-005`, `PLATFORM-NFR-003`
10
+ - Related AC: `PLATFORM-AC-003`
11
+
12
+ ## Behavior
13
+
14
+ ### `PLATFORM-SLACK-01` 앱 프로비저닝
15
+
16
+ - Trigger: 봇 생성 또는 재프로비저닝
17
+ - Main Flow:
18
+ - workspace의 Config Token을 읽는다.
19
+ - manifest 템플릿에 봇 이름, 이벤트 URL, redirect URL을 주입한다.
20
+ - Slack App Manifest API로 앱을 생성한다.
21
+ - app/client/signing secret 정보를 저장한다.
22
+
23
+ ### `PLATFORM-SLACK-02` 봇 OAuth 설치
24
+
25
+ - Trigger: `/oauth/start/:botId` -> `/oauth/callback`
26
+ - Main Flow:
27
+ - state를 만들고 authorize URL로 보낸다.
28
+ - callback에서 state를 소비하고 `oauth.v2.access`로 봇 토큰을 얻는다.
29
+ - 봇 토큰을 암호화 저장하고 봇 상태를 active로 바꾼다.
30
+
31
+ ### `PLATFORM-SLACK-03` Events API 수신
32
+
33
+ - Trigger: `POST /slack/events/:botId`
34
+ - Main Flow:
35
+ - active 봇 조회 및 signing secret 검증을 수행한다.
36
+ - `url_verification`이면 challenge를 반환한다.
37
+ - `event_callback`이면 relay로 전달하고 Slack에는 즉시 `{ ok: true }`를 응답한다.
38
+
39
+ ### `PLATFORM-SLACK-04` Config Token 갱신
40
+
41
+ - Trigger: 주기적 스케줄 또는 런타임별 bootstrap 로직
42
+ - Main Flow:
43
+ - refresh token으로 새 access/refresh token을 발급받아 저장한다.
44
+
45
+ ## Constraints
46
+
47
+ - `PLATFORM-SLACK-C-001`: 이벤트 서명 검증은 5분 이내 요청과 HMAC-SHA256 비교를 사용해야 한다.
48
+ - `PLATFORM-SLACK-C-002`: OAuth state는 일회용이어야 한다.
49
+ - `PLATFORM-SLACK-C-003`: Slack Events API는 3초 내 응답 가능 구조여야 한다.
50
+
51
+ ## Interface
52
+
53
+ - Routes:
54
+ - `GET /oauth/start/:botId`
55
+ - `GET /oauth/callback`
56
+ - `POST /slack/events/:botId`
57
+ - Provisioner:
58
+ - `rotateConfigToken(workspaceId)`
59
+ - `createApp(workspaceId, botId, botName, botUsername)`
60
+ - `deleteApp(workspaceId, appId)`
61
+
62
+ ## Realization
63
+
64
+ - 모듈 경계:
65
+ - `slack/provisioner.ts`, `slack/oauth.ts`, `slack/events.ts`로 나눈다.
66
+ - 실패 처리:
67
+ - Slack 앱 삭제 실패는 DB 삭제를 막지 않는다.
68
+ - 이벤트 전달 실패는 Slack 재시도와는 별개로 플랫폼 로그에 남긴다.
69
+
70
+ ## Dependencies
71
+
72
+ - Depends On: `database.md`, `relay.md`, `vault.md`, `runtime.md`
73
+ - Blocks: `web-ui.md`, `platform-node`, `platform-worker`
74
+ - Parallelizable With: `auth.md`
75
+
76
+ ## AC
77
+
78
+ - Given Config Token이 있을 때 When 프로비저닝을 실행하면 Then Slack 앱 생성 결과가 DB에 반영된다.
79
+ - Given OAuth callback이 성공할 때 When bot token을 받으면 Then 봇 상태가 active로 전환된다.
80
+ - Given Slack 이벤트가 들어올 때 When 서명 검증이 통과하면 Then 이벤트는 relay에 전달되고 Slack에는 즉시 응답한다.
81
+ ## 개편 메모
82
+
83
+ - AGENTS.md 가이드에 맞춰 상위/상세 스펙 섹션과 traceability를 정규화했다.
84
+ - 기존 구현 계약을 바꾸지 않고 문서 구조와 검증 기준을 명확히 재배치했다.
85
+
package/specs/vault.md ADDED
@@ -0,0 +1,70 @@
1
+ # Vault
2
+
3
+ ## 한 줄 요약
4
+
5
+ 플랫폼 민감 정보를 AES-256-GCM으로 암복호화하고 Node.js/CF Workers 간 호환 가능한 암호문 포맷을 유지한다.
6
+
7
+ ## 상위 스펙 연결
8
+
9
+ - Related Requirements: `PLATFORM-FR-005`, `PLATFORM-NFR-001`
10
+ - Related AC: `PLATFORM-AC-005`, `PLATFORM-AC-006`
11
+
12
+ ## Behavior
13
+
14
+ ### `PLATFORM-VAULT-01` 암호화
15
+
16
+ - Trigger: `encrypt(plaintext)`
17
+ - Main Flow:
18
+ - 32바이트 master key와 랜덤 IV로 AES-256-GCM 암호화를 수행한다.
19
+ - `base64(IV + AuthTag + Ciphertext)` 형식으로 인코딩한다.
20
+
21
+ ### `PLATFORM-VAULT-02` 복호화
22
+
23
+ - Trigger: `decrypt(encoded)`
24
+ - Main Flow:
25
+ - 저장 형식을 파싱해 IV, auth tag, ciphertext를 분리한다.
26
+ - 원문 문자열을 복원한다.
27
+
28
+ ### `PLATFORM-VAULT-03` 런타임 간 호환
29
+
30
+ - Trigger: Node에서 쓴 암호문을 CF에서 읽거나 그 반대
31
+ - Main Flow:
32
+ - 두 구현이 동일한 바이너리 레이아웃을 유지한다.
33
+
34
+ ## Constraints
35
+
36
+ - `PLATFORM-VAULT-C-001`: master key는 64자 hex, 32바이트 길이여야 한다.
37
+ - `PLATFORM-VAULT-C-002`: 저장 포맷은 Node/CF 양쪽에서 동일해야 한다.
38
+ - `PLATFORM-VAULT-C-003`: 평문 토큰/시크릿은 저장 계층에 직접 내려가면 안 된다.
39
+
40
+ ## Interface
41
+
42
+ - `Vault`
43
+ - `encrypt(plaintext: string): Promise<string>`
44
+ - `decrypt(encoded: string): Promise<string>`
45
+ - Runtimes:
46
+ - `createNodeVault(masterKeyHex)`
47
+ - `createCfVault(masterKeyHex)`
48
+
49
+ ## Realization
50
+
51
+ - 모듈 경계:
52
+ - `runtime/node/vault.ts`, `runtime/cf/vault.ts`
53
+ - 상태 모델:
54
+ - 키는 초기화 시 import/Buffer 변환 후 런타임 객체 안에 유지한다.
55
+
56
+ ## Dependencies
57
+
58
+ - Depends On: Node crypto, Web Crypto
59
+ - Blocks: `auth.md`, `database.md`, `relay.md`, `slack-integration.md`
60
+ - Parallelizable With: `runtime.md`
61
+
62
+ ## AC
63
+
64
+ - Given 유효한 master key가 있을 때 When `encrypt()` 후 `decrypt()`를 호출하면 Then 원문이 복원된다.
65
+ - Given Node 또는 CF 환경일 때 When 같은 암호문을 처리하면 Then 동일한 저장 포맷으로 해석된다.
66
+ ## 개편 메모
67
+
68
+ - AGENTS.md 가이드에 맞춰 상위/상세 스펙 섹션과 traceability를 정규화했다.
69
+ - 기존 구현 계약을 바꾸지 않고 문서 구조와 검증 기준을 명확히 재배치했다.
70
+
@@ -0,0 +1,84 @@
1
+ # Web UI & API
2
+
3
+ ## 한 줄 요약
4
+
5
+ 관리자는 웹 UI와 JSON API로 Slack 봇을 생성, 조회, 삭제하고 로컬 부트스트랩 정보를 얻는다.
6
+
7
+ ## 상위 스펙 연결
8
+
9
+ - Related Requirements: `PLATFORM-FR-001`, `PLATFORM-FR-004`, `PLATFORM-FR-008`
10
+ - Related AC: `PLATFORM-AC-001`, `PLATFORM-AC-004`
11
+
12
+ ## Behavior
13
+
14
+ ### `PLATFORM-WEB-01` 봇 생성/조회/삭제 API
15
+
16
+ - Routes:
17
+ - `POST /api/bots`
18
+ - `GET /api/bots/:botId`
19
+ - `POST /api/bots/:botId/provision`
20
+ - `DELETE /api/bots/:botId`
21
+ - `POST /api/bots/:botId/icon`
22
+ - Main Flow:
23
+ - 입력 검증 후 봇 레코드를 만들고 프로비저닝을 시작한다.
24
+ - 상태 조회와 삭제, 아이콘 업로드를 제공한다.
25
+
26
+ ### `PLATFORM-WEB-02` 관리 페이지
27
+
28
+ - Routes:
29
+ - `/`
30
+ - `/bots/new`
31
+ - `/bots/:botId/setup`
32
+ - `/bots/:botId/complete`
33
+ - `/admin/bots`
34
+ - `/admin/connections`
35
+ - Main Flow:
36
+ - 대시보드에 봇 목록과 상태를 보여준다.
37
+ - setup/complete 페이지에서 프로비저닝 상태와 설치/부트스트랩 안내를 제공한다.
38
+
39
+ ### `PLATFORM-WEB-03` 초기 설정과 부트스트랩 스크립트
40
+
41
+ - Routes:
42
+ - `GET /setup`
43
+ - `POST /api/setup`
44
+ - `GET /install.sh`
45
+ - Main Flow:
46
+ - Slack 로그인 앱 설정을 저장한다.
47
+ - `install.sh`는 로컬 프로젝트 초기화를 위한 shell 스크립트를 제공한다.
48
+
49
+ ## Constraints
50
+
51
+ - `PLATFORM-WEB-C-001`: 보호된 페이지와 API는 auth middleware 뒤에 있어야 한다.
52
+ - `PLATFORM-WEB-C-002`: 봇 username은 영문소문자/숫자/하이픈 규칙을 따라야 한다.
53
+ - `PLATFORM-WEB-C-003`: 부트스트랩 스크립트는 connect key와 platform URL을 명시적으로 주입받아야 한다.
54
+
55
+ ## Interface
56
+
57
+ - Web pages: dashboard, new bot, setup progress, completion, admin pages
58
+ - API routes: 봇 생성/조회/재프로비저닝/삭제/아이콘 업로드, setup 저장
59
+
60
+ ## Realization
61
+
62
+ - 모듈 경계:
63
+ - `web/api.ts`, `web/pages.ts`, `web/setup.ts`
64
+ - 상태 모델:
65
+ - 봇 상태는 DB를 source of truth로 하고 setup page는 polling으로 반영한다.
66
+ - 실패 처리:
67
+ - 프로비저닝 실패는 상태와 재시도 API로 노출한다.
68
+
69
+ ## Dependencies
70
+
71
+ - Depends On: `auth.md`, `database.md`, `slack-integration.md`
72
+ - Blocks: 운영 UI
73
+ - Parallelizable With: `relay.md`
74
+
75
+ ## AC
76
+
77
+ - Given 인증된 관리자가 있을 때 When `POST /api/bots`를 호출하면 Then pending 봇이 생성되고 프로비저닝이 시작된다.
78
+ - Given setup/complete 페이지를 열 때 When Slack 앱 생성 또는 OAuth가 완료되면 Then 다음 단계 안내가 반영된다.
79
+ - Given `GET /install.sh`를 호출할 때 When 필요한 인자를 전달하면 Then 로컬 부트스트랩용 shell 스크립트를 받을 수 있다.
80
+ ## 개편 메모
81
+
82
+ - AGENTS.md 가이드에 맞춰 상위/상세 스펙 섹션과 traceability를 정규화했다.
83
+ - 기존 구현 계약을 바꾸지 않고 문서 구조와 검증 기준을 명확히 재배치했다.
84
+