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.
- package/.claude/settings.local.json +21 -0
- package/.gitspace/bundle.json +50 -0
- package/.gitspace/select/01-status.sh +40 -0
- package/.gitspace/setup/01-install-deps.sh +12 -0
- package/.gitspace/setup/02-typecheck.sh +16 -0
- package/AGENTS.md +439 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +25 -0
- package/README.md +607 -0
- package/bin/gssh +62 -0
- package/bun.lock +647 -0
- package/docs/CONNECTION.md +623 -0
- package/docs/GATEWAY-WORKER.md +319 -0
- package/docs/GETTING-STARTED.md +448 -0
- package/docs/GITSPACE-PLATFORM.md +1819 -0
- package/docs/INFRASTRUCTURE.md +1347 -0
- package/docs/PROTOCOL.md +619 -0
- package/docs/QUICKSTART.md +174 -0
- package/docs/RELAY.md +327 -0
- package/docs/REMOTE-DESIGN.md +549 -0
- package/docs/ROADMAP.md +564 -0
- package/docs/SITE_DOCS_FIGMA_MAKE.md +1167 -0
- package/docs/STACK-DESIGN.md +588 -0
- package/docs/UNIFIED_ARCHITECTURE.md +292 -0
- package/experiments/pty-benchmark.ts +148 -0
- package/experiments/pty-latency.ts +100 -0
- package/experiments/router/client.ts +199 -0
- package/experiments/router/protocol.ts +74 -0
- package/experiments/router/router.ts +217 -0
- package/experiments/router/session.ts +180 -0
- package/experiments/router/test.ts +133 -0
- package/experiments/socket-bandwidth.ts +77 -0
- package/homebrew/gitspace.rb +45 -0
- package/landing-page/ATTRIBUTIONS.md +3 -0
- package/landing-page/README.md +11 -0
- package/landing-page/bun.lock +801 -0
- package/landing-page/guidelines/Guidelines.md +61 -0
- package/landing-page/index.html +37 -0
- package/landing-page/package.json +90 -0
- package/landing-page/postcss.config.mjs +15 -0
- package/landing-page/public/_redirects +1 -0
- package/landing-page/public/favicon.png +0 -0
- package/landing-page/src/app/App.tsx +53 -0
- package/landing-page/src/app/components/figma/ImageWithFallback.tsx +27 -0
- package/landing-page/src/app/components/ui/accordion.tsx +66 -0
- package/landing-page/src/app/components/ui/alert-dialog.tsx +157 -0
- package/landing-page/src/app/components/ui/alert.tsx +66 -0
- package/landing-page/src/app/components/ui/aspect-ratio.tsx +11 -0
- package/landing-page/src/app/components/ui/avatar.tsx +53 -0
- package/landing-page/src/app/components/ui/badge.tsx +46 -0
- package/landing-page/src/app/components/ui/breadcrumb.tsx +109 -0
- package/landing-page/src/app/components/ui/button.tsx +57 -0
- package/landing-page/src/app/components/ui/calendar.tsx +75 -0
- package/landing-page/src/app/components/ui/card.tsx +92 -0
- package/landing-page/src/app/components/ui/carousel.tsx +241 -0
- package/landing-page/src/app/components/ui/chart.tsx +353 -0
- package/landing-page/src/app/components/ui/checkbox.tsx +32 -0
- package/landing-page/src/app/components/ui/collapsible.tsx +33 -0
- package/landing-page/src/app/components/ui/command.tsx +177 -0
- package/landing-page/src/app/components/ui/context-menu.tsx +252 -0
- package/landing-page/src/app/components/ui/dialog.tsx +135 -0
- package/landing-page/src/app/components/ui/drawer.tsx +132 -0
- package/landing-page/src/app/components/ui/dropdown-menu.tsx +257 -0
- package/landing-page/src/app/components/ui/form.tsx +168 -0
- package/landing-page/src/app/components/ui/hover-card.tsx +44 -0
- package/landing-page/src/app/components/ui/input-otp.tsx +77 -0
- package/landing-page/src/app/components/ui/input.tsx +21 -0
- package/landing-page/src/app/components/ui/label.tsx +24 -0
- package/landing-page/src/app/components/ui/menubar.tsx +276 -0
- package/landing-page/src/app/components/ui/navigation-menu.tsx +168 -0
- package/landing-page/src/app/components/ui/pagination.tsx +127 -0
- package/landing-page/src/app/components/ui/popover.tsx +48 -0
- package/landing-page/src/app/components/ui/progress.tsx +31 -0
- package/landing-page/src/app/components/ui/radio-group.tsx +45 -0
- package/landing-page/src/app/components/ui/resizable.tsx +56 -0
- package/landing-page/src/app/components/ui/scroll-area.tsx +58 -0
- package/landing-page/src/app/components/ui/select.tsx +189 -0
- package/landing-page/src/app/components/ui/separator.tsx +28 -0
- package/landing-page/src/app/components/ui/sheet.tsx +139 -0
- package/landing-page/src/app/components/ui/sidebar.tsx +726 -0
- package/landing-page/src/app/components/ui/skeleton.tsx +13 -0
- package/landing-page/src/app/components/ui/slider.tsx +63 -0
- package/landing-page/src/app/components/ui/sonner.tsx +25 -0
- package/landing-page/src/app/components/ui/switch.tsx +31 -0
- package/landing-page/src/app/components/ui/table.tsx +116 -0
- package/landing-page/src/app/components/ui/tabs.tsx +66 -0
- package/landing-page/src/app/components/ui/textarea.tsx +18 -0
- package/landing-page/src/app/components/ui/toggle-group.tsx +73 -0
- package/landing-page/src/app/components/ui/toggle.tsx +47 -0
- package/landing-page/src/app/components/ui/tooltip.tsx +61 -0
- package/landing-page/src/app/components/ui/use-mobile.ts +21 -0
- package/landing-page/src/app/components/ui/utils.ts +6 -0
- package/landing-page/src/components/docs/DocsContent.tsx +718 -0
- package/landing-page/src/components/docs/DocsSidebar.tsx +84 -0
- package/landing-page/src/components/landing/CTA.tsx +59 -0
- package/landing-page/src/components/landing/Comparison.tsx +84 -0
- package/landing-page/src/components/landing/FaultyTerminal.tsx +424 -0
- package/landing-page/src/components/landing/Features.tsx +201 -0
- package/landing-page/src/components/landing/Hero.tsx +142 -0
- package/landing-page/src/components/landing/Pricing.tsx +140 -0
- package/landing-page/src/components/landing/Roadmap.tsx +86 -0
- package/landing-page/src/components/landing/Security.tsx +81 -0
- package/landing-page/src/components/landing/TerminalWindow.tsx +27 -0
- package/landing-page/src/components/landing/UseCases.tsx +55 -0
- package/landing-page/src/components/landing/Workflow.tsx +101 -0
- package/landing-page/src/components/layout/DashboardNavbar.tsx +37 -0
- package/landing-page/src/components/layout/Footer.tsx +55 -0
- package/landing-page/src/components/layout/LandingNavbar.tsx +82 -0
- package/landing-page/src/components/ui/badge.tsx +39 -0
- package/landing-page/src/components/ui/breadcrumb.tsx +115 -0
- package/landing-page/src/components/ui/button.tsx +57 -0
- package/landing-page/src/components/ui/card.tsx +79 -0
- package/landing-page/src/components/ui/mock-terminal.tsx +68 -0
- package/landing-page/src/components/ui/separator.tsx +28 -0
- package/landing-page/src/lib/utils.ts +6 -0
- package/landing-page/src/main.tsx +10 -0
- package/landing-page/src/pages/Dashboard.tsx +133 -0
- package/landing-page/src/pages/DocsPage.tsx +79 -0
- package/landing-page/src/pages/LandingPage.tsx +31 -0
- package/landing-page/src/pages/TerminalView.tsx +106 -0
- package/landing-page/src/styles/fonts.css +0 -0
- package/landing-page/src/styles/index.css +3 -0
- package/landing-page/src/styles/tailwind.css +4 -0
- package/landing-page/src/styles/theme.css +181 -0
- package/landing-page/vite.config.ts +19 -0
- package/npm/darwin-arm64/bin/gssh +0 -0
- package/npm/darwin-arm64/package.json +20 -0
- package/package.json +74 -0
- package/scripts/build.ts +284 -0
- package/scripts/release.ts +140 -0
- package/src/__tests__/test-utils.ts +298 -0
- package/src/commands/__tests__/serve-messages.test.ts +190 -0
- package/src/commands/access.ts +298 -0
- package/src/commands/add.ts +452 -0
- package/src/commands/auth.ts +364 -0
- package/src/commands/connect.ts +287 -0
- package/src/commands/directory.ts +16 -0
- package/src/commands/host.ts +396 -0
- package/src/commands/identity.ts +184 -0
- package/src/commands/list.ts +200 -0
- package/src/commands/relay.ts +315 -0
- package/src/commands/remove.ts +241 -0
- package/src/commands/serve.ts +1493 -0
- package/src/commands/share.ts +456 -0
- package/src/commands/status.ts +125 -0
- package/src/commands/switch.ts +353 -0
- package/src/commands/tmux.ts +317 -0
- package/src/core/__tests__/access.test.ts +240 -0
- package/src/core/access.ts +277 -0
- package/src/core/bundle.ts +342 -0
- package/src/core/config.ts +510 -0
- package/src/core/git.ts +317 -0
- package/src/core/github.ts +151 -0
- package/src/core/identity.ts +631 -0
- package/src/core/linear.ts +225 -0
- package/src/core/shell.ts +161 -0
- package/src/core/trusted-relays.ts +315 -0
- package/src/index.ts +821 -0
- package/src/lib/remote-session/index.ts +7 -0
- package/src/lib/remote-session/protocol.ts +267 -0
- package/src/lib/remote-session/session-handler.ts +581 -0
- package/src/lib/remote-session/workspace-scanner.ts +167 -0
- package/src/lib/tmux-lite/README.md +81 -0
- package/src/lib/tmux-lite/cli.ts +796 -0
- package/src/lib/tmux-lite/crypto/__tests__/helpers/handshake-runner.ts +349 -0
- package/src/lib/tmux-lite/crypto/__tests__/helpers/mock-relay.ts +291 -0
- package/src/lib/tmux-lite/crypto/__tests__/helpers/test-identities.ts +142 -0
- package/src/lib/tmux-lite/crypto/__tests__/integration/authorization.integration.test.ts +339 -0
- package/src/lib/tmux-lite/crypto/__tests__/integration/e2e-communication.integration.test.ts +477 -0
- package/src/lib/tmux-lite/crypto/__tests__/integration/error-handling.integration.test.ts +499 -0
- package/src/lib/tmux-lite/crypto/__tests__/integration/handshake.integration.test.ts +371 -0
- package/src/lib/tmux-lite/crypto/__tests__/integration/security.integration.test.ts +573 -0
- package/src/lib/tmux-lite/crypto/access-control.test.ts +512 -0
- package/src/lib/tmux-lite/crypto/access-control.ts +320 -0
- package/src/lib/tmux-lite/crypto/frames.test.ts +262 -0
- package/src/lib/tmux-lite/crypto/frames.ts +141 -0
- package/src/lib/tmux-lite/crypto/handshake.ts +894 -0
- package/src/lib/tmux-lite/crypto/identity.test.ts +220 -0
- package/src/lib/tmux-lite/crypto/identity.ts +286 -0
- package/src/lib/tmux-lite/crypto/index.ts +51 -0
- package/src/lib/tmux-lite/crypto/invites.test.ts +381 -0
- package/src/lib/tmux-lite/crypto/invites.ts +215 -0
- package/src/lib/tmux-lite/crypto/keyexchange.ts +435 -0
- package/src/lib/tmux-lite/crypto/keys.test.ts +58 -0
- package/src/lib/tmux-lite/crypto/keys.ts +47 -0
- package/src/lib/tmux-lite/crypto/secretbox.test.ts +169 -0
- package/src/lib/tmux-lite/crypto/secretbox.ts +124 -0
- package/src/lib/tmux-lite/handshake-handler.ts +451 -0
- package/src/lib/tmux-lite/protocol.test.ts +307 -0
- package/src/lib/tmux-lite/protocol.ts +266 -0
- package/src/lib/tmux-lite/relay-client.ts +506 -0
- package/src/lib/tmux-lite/server.ts +1250 -0
- package/src/lib/tmux-lite/shell-integration.sh +37 -0
- package/src/lib/tmux-lite/terminal-queries.test.ts +54 -0
- package/src/lib/tmux-lite/terminal-queries.ts +49 -0
- package/src/relay/__tests__/e2e-flow.test.ts +1284 -0
- package/src/relay/__tests__/helpers/auth.ts +354 -0
- package/src/relay/__tests__/helpers/ports.ts +51 -0
- package/src/relay/__tests__/protocol-validation.test.ts +265 -0
- package/src/relay/authorization.ts +303 -0
- package/src/relay/embedded-assets.generated.d.ts +15 -0
- package/src/relay/identity.ts +352 -0
- package/src/relay/index.ts +57 -0
- package/src/relay/pipes.test.ts +427 -0
- package/src/relay/pipes.ts +195 -0
- package/src/relay/protocol.ts +804 -0
- package/src/relay/registries.test.ts +437 -0
- package/src/relay/registries.ts +593 -0
- package/src/relay/server.test.ts +1323 -0
- package/src/relay/server.ts +1092 -0
- package/src/relay/signing.ts +238 -0
- package/src/relay/types.ts +69 -0
- package/src/serve/client-session-manager.ts +622 -0
- package/src/serve/daemon.ts +497 -0
- package/src/serve/pty-session.ts +236 -0
- package/src/serve/types.ts +169 -0
- package/src/shared/components/Flow.tsx +453 -0
- package/src/shared/components/Flow.tui.tsx +343 -0
- package/src/shared/components/Flow.web.tsx +442 -0
- package/src/shared/components/Inbox.tsx +446 -0
- package/src/shared/components/Inbox.tui.tsx +262 -0
- package/src/shared/components/Inbox.web.tsx +329 -0
- package/src/shared/components/MachineList.tsx +187 -0
- package/src/shared/components/MachineList.tui.tsx +161 -0
- package/src/shared/components/MachineList.web.tsx +210 -0
- package/src/shared/components/ProjectList.tsx +176 -0
- package/src/shared/components/ProjectList.tui.tsx +109 -0
- package/src/shared/components/ProjectList.web.tsx +143 -0
- package/src/shared/components/SpacesBrowser.tsx +332 -0
- package/src/shared/components/SpacesBrowser.tui.tsx +163 -0
- package/src/shared/components/SpacesBrowser.web.tsx +221 -0
- package/src/shared/components/index.ts +103 -0
- package/src/shared/hooks/index.ts +16 -0
- package/src/shared/hooks/useNavigation.ts +226 -0
- package/src/shared/index.ts +122 -0
- package/src/shared/providers/LocalMachineProvider.ts +425 -0
- package/src/shared/providers/MachineProvider.ts +165 -0
- package/src/shared/providers/RemoteMachineProvider.ts +444 -0
- package/src/shared/providers/index.ts +26 -0
- package/src/shared/types.ts +145 -0
- package/src/tui/adapters.ts +120 -0
- package/src/tui/app.tsx +1816 -0
- package/src/tui/components/Terminal.tsx +580 -0
- package/src/tui/hooks/index.ts +35 -0
- package/src/tui/hooks/useAppState.ts +314 -0
- package/src/tui/hooks/useDaemonStatus.ts +174 -0
- package/src/tui/hooks/useInboxTUI.ts +113 -0
- package/src/tui/hooks/useRemoteMachines.ts +209 -0
- package/src/tui/index.ts +24 -0
- package/src/tui/state.ts +299 -0
- package/src/tui/terminal-bracketed-paste.test.ts +45 -0
- package/src/tui/terminal-bracketed-paste.ts +47 -0
- package/src/types/bundle.ts +112 -0
- package/src/types/config.ts +89 -0
- package/src/types/errors.ts +206 -0
- package/src/types/identity.ts +284 -0
- package/src/types/workspace-fuzzy.ts +49 -0
- package/src/types/workspace.ts +151 -0
- package/src/utils/bun-socket-writer.ts +80 -0
- package/src/utils/deps.ts +127 -0
- package/src/utils/fuzzy-match.ts +125 -0
- package/src/utils/logger.ts +127 -0
- package/src/utils/markdown.ts +254 -0
- package/src/utils/onboarding.ts +229 -0
- package/src/utils/prompts.ts +114 -0
- package/src/utils/run-commands.ts +112 -0
- package/src/utils/run-scripts.ts +142 -0
- package/src/utils/sanitize.ts +98 -0
- package/src/utils/secrets.ts +122 -0
- package/src/utils/shell-escape.ts +40 -0
- package/src/utils/utf8.ts +79 -0
- package/src/utils/workspace-state.ts +47 -0
- package/src/web/README.md +73 -0
- package/src/web/bun.lock +575 -0
- package/src/web/eslint.config.js +23 -0
- package/src/web/index.html +16 -0
- package/src/web/package.json +37 -0
- package/src/web/public/vite.svg +1 -0
- package/src/web/src/App.tsx +604 -0
- package/src/web/src/assets/react.svg +1 -0
- package/src/web/src/components/Terminal.tsx +207 -0
- package/src/web/src/hooks/useRelayConnection.ts +224 -0
- package/src/web/src/hooks/useTerminal.ts +699 -0
- package/src/web/src/index.css +55 -0
- package/src/web/src/lib/crypto/__tests__/web-terminal.test.ts +1158 -0
- package/src/web/src/lib/crypto/frames.ts +205 -0
- package/src/web/src/lib/crypto/handshake.ts +396 -0
- package/src/web/src/lib/crypto/identity.ts +128 -0
- package/src/web/src/lib/crypto/keyexchange.ts +246 -0
- package/src/web/src/lib/crypto/relay-signing.ts +53 -0
- package/src/web/src/lib/invite.ts +58 -0
- package/src/web/src/lib/storage/identity-store.ts +94 -0
- package/src/web/src/main.tsx +10 -0
- package/src/web/src/types/identity.ts +45 -0
- package/src/web/tsconfig.app.json +28 -0
- package/src/web/tsconfig.json +7 -0
- package/src/web/tsconfig.node.json +26 -0
- package/src/web/vite.config.ts +31 -0
- package/todo-security.md +92 -0
- package/tsconfig.json +23 -0
- package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite +0 -0
- package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite-shm +0 -0
- package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite-wal +0 -0
- package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite +0 -0
- package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite-shm +0 -0
- package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite-wal +0 -0
- package/worker/bun.lock +237 -0
- package/worker/package.json +22 -0
- package/worker/schema.sql +96 -0
- package/worker/src/handlers/auth.ts +451 -0
- package/worker/src/handlers/subdomains.ts +376 -0
- package/worker/src/handlers/user.ts +98 -0
- package/worker/src/index.ts +70 -0
- package/worker/src/middleware/auth.ts +152 -0
- package/worker/src/services/cloudflare.ts +609 -0
- package/worker/src/types.ts +96 -0
- package/worker/tsconfig.json +15 -0
- package/worker/wrangler.toml +26 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# GitSpace Quick Start Guide
|
|
2
|
+
|
|
3
|
+
Get up and running with GitSpace in 5 minutes.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
Install these tools first:
|
|
10
|
+
|
|
11
|
+
- [Bun](https://bun.sh) - JavaScript runtime
|
|
12
|
+
- [Git](https://git-scm.com/) - Version control
|
|
13
|
+
- [GitHub CLI](https://cli.github.com/) - `gh auth login` before using GitSpace
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bun install -g https://github.com/inkibra/gitspace.sh
|
|
21
|
+
|
|
22
|
+
# Verify
|
|
23
|
+
gssh --version
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Part 1: Local Workspace Management
|
|
29
|
+
|
|
30
|
+
### Launch the TUI
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gssh
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use arrow keys to navigate, `Enter` to select, `?` for help, `q` to quit.
|
|
37
|
+
|
|
38
|
+
### Add a Project (CLI)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
gssh add project
|
|
42
|
+
# Select a GitHub repo from the list
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Create a Workspace
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# In a project directory
|
|
49
|
+
gssh add my-feature
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Switch Workspaces
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
gssh switch my-feature
|
|
56
|
+
# Or just: gssh switch (interactive)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Part 2: Remote Terminal Access
|
|
62
|
+
|
|
63
|
+
### Option A: gitspace.sh (Easiest)
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# 1. Sign in with GitHub
|
|
67
|
+
gssh auth login
|
|
68
|
+
|
|
69
|
+
# 2. Reserve your subdomain
|
|
70
|
+
gssh host reserve yourname
|
|
71
|
+
|
|
72
|
+
# 3. Start serving
|
|
73
|
+
gssh serve
|
|
74
|
+
|
|
75
|
+
# 4. Open https://yourname.gitspace.sh in browser
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Option B: Self-Hosted
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Terminal 1: Start relay
|
|
82
|
+
gssh relay start --port 4480
|
|
83
|
+
|
|
84
|
+
# Terminal 2: Setup identity and serve
|
|
85
|
+
gssh identity init --label "My Mac"
|
|
86
|
+
gssh serve --relay ws://localhost:4480/ws
|
|
87
|
+
|
|
88
|
+
# Terminal 3: Create invite
|
|
89
|
+
gssh share create
|
|
90
|
+
# Share the URL with collaborators
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Connect from Another Device
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# First time: create identity
|
|
97
|
+
gssh identity init --label "Laptop"
|
|
98
|
+
|
|
99
|
+
# Connect using invite
|
|
100
|
+
gssh connect <invite-token>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Common Commands
|
|
106
|
+
|
|
107
|
+
| Command | Description |
|
|
108
|
+
|---------|-------------|
|
|
109
|
+
| `gssh` | Launch TUI |
|
|
110
|
+
| `gssh add project` | Add GitHub project |
|
|
111
|
+
| `gssh add <name>` | Create workspace |
|
|
112
|
+
| `gssh switch` | Switch workspace |
|
|
113
|
+
| `gssh list` | List workspaces |
|
|
114
|
+
| `gssh serve` | Enable remote access |
|
|
115
|
+
| `gssh status` | Check daemon status |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Key Bindings (TUI)
|
|
120
|
+
|
|
121
|
+
| Key | Action |
|
|
122
|
+
|-----|--------|
|
|
123
|
+
| `Enter` | Select/Open |
|
|
124
|
+
| `Tab` | Switch panels |
|
|
125
|
+
| `n` | New item |
|
|
126
|
+
| `d` | Delete |
|
|
127
|
+
| `?` | Help |
|
|
128
|
+
| `q` | Quit |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Directory Structure
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
~/gitspace/
|
|
136
|
+
├── .config.json # Global config
|
|
137
|
+
├── <project>/
|
|
138
|
+
│ ├── base/ # Base repo clone
|
|
139
|
+
│ └── workspaces/ # Your worktrees
|
|
140
|
+
│ └── my-feature/
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Next Steps
|
|
146
|
+
|
|
147
|
+
- **Remote access in depth**: [docs/GETTING-STARTED.md](GETTING-STARTED.md)
|
|
148
|
+
- **Security architecture**: [docs/REMOTE-DESIGN.md](REMOTE-DESIGN.md)
|
|
149
|
+
- **Protocol reference**: [docs/PROTOCOL.md](PROTOCOL.md)
|
|
150
|
+
- **Full README**: [README.md](../README.md)
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Troubleshooting
|
|
155
|
+
|
|
156
|
+
### "GitHub CLI not authenticated"
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
gh auth login
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### "No identity found"
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
gssh identity init --label "My Device"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### "Machine offline"
|
|
169
|
+
|
|
170
|
+
Ensure `gssh serve` is running on the target machine.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
*Last updated: 2025-01*
|
package/docs/RELAY.md
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# Relay Server Design
|
|
2
|
+
|
|
3
|
+
The relay is the bridge between your machine and remote clients. It routes E2E encrypted terminal sessions between machines and clients over WebSocket.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The Big Picture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Cloudflare (optional)
|
|
11
|
+
(TLS termination, DDoS protection)
|
|
12
|
+
│
|
|
13
|
+
▼
|
|
14
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
15
|
+
│ Your Mac │◀═════▶│ Relay Server │◀═════▶│ Browser/CLI │
|
|
16
|
+
│ │ wss │ │ wss │ │
|
|
17
|
+
│ gssh serve │ │ Routes by: │ │ Terminal view │
|
|
18
|
+
│ (PTY sessions) │ │ - machine ID │ │ (xterm.js) │
|
|
19
|
+
│ │ │ - connection │ │ │
|
|
20
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Your machine maintains a single persistent WebSocket to the relay. Terminal I/O flows over that connection as encrypted bytes. The relay is just a router - it cannot decrypt any content.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Design Decisions
|
|
28
|
+
|
|
29
|
+
### Why One Connection from Machine?
|
|
30
|
+
|
|
31
|
+
**Single multiplexed WebSocket** rather than connection-per-session because:
|
|
32
|
+
|
|
33
|
+
1. **Simpler firewall/NAT traversal** - One outbound connection, no port opening
|
|
34
|
+
2. **Easier reconnection** - One reconnect restores everything
|
|
35
|
+
3. **Lower overhead** - WebSocket framing once, not per-session
|
|
36
|
+
4. **Single TLS handshake** - Less CPU on both ends
|
|
37
|
+
|
|
38
|
+
The machine-to-relay connection is the "trunk line." All client connections are multiplexed over it via `connectionId`.
|
|
39
|
+
|
|
40
|
+
### Why Relay Can't See Terminal Content?
|
|
41
|
+
|
|
42
|
+
The relay forwards encrypted bytes for terminal sessions. It never decrypts. This is critical for:
|
|
43
|
+
|
|
44
|
+
1. **Zero-knowledge** - We can't see your commands, secrets, or output
|
|
45
|
+
2. **Security** - Compromised relay can't read terminal traffic
|
|
46
|
+
3. **Trust** - Users can verify encryption client-side
|
|
47
|
+
|
|
48
|
+
See [PROTOCOL.md](./PROTOCOL.md) for the encryption protocol details.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Connection Types
|
|
53
|
+
|
|
54
|
+
### 1. Machine Connection
|
|
55
|
+
|
|
56
|
+
When `gssh serve` starts, it opens a WebSocket to `ws://relay:port/ws?role=machine`:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
→ Relay sends relay_identity with challenge nonce
|
|
60
|
+
→ Machine signs nonce with Ed25519 private key
|
|
61
|
+
→ Machine sends register_machine with signingKey/keyExchangeKey + challengeResponse
|
|
62
|
+
→ Relay verifies signature and that signingKey is authorized
|
|
63
|
+
→ Relay sends access_list with authorized clients
|
|
64
|
+
→ Machine is now registered and ready
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The machine ID is derived from the machine's signing key. This ensures identity consistency across reconnects.
|
|
68
|
+
|
|
69
|
+
### 2. Client Connection
|
|
70
|
+
|
|
71
|
+
Browser or CLI connects to `ws://relay:port/ws?role=client`:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
→ Sign list/connect messages with Ed25519 identity
|
|
75
|
+
→ Connect via invite OR directly (if pre-authorized)
|
|
76
|
+
→ Relay routes to the target machine
|
|
77
|
+
→ Perform X3DH handshake with machine
|
|
78
|
+
→ Exchange E2E encrypted terminal data
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The relay verifies signatures, checks authorization, and creates a bidirectional pipe. It doesn't decrypt the content.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Routing Architecture
|
|
86
|
+
|
|
87
|
+
The relay maintains four registries:
|
|
88
|
+
|
|
89
|
+
### Machine Registry
|
|
90
|
+
```typescript
|
|
91
|
+
machines: Map<machineId, {
|
|
92
|
+
accountId: string,
|
|
93
|
+
signingKey: string, // Ed25519 public key
|
|
94
|
+
keyExchangeKey: string, // X25519 public key
|
|
95
|
+
label: string,
|
|
96
|
+
ws: WebSocket | null, // null = offline
|
|
97
|
+
lastSeen: Date
|
|
98
|
+
}>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Invite Registry
|
|
102
|
+
```typescript
|
|
103
|
+
invites: Map<inviteId, {
|
|
104
|
+
machineId: string,
|
|
105
|
+
expiresAt: number,
|
|
106
|
+
maxUses: number,
|
|
107
|
+
usedCount: number
|
|
108
|
+
}>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Machine Authorization Registry
|
|
112
|
+
Per-machine client access:
|
|
113
|
+
```typescript
|
|
114
|
+
authorizations: Map<machineId, Map<clientIdentityId, {
|
|
115
|
+
signingKey: string,
|
|
116
|
+
keyExchangeKey: string,
|
|
117
|
+
accessType: 'full' | 'session-invite',
|
|
118
|
+
sessionId?: string,
|
|
119
|
+
label?: string,
|
|
120
|
+
grantedAt: number
|
|
121
|
+
}>>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Global Access Registry
|
|
125
|
+
Account-level access that applies to all machines:
|
|
126
|
+
```typescript
|
|
127
|
+
globalAccess: Map<accountId, Map<clientIdentityId, {
|
|
128
|
+
signingKey: string,
|
|
129
|
+
keyExchangeKey: string,
|
|
130
|
+
accessType: 'full' | 'session-invite',
|
|
131
|
+
label?: string,
|
|
132
|
+
grantedAt: number,
|
|
133
|
+
machineIds?: string[] // If set, only applies to specific machines
|
|
134
|
+
}>>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
When a machine connects, it receives the combined access list from both registries.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Data Flow
|
|
142
|
+
|
|
143
|
+
### Client → Machine
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
Client sends: { type: "data", data: "base64-encrypted" }
|
|
147
|
+
Relay wraps: { type: "data", connectionId: "xyz", data: "base64-encrypted" }
|
|
148
|
+
Machine receives with connectionId to identify the client
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Machine → Client
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
Machine sends: { type: "data", connectionId: "xyz", data: "base64-encrypted" }
|
|
155
|
+
Relay unwraps: { type: "data", data: "base64-encrypted" }
|
|
156
|
+
Client receives without connectionId (it's their only connection)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The `connectionId` is assigned by the relay when a client connects and is used to multiplex multiple clients over the single machine WebSocket.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Connection Health
|
|
164
|
+
|
|
165
|
+
### Machine Keepalive
|
|
166
|
+
|
|
167
|
+
WebSocket protocol ping/pong:
|
|
168
|
+
- Machine sends ping every 30 seconds
|
|
169
|
+
- Relay responds with pong
|
|
170
|
+
- If no traffic for 60 seconds, machine reconnects
|
|
171
|
+
|
|
172
|
+
### Client Keepalive
|
|
173
|
+
|
|
174
|
+
Same pattern. Relay pings clients, clients respond.
|
|
175
|
+
|
|
176
|
+
### Reconnection
|
|
177
|
+
|
|
178
|
+
When machine reconnects:
|
|
179
|
+
1. Re-authenticates with same account
|
|
180
|
+
2. Re-registers with same signing key (must match)
|
|
181
|
+
3. Relay updates routing tables
|
|
182
|
+
4. Connected clients receive notification or continue streaming
|
|
183
|
+
|
|
184
|
+
When machine goes offline:
|
|
185
|
+
1. Relay marks machine's `ws` as `null`
|
|
186
|
+
2. All connected clients receive `connection_failed`
|
|
187
|
+
3. Client connections are closed
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Auth Model
|
|
192
|
+
|
|
193
|
+
### Machine Authentication
|
|
194
|
+
|
|
195
|
+
1. Relay sends `relay_identity` with a random challenge nonce
|
|
196
|
+
2. Machine signs the nonce with its Ed25519 private key
|
|
197
|
+
3. Machine sends `register_machine` with signing keys + challengeResponse
|
|
198
|
+
4. Relay verifies the signature and checks the signing key is authorized
|
|
199
|
+
|
|
200
|
+
### Client Authentication
|
|
201
|
+
|
|
202
|
+
Clients sign relay messages with their Ed25519 identity. Then either:
|
|
203
|
+
|
|
204
|
+
1. **Via Invite** - `connect_with_invite` with invite ID
|
|
205
|
+
- Relay validates invite exists and isn't expired/exhausted
|
|
206
|
+
- Relay decrements use count
|
|
207
|
+
- Routes to target machine
|
|
208
|
+
|
|
209
|
+
2. **Direct** - `connect_to_machine` with machine ID + client identity
|
|
210
|
+
- Relay checks both per-machine and global authorization registries
|
|
211
|
+
- Client must be pre-authorized
|
|
212
|
+
|
|
213
|
+
### Authorization Flow
|
|
214
|
+
|
|
215
|
+
1. Client connects with invite → routed to machine
|
|
216
|
+
2. Machine performs X3DH handshake, validates invite signature
|
|
217
|
+
3. Machine sends `authorize_client` to relay (unless single-use invite)
|
|
218
|
+
4. Client is now authorized for future direct connections
|
|
219
|
+
|
|
220
|
+
### Global Access Flow
|
|
221
|
+
|
|
222
|
+
1. Machine owner sends `add_global_access` via any connected machine
|
|
223
|
+
2. Relay adds to global access registry
|
|
224
|
+
3. Relay broadcasts `access_update` to all connected machines in the account
|
|
225
|
+
4. All machines update their local access lists immediately
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Relay CLI Commands
|
|
230
|
+
|
|
231
|
+
The relay server and its management are controlled via the `gssh relay` command group:
|
|
232
|
+
|
|
233
|
+
| Command | Description |
|
|
234
|
+
|---------|-------------|
|
|
235
|
+
| `gssh relay start` | Start the relay server |
|
|
236
|
+
| `gssh relay authorize <pubkey>` | Authorize a machine by its public key |
|
|
237
|
+
| `gssh relay revoke <pubkey>` | Revoke a machine's authorization |
|
|
238
|
+
| `gssh relay machines` | List authorized machines |
|
|
239
|
+
| `gssh relay trusted` | List trusted relays (client-side) |
|
|
240
|
+
| `gssh relay untrust <url>` | Remove relay trust (client-side) |
|
|
241
|
+
|
|
242
|
+
**Note:** There is no separate `gssh machine` command group. Machine authorization is managed through the relay commands above.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Error States
|
|
247
|
+
|
|
248
|
+
| Situation | Response |
|
|
249
|
+
|-----------|----------|
|
|
250
|
+
| Invalid signature | `{ type: "error", code: "INVALID_SIGNATURE" }` |
|
|
251
|
+
| Machine offline | `{ type: "error", code: "OFFLINE" }` |
|
|
252
|
+
| Invite not found | `{ type: "error", code: "NOT_FOUND" }` |
|
|
253
|
+
| Invite expired | `{ type: "error", code: "INVALID" }` |
|
|
254
|
+
| Not authorized | `{ type: "error", code: "FORBIDDEN" }` |
|
|
255
|
+
| Machine re-registration conflict | `{ success: false, error: "..." }` |
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Health Check Endpoint
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
GET /health
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"machineCount": 5,
|
|
269
|
+
"onlineMachineCount": 3,
|
|
270
|
+
"inviteCount": 12,
|
|
271
|
+
"authorizationCount": 8,
|
|
272
|
+
"connectedClients": 7
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Implementation Notes
|
|
279
|
+
|
|
280
|
+
Using Bun APIs:
|
|
281
|
+
- `Bun.serve()` with `websocket` handler for all WS connections
|
|
282
|
+
- `fetch` handler for health check and static file serving
|
|
283
|
+
- WebSocket `data` field holds connection metadata
|
|
284
|
+
- All state in memory Maps (no database for MVP)
|
|
285
|
+
|
|
286
|
+
The server is essentially:
|
|
287
|
+
1. Parse incoming connection (machine or client?)
|
|
288
|
+
2. Route to appropriate handler
|
|
289
|
+
3. Maintain registries of connections
|
|
290
|
+
4. Forward messages between matched pairs
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Current Features
|
|
295
|
+
|
|
296
|
+
### Cloudflare Hosting (Implemented)
|
|
297
|
+
|
|
298
|
+
Users can expose their machine at `yourname.gitspace.sh`:
|
|
299
|
+
- `gssh auth login` - Authenticate with GitHub
|
|
300
|
+
- `gssh host reserve <name>` - Reserve a subdomain
|
|
301
|
+
- `gssh serve` - Connects to gitspace.sh relay + Cloudflare tunnel
|
|
302
|
+
|
|
303
|
+
See [GATEWAY-WORKER.md](./GATEWAY-WORKER.md) for the gateway architecture.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Future Considerations
|
|
308
|
+
|
|
309
|
+
### Scaling
|
|
310
|
+
|
|
311
|
+
Single relay works for MVP. For scale:
|
|
312
|
+
- Consistent hash machines to specific relay pods
|
|
313
|
+
- Redis/Valkey for cross-pod routing info
|
|
314
|
+
- Load balancer with sticky sessions
|
|
315
|
+
|
|
316
|
+
### Port Tunnels (Not Yet Implemented)
|
|
317
|
+
|
|
318
|
+
Future feature to expose `localhost:3000` at a public URL:
|
|
319
|
+
- HTTP request proxying
|
|
320
|
+
- WebSocket tunneling for HMR
|
|
321
|
+
- Subdomain allocation per service
|
|
322
|
+
|
|
323
|
+
See [INFRASTRUCTURE.md](./INFRASTRUCTURE.md) for the full vision.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
*Last updated: 2025-01*
|