Antonio Mika
·
2026-10-22
1package prose
2
3import (
4 "context"
5 "os"
6 "os/signal"
7 "syscall"
8
9 "github.com/picosh/pico/pkg/db/postgres"
10 "github.com/picosh/pico/pkg/pssh"
11 "github.com/picosh/pico/pkg/send/auth"
12 "github.com/picosh/pico/pkg/send/list"
13 "github.com/picosh/pico/pkg/send/pipe"
14 "github.com/picosh/pico/pkg/send/protocols/rsync"
15 "github.com/picosh/pico/pkg/send/protocols/scp"
16 "github.com/picosh/pico/pkg/send/protocols/sftp"
17 "github.com/picosh/pico/pkg/shared"
18 "github.com/picosh/pico/pkg/storage"
19)
20
21const help = `prose.sh: a blog you publish over ssh
22
23 rsync ~/blog/* prose.sh:/ publish posts and images; scp, sftp and sshfs work too
24 cat post.md | ssh prose.sh hello-world.md upload one post
25 cat post.md | ssh prose.sh upload with a random name
26
27Run ssh pico.sh for your dashboard. Docs: https://pico.sh/prose
28`
29
30func StartSshServer() {
31 appName := "prose-ssh"
32
33 host := shared.GetEnv("PROSE_HOST", "0.0.0.0")
34 port := shared.GetEnv("PROSE_SSH_PORT", "2222")
35 promPort := shared.GetEnv("PROSE_PROM_PORT", "9222")
36 cfg := NewConfig(appName)
37 logger := cfg.Logger
38
39 ctx, cancel := context.WithCancel(context.Background())
40 defer cancel()
41
42 dbh := NewDB(postgres.NewDB(cfg.DbURL, cfg.Logger))
43 defer func() {
44 _ = dbh.Close()
45 }()
46
47 adapter := storage.GetStorageTypeFromEnv()
48 st, err := storage.NewStorage(cfg.Logger, adapter)
49 if err != nil {
50 logger.Error("loading storage", "err", err)
51 return
52 }
53
54 handler := NewFileHandler(cfg, dbh, st)
55
56 sshAuth := shared.NewSshAuthHandler(dbh, logger, "prose")
57
58 // Create a new SSH server
59 server, err := pssh.NewSSHServerWithConfig(
60 ctx,
61 logger,
62 appName,
63 host,
64 port,
65 promPort,
66 "ssh_data/term_info_ed25519",
67 sshAuth.PubkeyAuthHandler,
68 []pssh.SSHServerMiddleware{
69 pipe.Middleware(handler, ".md"),
70 list.Middleware(handler),
71 scp.Middleware(handler),
72 rsync.Middleware(handler),
73 auth.Middleware(handler),
74 pssh.PtyMdw(pssh.HelpNotice(help)),
75 pssh.LogMiddleware(handler, dbh),
76 },
77 []pssh.SSHServerMiddleware{
78 sftp.Middleware(handler),
79 pssh.LogMiddleware(handler, dbh),
80 },
81 nil,
82 )
83
84 if err != nil {
85 logger.Error("failed to create ssh server", "err", err.Error())
86 os.Exit(1)
87 }
88
89 done := make(chan os.Signal, 1)
90 signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
91 logger.Info("Starting SSH server", "addr", server.Config.ListenAddr)
92 go func() {
93 if err = server.ListenAndServe(); err != nil {
94 logger.Error("serve", "err", err.Error())
95 os.Exit(1)
96 }
97 }()
98
99 exit := func() {
100 logger.Info("stopping ssh server")
101 cancel()
102 }
103
104 <-done
105 exit()
106}