repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

commit
c9b9480
parent
3d03d7b
author
Antonio Mika
date
2024-10-11 16:21:28 +0000 UTC
Merge pull request #147 from picosh/am/light-refactor

Refactor pubsub->pipe
45 files changed,  +285, -397
M .env.example
+16, -16
 1@@ -146,19 +146,19 @@ AUTH_DOMAIN=http://auth.dev.pico.sh:3006
 2 AUTH_ISSUER=pico.sh
 3 AUTH_WEB_PORT=3000
 4 
 5-PUBSUB_CADDYFILE=./caddy/Caddyfile
 6-PUBSUB_V4=
 7-PUBSUB_V6=
 8-PUBSUB_HTTP_V4=$PUBSUB_V4:80
 9-PUBSUB_HTTP_V6=[$PUBSUB_V6]:80
10-PUBSUB_HTTPS_V4=$PUBSUB_V4:443
11-PUBSUB_HTTPS_V6=[$PUBSUB_V6]:443
12-PUBSUB_SSH_V4=$PUBSUB_V4:22
13-PUBSUB_SSH_V6=[$PUBSUB_V6]:22
14-PUBSUB_HOST=
15-PUBSUB_SSH_PORT=2222
16-PUBSUB_WEB_PORT=3000
17-PUBSUB_PROM_PORT=9222
18-PUBSUB_DOMAIN=pubsub.dev.pico.sh:3001
19-PUBSUB_PROTOCOL=http
20-PUBSUB_DEBUG=1
21+PIPE_CADDYFILE=./caddy/Caddyfile
22+PIPE_V4=
23+PIPE_V6=
24+PIPE_HTTP_V4=$PIPE_V4:80
25+PIPE_HTTP_V6=[$PIPE_V6]:80
26+PIPE_HTTPS_V4=$PIPE_V4:443
27+PIPE_HTTPS_V6=[$PIPE_V6]:443
28+PIPE_SSH_V4=$PIPE_V4:22
29+PIPE_SSH_V6=[$PIPE_V6]:22
30+PIPE_HOST=
31+PIPE_SSH_PORT=2222
32+PIPE_WEB_PORT=3000
33+PIPE_PROM_PORT=9222
34+PIPE_DOMAIN=pipe.dev.pico.sh:3001
35+PIPE_PROTOCOL=http
36+PIPE_DEBUG=1
M .github/workflows/build.yml
+1, -1
1@@ -41,7 +41,7 @@ jobs:
2     needs: test
3     strategy:
4       matrix:
5-        APP: [prose, pastes, imgs, pgs, feeds, pubsub]
6+        APP: [prose, pastes, imgs, pgs, feeds, pipe]
7     steps:
8     - name: Checkout repo
9       uses: actions/checkout@v3
M Makefile
+2, -2
 1@@ -58,7 +58,7 @@ bp-%: bp-setup
 2 	$(DOCKER_BUILDX_BUILD) --build-arg "APP=$*" -t "ghcr.io/picosh/pico/$*-web:$(DOCKER_TAG)" --target release-web .
 3 .PHONY: bp-%
 4 
 5-bp-all: bp-prose bp-pastes bp-imgs bp-feeds bp-pgs bp-auth bp-bouncer bp-pubsub
 6+bp-all: bp-prose bp-pastes bp-imgs bp-feeds bp-pgs bp-auth bp-bouncer bp-pipe
 7 .PHONY: bp-all
 8 
 9 build-auth:
10@@ -74,7 +74,7 @@ build-%:
11 	go build -o "build/$*-ssh" "./cmd/$*/ssh"
12 .PHONY: build-%
13 
14-build: build-prose build-pastes build-imgs build-feeds build-pgs build-auth build-pico build-pubsub
15+build: build-prose build-pastes build-imgs build-feeds build-pgs build-auth build-pico build-pipe
16 .PHONY: build
17 
18 store-clean:
M caddy/Caddyfile.auth
+0, -13
 1@@ -1,9 +1,4 @@
 2 {
 3-	on_demand_tls {
 4-		ask http://web:3000/check
 5-		interval 1m
 6-		burst 10
 7-	}
 8 	servers {
 9 		metrics
10 	}
11@@ -129,11 +124,3 @@
12 		Access-Control-Allow-Headers "*"
13 	}
14 }
15-
16-:443 {
17-	reverse_proxy web:3000
18-	tls {$APP_EMAIL} {
19-		on_demand
20-	}
21-	encode zstd gzip
22-}
D caddy/Caddyfile.monitoring
+0, -102
  1@@ -1,102 +0,0 @@
  2-{
  3-	on_demand_tls {
  4-		ask http://web:3000/check
  5-		interval 1m
  6-		burst 10
  7-	}
  8-	servers {
  9-		metrics
 10-	}
 11-}
 12-
 13-*.{$APP_DOMAIN}, {$APP_DOMAIN} {
 14-	reverse_proxy web:3000
 15-	tls {$APP_EMAIL} {
 16-		dns cloudflare {$CF_API_TOKEN}
 17-		resolvers 1.1.1.1
 18-	}
 19-	encode zstd gzip
 20-
 21-	header {
 22-		# disable FLoC tracking
 23-		Permissions-Policy interest-cohort=()
 24-
 25-		# enable HSTS
 26-		Strict-Transport-Security max-age=31536000;
 27-
 28-		# disable clients from sniffing the media type
 29-		X-Content-Type-Options nosniff
 30-
 31-		# clickjacking protection
 32-		X-Frame-Options DENY
 33-
 34-		# keep referrer data off of HTTP connections
 35-		Referrer-Policy no-referrer-when-downgrade
 36-
 37-		Content-Security-Policy "default-src 'self'; img-src * 'unsafe-inline'; style-src * 'unsafe-inline'"
 38-
 39-		X-XSS-Protection "1; mode=block"
 40-	}
 41-
 42-	@caddymetrics {
 43-		host {$APP_DOMAIN}
 44-		path /_caddy/metrics
 45-	}
 46-
 47-	metrics @caddymetrics {
 48-		disable_openmetrics
 49-	}
 50-
 51-	@appmetrics {
 52-		host {$APP_DOMAIN}
 53-		path /_app/metrics
 54-	}
 55-
 56-	handle @appmetrics {
 57-		rewrite * /metrics
 58-		reverse_proxy ssh:9222
 59-	}
 60-}
 61-
 62-monitoring.{$MONITORING_APP_DOMAIN}, prometheus.{$MONITORING_APP_DOMAIN}, grafana.{$MONITORING_APP_DOMAIN} {
 63-       @grafana {
 64-               host grafana.{$MONITORING_APP_DOMAIN}
 65-       }
 66-
 67-       @prometheus {
 68-               host prometheus.{$MONITORING_APP_DOMAIN}
 69-       }
 70-
 71-       tls {$MONITORING_APP_EMAIL} {
 72-               dns cloudflare {$CF_API_TOKEN}
 73-               resolvers 1.1.1.1
 74-       }
 75-
 76-       encode zstd gzip
 77-
 78-       reverse_proxy @grafana grafana:3000
 79-
 80-       basicauth @prometheus {
 81-               eric JDJhJDE0JDdPOXhoNUdhSmNVNDl6UWpmeTE0cWVkLjRwcUNJUnc0dVQ4MTZNSmVaNjA1TlptaVZYY1hh
 82-               antonio JDJhJDE0JHI5dkVtMW0vcGxIb011OG4vME5HOU91c3U2VjM2QTZiWVpUeXdSbEg3VUtNZVdhN3BRazFH
 83-               bot JDJhJDE0JFVsRlNHSDlJbFhDeUd0NldRR2JkcGVFYUJtWGluTHZDVlc5L3QwNWNwWUMuODRlcXZNZHpT
 84-       }
 85-       reverse_proxy @prometheus prometheus:9090
 86-
 87-       @caddymetrics {
 88-               host monitoring.{$MONITORING_APP_DOMAIN}
 89-               path /_caddy/metrics
 90-       }
 91-
 92-       metrics @caddymetrics {
 93-               disable_openmetrics
 94-       }
 95-}
 96-
 97-:443 {
 98-	reverse_proxy web:3000
 99-	tls {$APP_EMAIL} {
100-		on_demand
101-	}
102-	encode zstd gzip
103-}
M caddy/Caddyfile.pico
+1, -1
1@@ -1,4 +1,4 @@
2-{$APP_DOMAIN}, tmp.pico.sh {
3+{$APP_DOMAIN} {
4   reverse_proxy https://pico-docs-prod.pgs.sh {
5     header_up Host pico-docs-prod.pgs.sh
6   }
A caddy/Caddyfile.pipe
+54, -0
 1@@ -0,0 +1,54 @@
 2+{
 3+	servers {
 4+		metrics
 5+	}
 6+}
 7+
 8+*.{$APP_DOMAIN}, {$APP_DOMAIN} {
 9+	reverse_proxy web:3000
10+	tls {$APP_EMAIL} {
11+		dns cloudflare {$CF_API_TOKEN}
12+		resolvers 1.1.1.1
13+	}
14+	encode zstd gzip
15+
16+	header {
17+		# disable FLoC tracking
18+		Permissions-Policy interest-cohort=()
19+
20+		# enable HSTS
21+		Strict-Transport-Security max-age=31536000;
22+
23+		# disable clients from sniffing the media type
24+		X-Content-Type-Options nosniff
25+
26+		# clickjacking protection
27+		X-Frame-Options DENY
28+
29+		# keep referrer data off of HTTP connections
30+		Referrer-Policy no-referrer-when-downgrade
31+
32+		Content-Security-Policy "default-src 'self'; img-src * 'unsafe-inline'; style-src * 'unsafe-inline'"
33+
34+		X-XSS-Protection "1; mode=block"
35+	}
36+
37+	@caddymetrics {
38+		host {$APP_DOMAIN}
39+		path /_caddy/metrics
40+	}
41+
42+	metrics @caddymetrics {
43+		disable_openmetrics
44+	}
45+
46+	@appmetrics {
47+		host {$APP_DOMAIN}
48+		path /_app/metrics
49+	}
50+
51+	handle @appmetrics {
52+		rewrite * /metrics
53+		reverse_proxy ssh:9222
54+	}
55+}
D caddy/Caddyfile.pubsub
+0, -8
1@@ -1,8 +0,0 @@
2-{$APP_DOMAIN} {
3-	reverse_proxy web:3000
4-
5-  tls {$APP_EMAIL} {
6-		dns cloudflare {$CF_API_TOKEN}
7-		resolvers 1.1.1.1
8-	}
9-}
A cmd/pipe/ssh/main.go
+7, -0
1@@ -0,0 +1,7 @@
2+package main
3+
4+import "github.com/picosh/pico/pipe"
5+
6+func main() {
7+	pipe.StartSshServer()
8+}
A cmd/pipe/web/main.go
+7, -0
1@@ -0,0 +1,7 @@
2+package main
3+
4+import "github.com/picosh/pico/pipe"
5+
6+func main() {
7+	pipe.StartApiServer()
8+}
D cmd/pubsub/ssh/main.go
+0, -7
1@@ -1,7 +0,0 @@
2-package main
3-
4-import "github.com/picosh/pico/pubsub"
5-
6-func main() {
7-	pubsub.StartSshServer()
8-}
D cmd/pubsub/web/main.go
+0, -7
1@@ -1,7 +0,0 @@
2-package main
3-
4-import "github.com/picosh/pico/pubsub"
5-
6-func main() {
7-	pubsub.StartApiServer()
8-}
M docker-compose.override.yml
+5, -5
 1@@ -45,24 +45,24 @@ services:
 2       - ./data/pastes-ssh/data:/app/ssh_data
 3     ports:
 4       - "2221:2222"
 5-  pubsub-web:
 6+  pipe-web:
 7     build:
 8       args:
 9-        APP: pubsub
10+        APP: pipe
11       target: release-web
12     env_file:
13       - .env.example
14     ports:
15       - "3001:3000"
16-  pubsub-ssh:
17+  pipe-ssh:
18     build:
19       args:
20-        APP: pubsub
21+        APP: pipe
22       target: release-ssh
23     env_file:
24       - .env.example
25     volumes:
26-      - ./data/pubsub-ssh/data:/app/ssh_data
27+      - ./data/pipe-ssh/data:/app/ssh_data
28     ports:
29       - "2221:2222"
30   prose-web:
M docker-compose.prod.yml
+22, -22
 1@@ -88,50 +88,50 @@ services:
 2     ports:
 3       - "${PASTES_SSH_V4:-22}:2222"
 4       - "${PASTES_SSH_V6:-[::1]:22}:2222"
 5-  pubsub-caddy:
 6+  pipe-caddy:
 7     image: ghcr.io/picosh/pico/caddy:latest
 8     restart: always
 9     networks:
10-      - pubsub
11+      - pipe
12     env_file:
13       - .env.prod
14     environment:
15-      APP_DOMAIN: ${PUBSUB_DOMAIN:-pipe.pico.sh}
16-      APP_EMAIL: ${PUBSUB_EMAIL:-hello@pico.sh}
17+      APP_DOMAIN: ${PIPE_DOMAIN:-pipe.pico.sh}
18+      APP_EMAIL: ${PIPE_EMAIL:-hello@pico.sh}
19     volumes:
20-      - ${PUBSUB_CADDYFILE}:/etc/caddy/Caddyfile
21-      - ./data/pubsub-caddy/data:/data
22-      - ./data/pubsub-caddy/config:/config
23+      - ${PIPE_CADDYFILE}:/etc/caddy/Caddyfile
24+      - ./data/pipe-caddy/data:/data
25+      - ./data/pipe-caddy/config:/config
26     ports:
27-      - "${PUBSUB_HTTPS_V4:-443}:443"
28-      - "${PUBSUB_HTTP_V4:-80}:80"
29-      - "${PUBSUB_HTTPS_V6:-[::1]:443}:443"
30-      - "${PUBSUB_HTTP_V6:-[::1]:80}:80"
31+      - "${PIPE_HTTPS_V4:-443}:443"
32+      - "${PIPE_HTTP_V4:-80}:80"
33+      - "${PIPE_HTTPS_V6:-[::1]:443}:443"
34+      - "${PIPE_HTTP_V6:-[::1]:80}:80"
35     profiles:
36-      - pubsub
37+      - pipe
38       - caddy
39       - all
40-  pubsub-web:
41+  pipe-web:
42     networks:
43-      pubsub:
44+      pipe:
45         aliases:
46           - web
47     env_file:
48       - .env.prod
49     volumes:
50-      - ./data/pubsub-ssh/data:/app/ssh_data
51-  pubsub-ssh:
52+      - ./data/pipe-ssh/data:/app/ssh_data
53+  pipe-ssh:
54     networks:
55-      pubsub:
56+      pipe:
57         aliases:
58           - ssh
59     env_file:
60       - .env.prod
61     volumes:
62-      - ./data/pubsub-ssh/data:/app/ssh_data
63+      - ./data/pipe-ssh/data:/app/ssh_data
64     ports:
65-      - "${PUBSUB_SSH_V4:-22}:2222"
66-      - "${PUBSUB_SSH_V6:-[::1]:22}:2222"
67+      - "${PIPE_SSH_V4:-22}:2222"
68+      - "${PIPE_SSH_V6:-[::1]:22}:2222"
69   prose-caddy:
70     image: ghcr.io/picosh/pico/caddy:latest
71     restart: always
72@@ -396,9 +396,9 @@ networks:
73     ipam:
74       config:
75         - subnet: 172.25.0.0/16
76-  pubsub:
77+  pipe:
78     driver_opts:
79-      com.docker.network.bridge.name: pubsub
80+      com.docker.network.bridge.name: pipe
81     ipam:
82       config:
83         - subnet: 172.27.0.0/16
M docker-compose.yml
+6, -6
 1@@ -43,18 +43,18 @@ services:
 2       - pastes
 3       - services
 4       - all
 5-  pubsub-web:
 6-    image: ghcr.io/picosh/pico/pubsub-web:latest
 7+  pipe-web:
 8+    image: ghcr.io/picosh/pico/pipe-web:latest
 9     restart: always
10     profiles:
11-      - pubsub
12+      - pipe
13       - services
14       - all
15-  pubsub-ssh:
16-    image: ghcr.io/picosh/pico/pubsub-ssh:latest
17+  pipe-ssh:
18+    image: ghcr.io/picosh/pico/pipe-ssh:latest
19     restart: always
20     profiles:
21-      - pubsub
22+      - pipe
23       - services
24       - all
25   prose-web:
M feeds/ssh.go
+2, -2
 1@@ -13,7 +13,7 @@ import (
 2 	"github.com/charmbracelet/wish"
 3 	"github.com/picosh/pico/db/postgres"
 4 	"github.com/picosh/pico/filehandlers"
 5-	"github.com/picosh/pico/filehandlers/util"
 6+	"github.com/picosh/pico/shared"
 7 	"github.com/picosh/pico/shared/storage"
 8 	wsh "github.com/picosh/pico/wish"
 9 	"github.com/picosh/send/auth"
10@@ -83,7 +83,7 @@ func StartSshServer() {
11 	}
12 	handler := filehandlers.NewFileHandlerRouter(cfg, dbh, fileMap)
13 
14-	sshAuth := util.NewSshAuthHandler(dbh, logger, cfg)
15+	sshAuth := shared.NewSshAuthHandler(dbh, logger, cfg)
16 	s, err := wish.NewServer(
17 		wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
18 		wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
M filehandlers/assets/handler.go
+6, -8
 1@@ -16,8 +16,6 @@ import (
 2 	"github.com/charmbracelet/ssh"
 3 	"github.com/charmbracelet/wish"
 4 	"github.com/picosh/pico/db"
 5-	"github.com/picosh/pico/filehandlers/util"
 6-	futil "github.com/picosh/pico/filehandlers/util"
 7 	"github.com/picosh/pico/shared"
 8 	"github.com/picosh/pico/shared/storage"
 9 	"github.com/picosh/pobj"
10@@ -118,7 +116,7 @@ func (h *UploadAssetHandler) GetLogger() *slog.Logger {
11 }
12 
13 func (h *UploadAssetHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
14-	user, err := futil.GetUser(s.Context())
15+	user, err := shared.GetUser(s.Context())
16 	if err != nil {
17 		return nil, nil, err
18 	}
19@@ -152,7 +150,7 @@ func (h *UploadAssetHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os
20 func (h *UploadAssetHandler) List(s ssh.Session, fpath string, isDir bool, recursive bool) ([]os.FileInfo, error) {
21 	var fileList []os.FileInfo
22 
23-	user, err := futil.GetUser(s.Context())
24+	user, err := shared.GetUser(s.Context())
25 	if err != nil {
26 		return fileList, err
27 	}
28@@ -194,7 +192,7 @@ func (h *UploadAssetHandler) List(s ssh.Session, fpath string, isDir bool, recur
29 }
30 
31 func (h *UploadAssetHandler) Validate(s ssh.Session) error {
32-	user, err := util.GetUser(s.Context())
33+	user, err := shared.GetUser(s.Context())
34 	if err != nil {
35 		return err
36 	}
37@@ -245,7 +243,7 @@ func (h *UploadAssetHandler) findDenylist(bucket sst.Bucket, project *db.Project
38 }
39 
40 func (h *UploadAssetHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (string, error) {
41-	user, err := futil.GetUser(s.Context())
42+	user, err := shared.GetUser(s.Context())
43 	if err != nil {
44 		h.Cfg.Logger.Error("user not found in ctx", "err", err.Error())
45 		return "", err
46@@ -311,7 +309,7 @@ func (h *UploadAssetHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (s
47 		return "", err
48 	}
49 
50-	featureFlag, err := futil.GetFeatureFlag(s.Context())
51+	featureFlag, err := shared.GetFeatureFlag(s.Context())
52 	if err != nil {
53 		return "", err
54 	}
55@@ -403,7 +401,7 @@ func (h *UploadAssetHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (s
56 }
57 
58 func (h *UploadAssetHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) error {
59-	user, err := futil.GetUser(s.Context())
60+	user, err := shared.GetUser(s.Context())
61 	if err != nil {
62 		h.Cfg.Logger.Error("user not found in ctx", "err", err.Error())
63 		return err
M filehandlers/imgs/handler.go
+4, -5
 1@@ -14,7 +14,6 @@ import (
 2 	"github.com/charmbracelet/ssh"
 3 	exifremove "github.com/neurosnap/go-exif-remove"
 4 	"github.com/picosh/pico/db"
 5-	"github.com/picosh/pico/filehandlers/util"
 6 	"github.com/picosh/pico/shared"
 7 	"github.com/picosh/pico/shared/storage"
 8 	"github.com/picosh/pobj"
 9@@ -49,7 +48,7 @@ func NewUploadImgHandler(dbpool db.DB, cfg *shared.ConfigSite, storage storage.S
10 }
11 
12 func (h *UploadImgHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
13-	user, err := util.GetUser(s.Context())
14+	user, err := shared.GetUser(s.Context())
15 	if err != nil {
16 		return nil, nil, err
17 	}
18@@ -89,7 +88,7 @@ func (h *UploadImgHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.F
19 
20 func (h *UploadImgHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (string, error) {
21 	logger := h.Cfg.Logger
22-	user, err := util.GetUser(s.Context())
23+	user, err := shared.GetUser(s.Context())
24 	if err != nil {
25 		logger.Error("could not get user from ctx", "err", err.Error())
26 		return "", err
27@@ -146,7 +145,7 @@ func (h *UploadImgHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (str
28 		logger.Info("unable to find image, continuing", "filename", nextPost.Filename, "err", err.Error())
29 	}
30 
31-	featureFlag, err := util.GetFeatureFlag(s.Context())
32+	featureFlag, err := shared.GetFeatureFlag(s.Context())
33 	if err != nil {
34 		return "", err
35 	}
36@@ -193,7 +192,7 @@ func (h *UploadImgHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (str
37 }
38 
39 func (h *UploadImgHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) error {
40-	user, err := util.GetUser(s.Context())
41+	user, err := shared.GetUser(s.Context())
42 	if err != nil {
43 		return err
44 	}
M filehandlers/imgs/img.go
+1, -2
 1@@ -8,7 +8,6 @@ import (
 2 
 3 	"github.com/charmbracelet/ssh"
 4 	"github.com/picosh/pico/db"
 5-	"github.com/picosh/pico/filehandlers/util"
 6 	"github.com/picosh/pico/shared"
 7 	sendutils "github.com/picosh/send/utils"
 8 	"github.com/picosh/utils"
 9@@ -81,7 +80,7 @@ func (h *UploadImgHandler) writeImg(s ssh.Session, data *PostMetaData) error {
10 	if !valid {
11 		return err
12 	}
13-	user, err := util.GetUser(s.Context())
14+	user, err := shared.GetUser(s.Context())
15 	if err != nil {
16 		return err
17 	}
M filehandlers/post_handler.go
+3, -4
 1@@ -12,7 +12,6 @@ import (
 2 
 3 	"github.com/charmbracelet/ssh"
 4 	"github.com/picosh/pico/db"
 5-	"github.com/picosh/pico/filehandlers/util"
 6 	"github.com/picosh/pico/shared"
 7 	"github.com/picosh/pico/shared/storage"
 8 	sendutils "github.com/picosh/send/utils"
 9@@ -48,7 +47,7 @@ func NewScpPostHandler(dbpool db.DB, cfg *shared.ConfigSite, hooks ScpFileHooks,
10 }
11 
12 func (h *ScpUploadHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
13-	user, err := util.GetUser(s.Context())
14+	user, err := shared.GetUser(s.Context())
15 	if err != nil {
16 		return nil, nil, err
17 	}
18@@ -77,7 +76,7 @@ func (h *ScpUploadHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.F
19 
20 func (h *ScpUploadHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (string, error) {
21 	logger := h.Cfg.Logger
22-	user, err := util.GetUser(s.Context())
23+	user, err := shared.GetUser(s.Context())
24 	if err != nil {
25 		logger.Error("error getting user from ctx", "err", err.Error())
26 		return "", err
27@@ -264,7 +263,7 @@ func (h *ScpUploadHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (str
28 
29 func (h *ScpUploadHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) error {
30 	logger := h.Cfg.Logger
31-	user, err := util.GetUser(s.Context())
32+	user, err := shared.GetUser(s.Context())
33 	if err != nil {
34 		logger.Error("could not get user from ctx", "err", err.Error())
35 		return err
M filehandlers/router_handler.go
+2, -3
 1@@ -10,7 +10,6 @@ import (
 2 
 3 	"github.com/charmbracelet/ssh"
 4 	"github.com/picosh/pico/db"
 5-	"github.com/picosh/pico/filehandlers/util"
 6 	"github.com/picosh/pico/shared"
 7 	"github.com/picosh/send/utils"
 8 )
 9@@ -83,7 +82,7 @@ func (r *FileHandlerRouter) Read(s ssh.Session, entry *utils.FileEntry) (os.File
10 
11 func BaseList(s ssh.Session, fpath string, isDir bool, recursive bool, spaces []string, dbpool db.DB) ([]os.FileInfo, error) {
12 	var fileList []os.FileInfo
13-	user, err := util.GetUser(s.Context())
14+	user, err := shared.GetUser(s.Context())
15 	if err != nil {
16 		return fileList, err
17 	}
18@@ -154,7 +153,7 @@ func (r *FileHandlerRouter) GetLogger() *slog.Logger {
19 }
20 
21 func (r *FileHandlerRouter) Validate(s ssh.Session) error {
22-	user, err := util.GetUser(s.Context())
23+	user, err := shared.GetUser(s.Context())
24 	if err != nil {
25 		return err
26 	}
D filehandlers/util/pubkey_auth.go
+0, -64
 1@@ -1,64 +0,0 @@
 2-package util
 3-
 4-import (
 5-	"log/slog"
 6-
 7-	"github.com/charmbracelet/ssh"
 8-	"github.com/picosh/pico/db"
 9-	"github.com/picosh/pico/shared"
10-	"github.com/picosh/utils"
11-)
12-
13-type SshAuthHandler struct {
14-	DBPool db.DB
15-	Logger *slog.Logger
16-	Cfg    *shared.ConfigSite
17-}
18-
19-func NewSshAuthHandler(dbpool db.DB, logger *slog.Logger, cfg *shared.ConfigSite) *SshAuthHandler {
20-	return &SshAuthHandler{
21-		DBPool: dbpool,
22-		Logger: logger,
23-		Cfg:    cfg,
24-	}
25-}
26-
27-func (r *SshAuthHandler) PubkeyAuthHandler(ctx ssh.Context, key ssh.PublicKey) bool {
28-	shared.SetPublicKeyCtx(ctx, key)
29-
30-	pubkey := utils.KeyForKeyText(key)
31-
32-	user, err := r.DBPool.FindUserForKey(ctx.User(), pubkey)
33-	if err != nil {
34-		r.Logger.Error(
35-			"could not find user for key",
36-			"key", key,
37-			"err", err,
38-		)
39-		return false
40-	}
41-
42-	if user.Name == "" {
43-		r.Logger.Error("username is not set")
44-		return false
45-	}
46-
47-	ff, _ := r.DBPool.FindFeatureForUser(user.ID, "plus")
48-	// we have free tiers so users might not have a feature flag
49-	// in which case we set sane defaults
50-	if ff == nil {
51-		ff = db.NewFeatureFlag(
52-			user.ID,
53-			"plus",
54-			r.Cfg.MaxSize,
55-			r.Cfg.MaxAssetSize,
56-		)
57-	}
58-	// this is jank
59-	ff.Data.StorageMax = ff.FindStorageMax(r.Cfg.MaxSize)
60-	ff.Data.FileMax = ff.FindFileMax(r.Cfg.MaxAssetSize)
61-
62-	SetUser(ctx, user)
63-	SetFeatureFlag(ctx, ff)
64-	return true
65-}
D filehandlers/util/util.go
+0, -35
 1@@ -1,35 +0,0 @@
 2-package util
 3-
 4-import (
 5-	"fmt"
 6-
 7-	"github.com/charmbracelet/ssh"
 8-	"github.com/picosh/pico/db"
 9-)
10-
11-type ctxUserKey struct{}
12-type ctxFeatureFlagKey struct{}
13-
14-func GetUser(ctx ssh.Context) (*db.User, error) {
15-	user, ok := ctx.Value(ctxUserKey{}).(*db.User)
16-	if !ok {
17-		return user, fmt.Errorf("user not set on `ssh.Context()` for connection")
18-	}
19-	return user, nil
20-}
21-
22-func SetUser(ctx ssh.Context, user *db.User) {
23-	ctx.SetValue(ctxUserKey{}, user)
24-}
25-
26-func GetFeatureFlag(ctx ssh.Context) (*db.FeatureFlag, error) {
27-	ff, ok := ctx.Value(ctxFeatureFlagKey{}).(*db.FeatureFlag)
28-	if !ok || ff.Name == "" {
29-		return ff, fmt.Errorf("feature flag not set on `ssh.Context()` for connection")
30-	}
31-	return ff, nil
32-}
33-
34-func SetFeatureFlag(ctx ssh.Context, ff *db.FeatureFlag) {
35-	ctx.SetValue(ctxFeatureFlagKey{}, ff)
36-}
M pastes/ssh.go
+2, -2
 1@@ -13,7 +13,7 @@ import (
 2 	"github.com/charmbracelet/wish"
 3 	"github.com/picosh/pico/db/postgres"
 4 	"github.com/picosh/pico/filehandlers"
 5-	"github.com/picosh/pico/filehandlers/util"
 6+	"github.com/picosh/pico/shared"
 7 	"github.com/picosh/pico/shared/storage"
 8 	wsh "github.com/picosh/pico/wish"
 9 	"github.com/picosh/send/auth"
10@@ -81,7 +81,7 @@ func StartSshServer() {
11 		"fallback": filehandlers.NewScpPostHandler(dbh, cfg, hooks, st),
12 	}
13 	handler := filehandlers.NewFileHandlerRouter(cfg, dbh, fileMap)
14-	sshAuth := util.NewSshAuthHandler(dbh, logger, cfg)
15+	sshAuth := shared.NewSshAuthHandler(dbh, logger, cfg)
16 	s, err := wish.NewServer(
17 		wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
18 		wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
M pgs/ssh.go
+1, -2
 1@@ -14,7 +14,6 @@ import (
 2 	"github.com/picosh/pico/db"
 3 	"github.com/picosh/pico/db/postgres"
 4 	uploadassets "github.com/picosh/pico/filehandlers/assets"
 5-	"github.com/picosh/pico/filehandlers/util"
 6 	"github.com/picosh/pico/shared"
 7 	"github.com/picosh/pico/shared/storage"
 8 	wsh "github.com/picosh/pico/wish"
 9@@ -97,7 +96,7 @@ func StartSshServer() {
10 		HttpHandler: createHttpHandler(apiConfig),
11 	}
12 
13-	sshAuth := util.NewSshAuthHandler(dbpool, logger, cfg)
14+	sshAuth := shared.NewSshAuthHandler(dbpool, logger, cfg)
15 	s, err := wish.NewServer(
16 		wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
17 		wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
M pgs/tunnel.go
+3, -3
 1@@ -38,7 +38,7 @@ func createHttpHandler(apiConfig *shared.ApiConfig) CtxHttpBridge {
 2 			"impersonating", asUser,
 3 		)
 4 
 5-		pubkey, err := shared.GetPublicKeyCtx(ctx)
 6+		pubkey, err := shared.GetPublicKey(ctx)
 7 		if err != nil {
 8 			log.Error(err.Error(), "subdomain", subdomain)
 9 			return http.HandlerFunc(shared.UnauthorizedHandler)
10@@ -88,7 +88,7 @@ func createHttpHandler(apiConfig *shared.ApiConfig) CtxHttpBridge {
11 			requester, _ = dbh.FindUserForName(asUser)
12 		}
13 
14-		shared.SetUserCtx(ctx, requester)
15+		shared.SetUser(ctx, requester)
16 
17 		if !HasProjectAccess(project, owner, requester, pubkey) {
18 			log.Error("no access")
19@@ -101,7 +101,7 @@ func createHttpHandler(apiConfig *shared.ApiConfig) CtxHttpBridge {
20 			// special API endpoint for tunnel users accessing site
21 			shared.NewCorsRoute("GET", "/api/current_user", func(w http.ResponseWriter, r *http.Request) {
22 				w.Header().Set("Content-Type", "application/json")
23-				user, err := shared.GetUserCtx(ctx)
24+				user, err := shared.GetUser(ctx)
25 				if err != nil {
26 					logger.Error("could not find user", "err", err.Error())
27 					shared.JSONError(w, err.Error(), http.StatusNotFound)
M pico/file_handler.go
+4, -5
 1@@ -14,7 +14,6 @@ import (
 2 	"github.com/charmbracelet/ssh"
 3 	"github.com/charmbracelet/wish"
 4 	"github.com/picosh/pico/db"
 5-	"github.com/picosh/pico/filehandlers/util"
 6 	"github.com/picosh/pico/shared"
 7 	sendutils "github.com/picosh/send/utils"
 8 	"github.com/picosh/utils"
 9@@ -57,7 +56,7 @@ func (h *UploadHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) error
10 }
11 
12 func (h *UploadHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
13-	user, err := util.GetUser(s.Context())
14+	user, err := shared.GetUser(s.Context())
15 	if err != nil {
16 		return nil, nil, err
17 	}
18@@ -81,7 +80,7 @@ func (h *UploadHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.File
19 
20 func (h *UploadHandler) List(s ssh.Session, fpath string, isDir bool, recursive bool) ([]os.FileInfo, error) {
21 	var fileList []os.FileInfo
22-	user, err := util.GetUser(s.Context())
23+	user, err := shared.GetUser(s.Context())
24 	if err != nil {
25 		return fileList, err
26 	}
27@@ -136,7 +135,7 @@ func (h *UploadHandler) Validate(s ssh.Session) error {
28 		return fmt.Errorf("must have username set")
29 	}
30 
31-	util.SetUser(s.Context(), user)
32+	shared.SetUser(s.Context(), user)
33 	return nil
34 }
35 
36@@ -277,7 +276,7 @@ func (h *UploadHandler) ProcessAuthorizedKeys(text []byte, logger *slog.Logger,
37 
38 func (h *UploadHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (string, error) {
39 	logger := h.Cfg.Logger
40-	user, err := util.GetUser(s.Context())
41+	user, err := shared.GetUser(s.Context())
42 	if err != nil {
43 		logger.Error(err.Error())
44 		return "", err
M pico/ssh.go
+1, -1
1@@ -28,7 +28,7 @@ import (
2 )
3 
4 func authHandler(ctx ssh.Context, key ssh.PublicKey) bool {
5-	shared.SetPublicKeyCtx(ctx, key)
6+	shared.SetPublicKey(ctx, key)
7 	return true
8 }
9 
R pubsub/api.go => pipe/api.go
+1, -1
1@@ -1,4 +1,4 @@
2-package pubsub
3+package pipe
4 
5 import (
6 	"fmt"
R pubsub/cli.go => pipe/cli.go
+1, -1
1@@ -1,4 +1,4 @@
2-package pubsub
3+package pipe
4 
5 import (
6 	"bytes"
R pubsub/config.go => pipe/config.go
+6, -6
 1@@ -1,4 +1,4 @@
 2-package pubsub
 3+package pipe
 4 
 5 import (
 6 	"github.com/picosh/pico/shared"
 7@@ -6,17 +6,17 @@ import (
 8 )
 9 
10 func NewConfigSite() *shared.ConfigSite {
11-	domain := utils.GetEnv("PUBSUB_DOMAIN", "pipe.pico.sh")
12-	port := utils.GetEnv("PUBSUB_WEB_PORT", "3000")
13+	domain := utils.GetEnv("PIPE_DOMAIN", "pipe.pico.sh")
14+	port := utils.GetEnv("PIPE_WEB_PORT", "3000")
15 	dbURL := utils.GetEnv("DATABASE_URL", "")
16-	protocol := utils.GetEnv("PUBSUB_PROTOCOL", "https")
17+	protocol := utils.GetEnv("PIPE_PROTOCOL", "https")
18 
19 	return &shared.ConfigSite{
20 		Domain:   domain,
21 		Port:     port,
22 		Protocol: protocol,
23 		DbURL:    dbURL,
24-		Logger:   shared.CreateLogger("pubsub"),
25-		Space:    "pubsub",
26+		Logger:   shared.CreateLogger("pipe"),
27+		Space:    "pipe",
28 	}
29 }
R pubsub/html/base.layout.tmpl => pipe/html/base.layout.tmpl
+0, -0
R pubsub/html/footer.partial.tmpl => pipe/html/footer.partial.tmpl
+0, -0
R pubsub/html/marketing-footer.partial.tmpl => pipe/html/marketing-footer.partial.tmpl
+0, -0
R pubsub/html/marketing.page.tmpl => pipe/html/marketing.page.tmpl
+0, -0
R pubsub/public/anim.js => pipe/public/anim.js
+0, -0
R pubsub/public/apple-touch-icon.png => pipe/public/apple-touch-icon.png
+0, -0
R pubsub/public/favicon-16x16.png => pipe/public/favicon-16x16.png
+0, -0
R pubsub/public/favicon.ico => pipe/public/favicon.ico
+0, -0
R pubsub/public/robots.txt => pipe/public/robots.txt
+0, -0
R pubsub/ssh.go => pipe/ssh.go
+8, -8
 1@@ -1,4 +1,4 @@
 2-package pubsub
 3+package pipe
 4 
 5 import (
 6 	"context"
 7@@ -12,17 +12,17 @@ import (
 8 	"github.com/charmbracelet/promwish"
 9 	"github.com/charmbracelet/wish"
10 	"github.com/picosh/pico/db/postgres"
11-	"github.com/picosh/pico/filehandlers/util"
12+	"github.com/picosh/pico/shared"
13 	wsh "github.com/picosh/pico/wish"
14 	psub "github.com/picosh/pubsub"
15 	"github.com/picosh/utils"
16 )
17 
18 func StartSshServer() {
19-	host := utils.GetEnv("PUBSUB_HOST", "0.0.0.0")
20-	port := utils.GetEnv("PUBSUB_SSH_PORT", "2222")
21-	portOverride := utils.GetEnv("PUBSUB_SSH_PORT_OVERRIDE", port)
22-	promPort := utils.GetEnv("PUBSUB_PROM_PORT", "9222")
23+	host := utils.GetEnv("PIPE_HOST", "0.0.0.0")
24+	port := utils.GetEnv("PIPE_SSH_PORT", "2222")
25+	portOverride := utils.GetEnv("PIPE_SSH_PORT_OVERRIDE", port)
26+	promPort := utils.GetEnv("PIPE_PROM_PORT", "9222")
27 	cfg := NewConfigSite()
28 	logger := cfg.Logger
29 	dbh := postgres.NewDB(cfg.DbURL, cfg.Logger)
30@@ -40,14 +40,14 @@ func StartSshServer() {
31 		Waiters: syncmap.New[string, []string](),
32 	}
33 
34-	sshAuth := util.NewSshAuthHandler(dbh, logger, cfg)
35+	sshAuth := shared.NewSshAuthHandler(dbh, logger, cfg)
36 	s, err := wish.NewServer(
37 		wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
38 		wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
39 		wish.WithPublicKeyAuth(sshAuth.PubkeyAuthHandler),
40 		wish.WithMiddleware(
41 			WishMiddleware(handler),
42-			promwish.Middleware(fmt.Sprintf("%s:%s", host, promPort), "pubsub-ssh"),
43+			promwish.Middleware(fmt.Sprintf("%s:%s", host, promPort), "pipe-ssh"),
44 			wsh.LogMiddleware(logger),
45 		),
46 	)
M prose/ssh.go
+2, -2
 1@@ -14,7 +14,7 @@ import (
 2 	"github.com/picosh/pico/db/postgres"
 3 	"github.com/picosh/pico/filehandlers"
 4 	uploadimgs "github.com/picosh/pico/filehandlers/imgs"
 5-	"github.com/picosh/pico/filehandlers/util"
 6+	"github.com/picosh/pico/shared"
 7 	"github.com/picosh/pico/shared/storage"
 8 	wsh "github.com/picosh/pico/wish"
 9 	"github.com/picosh/send/auth"
10@@ -92,7 +92,7 @@ func StartSshServer() {
11 		DBPool: dbh,
12 	}
13 
14-	sshAuth := util.NewSshAuthHandler(dbh, logger, cfg)
15+	sshAuth := shared.NewSshAuthHandler(dbh, logger, cfg)
16 	s, err := wish.NewServer(
17 		wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
18 		wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
M shared/ssh.go
+100, -2
  1@@ -1,7 +1,105 @@
  2 package shared
  3 
  4-import "github.com/charmbracelet/ssh"
  5+import (
  6+	"fmt"
  7+	"log/slog"
  8 
  9-func PubkeyAuthHandler(ctx ssh.Context, key ssh.PublicKey) bool {
 10+	"github.com/charmbracelet/ssh"
 11+	"github.com/picosh/pico/db"
 12+	"github.com/picosh/utils"
 13+)
 14+
 15+type ctxUserKey struct{}
 16+type ctxFeatureFlagKey struct{}
 17+
 18+func GetUser(ctx ssh.Context) (*db.User, error) {
 19+	user, ok := ctx.Value(ctxUserKey{}).(*db.User)
 20+	if !ok {
 21+		return user, fmt.Errorf("user not set on `ssh.Context()` for connection")
 22+	}
 23+	return user, nil
 24+}
 25+
 26+func SetUser(ctx ssh.Context, user *db.User) {
 27+	ctx.SetValue(ctxUserKey{}, user)
 28+}
 29+
 30+func GetFeatureFlag(ctx ssh.Context) (*db.FeatureFlag, error) {
 31+	ff, ok := ctx.Value(ctxFeatureFlagKey{}).(*db.FeatureFlag)
 32+	if !ok || ff.Name == "" {
 33+		return ff, fmt.Errorf("feature flag not set on `ssh.Context()` for connection")
 34+	}
 35+	return ff, nil
 36+}
 37+
 38+func SetFeatureFlag(ctx ssh.Context, ff *db.FeatureFlag) {
 39+	ctx.SetValue(ctxFeatureFlagKey{}, ff)
 40+}
 41+
 42+type ctxPublicKey struct{}
 43+
 44+func GetPublicKey(ctx ssh.Context) (ssh.PublicKey, error) {
 45+	pk, ok := ctx.Value(ctxPublicKey{}).(ssh.PublicKey)
 46+	if !ok {
 47+		return nil, fmt.Errorf("public key not set on `ssh.Context()` for connection")
 48+	}
 49+	return pk, nil
 50+}
 51+
 52+func SetPublicKey(ctx ssh.Context, pk ssh.PublicKey) {
 53+	ctx.SetValue(ctxPublicKey{}, pk)
 54+}
 55+
 56+type SshAuthHandler struct {
 57+	DBPool db.DB
 58+	Logger *slog.Logger
 59+	Cfg    *ConfigSite
 60+}
 61+
 62+func NewSshAuthHandler(dbpool db.DB, logger *slog.Logger, cfg *ConfigSite) *SshAuthHandler {
 63+	return &SshAuthHandler{
 64+		DBPool: dbpool,
 65+		Logger: logger,
 66+		Cfg:    cfg,
 67+	}
 68+}
 69+
 70+func (r *SshAuthHandler) PubkeyAuthHandler(ctx ssh.Context, key ssh.PublicKey) bool {
 71+	SetPublicKey(ctx, key)
 72+
 73+	pubkey := utils.KeyForKeyText(key)
 74+
 75+	user, err := r.DBPool.FindUserForKey(ctx.User(), pubkey)
 76+	if err != nil {
 77+		r.Logger.Error(
 78+			"could not find user for key",
 79+			"key", key,
 80+			"err", err,
 81+		)
 82+		return false
 83+	}
 84+
 85+	if user.Name == "" {
 86+		r.Logger.Error("username is not set")
 87+		return false
 88+	}
 89+
 90+	ff, _ := r.DBPool.FindFeatureForUser(user.ID, "plus")
 91+	// we have free tiers so users might not have a feature flag
 92+	// in which case we set sane defaults
 93+	if ff == nil {
 94+		ff = db.NewFeatureFlag(
 95+			user.ID,
 96+			"plus",
 97+			r.Cfg.MaxSize,
 98+			r.Cfg.MaxAssetSize,
 99+		)
100+	}
101+	// this is jank
102+	ff.Data.StorageMax = ff.FindStorageMax(r.Cfg.MaxSize)
103+	ff.Data.FileMax = ff.FindFileMax(r.Cfg.MaxAssetSize)
104+
105+	SetUser(ctx, user)
106+	SetFeatureFlag(ctx, ff)
107 	return true
108 }
M shared/tunnel.go
+0, -34
 1@@ -1,35 +1 @@
 2 package shared
 3-
 4-import (
 5-	"fmt"
 6-
 7-	"github.com/charmbracelet/ssh"
 8-	"github.com/picosh/pico/db"
 9-)
10-
11-type ctxPublicKey struct{}
12-type ctxUserKey struct{}
13-
14-func GetPublicKeyCtx(ctx ssh.Context) (ssh.PublicKey, error) {
15-	pk, ok := ctx.Value(ctxPublicKey{}).(ssh.PublicKey)
16-	if !ok {
17-		return nil, fmt.Errorf("public key not set on `ssh.Context()` for connection")
18-	}
19-	return pk, nil
20-}
21-
22-func SetPublicKeyCtx(ctx ssh.Context, pk ssh.PublicKey) {
23-	ctx.SetValue(ctxPublicKey{}, pk)
24-}
25-
26-func GetUserCtx(ctx ssh.Context) (*db.User, error) {
27-	pk, ok := ctx.Value(ctxUserKey{}).(*db.User)
28-	if !ok {
29-		return nil, fmt.Errorf("user not set on `ssh.Context()` for connection")
30-	}
31-	return pk, nil
32-}
33-
34-func SetUserCtx(ctx ssh.Context, user *db.User) {
35-	ctx.SetValue(ctxUserKey{}, user)
36-}
M ui/api.go
+17, -17
  1@@ -47,7 +47,7 @@ func registerUser(apiConfig *shared.ApiConfig, ctx ssh.Context, pubkey ssh.Publi
  2 		}
  3 
  4 		picoApi := shared.NewUserApi(user, pubkey)
  5-		shared.SetUserCtx(ctx, user)
  6+		shared.SetUser(ctx, user)
  7 		err = json.NewEncoder(w).Encode(picoApi)
  8 		if err != nil {
  9 			logger.Error("json encoding error", "err", err.Error())
 10@@ -63,7 +63,7 @@ func getFeatures(apiConfig *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc
 11 	logger := apiConfig.Cfg.Logger
 12 	return func(w http.ResponseWriter, r *http.Request) {
 13 		w.Header().Set("Content-Type", "application/json")
 14-		user, _ := shared.GetUserCtx(ctx)
 15+		user, _ := shared.GetUser(ctx)
 16 		if !ensureUser(w, user) {
 17 			return
 18 		}
 19@@ -93,7 +93,7 @@ func findOrCreateRssToken(apiConfig *shared.ApiConfig, ctx ssh.Context) http.Han
 20 	logger := apiConfig.Cfg.Logger
 21 	return func(w http.ResponseWriter, r *http.Request) {
 22 		w.Header().Set("Content-Type", "application/json")
 23-		user, _ := shared.GetUserCtx(ctx)
 24+		user, _ := shared.GetUser(ctx)
 25 		if !ensureUser(w, user) {
 26 			return
 27 		}
 28@@ -132,7 +132,7 @@ func getPublicKeys(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc
 29 	logger := httpCtx.Cfg.Logger
 30 	return func(w http.ResponseWriter, r *http.Request) {
 31 		w.Header().Set("Content-Type", "application/json")
 32-		user, _ := shared.GetUserCtx(ctx)
 33+		user, _ := shared.GetUser(ctx)
 34 		if !ensureUser(w, user) {
 35 			return
 36 		}
 37@@ -169,7 +169,7 @@ func createPubkey(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc {
 38 	logger := httpCtx.Cfg.Logger
 39 	return func(w http.ResponseWriter, r *http.Request) {
 40 		w.Header().Set("Content-Type", "application/json")
 41-		user, _ := shared.GetUserCtx(ctx)
 42+		user, _ := shared.GetUser(ctx)
 43 		if !ensureUser(w, user) {
 44 			return
 45 		}
 46@@ -207,7 +207,7 @@ func deletePubkey(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc {
 47 	logger := httpCtx.Cfg.Logger
 48 	return func(w http.ResponseWriter, r *http.Request) {
 49 		w.Header().Set("Content-Type", "application/json")
 50-		user, _ := shared.GetUserCtx(ctx)
 51+		user, _ := shared.GetUser(ctx)
 52 		if !ensureUser(w, user) {
 53 			return
 54 		}
 55@@ -249,7 +249,7 @@ func patchPubkey(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc {
 56 	logger := httpCtx.Cfg.Logger
 57 	return func(w http.ResponseWriter, r *http.Request) {
 58 		w.Header().Set("Content-Type", "application/json")
 59-		user, _ := shared.GetUserCtx(ctx)
 60+		user, _ := shared.GetUser(ctx)
 61 		if !ensureUser(w, user) {
 62 			return
 63 		}
 64@@ -303,7 +303,7 @@ func getTokens(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc {
 65 	logger := httpCtx.Cfg.Logger
 66 	return func(w http.ResponseWriter, r *http.Request) {
 67 		w.Header().Set("Content-Type", "application/json")
 68-		user, _ := shared.GetUserCtx(ctx)
 69+		user, _ := shared.GetUser(ctx)
 70 		if !ensureUser(w, user) {
 71 			return
 72 		}
 73@@ -339,7 +339,7 @@ func createToken(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc {
 74 	logger := httpCtx.Cfg.Logger
 75 	return func(w http.ResponseWriter, r *http.Request) {
 76 		w.Header().Set("Content-Type", "application/json")
 77-		user, _ := shared.GetUserCtx(ctx)
 78+		user, _ := shared.GetUser(ctx)
 79 		if !ensureUser(w, user) {
 80 			return
 81 		}
 82@@ -379,7 +379,7 @@ func deleteToken(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc {
 83 	logger := httpCtx.Cfg.Logger
 84 	return func(w http.ResponseWriter, r *http.Request) {
 85 		w.Header().Set("Content-Type", "application/json")
 86-		user, _ := shared.GetUserCtx(ctx)
 87+		user, _ := shared.GetUser(ctx)
 88 		if !ensureUser(w, user) {
 89 			return
 90 		}
 91@@ -425,7 +425,7 @@ func getProjects(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc {
 92 	logger := httpCtx.Cfg.Logger
 93 	return func(w http.ResponseWriter, r *http.Request) {
 94 		w.Header().Set("Content-Type", "application/json")
 95-		user, _ := shared.GetUserCtx(ctx)
 96+		user, _ := shared.GetUser(ctx)
 97 		if !ensureUser(w, user) {
 98 			return
 99 		}
100@@ -456,7 +456,7 @@ func getPosts(httpCtx *shared.ApiConfig, ctx ssh.Context, space string) http.Han
101 	logger := httpCtx.Cfg.Logger
102 	return func(w http.ResponseWriter, r *http.Request) {
103 		w.Header().Set("Content-Type", "application/json")
104-		user, _ := shared.GetUserCtx(ctx)
105+		user, _ := shared.GetUser(ctx)
106 		if !ensureUser(w, user) {
107 			return
108 		}
109@@ -502,7 +502,7 @@ func createFeature(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc
110 	logger := httpCtx.Cfg.Logger
111 	return func(w http.ResponseWriter, r *http.Request) {
112 		w.Header().Set("Content-Type", "application/json")
113-		user, _ := shared.GetUserCtx(ctx)
114+		user, _ := shared.GetUser(ctx)
115 		if !ensureUser(w, user) {
116 			return
117 		}
118@@ -542,7 +542,7 @@ func deleteFeature(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc
119 	logger := httpCtx.Cfg.Logger
120 	return func(w http.ResponseWriter, r *http.Request) {
121 		w.Header().Set("Content-Type", "application/json")
122-		user, _ := shared.GetUserCtx(ctx)
123+		user, _ := shared.GetUser(ctx)
124 		if !ensureUser(w, user) {
125 			return
126 		}
127@@ -574,7 +574,7 @@ func getProjectObjects(apiConfig *shared.ApiConfig, ctx ssh.Context) http.Handle
128 	storage := apiConfig.Storage
129 	return func(w http.ResponseWriter, r *http.Request) {
130 		w.Header().Set("Content-Type", "application/json")
131-		user, _ := shared.GetUserCtx(ctx)
132+		user, _ := shared.GetUser(ctx)
133 		if !ensureUser(w, user) {
134 			return
135 		}
136@@ -616,7 +616,7 @@ func getAnalytics(apiConfig *shared.ApiConfig, ctx ssh.Context, sumtype, bytype,
137 	dbpool := apiConfig.Dbpool
138 	return func(w http.ResponseWriter, r *http.Request) {
139 		w.Header().Set("Content-Type", "application/json")
140-		user, _ := shared.GetUserCtx(ctx)
141+		user, _ := shared.GetUser(ctx)
142 		if !ensureUser(w, user) {
143 			return
144 		}
145@@ -655,7 +655,7 @@ func getAnalytics(apiConfig *shared.ApiConfig, ctx ssh.Context, sumtype, bytype,
146 
147 func CreateRoutes(apiConfig *shared.ApiConfig, ctx ssh.Context) []shared.Route {
148 	logger := apiConfig.Cfg.Logger
149-	pubkey, err := shared.GetPublicKeyCtx(ctx)
150+	pubkey, err := shared.GetPublicKey(ctx)
151 	if err != nil {
152 		logger.Error("could not get pubkey from ctx", "err", err.Error())
153 		return []shared.Route{}