Skip to content

Commit bd57baf

Browse files
authored
feat: add public chat share links (#33)
* feat: add public chat share links * fix: render public share markdown * fix: render public share markdown * fix: use hostname labels for public share urls * fix: render share source urls as links * fix: remove duplicate public share summary excerpt * Add link to sourcecode at bottom.
1 parent 3cc7545 commit bd57baf

18 files changed

Lines changed: 2041 additions & 121 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ development date for the change set.
55

66
## Recent Improvements
77

8+
### Public Chat Shares (2026-05-15)
9+
10+
- **Added**: Completed browser Chat answers can now be shared as stable public `/share/{slug}` pages, with deterministic summaries, categories, original external URLs, and per-owner share history in the web UI.
11+
- **Hardening**: Public share pages bypass web auth only for `/share/{slug}`, render with server-side escaping, avoid booting the authenticated SPA, and redact internal source keys, lookup IDs, note paths, local filesystem paths, and protected app routes.
12+
- **Display**: Public share pages now render the sanitized chat answer as Markdown-derived HTML without duplicating the raw Markdown summary excerpt, convert source URLs into real links, include source titles/summaries beside full original URL links, and clean trailing URL punctuation/backticks including `%60`-encoded backticks so copied links do not 404, while keeping raw HTML inert.
13+
- **Schema/Tests**: Added DB-backed share storage with HMAC slugs derived from a local random share salt, plus migration and web auth boundary regression tests.
14+
- **Location**: `internal/store/`, `web/`, `web/ui/`
15+
816
### Authenticated Access Logging (2026-05-14)
917

1018
- **Operations**: Web requests now emit app-layer access logs with the authenticated GitHub identity after session validation, which keeps Funnel traffic attributable even without Tailscale identity headers.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ require (
101101
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da // indirect
102102
github.com/x448/float16 v0.8.4 // indirect
103103
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
104+
github.com/yuin/goldmark v1.8.2 // indirect
104105
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
105106
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
106107
golang.org/x/crypto v0.46.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
297297
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
298298
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
299299
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
300+
github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
301+
github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
300302
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
301303
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
302304
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=

internal/store/migrations.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"time"
77
)
88

9-
const currentSchemaVersion = 8
9+
const currentSchemaVersion = 9
1010

1111
type schemaMigration struct {
1212
Version int
@@ -95,6 +95,13 @@ var schemaMigrations = []schemaMigration{
9595
return s.ensureAuthUserTables()
9696
},
9797
},
98+
{
99+
Version: 9,
100+
Name: "public_chat_shares",
101+
Run: func(s *Store) error {
102+
return s.ensurePublicChatShareTables()
103+
},
104+
},
98105
}
99106

100107
func (s *Store) migrate(reporter MigrationReporter) error {

0 commit comments

Comments
 (0)