docs: research milestone v2.0 collaboration planning social sign-in

This commit is contained in:
Arthur Belleville 2026-05-15 20:11:23 +02:00
parent 0d23d94700
commit 367364e9f8
No known key found for this signature in database
5 changed files with 279 additions and 0 deletions

View 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.

View 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.

View 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.

View 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.

View 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