docs: research milestone v2.0 collaboration planning social sign-in
This commit is contained in:
parent
0d23d94700
commit
367364e9f8
5 changed files with 279 additions and 0 deletions
69
.planning/research/ARCHITECTURE.md
Normal file
69
.planning/research/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# v2.0 Research: Architecture Notes
|
||||
|
||||
**Date:** 2026-05-15
|
||||
|
||||
## Integration Points
|
||||
|
||||
### Auth
|
||||
|
||||
Add a provider layer beside `backend/internal/auth`:
|
||||
|
||||
- `internal/auth/oauth.go` or `internal/identity/`
|
||||
- `user_identities` table:
|
||||
- `id`
|
||||
- `user_id`
|
||||
- `provider` (`google`, `apple`)
|
||||
- `provider_subject`
|
||||
- `email`
|
||||
- `email_verified`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- unique `(provider, provider_subject)`
|
||||
|
||||
Keep `users` and `sessions` authoritative. Social sign-in should finish by using the same session store used by email/password login.
|
||||
|
||||
### Chat
|
||||
|
||||
Add:
|
||||
|
||||
- `tablo_messages` table:
|
||||
- `id`
|
||||
- `tablo_id`
|
||||
- `author_user_id`
|
||||
- `body`
|
||||
- `created_at`
|
||||
- `edited_at`
|
||||
- `deleted_at`
|
||||
- `GET /tablos/{id}/messages` renders current message tab/history.
|
||||
- `POST /tablos/{id}/messages` creates a message and returns a message fragment.
|
||||
- `GET /tablos/{id}/messages/stream` holds an SSE connection and streams new message fragments or JSON payloads.
|
||||
|
||||
For a single-host v2, an in-process hub keyed by `tablo_id` is acceptable. The database remains the source of truth; the hub is only a delivery accelerator. If multi-instance deployment arrives later, replace hub fanout with Postgres LISTEN/NOTIFY or Redis.
|
||||
|
||||
### Etapes
|
||||
|
||||
Add:
|
||||
|
||||
- `etapes` table with `id`, `tablo_id`, `title`, `description`, `position`, timestamps.
|
||||
- `tasks.etape_id nullable references etapes(id) on delete set null`.
|
||||
- Keep existing `tasks.status` and `tasks.position`; etape grouping is orthogonal to kanban status.
|
||||
|
||||
This matches the constraint: only tasks may reference an etape, and etapes do not reference each other.
|
||||
|
||||
### Events / Planning
|
||||
|
||||
Add:
|
||||
|
||||
- `tablo_events` table with `id`, `tablo_id`, `created_by_user_id`, `title`, `description`, `location`, `starts_at`, `ends_at`, timestamps.
|
||||
- `GET /planning` renders current user's event list/calendar-like agenda.
|
||||
- `GET /tablos/{id}/events` renders a tablo events tab.
|
||||
- Event ownership initially follows tablo ownership. If shared tablos arrive later, planning aggregation should include events from tablos the user can access.
|
||||
|
||||
## Phase Order
|
||||
|
||||
1. Social sign-in first, because it touches the existing auth/session foundation and should be isolated from collaboration schema changes.
|
||||
2. Etapes next, because they extend existing tasks and can be tested without real-time concerns.
|
||||
3. Events/planning next, because they add a new core domain with straightforward CRUD.
|
||||
4. Chat last or near-last, because real-time delivery needs extra browser/manual verification and careful deployment behavior.
|
||||
|
||||
For roadmap ergonomics, chat can also be split into persistence first and real-time second.
|
||||
58
.planning/research/FEATURES.md
Normal file
58
.planning/research/FEATURES.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# v2.0 Research: Feature Expectations
|
||||
|
||||
**Date:** 2026-05-15
|
||||
|
||||
## Authentication
|
||||
|
||||
Table stakes:
|
||||
- User can choose Google or Apple from the login/signup page.
|
||||
- OAuth callbacks validate state/nonce and provider identity before issuing a local Xtablo session.
|
||||
- Existing email/password accounts still work.
|
||||
- If a provider identity has a verified email matching an existing account, the flow links to that account after explicit or safe same-session handling.
|
||||
- User can later see linked providers in account settings.
|
||||
|
||||
Differentiators for later:
|
||||
- Provider unlinking.
|
||||
- Multiple emails per user.
|
||||
- Account merge conflict UI.
|
||||
|
||||
## Chat / Messaging
|
||||
|
||||
Table stakes:
|
||||
- Each tablo has one discussion stream.
|
||||
- Authenticated tablo owner can post a text message.
|
||||
- Messages persist in Postgres with author, body, created timestamp, edited/deleted markers.
|
||||
- Other open browsers on that tablo receive new messages without manual refresh.
|
||||
- Reloading the page shows message history.
|
||||
|
||||
Differentiators for later:
|
||||
- Mentions, reactions, typing indicators, file previews, threads, read receipts, moderation.
|
||||
|
||||
## Etapes
|
||||
|
||||
Table stakes:
|
||||
- User can create an etape inside a tablo.
|
||||
- User can edit/delete/reorder etapes.
|
||||
- A task can belong to zero or one etape.
|
||||
- An etape cannot belong to another etape.
|
||||
- The tasks UI can filter or group by etape while preserving the existing kanban status model.
|
||||
|
||||
Differentiators for later:
|
||||
- Etape progress rollups, due dates, dependencies, templates.
|
||||
|
||||
## Events and Individual Planning
|
||||
|
||||
Table stakes:
|
||||
- User can create a scheduled event attached to a tablo.
|
||||
- Event has title, start time, optional end time, optional description/location.
|
||||
- User can edit/delete events.
|
||||
- Tablo detail page shows its events.
|
||||
- Individual planning page shows the authenticated user's scheduled events across tablos.
|
||||
- Events are authorized through tablo ownership/membership rules.
|
||||
|
||||
Differentiators for later:
|
||||
- Recurrence, reminders, ICS import/export, external calendar sync, multi-person assignments.
|
||||
|
||||
## Scope Boundary
|
||||
|
||||
This milestone should deliver working behavior with plain UI. Visual polish is intentionally deferred because the user will provide a beautiful UI later.
|
||||
39
.planning/research/PITFALLS.md
Normal file
39
.planning/research/PITFALLS.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# v2.0 Research: Pitfalls
|
||||
|
||||
**Date:** 2026-05-15
|
||||
|
||||
## Authentication
|
||||
|
||||
- Do not trust OAuth callback parameters without validating state and nonce.
|
||||
- Do not use provider access tokens as Xtablo sessions. They are not the local session authority.
|
||||
- Do not link accounts by unverified email.
|
||||
- Apple may only provide some profile fields on first authorization; persist what is needed immediately.
|
||||
- Apple client secret generation is easy to get wrong: use ES256, correct `kid`, `iss`, `aud`, `sub`, and expiration.
|
||||
- Provider subject, not email, is the stable external identity key.
|
||||
|
||||
## Chat
|
||||
|
||||
- Do not treat the in-process real-time hub as durable storage. Every message must commit to Postgres first.
|
||||
- SSE connections need keep-alive comments/events to avoid idle timeouts.
|
||||
- Browser/domain connection limits matter for SSE over HTTP/1.1; keep one stream per tablo view and document the limitation.
|
||||
- Message POSTs must keep CSRF protection. SSE stream endpoints should be GET-only and authenticated.
|
||||
- Avoid storing unbounded message bodies; enforce length limits.
|
||||
- Avoid HTML injection by rendering message bodies through templ escaping, not safe HTML.
|
||||
|
||||
## Etapes
|
||||
|
||||
- Do not model etapes as recursive tasks unless the product explicitly needs nested hierarchy later.
|
||||
- Adding `tasks.etape_id` should not break existing kanban ordering.
|
||||
- Deleting an etape should not delete its tasks unless the user explicitly confirms that behavior; default to unassigning tasks.
|
||||
|
||||
## Events / Planning
|
||||
|
||||
- Store timestamps as `timestamptz`.
|
||||
- Require `ends_at` to be null or after `starts_at`.
|
||||
- Decide the first timezone behavior explicitly. For v2, storing absolute times and displaying in the user's browser/local preference is enough.
|
||||
- Planning should not leak events from tablos the user cannot access.
|
||||
|
||||
## Deployment
|
||||
|
||||
- WebSockets may require reverse-proxy upgrade config. SSE usually works through normal HTTP but still needs buffering disabled if the proxy buffers streams.
|
||||
- If using Caddy, verify streaming behavior during human UAT.
|
||||
77
.planning/research/STACK.md
Normal file
77
.planning/research/STACK.md
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# v2.0 Research: Stack Additions
|
||||
|
||||
**Date:** 2026-05-15
|
||||
**Scope:** Native per-tablo chat, etapes, Google/Apple sign-in, individual planning, tablo events.
|
||||
|
||||
## Existing Stack
|
||||
|
||||
- Go 1.26 module in `backend/`
|
||||
- chi router, gorilla/csrf, server-managed sessions
|
||||
- templ + HTMX + Tailwind
|
||||
- Postgres via pgx + sqlc + goose migrations
|
||||
- River worker already installed for background jobs
|
||||
- Single container / VPS-oriented deploy
|
||||
|
||||
## Recommended Additions
|
||||
|
||||
### Social sign-in
|
||||
|
||||
- Add `golang.org/x/oauth2` for OAuth2 authorization-code exchange and state handling helpers.
|
||||
- Add a small internal OIDC/JWT verifier package rather than a managed auth platform.
|
||||
- Store external identities in Postgres with provider, provider subject, verified email, and linked user ID.
|
||||
- Keep existing session issuance path: after provider verification, create or find a local `users` row, then create a local `sessions` row and cookie.
|
||||
|
||||
### Google sign-in
|
||||
|
||||
- Use Google's OpenID Connect server flow.
|
||||
- Required provider setup: Google Cloud OAuth client ID/secret and registered redirect URI.
|
||||
- Required runtime config: `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URL`.
|
||||
- Verify ID token claims before linking:
|
||||
- issuer is Google
|
||||
- audience equals configured client ID
|
||||
- subject is stable provider identity
|
||||
- email is present and verified
|
||||
|
||||
Sources:
|
||||
- Google OIDC docs: https://developers.google.com/identity/openid-connect/openid-connect
|
||||
- Google OAuth web-server docs: https://developers.google.com/identity/protocols/oauth2/web-server
|
||||
|
||||
### Apple sign-in
|
||||
|
||||
- Use Apple's web OAuth/REST flow.
|
||||
- Required provider setup: Apple Services ID, Team ID, Key ID, private key, redirect URI.
|
||||
- Required runtime config: `APPLE_CLIENT_ID`, `APPLE_TEAM_ID`, `APPLE_KEY_ID`, `APPLE_PRIVATE_KEY`, `APPLE_REDIRECT_URL`.
|
||||
- Generate Apple's `client_secret` as an ES256 JWT server-side; cache it until near expiration.
|
||||
- Exchange authorization code at `POST https://appleid.apple.com/auth/token`.
|
||||
- Verify Apple's ID token with Apple's public keys and persist provider subject. Treat Apple email/name as first-login-only data.
|
||||
|
||||
Sources:
|
||||
- Apple token validation: https://developer.apple.com/documentation/SigninwithAppleRESTAPI/Generate-and-validate-tokens
|
||||
- Apple authorization request: https://developer.apple.com/documentation/signinwithapplerestapi/request-an-authorization-to-the-sign-in-with-apple-server.
|
||||
- Apple client secret creation: https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret
|
||||
|
||||
### Real-time chat
|
||||
|
||||
Two viable options:
|
||||
|
||||
- SSE receive + normal POST send:
|
||||
- Best fit for HTMX/server-rendered UI.
|
||||
- Browser opens a per-tablo event stream; messages are posted with normal forms/HTMX.
|
||||
- Easier auth/CSRF story because writes remain HTTP POSTs.
|
||||
- One-way stream is enough for "new messages appear in real time".
|
||||
|
||||
- WebSocket:
|
||||
- Best if the same connection must both send and receive messages.
|
||||
- Requires explicit message protocol, origin checks, backpressure decisions, and CSRF-equivalent protection on upgrade.
|
||||
|
||||
Recommendation for v2: implement SSE for receive and HTMX POST for send. It satisfies "real time" without bringing in a managed chat vendor or forcing a larger client-side app model. Revisit WebSockets only if typing indicators, presence, or very high-frequency bidirectional events become requirements.
|
||||
|
||||
Sources:
|
||||
- MDN SSE guide: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
|
||||
- MDN WebSocket API: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
|
||||
|
||||
## Avoid
|
||||
|
||||
- No Stream Chat, Ably, Pusher, Firebase, Supabase Realtime, or managed chat/realtime services.
|
||||
- No Clerk/Auth0/Lucia. Google and Apple should only verify identity; Xtablo still owns account rows and sessions.
|
||||
- No recursive task hierarchy. Etapes are a one-level grouping table, not nested tasks.
|
||||
36
.planning/research/SUMMARY.md
Normal file
36
.planning/research/SUMMARY.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# v2.0 Research Summary
|
||||
|
||||
**Date:** 2026-05-15
|
||||
|
||||
## Stack Additions
|
||||
|
||||
- Add `golang.org/x/oauth2` and a small internal provider verification layer for Google/Apple.
|
||||
- Keep server-managed sessions as the only Xtablo session mechanism.
|
||||
- Use Postgres tables for social identities, messages, etapes, and events.
|
||||
- Implement real-time chat as SSE receive + HTMX POST send for v2. This satisfies real-time updates with less client complexity than WebSockets.
|
||||
|
||||
## Feature Table Stakes
|
||||
|
||||
- Google and Apple sign-in both validate provider identity, then create/link a local user and issue the existing session cookie.
|
||||
- Each tablo has a persistent message stream with authenticated posting and real-time updates.
|
||||
- Etapes group tasks one level deep: task -> optional etape; etapes have no parent.
|
||||
- Events belong to tablos and appear both in tablo context and in each user's planning page.
|
||||
|
||||
## Watch Outs
|
||||
|
||||
- OAuth state/nonce and ID token validation are non-negotiable.
|
||||
- Provider subject is the durable identity key; email alone is insufficient.
|
||||
- Apple profile data can be first-login-only.
|
||||
- Real-time delivery must be separate from message durability.
|
||||
- SSE/proxy buffering and keep-alives need manual verification.
|
||||
- Event authorization must follow tablo access.
|
||||
|
||||
## Sources
|
||||
|
||||
- Google OpenID Connect: https://developers.google.com/identity/openid-connect/openid-connect
|
||||
- Google OAuth 2.0 web server flow: https://developers.google.com/identity/protocols/oauth2/web-server
|
||||
- Apple token validation: https://developer.apple.com/documentation/SigninwithAppleRESTAPI/Generate-and-validate-tokens
|
||||
- Apple authorization request: https://developer.apple.com/documentation/signinwithapplerestapi/request-an-authorization-to-the-sign-in-with-apple-server.
|
||||
- Apple client secret creation: https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret
|
||||
- MDN Server-sent events: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
|
||||
- MDN WebSocket API: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
|
||||
Loading…
Reference in a new issue