update roadmap and README files

This commit is contained in:
toki 2026-05-30 22:42:52 +09:00
parent 625a50bcc9
commit 2904ee663c
5 changed files with 156 additions and 15 deletions

View file

@ -7,7 +7,7 @@
## 활성 Milestone
- [진행중] 메시징 계약 표준화
- [검토중] 메시징 계약 표준화
- Phase: `agent-roadmap/phase/messaging-runtime/PHASE.md`
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/messaging-contract.md`

View file

@ -16,7 +16,7 @@ Mattermost server/webapp/push-proxy는 upstream-followable runtime으로 두고,
완료, 검토중, 진행중, 계획, 스케치 순서로 두어 아래로 갈수록 미래 작업에 가까워지게 정렬한다.
스케치 Milestone은 아직 구현 가능한 계획이 아니므로 계획 Milestone보다 아래에 둔다.
- [진행중] 메시징 계약 표준화
- [검토중] 메시징 계약 표준화
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/messaging-contract.md`
- 요약: 서버와 Flutter 플러그인이 공유할 메시징/알림 API, 이벤트, 저장 계약을 정리한다.
- [계획] 알림 파이프라인 고도화

View file

@ -12,7 +12,7 @@ upstream-followable server/push runtime과 nexo-owned Flutter SDK가 주고받
## 상태
[진행중]
[검토중]
## 승격 조건
@ -41,28 +41,32 @@ upstream-followable server/push runtime과 nexo-owned Flutter SDK가 주고받
앱이 의존할 수 있는 최소 메시징/알림 API 표면을 정리한다.
- [x] [dart-api] Dart public API에서 유지할 이름, deprecated 후보, nexo wrapper 후보를 분리한다.
- [ ] [event-shape] notification event, opened event, token event의 payload shape와 backward compatibility 기준을 문서화한다.
- [ ] [token-flow] device token 저장, auth token 저장, signing key 저장 책임과 호출 순서를 정리한다.
- [ ] [sdk-runtime-boundary] Flutter SDK가 server/push contract를 추적하되 Mattermost mobile implementation을 팔로잉하지 않는 경계를 문서화한다.
- [x] [event-shape] notification event, opened event, token event의 payload shape와 backward compatibility 기준을 문서화한다.
- [x] [token-flow] device token 저장, auth token 저장, signing key 저장 책임과 호출 순서를 정리한다.
- [x] [sdk-runtime-boundary] Flutter SDK가 server/push contract를 추적하되 Mattermost mobile implementation을 팔로잉하지 않는 경계를 문서화한다.
### Epic: [server-contract] Server contract
서버 core와 플러그인이 공유하는 메시징/알림 계약을 확인 가능한 형태로 만든다.
- [ ] [push-payload] push payload 필드와 검증 기준을 서버/플러그인 양쪽 문서에서 연결한다.
- [ ] [ack-contract] ACK 요청 형식과 실패 처리 기준을 정리한다.
- [ ] [routing-contract] channel/thread navigation event가 client router로 전달되는 기준을 정리한다.
- [ ] [front-compat] Mattermost webapp/reference front에서 동작하는 기본 메시징 기능과 Flutter SDK embedded 흐름이 충돌하지 않는 기준을 정리한다.
- [x] [push-payload] push payload 필드와 검증 기준을 서버/플러그인 양쪽 문서에서 연결한다.
- [x] [ack-contract] ACK 요청 형식과 실패 처리 기준을 정리한다.
- [x] [routing-contract] channel/thread navigation event가 client router로 전달되는 기준을 정리한다.
- [x] [front-compat] Mattermost webapp/reference front에서 동작하는 기본 메시징 기능과 Flutter SDK embedded 흐름이 충돌하지 않는 기준을 정리한다.
## 완료 리뷰
- 상태: 없음
- 요청일: 없음
- 완료 근거: 없음
- 상태: 요청됨
- 요청일: 2026-05-30
- 완료 근거:
- `packages/messaging_flutter/README.md`에 plugin-side public API, event shape, token/credential flow, push payload, ACK, routing, reference front compatibility 기준을 정리했다.
- `services/core/compose/README.md`에 server-side source anchors, payload fields, ACK endpoint, webapp/reference front compatibility 기준을 연결했다.
- 모든 기능 Task가 문서/테스트/source anchor 근거와 함께 완료 후보가 되었다.
- 리뷰 필요:
- [ ] 사용자가 완료 결과를 확인했다
- [ ] Flutter 앱 우선 노출 범위가 현재 Public API Contract 기준으로 충분한지 확인했다
- [ ] archive 이동을 승인했다
- 리뷰 코멘트: 없음
- 리뷰 코멘트: 모든 기능 Task는 완료 후보이며, 남은 판단은 사용자 최종 확인과 archive 승인이다.
## 범위 제외
@ -74,7 +78,10 @@ upstream-followable server/push runtime과 nexo-owned Flutter SDK가 주고받
- 관련 경로: `packages/messaging_flutter/lib/`, `packages/messaging_flutter/android/`, `services/core/server/`, `services/core/webapp/`, `services/core/compose/`, `apps/flutter-test/`
- 표준선(선택): SDK public API는 nexo-owned wrapper로 유지하고, server/webapp/push-proxy compatibility는 얇은 layer로 흡수한다
- 진행 근거: 2026-05-30 `packages/messaging_flutter/README.md`에 Public API Contract 섹션을 추가해 유지 API, compatibility/deprecated 후보, nexo wrapper 후보를 분리했다.
- 진행 근거:
- 2026-05-30 `packages/messaging_flutter/README.md`에 Public API Contract 섹션을 추가해 유지 API, compatibility/deprecated 후보, nexo wrapper 후보를 분리했다.
- 2026-05-30 `packages/messaging_flutter/README.md`의 Event Shape Contract, Token And Credential Flow, SDK Ownership Boundary, Runtime Tracking Boundary 섹션과 `packages/messaging_flutter/test/nexo_messaging_test.dart` 근거로 event shape, token flow, SDK/runtime boundary, routing contract를 완료로 판정했다.
- 2026-05-30 `packages/messaging_flutter/README.md``services/core/compose/README.md`를 상호 연결해 push payload, ACK, reference front compatibility 계약 기준을 완료 후보로 정리했다.
- 선행 작업: 정체성 기준선, 업스트림 팔로잉 기준선, 클라이언트 검증 기준선
- 후속 작업: 알림 파이프라인 고도화, 멀티앱 채널 모델
- 확인 필요: Flutter 앱 우선 노출 범위

View file

@ -278,6 +278,104 @@ The Dart API currently recognizes these native event types:
`onNavigateToThread`; otherwise it calls `onNavigateToChannel` when a channel is
available.
## Server And Push Runtime Contract
This package consumes the Mattermost-compatible push runtime documented from the
server side in
[`services/core/compose/README.md`](../../services/core/compose/README.md#sdk-contract-anchors).
The plugin keeps compatibility with those fields, while typed app-facing API
stays in the Dart surface above.
### Push Payload Fields
Server-side source anchor:
`services/core/server/public/model/push_notification.go`.
Plugin-side source anchors:
`android/src/main/kotlin/com/tokilabs/nexo/messaging/NexoFirebaseMessagingService.kt`,
`android/src/main/java/com/tokilabs/nexo/messaging/helpers/CustomPushNotificationHelper.java`,
and `android/src/main/kotlin/com/tokilabs/nexo/messaging/LaunchIntentHelper.kt`.
| Field | Required for | Plugin handling |
| --- | --- | --- |
| `type` | Message, clear, session dispatch | Must match the native `PushNotificationType` constants. Unknown types are forwarded only as raw compatibility events. |
| `ack_id` | ACK and signature validation | Sent back to `/api/v4/notifications/ack` and required by signed push JWT validation when a signature is present. |
| `server_url` | ACK/reply delivery, signature lookup, routing | Preferred server identity. If omitted, Android may resolve it from `server_id` or the single stored server URL. |
| `server_id` | Server URL fallback | Resolved through the identifier stored by `setAuthToken(serverUrl, token, identifier: ...)`. |
| `channel_id` | Notification grouping and channel routing | Used for notification grouping, clear handling, and `onNavigateToChannel`. |
| `root_id` | CRT/thread routing | When CRT is enabled, this groups thread notifications and drives `onNavigateToThread`. |
| `is_crt_enabled` | Thread behavior | The string value `"true"` enables thread routing; other values are treated as false. |
| `post_id` | ACK id-loaded response and notification identity | Included in ACK requests and used as the Android notification id source. |
| `signature` | Payload verification | Verified against the server signing key and stored device token. Missing signatures are accepted for backward compatibility. |
| `id_loaded` | ACK response enrichment | The string value `"true"` asks the server to return post data that Android can merge into the notification bundle. |
| `category` | Inline reply affordance | `CAN_REPLY` enables the Android inline reply action when `post_id` and `server_url` are present. |
Validation criteria:
- A signed payload passes only when the JWT contains the expected `ack_id` and
raw device token, and the signing key resolves from the server database or the
`setSigningKey()` fallback.
- A payload without `server_url` is valid only when `server_id` or the one-server
fallback resolves to a server URL before ACK, reply, or signature lookup.
- Payload fields not listed here may pass through raw `onNotification` maps for
diagnostics, but consuming apps should not treat them as stable API.
### ACK Contract
Plugin-side source anchor:
`android/src/main/java/com/tokilabs/nexo/messaging/ReceiptDelivery.java`.
Server-side source anchors:
`services/core/server/public/model/push_notification.go` and
`services/core/server/channels/api4/system.go`.
The plugin POSTs ACKs to `/api/v4/notifications/ack` with:
| Request field | Value |
| --- | --- |
| `id` | Push payload `ack_id`. |
| `received_at` | Client receipt timestamp in milliseconds. |
| `platform` | `android`. |
| `type` | Push payload `type`. |
| `post_id` | Push payload `post_id`, when present. |
| `is_id_loaded` | Boolean derived from payload `id_loaded == "true"`. |
The server returns `OK` for ordinary ACKs. When `is_id_loaded` is true and the
ACK references a message post, the server may return message fields such as
`post_id`, `root_id`, `category`, `message`, `team_id`, `channel_id`,
`channel_name`, `type`, `sender_id`, `sender_name`, and `version`; Android merges
those fields into the notification bundle before rendering.
Failure handling criteria:
- If ACK delivery fails, Android logs the failure and continues signature
validation and notification processing with the original payload.
- If an id-loaded ACK response cannot be parsed, Android treats it as absent and
continues with the original payload.
- If the server rejects the ACK because push notifications are disabled,
unauthenticated, or the post is unavailable, that is a runtime/server state
failure, not a Dart API contract change.
### Reference Front Compatibility
The Mattermost webapp under `services/core/webapp` remains the reference front
for browser messaging features such as posts, replies, post acknowledgements,
channel navigation, and thread state. The Flutter SDK embedded flow must not
replace or fork that webapp behavior inside this repository.
Compatibility criteria:
- Webapp routes, Redux state, and product UI stay upstream-followable in
`services/core/webapp`.
- Flutter consuming apps own their own screens and routers; this plugin emits
opened events and optional channel/thread callbacks only.
- Server compatibility belongs to push payload fields, ACK/reply endpoints,
signing-key verification, and device-token format, not to Mattermost mobile or
webapp UI implementation details.
- If a future Flutter app needs product UI parity with the reference webapp, that
work belongs in the consuming app or a separate roadmap item, not in this
plugin contract milestone.
## Android Notes
The Android manifest declares the plugin's own

View file

@ -59,6 +59,42 @@ docker compose config | rg --fixed-strings \
FCM/APNS credential validation is not part of the default smoke. Run it only
when a real push-proxy config is present outside git.
## SDK Contract Anchors
The Flutter SDK side of this contract is documented in
[`packages/messaging_flutter/README.md`](../../../packages/messaging_flutter/README.md#server-and-push-runtime-contract).
This compose runtime keeps the Mattermost-compatible server and push-proxy shape
that the SDK consumes.
Server source anchors:
- Push payload model: `services/core/server/public/model/push_notification.go`
- ACK endpoint route and handler: `services/core/server/channels/api4/system.go`
- Push signing and push-proxy dispatch: `services/core/server/channels/app/notification_push.go`
- Reference webapp client behavior: `services/core/webapp/platform/client/src/client4.ts`,
`services/core/webapp/platform/client/src/websocket.ts`, and
`services/core/webapp/channels/src/`
SDK-facing payload fields are `type`, `ack_id`, `server_id`, `device_id`,
`post_id`, `category`, `team_id`, `channel_id`, `root_id`, `channel_name`,
`message`, `sender_id`, `sender_name`, `version`, `is_crt_enabled`,
`is_id_loaded`, and `signature`. The server signs push notifications with the
`ack_id` and raw device token after splitting the stored device id into platform
and token parts. The Android SDK stores device tokens with the
`android_rn-v2:` compatibility prefix and validates the raw token portion.
ACK delivery uses `POST /api/v4/notifications/ack` with `id`, `received_at`,
`platform`, `type`, `post_id`, and `is_id_loaded`. When id-loaded ACK is enabled
for a message notification, the server may return post and routing fields that
the SDK merges into the notification bundle.
The webapp remains the reference browser front. Compose and server updates must
keep webapp messaging, replies, post acknowledgements, channel navigation, and
thread behavior upstream-followable. Flutter embedded SDK compatibility is
limited to payload fields, ACK/reply endpoints, signing-key verification,
device-token format, and opened channel/thread routing events; product UI parity
belongs to consuming apps or a separate roadmap item.
## Remote Parity
The remote development copy lives under `~/docker/services/nexo/compose`. Local