gitspace 0.2.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/.claude/settings.local.json +21 -0
  2. package/.gitspace/bundle.json +50 -0
  3. package/.gitspace/select/01-status.sh +40 -0
  4. package/.gitspace/setup/01-install-deps.sh +12 -0
  5. package/.gitspace/setup/02-typecheck.sh +16 -0
  6. package/AGENTS.md +439 -0
  7. package/CLAUDE.md +1 -0
  8. package/LICENSE +25 -0
  9. package/README.md +607 -0
  10. package/bin/gssh +62 -0
  11. package/bun.lock +647 -0
  12. package/docs/CONNECTION.md +623 -0
  13. package/docs/GATEWAY-WORKER.md +319 -0
  14. package/docs/GETTING-STARTED.md +448 -0
  15. package/docs/GITSPACE-PLATFORM.md +1819 -0
  16. package/docs/INFRASTRUCTURE.md +1347 -0
  17. package/docs/PROTOCOL.md +619 -0
  18. package/docs/QUICKSTART.md +174 -0
  19. package/docs/RELAY.md +327 -0
  20. package/docs/REMOTE-DESIGN.md +549 -0
  21. package/docs/ROADMAP.md +564 -0
  22. package/docs/SITE_DOCS_FIGMA_MAKE.md +1167 -0
  23. package/docs/STACK-DESIGN.md +588 -0
  24. package/docs/UNIFIED_ARCHITECTURE.md +292 -0
  25. package/experiments/pty-benchmark.ts +148 -0
  26. package/experiments/pty-latency.ts +100 -0
  27. package/experiments/router/client.ts +199 -0
  28. package/experiments/router/protocol.ts +74 -0
  29. package/experiments/router/router.ts +217 -0
  30. package/experiments/router/session.ts +180 -0
  31. package/experiments/router/test.ts +133 -0
  32. package/experiments/socket-bandwidth.ts +77 -0
  33. package/homebrew/gitspace.rb +45 -0
  34. package/landing-page/ATTRIBUTIONS.md +3 -0
  35. package/landing-page/README.md +11 -0
  36. package/landing-page/bun.lock +801 -0
  37. package/landing-page/guidelines/Guidelines.md +61 -0
  38. package/landing-page/index.html +37 -0
  39. package/landing-page/package.json +90 -0
  40. package/landing-page/postcss.config.mjs +15 -0
  41. package/landing-page/public/_redirects +1 -0
  42. package/landing-page/public/favicon.png +0 -0
  43. package/landing-page/src/app/App.tsx +53 -0
  44. package/landing-page/src/app/components/figma/ImageWithFallback.tsx +27 -0
  45. package/landing-page/src/app/components/ui/accordion.tsx +66 -0
  46. package/landing-page/src/app/components/ui/alert-dialog.tsx +157 -0
  47. package/landing-page/src/app/components/ui/alert.tsx +66 -0
  48. package/landing-page/src/app/components/ui/aspect-ratio.tsx +11 -0
  49. package/landing-page/src/app/components/ui/avatar.tsx +53 -0
  50. package/landing-page/src/app/components/ui/badge.tsx +46 -0
  51. package/landing-page/src/app/components/ui/breadcrumb.tsx +109 -0
  52. package/landing-page/src/app/components/ui/button.tsx +57 -0
  53. package/landing-page/src/app/components/ui/calendar.tsx +75 -0
  54. package/landing-page/src/app/components/ui/card.tsx +92 -0
  55. package/landing-page/src/app/components/ui/carousel.tsx +241 -0
  56. package/landing-page/src/app/components/ui/chart.tsx +353 -0
  57. package/landing-page/src/app/components/ui/checkbox.tsx +32 -0
  58. package/landing-page/src/app/components/ui/collapsible.tsx +33 -0
  59. package/landing-page/src/app/components/ui/command.tsx +177 -0
  60. package/landing-page/src/app/components/ui/context-menu.tsx +252 -0
  61. package/landing-page/src/app/components/ui/dialog.tsx +135 -0
  62. package/landing-page/src/app/components/ui/drawer.tsx +132 -0
  63. package/landing-page/src/app/components/ui/dropdown-menu.tsx +257 -0
  64. package/landing-page/src/app/components/ui/form.tsx +168 -0
  65. package/landing-page/src/app/components/ui/hover-card.tsx +44 -0
  66. package/landing-page/src/app/components/ui/input-otp.tsx +77 -0
  67. package/landing-page/src/app/components/ui/input.tsx +21 -0
  68. package/landing-page/src/app/components/ui/label.tsx +24 -0
  69. package/landing-page/src/app/components/ui/menubar.tsx +276 -0
  70. package/landing-page/src/app/components/ui/navigation-menu.tsx +168 -0
  71. package/landing-page/src/app/components/ui/pagination.tsx +127 -0
  72. package/landing-page/src/app/components/ui/popover.tsx +48 -0
  73. package/landing-page/src/app/components/ui/progress.tsx +31 -0
  74. package/landing-page/src/app/components/ui/radio-group.tsx +45 -0
  75. package/landing-page/src/app/components/ui/resizable.tsx +56 -0
  76. package/landing-page/src/app/components/ui/scroll-area.tsx +58 -0
  77. package/landing-page/src/app/components/ui/select.tsx +189 -0
  78. package/landing-page/src/app/components/ui/separator.tsx +28 -0
  79. package/landing-page/src/app/components/ui/sheet.tsx +139 -0
  80. package/landing-page/src/app/components/ui/sidebar.tsx +726 -0
  81. package/landing-page/src/app/components/ui/skeleton.tsx +13 -0
  82. package/landing-page/src/app/components/ui/slider.tsx +63 -0
  83. package/landing-page/src/app/components/ui/sonner.tsx +25 -0
  84. package/landing-page/src/app/components/ui/switch.tsx +31 -0
  85. package/landing-page/src/app/components/ui/table.tsx +116 -0
  86. package/landing-page/src/app/components/ui/tabs.tsx +66 -0
  87. package/landing-page/src/app/components/ui/textarea.tsx +18 -0
  88. package/landing-page/src/app/components/ui/toggle-group.tsx +73 -0
  89. package/landing-page/src/app/components/ui/toggle.tsx +47 -0
  90. package/landing-page/src/app/components/ui/tooltip.tsx +61 -0
  91. package/landing-page/src/app/components/ui/use-mobile.ts +21 -0
  92. package/landing-page/src/app/components/ui/utils.ts +6 -0
  93. package/landing-page/src/components/docs/DocsContent.tsx +718 -0
  94. package/landing-page/src/components/docs/DocsSidebar.tsx +84 -0
  95. package/landing-page/src/components/landing/CTA.tsx +59 -0
  96. package/landing-page/src/components/landing/Comparison.tsx +84 -0
  97. package/landing-page/src/components/landing/FaultyTerminal.tsx +424 -0
  98. package/landing-page/src/components/landing/Features.tsx +201 -0
  99. package/landing-page/src/components/landing/Hero.tsx +142 -0
  100. package/landing-page/src/components/landing/Pricing.tsx +140 -0
  101. package/landing-page/src/components/landing/Roadmap.tsx +86 -0
  102. package/landing-page/src/components/landing/Security.tsx +81 -0
  103. package/landing-page/src/components/landing/TerminalWindow.tsx +27 -0
  104. package/landing-page/src/components/landing/UseCases.tsx +55 -0
  105. package/landing-page/src/components/landing/Workflow.tsx +101 -0
  106. package/landing-page/src/components/layout/DashboardNavbar.tsx +37 -0
  107. package/landing-page/src/components/layout/Footer.tsx +55 -0
  108. package/landing-page/src/components/layout/LandingNavbar.tsx +82 -0
  109. package/landing-page/src/components/ui/badge.tsx +39 -0
  110. package/landing-page/src/components/ui/breadcrumb.tsx +115 -0
  111. package/landing-page/src/components/ui/button.tsx +57 -0
  112. package/landing-page/src/components/ui/card.tsx +79 -0
  113. package/landing-page/src/components/ui/mock-terminal.tsx +68 -0
  114. package/landing-page/src/components/ui/separator.tsx +28 -0
  115. package/landing-page/src/lib/utils.ts +6 -0
  116. package/landing-page/src/main.tsx +10 -0
  117. package/landing-page/src/pages/Dashboard.tsx +133 -0
  118. package/landing-page/src/pages/DocsPage.tsx +79 -0
  119. package/landing-page/src/pages/LandingPage.tsx +31 -0
  120. package/landing-page/src/pages/TerminalView.tsx +106 -0
  121. package/landing-page/src/styles/fonts.css +0 -0
  122. package/landing-page/src/styles/index.css +3 -0
  123. package/landing-page/src/styles/tailwind.css +4 -0
  124. package/landing-page/src/styles/theme.css +181 -0
  125. package/landing-page/vite.config.ts +19 -0
  126. package/npm/darwin-arm64/bin/gssh +0 -0
  127. package/npm/darwin-arm64/package.json +20 -0
  128. package/package.json +74 -0
  129. package/scripts/build.ts +284 -0
  130. package/scripts/release.ts +140 -0
  131. package/src/__tests__/test-utils.ts +298 -0
  132. package/src/commands/__tests__/serve-messages.test.ts +190 -0
  133. package/src/commands/access.ts +298 -0
  134. package/src/commands/add.ts +452 -0
  135. package/src/commands/auth.ts +364 -0
  136. package/src/commands/connect.ts +287 -0
  137. package/src/commands/directory.ts +16 -0
  138. package/src/commands/host.ts +396 -0
  139. package/src/commands/identity.ts +184 -0
  140. package/src/commands/list.ts +200 -0
  141. package/src/commands/relay.ts +315 -0
  142. package/src/commands/remove.ts +241 -0
  143. package/src/commands/serve.ts +1493 -0
  144. package/src/commands/share.ts +456 -0
  145. package/src/commands/status.ts +125 -0
  146. package/src/commands/switch.ts +353 -0
  147. package/src/commands/tmux.ts +317 -0
  148. package/src/core/__tests__/access.test.ts +240 -0
  149. package/src/core/access.ts +277 -0
  150. package/src/core/bundle.ts +342 -0
  151. package/src/core/config.ts +510 -0
  152. package/src/core/git.ts +317 -0
  153. package/src/core/github.ts +151 -0
  154. package/src/core/identity.ts +631 -0
  155. package/src/core/linear.ts +225 -0
  156. package/src/core/shell.ts +161 -0
  157. package/src/core/trusted-relays.ts +315 -0
  158. package/src/index.ts +821 -0
  159. package/src/lib/remote-session/index.ts +7 -0
  160. package/src/lib/remote-session/protocol.ts +267 -0
  161. package/src/lib/remote-session/session-handler.ts +581 -0
  162. package/src/lib/remote-session/workspace-scanner.ts +167 -0
  163. package/src/lib/tmux-lite/README.md +81 -0
  164. package/src/lib/tmux-lite/cli.ts +796 -0
  165. package/src/lib/tmux-lite/crypto/__tests__/helpers/handshake-runner.ts +349 -0
  166. package/src/lib/tmux-lite/crypto/__tests__/helpers/mock-relay.ts +291 -0
  167. package/src/lib/tmux-lite/crypto/__tests__/helpers/test-identities.ts +142 -0
  168. package/src/lib/tmux-lite/crypto/__tests__/integration/authorization.integration.test.ts +339 -0
  169. package/src/lib/tmux-lite/crypto/__tests__/integration/e2e-communication.integration.test.ts +477 -0
  170. package/src/lib/tmux-lite/crypto/__tests__/integration/error-handling.integration.test.ts +499 -0
  171. package/src/lib/tmux-lite/crypto/__tests__/integration/handshake.integration.test.ts +371 -0
  172. package/src/lib/tmux-lite/crypto/__tests__/integration/security.integration.test.ts +573 -0
  173. package/src/lib/tmux-lite/crypto/access-control.test.ts +512 -0
  174. package/src/lib/tmux-lite/crypto/access-control.ts +320 -0
  175. package/src/lib/tmux-lite/crypto/frames.test.ts +262 -0
  176. package/src/lib/tmux-lite/crypto/frames.ts +141 -0
  177. package/src/lib/tmux-lite/crypto/handshake.ts +894 -0
  178. package/src/lib/tmux-lite/crypto/identity.test.ts +220 -0
  179. package/src/lib/tmux-lite/crypto/identity.ts +286 -0
  180. package/src/lib/tmux-lite/crypto/index.ts +51 -0
  181. package/src/lib/tmux-lite/crypto/invites.test.ts +381 -0
  182. package/src/lib/tmux-lite/crypto/invites.ts +215 -0
  183. package/src/lib/tmux-lite/crypto/keyexchange.ts +435 -0
  184. package/src/lib/tmux-lite/crypto/keys.test.ts +58 -0
  185. package/src/lib/tmux-lite/crypto/keys.ts +47 -0
  186. package/src/lib/tmux-lite/crypto/secretbox.test.ts +169 -0
  187. package/src/lib/tmux-lite/crypto/secretbox.ts +124 -0
  188. package/src/lib/tmux-lite/handshake-handler.ts +451 -0
  189. package/src/lib/tmux-lite/protocol.test.ts +307 -0
  190. package/src/lib/tmux-lite/protocol.ts +266 -0
  191. package/src/lib/tmux-lite/relay-client.ts +506 -0
  192. package/src/lib/tmux-lite/server.ts +1250 -0
  193. package/src/lib/tmux-lite/shell-integration.sh +37 -0
  194. package/src/lib/tmux-lite/terminal-queries.test.ts +54 -0
  195. package/src/lib/tmux-lite/terminal-queries.ts +49 -0
  196. package/src/relay/__tests__/e2e-flow.test.ts +1284 -0
  197. package/src/relay/__tests__/helpers/auth.ts +354 -0
  198. package/src/relay/__tests__/helpers/ports.ts +51 -0
  199. package/src/relay/__tests__/protocol-validation.test.ts +265 -0
  200. package/src/relay/authorization.ts +303 -0
  201. package/src/relay/embedded-assets.generated.d.ts +15 -0
  202. package/src/relay/identity.ts +352 -0
  203. package/src/relay/index.ts +57 -0
  204. package/src/relay/pipes.test.ts +427 -0
  205. package/src/relay/pipes.ts +195 -0
  206. package/src/relay/protocol.ts +804 -0
  207. package/src/relay/registries.test.ts +437 -0
  208. package/src/relay/registries.ts +593 -0
  209. package/src/relay/server.test.ts +1323 -0
  210. package/src/relay/server.ts +1092 -0
  211. package/src/relay/signing.ts +238 -0
  212. package/src/relay/types.ts +69 -0
  213. package/src/serve/client-session-manager.ts +622 -0
  214. package/src/serve/daemon.ts +497 -0
  215. package/src/serve/pty-session.ts +236 -0
  216. package/src/serve/types.ts +169 -0
  217. package/src/shared/components/Flow.tsx +453 -0
  218. package/src/shared/components/Flow.tui.tsx +343 -0
  219. package/src/shared/components/Flow.web.tsx +442 -0
  220. package/src/shared/components/Inbox.tsx +446 -0
  221. package/src/shared/components/Inbox.tui.tsx +262 -0
  222. package/src/shared/components/Inbox.web.tsx +329 -0
  223. package/src/shared/components/MachineList.tsx +187 -0
  224. package/src/shared/components/MachineList.tui.tsx +161 -0
  225. package/src/shared/components/MachineList.web.tsx +210 -0
  226. package/src/shared/components/ProjectList.tsx +176 -0
  227. package/src/shared/components/ProjectList.tui.tsx +109 -0
  228. package/src/shared/components/ProjectList.web.tsx +143 -0
  229. package/src/shared/components/SpacesBrowser.tsx +332 -0
  230. package/src/shared/components/SpacesBrowser.tui.tsx +163 -0
  231. package/src/shared/components/SpacesBrowser.web.tsx +221 -0
  232. package/src/shared/components/index.ts +103 -0
  233. package/src/shared/hooks/index.ts +16 -0
  234. package/src/shared/hooks/useNavigation.ts +226 -0
  235. package/src/shared/index.ts +122 -0
  236. package/src/shared/providers/LocalMachineProvider.ts +425 -0
  237. package/src/shared/providers/MachineProvider.ts +165 -0
  238. package/src/shared/providers/RemoteMachineProvider.ts +444 -0
  239. package/src/shared/providers/index.ts +26 -0
  240. package/src/shared/types.ts +145 -0
  241. package/src/tui/adapters.ts +120 -0
  242. package/src/tui/app.tsx +1816 -0
  243. package/src/tui/components/Terminal.tsx +580 -0
  244. package/src/tui/hooks/index.ts +35 -0
  245. package/src/tui/hooks/useAppState.ts +314 -0
  246. package/src/tui/hooks/useDaemonStatus.ts +174 -0
  247. package/src/tui/hooks/useInboxTUI.ts +113 -0
  248. package/src/tui/hooks/useRemoteMachines.ts +209 -0
  249. package/src/tui/index.ts +24 -0
  250. package/src/tui/state.ts +299 -0
  251. package/src/tui/terminal-bracketed-paste.test.ts +45 -0
  252. package/src/tui/terminal-bracketed-paste.ts +47 -0
  253. package/src/types/bundle.ts +112 -0
  254. package/src/types/config.ts +89 -0
  255. package/src/types/errors.ts +206 -0
  256. package/src/types/identity.ts +284 -0
  257. package/src/types/workspace-fuzzy.ts +49 -0
  258. package/src/types/workspace.ts +151 -0
  259. package/src/utils/bun-socket-writer.ts +80 -0
  260. package/src/utils/deps.ts +127 -0
  261. package/src/utils/fuzzy-match.ts +125 -0
  262. package/src/utils/logger.ts +127 -0
  263. package/src/utils/markdown.ts +254 -0
  264. package/src/utils/onboarding.ts +229 -0
  265. package/src/utils/prompts.ts +114 -0
  266. package/src/utils/run-commands.ts +112 -0
  267. package/src/utils/run-scripts.ts +142 -0
  268. package/src/utils/sanitize.ts +98 -0
  269. package/src/utils/secrets.ts +122 -0
  270. package/src/utils/shell-escape.ts +40 -0
  271. package/src/utils/utf8.ts +79 -0
  272. package/src/utils/workspace-state.ts +47 -0
  273. package/src/web/README.md +73 -0
  274. package/src/web/bun.lock +575 -0
  275. package/src/web/eslint.config.js +23 -0
  276. package/src/web/index.html +16 -0
  277. package/src/web/package.json +37 -0
  278. package/src/web/public/vite.svg +1 -0
  279. package/src/web/src/App.tsx +604 -0
  280. package/src/web/src/assets/react.svg +1 -0
  281. package/src/web/src/components/Terminal.tsx +207 -0
  282. package/src/web/src/hooks/useRelayConnection.ts +224 -0
  283. package/src/web/src/hooks/useTerminal.ts +699 -0
  284. package/src/web/src/index.css +55 -0
  285. package/src/web/src/lib/crypto/__tests__/web-terminal.test.ts +1158 -0
  286. package/src/web/src/lib/crypto/frames.ts +205 -0
  287. package/src/web/src/lib/crypto/handshake.ts +396 -0
  288. package/src/web/src/lib/crypto/identity.ts +128 -0
  289. package/src/web/src/lib/crypto/keyexchange.ts +246 -0
  290. package/src/web/src/lib/crypto/relay-signing.ts +53 -0
  291. package/src/web/src/lib/invite.ts +58 -0
  292. package/src/web/src/lib/storage/identity-store.ts +94 -0
  293. package/src/web/src/main.tsx +10 -0
  294. package/src/web/src/types/identity.ts +45 -0
  295. package/src/web/tsconfig.app.json +28 -0
  296. package/src/web/tsconfig.json +7 -0
  297. package/src/web/tsconfig.node.json +26 -0
  298. package/src/web/vite.config.ts +31 -0
  299. package/todo-security.md +92 -0
  300. package/tsconfig.json +23 -0
  301. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite +0 -0
  302. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite-shm +0 -0
  303. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite-wal +0 -0
  304. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite +0 -0
  305. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite-shm +0 -0
  306. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite-wal +0 -0
  307. package/worker/bun.lock +237 -0
  308. package/worker/package.json +22 -0
  309. package/worker/schema.sql +96 -0
  310. package/worker/src/handlers/auth.ts +451 -0
  311. package/worker/src/handlers/subdomains.ts +376 -0
  312. package/worker/src/handlers/user.ts +98 -0
  313. package/worker/src/index.ts +70 -0
  314. package/worker/src/middleware/auth.ts +152 -0
  315. package/worker/src/services/cloudflare.ts +609 -0
  316. package/worker/src/types.ts +96 -0
  317. package/worker/tsconfig.json +15 -0
  318. package/worker/wrangler.toml +26 -0
@@ -0,0 +1,319 @@
1
+ # Gateway Worker Specification
2
+
3
+ > **Status: SPECIFICATION ONLY - NOT YET IMPLEMENTED**
4
+ >
5
+ > This document describes the planned Gateway Worker for subdomain routing and
6
+ > authentication. The current implementation only includes the API Worker
7
+ > (`api.gitspace.sh`) for user/subdomain management. The Gateway Worker
8
+ > described here is Phase 2 of the platform architecture.
9
+
10
+ ## Overview
11
+
12
+ A Cloudflare Worker that sits in front of all user subdomains (`*.{user}.gitspace.sh`), providing:
13
+ - Authentication via gitspace.sh GitHub OAuth
14
+ - Authorization for shared services
15
+ - Routing to appropriate Cloudflare Tunnels
16
+
17
+ ## Why Not Cloudflare Access?
18
+
19
+ Cloudflare Access:
20
+ - Uses its own identity providers (separate OAuth flows)
21
+ - Policies configured per-app in dashboard, not programmatically
22
+ - Can't query our D1 database for ACL/invites
23
+ - Can't validate our signed tokens
24
+
25
+ We need:
26
+ - Single identity across all gitspace.sh subdomains
27
+ - Programmatic access control via our API
28
+ - Custom authorization logic (invites, ACL, port sharing)
29
+ - Portable session (one login works everywhere)
30
+
31
+ ## Architecture
32
+
33
+ ```
34
+ ┌─────────────────────────────────────────────────────────────────────┐
35
+ │ Cloudflare Edge │
36
+ │ │
37
+ │ Request: app.username.gitspace.sh │
38
+ │ │ │
39
+ │ ▼ │
40
+ │ ┌────────────────────────────────────────────────────────────┐ │
41
+ │ │ Gateway Worker │ │
42
+ │ │ │ │
43
+ │ │ 1. Parse: owner=username, service=app │ │
44
+ │ │ 2. Validate session cookie (JWT signed by gitspace.sh) │ │
45
+ │ │ 3. Check authorization (D1 query) │ │
46
+ │ │ 4. Route to tunnel │ │
47
+ │ │ │ │
48
+ │ └────────────────────────────────────────────────────────────┘ │
49
+ │ │ │
50
+ │ ▼ │
51
+ │ ┌──────────────────────────────────────────────────────────────┐ │
52
+ │ │ Cloudflare Tunnel → User's Machine │ │
53
+ │ └──────────────────────────────────────────────────────────────┘ │
54
+ └─────────────────────────────────────────────────────────────────────┘
55
+ ```
56
+
57
+ ## URL Structure
58
+
59
+ ```
60
+ {service}.{owner}.gitspace.sh
61
+
62
+ Examples:
63
+ username.gitspace.sh → relay (default, terminal access)
64
+ app.username.gitspace.sh → localhost:3000
65
+ api.username.gitspace.sh → localhost:8080
66
+ preview.username.gitspace.sh → localhost:5173
67
+ ```
68
+
69
+ ## Data Model
70
+
71
+ ```sql
72
+ -- Services (ports exposed under a subdomain)
73
+ CREATE TABLE services (
74
+ id TEXT PRIMARY KEY,
75
+ subdomain_id TEXT REFERENCES subdomains(id),
76
+ name TEXT NOT NULL, -- 'app', 'api', '' (root = relay)
77
+ local_port INTEGER NOT NULL, -- 3000, 8080, 4480 (relay)
78
+ visibility TEXT DEFAULT 'owner', -- 'public', 'owner', 'shared'
79
+ created_at INTEGER,
80
+ updated_at INTEGER,
81
+
82
+ UNIQUE(subdomain_id, name)
83
+ );
84
+
85
+ -- Service access grants (for visibility='shared')
86
+ CREATE TABLE service_access (
87
+ id TEXT PRIMARY KEY,
88
+ service_id TEXT REFERENCES services(id),
89
+ user_id TEXT REFERENCES users(id), -- GitHub-authenticated user
90
+ invite_token_hash TEXT, -- Alternative: invite-based access
91
+ expires_at INTEGER,
92
+ created_at INTEGER
93
+ );
94
+ ```
95
+
96
+ ## Authentication
97
+
98
+ ### Session Token (JWT)
99
+
100
+ Issued by gitspace.sh after GitHub OAuth:
101
+
102
+ ```typescript
103
+ interface SessionToken {
104
+ sub: string; // gitspace.sh user ID
105
+ github_id: string; // GitHub user ID
106
+ github_username: string;
107
+ iat: number;
108
+ exp: number;
109
+ }
110
+ ```
111
+
112
+ Cookie settings:
113
+ ```
114
+ __gitspace_session=<jwt>
115
+ Domain=.gitspace.sh // Works for all subdomains
116
+ HttpOnly=true
117
+ Secure=true
118
+ SameSite=Lax
119
+ ```
120
+
121
+ ### Auth Flow
122
+
123
+ **First visit (no session):**
124
+ 1. User visits `app.username.gitspace.sh`
125
+ 2. Gateway Worker: no valid session cookie
126
+ 3. Redirect → `gitspace.sh/login?redirect=https://app.username.gitspace.sh`
127
+ 4. User authenticates with GitHub
128
+ 5. gitspace.sh sets session cookie on `.gitspace.sh`
129
+ 6. Redirect back to original URL
130
+ 7. Gateway Worker validates session, checks authorization
131
+
132
+ **Return visit (has session):**
133
+ 1. User visits `app.username.gitspace.sh`
134
+ 2. Gateway Worker: session cookie present
135
+ 3. Validate JWT signature and expiry
136
+ 4. Query D1: does this user have access?
137
+ 5. Forward to tunnel
138
+
139
+ **Invite link (no login required):**
140
+ 1. User visits `app.username.gitspace.sh?invite=xxx`
141
+ 2. Gateway Worker: validate invite signature (Ed25519)
142
+ 3. Valid → forward to tunnel
143
+ 4. Optionally prompt to "claim" by logging in
144
+
145
+ ## Authorization Logic
146
+
147
+ ```typescript
148
+ async function checkAccess(
149
+ user: SessionUser | null,
150
+ service: Service,
151
+ inviteToken: string | null
152
+ ): Promise<boolean> {
153
+ // Public services: anyone
154
+ if (service.visibility === 'public') {
155
+ return true;
156
+ }
157
+
158
+ // Valid invite token: allow
159
+ if (inviteToken && await validateInvite(inviteToken, service)) {
160
+ return true;
161
+ }
162
+
163
+ // Must be logged in from here
164
+ if (!user) {
165
+ return false;
166
+ }
167
+
168
+ // Owner: always has access to their own services
169
+ const subdomain = await getSubdomain(service.subdomain_id);
170
+ if (subdomain.user_id === user.id) {
171
+ return true;
172
+ }
173
+
174
+ // Shared: check access grants
175
+ if (service.visibility === 'shared') {
176
+ const grant = await getAccessGrant(service.id, user.id);
177
+ return grant !== null && (grant.expires_at === null || grant.expires_at > Date.now());
178
+ }
179
+
180
+ return false;
181
+ }
182
+ ```
183
+
184
+ ## Gateway Worker Implementation
185
+
186
+ ```typescript
187
+ // worker-gateway/src/index.ts
188
+ export default {
189
+ async fetch(request: Request, env: Env): Promise<Response> {
190
+ const url = new URL(request.url);
191
+ const host = url.hostname;
192
+
193
+ // Parse subdomain: app.username.gitspace.sh
194
+ const parsed = parseHost(host);
195
+ if (!parsed) {
196
+ return new Response('Invalid hostname', { status: 400 });
197
+ }
198
+
199
+ const { owner, serviceName } = parsed;
200
+
201
+ // Lookup service in D1
202
+ const service = await env.DB.prepare(`
203
+ SELECT s.*, sub.user_id as owner_user_id, sub.tunnel_id
204
+ FROM services s
205
+ JOIN subdomains sub ON s.subdomain_id = sub.id
206
+ WHERE sub.subdomain = ? AND s.name = ?
207
+ `).bind(owner, serviceName || '').first();
208
+
209
+ if (!service) {
210
+ return new Response('Service not found', { status: 404 });
211
+ }
212
+
213
+ // Get session from cookie
214
+ const sessionToken = getSessionCookie(request);
215
+ const user = sessionToken ? await validateSession(sessionToken, env) : null;
216
+
217
+ // Get invite from query param
218
+ const inviteToken = url.searchParams.get('invite');
219
+
220
+ // Check authorization
221
+ const hasAccess = await checkAccess(user, service, inviteToken);
222
+
223
+ if (!hasAccess) {
224
+ // No session? Redirect to login
225
+ if (!user) {
226
+ const loginUrl = new URL('https://gitspace.sh/login');
227
+ loginUrl.searchParams.set('redirect', request.url);
228
+ return Response.redirect(loginUrl.toString(), 302);
229
+ }
230
+
231
+ // Has session but not authorized
232
+ return new Response('Access denied', { status: 403 });
233
+ }
234
+
235
+ // Forward to tunnel
236
+ // The tunnel is already configured with ingress rules
237
+ // We just need to pass through the request
238
+ return fetch(request);
239
+ }
240
+ }
241
+ ```
242
+
243
+ ## CLI Commands
244
+
245
+ ```bash
246
+ # Share a port as a service
247
+ gssh share port 3000 --as app
248
+ # Creates: app.username.gitspace.sh → localhost:3000
249
+
250
+ # Share with specific user
251
+ gssh share port 3000 --as app --with alice
252
+ # Adds alice to service_access
253
+
254
+ # Make public (no auth required)
255
+ gssh share port 3000 --as app --public
256
+
257
+ # List shared services
258
+ gssh share list
259
+ # app.username.gitspace.sh → :3000 (owner-only)
260
+ # api.username.gitspace.sh → :8080 (shared: alice, bob)
261
+ # preview.username.gitspace.sh → :5173 (public)
262
+
263
+ # Stop sharing
264
+ gssh share remove app
265
+
266
+ # Grant access to existing service
267
+ gssh share grant app --to bob
268
+
269
+ # Revoke access
270
+ gssh share revoke app --from bob
271
+ ```
272
+
273
+ ## Tunnel Configuration
274
+
275
+ When a user shares a port, the tunnel ingress is updated:
276
+
277
+ ```yaml
278
+ ingress:
279
+ - hostname: username.gitspace.sh
280
+ service: http://localhost:4480 # relay
281
+ - hostname: "*.username.gitspace.sh"
282
+ service: http://localhost:4480 # default to relay
283
+ - hostname: app.username.gitspace.sh
284
+ service: http://localhost:3000 # specific service
285
+ - service: http_status:404
286
+ ```
287
+
288
+ The Gateway Worker handles auth BEFORE the request reaches the tunnel.
289
+
290
+ ## Migration Path
291
+
292
+ ### Phase 1 (Current)
293
+ - Relay-level auth only (signed messages + challenge-response)
294
+ - No Gateway Worker
295
+
296
+ ### Phase 2 (This Spec)
297
+ - Deploy Gateway Worker
298
+ - Session cookies via gitspace.sh OAuth
299
+ - Owner-only access (prove you own the GitHub account)
300
+
301
+ ### Phase 3 (Future)
302
+ - Invite system for sharing with others
303
+ - Service-level access grants
304
+ - Port sharing CLI commands
305
+
306
+ ## Security Considerations
307
+
308
+ 1. **Session tokens** - Signed by gitspace.sh, validated at edge
309
+ 2. **Invite tokens** - Ed25519 signed by service owner, time-limited
310
+ 3. **Cookie scope** - `.gitspace.sh` allows SSO across all subdomains
311
+ 4. **HTTPS only** - All traffic through Cloudflare, TLS enforced
312
+ 5. **No secrets in Worker** - Only public keys for signature validation
313
+
314
+ ## Open Questions
315
+
316
+ 1. **Wildcard routing** - How to handle `*.username.gitspace.sh` efficiently?
317
+ 2. **WebSocket support** - Gateway Worker must handle WS upgrade
318
+ 3. **Rate limiting** - Per-user or per-service limits?
319
+ 4. **Audit logging** - Track access for security/debugging?