1package knotserver
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7
8 "github.com/urfave/cli/v3"
9 "tangled.sh/tangled.sh/core/api/tangled"
10 "tangled.sh/tangled.sh/core/jetstream"
11 "tangled.sh/tangled.sh/core/knotserver/config"
12 "tangled.sh/tangled.sh/core/knotserver/db"
13 "tangled.sh/tangled.sh/core/log"
14 "tangled.sh/tangled.sh/core/rbac"
15)
16
17func Command() *cli.Command {
18 return &cli.Command{
19 Name: "server",
20 Usage: "run a knot server",
21 Action: Run,
22 Description: `
23Environment variables:
24 KNOT_SERVER_SECRET (required)
25 KNOT_SERVER_HOSTNAME (required)
26 KNOT_SERVER_LISTEN_ADDR (default: 0.0.0.0:5555)
27 KNOT_SERVER_INTERNAL_LISTEN_ADDR (default: 127.0.0.1:5444)
28 KNOT_SERVER_DB_PATH (default: knotserver.db)
29 KNOT_SERVER_JETSTREAM_ENDPOINT (default: wss://jetstream1.us-west.bsky.network/subscribe)
30 KNOT_SERVER_DEV (default: false)
31 KNOT_REPO_SCAN_PATH (default: /home/git)
32 KNOT_REPO_README (comma-separated list)
33 KNOT_REPO_MAIN_BRANCH (default: main)
34 APPVIEW_ENDPOINT (default: https://tangled.sh)
35`,
36 }
37}
38
39func Run(ctx context.Context, cmd *cli.Command) error {
40 l := log.FromContext(ctx)
41
42 c, err := config.Load(ctx)
43 if err != nil {
44 return fmt.Errorf("failed to load config: %w", err)
45 }
46
47 if c.Server.Dev {
48 l.Info("running in dev mode, signature verification is disabled")
49 }
50
51 db, err := db.Setup(c.Server.DBPath)
52 if err != nil {
53 return fmt.Errorf("failed to load db: %w", err)
54 }
55
56 e, err := rbac.NewEnforcer(c.Server.DBPath)
57 if err != nil {
58 return fmt.Errorf("failed to setup rbac enforcer: %w", err)
59 }
60
61 e.E.EnableAutoSave(true)
62
63 jc, err := jetstream.NewJetstreamClient(c.Server.JetstreamEndpoint, "knotserver", []string{
64 tangled.PublicKeyNSID,
65 tangled.KnotMemberNSID,
66 }, nil, l, db, true)
67 if err != nil {
68 l.Error("failed to setup jetstream", "error", err)
69 }
70
71 mux, err := Setup(ctx, c, db, e, jc, l)
72 if err != nil {
73 return fmt.Errorf("failed to setup server: %w", err)
74 }
75 imux := Internal(ctx, db, e)
76
77 l.Info("starting internal server", "address", c.Server.InternalListenAddr)
78 go http.ListenAndServe(c.Server.InternalListenAddr, imux)
79
80 l.Info("starting main server", "address", c.Server.ListenAddr)
81 l.Error("server error", "error", http.ListenAndServe(c.Server.ListenAddr, mux))
82
83 return nil
84}