Changeset 0.24.0 (#156)

This commit is contained in:
2026-03-07 11:27:24 +00:00
parent 2dc4514a57
commit a63728a481
23 changed files with 1850 additions and 385 deletions

1110
docs/DESIGN-0.24.0.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -109,16 +109,23 @@ v0.22.5 Surfaces + Go Templates + Admin Bug Fixes ✅
v0.22.6 Split Deployment Fix + CI Timeout + Code Pruning ✅
|
v0.22.7 Surface Prototypes (Theme + ChatPane + Splash) ✅
|
v0.22.8 Surface Integration (Wire into existing JS)
|
v0.23.0 @mention Routing + Persona Handles + Proxy ✅
v0.23.1 Multi-User: DMs + Group Chat
v0.22.8 Surface Integration (Wire into existing JS) ✅
v0.24.0 Auth Strategy (mTLS/OIDC) + Full RBAC + Group permissions
v0.23.0 @mention Routing + Persona Handles + Proxy ✅
v0.23.1 Multi-User Navigation + Conversation Taxonomy ✅
v0.23.2 Multi-User Polish + Channel Lifecycle ✅
v0.24.0 Auth Abstraction + User Identity ← active
├── v0.24.1 mTLS + OIDC Providers (parallel)
│ │
│ └── v0.24.3 Anonymous Sessions
└── v0.24.2 Fine-Grained Permissions (parallel)
┌───────┴──────────────┐
│ │
@@ -1169,34 +1176,39 @@ Depends on: surfaces + Go templates (v0.22.5), code pruning (v0.22.6).
---
## v0.22.8 — Surface Integration (Wire into Existing JS)
## v0.22.8 — Surface Integration (Wire into Existing JS)
Connect v0.22.7 prototypes to the existing frontend JS modules.
ChatPane.primary replaces singleton UI.* calls. Theme system wired
into appearance settings. Admin surface hybrid loader completed.
Naming cleanup: preset → persona, APIConfigID → ProviderConfigID.
Depends on: surface prototypes (v0.22.7).
**ChatPane Bridge:**
- [ ] `ChatPane.primary` creation in `startApp()` from server-rendered mount points
- [ ] `ui-core.js` delegation: `UI.renderMessages``ChatPane.primary.renderMessages`, etc.
- [ ] `editor-mode.js` ChatPane creation for workspace chat
- [ ] Branding init from `window.__SETTINGS__` (instanceName, logoURL)
- [x] `ChatPane.primary` creation in `startApp()` from server-rendered mount points
- [x] `ui-core.js` delegation: `UI.renderMessages``ChatPane.primary.renderMessages`, etc.
- [x] Branding init from `window.__SETTINGS__` (instanceName, logoURL)
**Theme Integration:**
- [ ] Wire `Theme` into appearance settings save/load cycle
- [ ] CM6 theme sync on theme change
- [ ] Persist theme preference to user settings API
- [x] Wire `Theme` into appearance settings save/load cycle
- [x] CM6 theme sync on theme change
- [x] Persist theme preference to user settings API
**Admin Surface:**
- [ ] Complete hybrid section loaders for all JS-loaded sections
- [ ] Admin surface stats/overview server-rendered content
- [x] Complete hybrid section loaders for all JS-loaded sections
**Settings Surface:**
- [ ] Models visibility toggles (API.saveModelPreferences)
- [ ] User Personas CRUD (add/edit/delete with confirm dialogs)
- [ ] BYOK provider CRUD in settings context
- [ ] Teams tab content
- [x] Models visibility toggles (API.saveModelPreferences)
- [x] User Personas CRUD (add/edit/delete with confirm dialogs)
- [x] BYOK provider CRUD in settings context
- [x] Teams tab content
**Naming Cleanup:**
- [x] preset → persona: routes, JSON keys, Go structs, JS, CSS, DOM IDs, templates
- [x] APIConfigID → ProviderConfigID: all Go structs and JSON fields
- [x] Shared `app-state.js` extracted from `app.js` — available on all surfaces
- [x] Auth tokens loaded for all surfaces via `base.html`
---
@@ -1233,78 +1245,212 @@ or `@model-id` and send.
---
## v0.23.1 — Multi-User: DMs + Group Chat
## v0.23.1 — Multi-User Navigation + Conversation Taxonomy
Real human-to-human messaging. Builds on v0.23.0's participant infrastructure
(`channel_participants`, @mention routing, WebSocket events). The @mention
system already resolves personas — this release extends it to resolve users.
Conversation type taxonomy (direct/dm/group/channel/workflow), three-section
sidebar (Projects → Channels → Chats), folder system for chats, DM plumbing,
channel configuration with `ai_mode`, presence heartbeat, and the unified
active conversation model.
Depends on: @mention routing (v0.23.0), WebSocket infrastructure (exists).
See [DESIGN-0_23_1.md](DESIGN-0_23_1.md) for the full spec.
**User-to-User DMs**
- [ ] `@username` resolution in `resolveMention()`: extend DB lookup to `users` table
- [ ] When `resolveMention()` returns a user (not persona): skip completion, deliver as notification
- [ ] DM channel type: two user participants, no AI unless explicitly @mentioned
- [ ] DM creation flow: user picker, creates channel with both users as participants
- [ ] Unread count per channel per user (read cursor in `channel_participants`)
- [ ] Real-time message delivery via WebSocket to recipient's active session
**Conversation Types:**
- [x] Five-type taxonomy: direct (1:1 AI), dm (human-to-human), group (multi-participant), channel (named persistent), workflow (staged, deferred)
- [x] Channel type constraint extended: `dm`, `channel` added to `type` CHECK
- [x] `ai_mode` column on channels: `auto` | `mention_only` | `off`
- [x] `ai_mode` guard in completion handler — `off` returns 403, `mention_only` without @mention delivers without AI
- [x] `topic` column on channels for header display
**Group Chat (Human + AI)**
- [ ] Persona group CRUD: create/edit/delete saved roster templates (`persona_groups`)
- [ ] "New Chat with Group" flow: pick a template, stamp onto fresh channel
- [ ] Group leader (default responder): `is_leader` flag on `persona_group_members`
- [ ] No @mention in group = leader responds. @mention overrides.
- [ ] Group editing: add/remove personas, change leader — affects future chats only
- [ ] `@all` routing: every persona participant responds (existing `multiModelStream` path)
**Sidebar Architecture:**
- [x] Three collapsible sections: Projects → Channels → Chats
- [x] `renderChannelsSection()` with # / person icons, unread badges, online dots
- [x] Channel items: context menu (rename, set topic, delete), ⋯ hover button
- [x] Section collapse/expand state in localStorage
**Presence + Real-time**
- [ ] Typing indicators for human participants (extend existing persona typing events)
- [ ] Online/offline status per user (heartbeat-based, stored in memory not DB)
- [ ] Participant join/leave WebSocket events
- [ ] Unread badge on sidebar chat items
**Folder System:**
- [x] `chat_folders` table, CRUD handler (`handlers/folders.go`)
- [x] Drag chats into/out of folders; folder context menu (rename, delete)
- [x] Folder delete modal: "Keep chats" / "Delete chats too" / Cancel
- [x] Unfiled drop zone when folders exist
**User @mention UX**
- [ ] Autocomplete includes users alongside models and personas (different icon)
- [ ] @mention pills distinguish user mentions (different color) from persona/model mentions
- [ ] Notification center: @mention notifications with channel deep-link
**DM Plumbing:**
- [x] `resolveMention()` extended to resolve users (exact + prefix on username)
- [x] User @mention skips AI, delivers `user.mentioned` WebSocket notification
- [x] DM creation flow with user search modal
- [x] DM dedup guard — one channel per participant pair
**Message Attribution**
- [ ] Messages from human users show their avatar + display name (not "You" for other participants)
- [ ] Messages from personas retain current avatar + handle rendering
- [ ] Thread view: flat chronological for now, threading deferred
**Presence:**
- [x] `user_presence` table with heartbeat upsert
- [x] `POST /presence/heartbeat` (30s interval), `GET /presence?users=...` (90s threshold)
- [x] Presence WebSocket events: `presence.changed`
**Migration**
- [ ] `users.handle` column (unique, auto-generated from username or display_name)
- [ ] Read cursor columns on `channel_participants`: `last_read_message_id`, `last_read_at`
- [ ] Notification table: `user_notifications` (type, channel_id, message_id, actor_id, read_at)
**Channel Participants:**
- [x] `channel_participants` CRUD handler, auto-created on channel creation
- [x] Persona group CRUD endpoints wired (`persona_groups`, `persona_group_members`)
- [x] DM participant auto-creation from `req.Participants`
**Migration:**
- [x] 016_v023_multiuser: `ai_mode`, `topic`, `user_presence`, channel type extension
---
## v0.24.0 — Auth Strategy (mTLS/OIDC) + Full RBAC
## v0.23.2 — Multi-User Polish + Channel Lifecycle
Enterprise auth modes on top of the teams + groups foundation.
Depends on: vault passphrase support from v0.9.3 (conditional unlock for
personal providers under external auth), user groups (v0.16.0).
Bug fixes, unified active conversation refactor, channel lifecycle (archive/delete),
group chat leader routing, `@all` fan-out, message attribution, and deferred
surface integration from v0.22.8.
_(Shifted from v0.21.0 — enhanced with group-based permissions)_
Depends on: v0.23.1 sidebar and conversation taxonomy.
- [ ] `AUTH_MODE` env var: `builtin` | `mtls` | `oidc`
- [ ] All three resolve to the same internal user model
- [ ] `auth_source` + `external_id` columns on users table
- [ ] mTLS: header trust, auto-provision from cert DN
- [ ] OIDC: Keycloak/Okta token validation, claim extraction, role mapping
- [ ] Per-source auto-activate policy: auto_activate, default_team, default_role
- [ ] Vault passphrase prompt for mTLS/OIDC users (v0.9.3 conditional unlock)
- [ ] Fine-grained permissions: model access, KB write, task create, token budgets
- [ ] Group-based permission sets: assign permissions to groups, users inherit from membership
- [ ] OIDC claim → group mapping: external IdP groups auto-sync to internal groups
**Unified Active Conversation:**
- [x] `App.activeConversation = { id, type }` replaces `currentChatId` + `currentChannelId`
- [x] `setActive(id, type)`, `activeId` getter, `getActiveChat()` helper
- [x] Send path, model bar, streaming, session restore all keyed off `activeConversation.id`
- [x] Sidebar highlight works across both chat and channel sections
**Anonymous / Session Participants**
- [ ] Session-based identity for unauthenticated visitors (workflow intake)
- [ ] mTLS: cert CN/fingerprint as stable session identity (no `users` row required)
- [ ] Non-mTLS: ephemeral session token (cookie or URL token)
- [ ] Session participants can send messages and trigger tool calls within workflow channels
- [ ] Session identity visible to assigned team members (cert DN, display name, or "Anonymous #N")
**Group Chat + Persona Groups:**
- [x] Group leader default response: no @mention in group → leader persona responds
- [x] `@all` fan-out: routes to every persona participant, depth-1 only
- [x] Participant mutation guards: DMs keep ≥2 humans, groups keep ≥1 persona
- [x] "New Group Chat" flow with ad-hoc persona picker
**Message Attribution:**
- [x] Human sender display: other participants show avatar + display name (not "You")
- [x] Persona sender display verified for loaded history in channel context
**Channel Lifecycle:**
- [x] Archive action via context menu (`is_archived`, `archived_at`)
- [x] Delete gated by `channel_retention.mode` (flexible/retain)
- [x] Admin purge with `purge_after_days` floor
- [x] Retention config keys in `global_config`
**Bug Fixes:**
- [x] Channel persistence through refresh (resp.channels → resp.data)
- [x] Folder drag-and-drop (folderId mapping, drop handlers, unfiled zone)
- [x] DM creation 404 → added `GET /users/search` endpoint
- [x] Channel ⋯ menu consistency (replaced ✕ with hover ⋯ pattern)
- [x] Folder ⋯ button reflow (visibility:hidden instead of display:none)
- [x] Unread subquery using `last_read_at` (not migration-017-dependent column)
- [x] Settings Models section template wiring
- [x] `u.avatar``u.avatar_url` in ListMessages JOIN and treepath
**Surface Integration (deferred v0.22.8):**
- [x] `ChatPane.primary` bridge from server-rendered mount points
- [x] Theme save/load cycle wired to settings appearance
- [x] Admin hybrid section loaders completed
- [x] Settings surface: models toggles, persona CRUD, BYOK, teams
**@mention UX:**
- [x] Autocomplete includes users alongside personas and models
- [x] User mention pill styling (distinct color from persona pills)
- [x] `user.mentioned` WebSocket notification with toast
**Migration:**
- [x] 017_unread: `last_read_message_id` on `channel_participants`
---
## v0.24.0 — Auth Abstraction + User Identity ← active
Auth provider interface decoupling builtin auth from the handler/middleware
layer. User model extended with `auth_source`, `external_id`, `handle`.
@mention resolution upgraded to use handles. Zero behavior change for
existing users — builtin mode goes through the new abstraction identically.
See [DESIGN-0.24.0.md](DESIGN-0.24.0.md) for the full spec including
v0.24.1v0.24.3 subversions.
Depends on: v0.23.0 (channel_participants), v0.16.0 (groups), v0.9.4 (vault).
**Auth Provider Interface (`server/auth/`):**
- [x] `Provider` interface: `Mode()`, `Authenticate()`, `SupportsRegistration()`, `Register()`
- [x] `BuiltinProvider`: extracts login/register from `handlers/auth.go`
- [x] `UniqueHandle()`: collision-safe handle generation with `-2`, `-3` suffixes
- [x] `ErrorToHTTPStatus()`: maps auth errors to HTTP codes
- [x] `ParseMode()`: validates `AUTH_MODE` string with error return
**Auth Handler Refactor:**
- [x] `AuthHandler` gains `provider auth.Provider` field
- [x] `Login()` delegates to `provider.Authenticate()`, then vault unlock + JWT
- [x] `Register()` delegates to `provider.Register()`, then vault init + JWT
- [x] `generateTokens()` response includes `handle`, `auth_source`
- [x] `BootstrapAdmin()` / `SeedUsers()` backfill handles for existing users
**User Model Extension:**
- [x] `AuthSource` (builtin/mtls/oidc), `ExternalID` (nullable), `Handle` (unique)
- [x] All user store queries (PG + SQLite) include three new columns
- [x] `GetByHandle()`, `GetByExternalID()` on both store implementations
- [x] Unified `scanOneUser` / `scanOne` helpers replace per-method scan blocks
**Config + Middleware:**
- [x] `AUTH_MODE` env var (default `builtin`)
- [x] `main.go` dispatch: builtin wired, mTLS/OIDC fail-fast at startup
- [x] Middleware unchanged — JWT validation is auth-mode-agnostic
**@mention Resolution:**
- [x] `resolveMention()` uses `LOWER(handle)` instead of `LOWER(username)`
- [x] `SearchUsers` returns `handle`, filters on handle
- [x] Frontend autocomplete matches on handle, uses handle as @mention token
**Migration:**
- [ ] 018_auth_abstraction: `auth_source`, `external_id`, `handle` columns + unique indexes + backfill
---
## v0.24.1 — mTLS + OIDC Providers
Two external auth implementations plugging into v0.24.0's provider interface.
Depends on: auth abstraction (v0.24.0).
See [DESIGN-0.24.0.md](DESIGN-0.24.0.md) §v0.24.1 for full spec.
- [ ] mTLS provider: trusted header extraction, cert DN parsing, auto-provision
- [ ] OIDC provider: well-known discovery, token validation, claim extraction
- [ ] Per-source auto-activate policy (global_config keys)
- [ ] OIDC claim → group mapping (external groups sync to internal groups)
- [ ] Login page adapts by `AUTH_MODE` (form / cert status / SSO button)
- [ ] mTLS users: no personal vault (`vault_set=false`), team/global providers only
- [ ] Migration 019: `oidc_auth_state` table, `groups.source` column
---
## v0.24.2 — Fine-Grained Permissions
Groups become capability carriers. Permission model on top of existing
group infrastructure.
Depends on: auth abstraction (v0.24.0). Parallel to v0.24.1.
See [DESIGN-0.24.0.md](DESIGN-0.24.0.md) §v0.24.2 for full spec.
- [ ] Permission constants (`domain.action` convention, code-level enum)
- [ ] `permissions` JSONB column on groups
- [ ] `ResolvePermissions()`: union of group permissions + `DefaultUserPerms`
- [ ] `RequirePermission()` middleware
- [ ] Token budgets: per-group daily/monthly ceilings, enforced in completion handler
- [ ] Model access control: per-group model allowlists
- [ ] Admin UI: permission checklist, budget fields, model picker on group edit
- [ ] `DefaultUserPerms` override via `global_config` key `default_permissions`
- [ ] Migration 020: permissions, budgets, allowed_models on groups
---
## v0.24.3 — Anonymous / Session Participants
Unauthenticated visitors for workflow intake channels. Direct
prerequisite for v0.25.0 (Workflow Engine).
Depends on: mTLS provider (v0.24.1) for cert-based anonymous identity.
See [DESIGN-0.24.0.md](DESIGN-0.24.0.md) §v0.24.3 for full spec.
- [ ] `session_participants` table (channel-scoped, ephemeral token)
- [ ] Session JWT: `SessionClaims{SessionID, ChannelID}` — distinct from user JWT
- [ ] `SessionOrAuth()` middleware: accepts either JWT type
- [ ] Capability scoping: session participants limited to their bound channel
- [ ] mTLS anonymous path: cert fingerprint as stable session identity
- [ ] `channels.allow_anonymous` flag
- [ ] Session display in participant list (team members see visitor identity)
- [ ] Migration 021: `session_participants` table, `allow_anonymous` column
---
@@ -1316,7 +1462,7 @@ handles structured data collection. See [ARCHITECTURE.md — Workflow
Architecture](ARCHITECTURE.md#workflow-architecture-future--v0210) for
the conceptual model.
Depends on: multi-participant channels (v0.23.0), anonymous identity (v0.24.0).
Depends on: multi-participant channels (v0.23.0), anonymous identity (v0.24.3).
_(Shifted from v0.22.0 — no content changes, dependency refs updated)_