chat over ssh, powered by atproto
1package main
2
3import (
4 "crypto/sha256"
5 "encoding/json"
6 "fmt"
7 "os"
8 "sync"
9
10 "github.com/charmbracelet/ssh"
11)
12
13// --- key store: maps SSH pubkey fingerprint -> atproto handle ---
14
15type keyStore struct {
16 mu sync.Mutex
17 path string
18 keys map[string]string // fingerprint -> handle
19}
20
21func newKeyStore(path string) *keyStore {
22 ks := &keyStore{path: path, keys: make(map[string]string)}
23 data, err := os.ReadFile(path)
24 if err == nil {
25 json.Unmarshal(data, &ks.keys)
26 }
27 return ks
28}
29
30func (ks *keyStore) lookup(pubkey ssh.PublicKey) string {
31 if pubkey == nil {
32 return ""
33 }
34 ks.mu.Lock()
35 defer ks.mu.Unlock()
36 fp := fingerprint(pubkey)
37 return ks.keys[fp]
38}
39
40func (ks *keyStore) save(pubkey ssh.PublicKey, handle string) {
41 if pubkey == nil {
42 return
43 }
44 ks.mu.Lock()
45 defer ks.mu.Unlock()
46 ks.keys[fingerprint(pubkey)] = handle
47 data, _ := json.MarshalIndent(ks.keys, "", " ")
48 os.MkdirAll(keysDir, 0700)
49 os.WriteFile(ks.path, data, 0600)
50}
51
52func fingerprint(pubkey ssh.PublicKey) string {
53 h := sha256.Sum256(pubkey.Marshal())
54 return fmt.Sprintf("%x", h)
55}