chat over ssh, powered by atproto
1package main
2
3import (
4 "context"
5 "fmt"
6 "net/url"
7 "time"
8
9 "github.com/charmbracelet/ssh"
10 "github.com/charmbracelet/wish"
11
12 "github.com/bluesky-social/indigo/atproto/identity"
13 "github.com/bluesky-social/indigo/atproto/syntax"
14)
15
16func checkPDSAllowed(handle string) error {
17 dir := identity.DefaultDirectory()
18 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
19 defer cancel()
20 h, err := syntax.ParseHandle(handle)
21 if err != nil {
22 return fmt.Errorf("invalid handle: %w", err)
23 }
24 ident, err := dir.LookupHandle(ctx, h)
25 if err != nil {
26 return fmt.Errorf("identity lookup: %w", err)
27 }
28 pdsURL := ident.PDSEndpoint()
29 if pdsURL == "" {
30 return fmt.Errorf("no PDS endpoint found")
31 }
32 parsed, err := url.Parse(pdsURL)
33 if err != nil {
34 return fmt.Errorf("invalid PDS URL: %w", err)
35 }
36 host := parsed.Hostname()
37 for _, allowed := range allowedPDS {
38 if host == allowed {
39 return nil
40 }
41 }
42 return fmt.Errorf("PDS %s is not on the allowlist", host)
43}
44
45func pdsGateMiddleware() wish.Middleware {
46 return func(next ssh.Handler) ssh.Handler {
47 return func(s ssh.Session) {
48 handle := s.User()
49 if err := checkPDSAllowed(handle); err != nil {
50 fmt.Fprintf(s, "\033[31mAccess denied: %s\033[0m\r\n", err)
51 return
52 }
53 next(s)
54 }
55 }
56}
57
58func goodbyeMiddleware() wish.Middleware {
59 return func(next ssh.Handler) ssh.Handler {
60 return func(s ssh.Session) {
61 next(s)
62 fmt.Fprintf(s, "Goodbye!\r\n")
63 }
64 }
65}