deskssh 0.0.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 (49) hide show
  1. package/.eslintrc.cjs +21 -0
  2. package/.github/workflows/ci.yml +40 -0
  3. package/.prettierignore +5 -0
  4. package/.prettierrc.json +6 -0
  5. package/CONTRIBUTING.md +59 -0
  6. package/LICENSE +661 -0
  7. package/README.md +66 -0
  8. package/package.json +51 -0
  9. package/packages/cli/README.md +11 -0
  10. package/packages/cli/bin/deskssh.js +12 -0
  11. package/packages/cli/package.json +37 -0
  12. package/packages/core/dist/index.d.ts +2 -0
  13. package/packages/core/dist/index.d.ts.map +1 -0
  14. package/packages/core/dist/index.js +6 -0
  15. package/packages/core/dist/index.js.map +1 -0
  16. package/packages/core/package.json +22 -0
  17. package/packages/core/src/index.test.ts +8 -0
  18. package/packages/core/src/index.ts +6 -0
  19. package/packages/core/tsconfig.json +9 -0
  20. package/packages/server/dist/index.d.ts +3 -0
  21. package/packages/server/dist/index.d.ts.map +1 -0
  22. package/packages/server/dist/index.js +7 -0
  23. package/packages/server/dist/index.js.map +1 -0
  24. package/packages/server/package.json +19 -0
  25. package/packages/server/src/index.ts +8 -0
  26. package/packages/server/tsconfig.json +10 -0
  27. package/packages/web/dist/assets/index-DNUNZ8WK.js +65 -0
  28. package/packages/web/dist/index.html +12 -0
  29. package/packages/web/index.html +12 -0
  30. package/packages/web/node_modules/.bin/browserslist +17 -0
  31. package/packages/web/node_modules/.bin/vite +17 -0
  32. package/packages/web/package.json +27 -0
  33. package/packages/web/src/App.tsx +17 -0
  34. package/packages/web/src/i18n/en.ts +8 -0
  35. package/packages/web/src/i18n/es.ts +7 -0
  36. package/packages/web/src/i18n/index.ts +27 -0
  37. package/packages/web/src/main.tsx +12 -0
  38. package/packages/web/tsconfig.json +14 -0
  39. package/packages/web/vite.config.ts +6 -0
  40. package/pnpm-workspace.yaml +2 -0
  41. package/specs/001-core/plan.md +246 -0
  42. package/specs/001-core/spec.md +206 -0
  43. package/specs/001-core/tasks.md +110 -0
  44. package/specs/constitution.md +110 -0
  45. package/specs/glossary.md +35 -0
  46. package/specs/vision.md +145 -0
  47. package/tsconfig.base.json +23 -0
  48. package/tsconfig.json +4 -0
  49. package/vitest.config.ts +7 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "composite": false,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "jsx": "react-jsx",
7
+ "module": "ESNext",
8
+ "moduleResolution": "Bundler",
9
+ "noEmit": true,
10
+ "allowImportingTsExtensions": true,
11
+ "types": ["vite/client"]
12
+ },
13
+ "include": ["src", "vite.config.ts"]
14
+ }
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ });
@@ -0,0 +1,2 @@
1
+ packages:
2
+ - 'packages/*'
@@ -0,0 +1,246 @@
1
+ # Technical plan — 001 Core
2
+
3
+ **How** to build what [`spec.md`](./spec.md) describes, respecting
4
+ [`constitution.md`](../constitution.md). The stack proposals are the recommended
5
+ starting point, not dogma; open decisions are marked `[NEEDS DECISION]`.
6
+
7
+ ---
8
+
9
+ ## 1. Delivery model (one web app, two distributions)
10
+
11
+ Decision of record: **agnostic core + web-first; a single web app delivered two
12
+ ways — hosted, or self-hosted via npm. No native desktop wrapper.**
13
+
14
+ Reasons:
15
+
16
+ - The user wants "anyone to be able to access it on the network" → favors web.
17
+ - The **browser cannot open raw SSH/TCP connections**, so the SSH session must live
18
+ in a **backend** (gateway). That forces, anyway, a core separable from the UI
19
+ (Art. 5).
20
+ - The offline/LAN need is met by **self-hosting the same web app via npm** (run it
21
+ locally, open it in the browser), not by a native desktop build. Local users
22
+ install from npm; there is **no Tauri/Electron app**.
23
+
24
+ Result: **a single logic base and one web UI**, distributed as **hosted** or
25
+ **self-hosted (npm)**. **Confirmed (2026-06-25; native desktop dropped 2026-06-26).**
26
+
27
+ ```
28
+ ┌────────────────────────────┐ ┌───────────────────────────────┐
29
+ │ Frontend (browser UI) │ WS/ │ Backend (DeskSSH gateway) │
30
+ │ desktop shell + UI │ <────> │ SSH sessions + API │
31
+ │ React + TS │ HTTP │ uses the CORE │
32
+ └────────────────────────────┘ │ ┌────────────────────────┐ │ SSH
33
+ │ │ core (agnostic) │ │ <─────> Remote
34
+ │ │ adapters, parsers, │ │ host
35
+ │ │ apps, sessions │ │ (POSIX)
36
+ │ └────────────────────────┘ │
37
+ └───────────────────────────────┘
38
+ ```
39
+
40
+ ## 2. Layered architecture
41
+
42
+ 1. **`core`** (agnostic, no UI nor presentation network I/O):
43
+ - _Session manager_: abstraction over an SSH connection (run command, open PTY,
44
+ open SFTP).
45
+ - _OS adapters_: detection + family-specific commands (Art. 6).
46
+ - _Parsers_: turn command output into data structures; with fallback to raw
47
+ output (Art. 7).
48
+ - _Apps_: each app defines what data it requests and what commands it runs (file
49
+ manager, processes, services, monitor, editor, logs).
50
+ - _Transparency_: every executed command is logged/exposed (Art. 3).
51
+ 2. **`server`** (web gateway): keeps SSH sessions alive, authenticates the user,
52
+ exposes an API (HTTP for one-off actions, WebSocket for PTY and streams),
53
+ isolates sessions between users.
54
+ 3. **`web`** (frontend): desktop shell (windows, taskbar, launcher) and each app's
55
+ views. Talks to `server`, never to SSH directly.
56
+ 4. **Distribution**: the same `server` + `web` runs **hosted** or **self-hosted via
57
+ npm** (local/LAN). No separate native app.
58
+
59
+ ## 3. Proposed stack (v1)
60
+
61
+ Core stack **confirmed (2026-06-26): TypeScript + Node + `ssh2` + React.** UI
62
+ details (styling framework, icon set) still open — see §8. Alternatives noted.
63
+
64
+ - **Language:** TypeScript across the stack → a single language lowers the
65
+ contribution barrier (Art. 9).
66
+ - **Monorepo:** pnpm workspaces. Packages: `core`, `server`, `web` (and later
67
+ `desktop`).
68
+ - **Backend (`server`):** Node.js + the `ssh2` SSH library (mature, supports exec,
69
+ PTY and SFTP) + WebSocket (`ws`). _Alternative:_ Rust (`russh`) for
70
+ security/performance, at the cost of a higher entry barrier → discarded for v1.
71
+ - **Frontend (`web`):** React + TypeScript. Terminal with **`xterm.js`**.
72
+ Movable/resizable windows with a lightweight library (e.g. `react-rnd` style) or
73
+ custom components.
74
+ - _Styling/components:_ **Tailwind CSS + Radix UI via shadcn/ui** (all **MIT** —
75
+ AGPL-compatible). Tailwind for the custom desktop look; Radix primitives bring
76
+ built-in accessibility (focus, keyboard, ARIA), key for the accessibility-first
77
+ goal; shadcn/ui delivers them as in-repo, editable components.
78
+ - _Icon set:_ **Lucide** (`lucide-react`, **ISC** — AGPL-compatible, no
79
+ attribution), chosen for its clean, uniform stroke style fitting the
80
+ accessibility-first UI. (Considered: Tabler/Phosphor/Heroicons MIT, Material
81
+ Symbols Apache-2.0, Font Awesome Free CC BY; FA Pro proprietary — avoided.)
82
+ - **Distribution:** published to **npm** so local/LAN users self-host the web app;
83
+ a hosted deployment serves internet-open servers. No native desktop build.
84
+
85
+ ## 4. Core design
86
+
87
+ ### Capability contract (adapter IR)
88
+
89
+ The core defines a **capability contract**: a closed catalog of abstract, **typed**
90
+ operations that apps invoke **without knowing which OS they run on**. It is the
91
+ system's "intermediate language" (an _intermediate representation_, IR). Two pieces:
92
+
93
+ 1. **Contract (typed interface).** Each capability declares inputs and, above all, a
94
+ **normalized output**. Examples:
95
+ - `listDir(path) → FileEntry[]` — not text, but an array of
96
+ `{ name, type, size, mode, owner, mtime }`.
97
+ - `listProcesses() → Process[]`
98
+ - `readFile(path) → bytes` · `writeFile(path, bytes) → void`
99
+ - `serviceAction(name, action) → ServiceState`
100
+
101
+ The value is in the **output type**: if an app knows `listDir` returns a
102
+ `FileEntry[]`, it no longer cares how it was obtained nor on which platform.
103
+
104
+ 2. **Adapter manifests (declarative) + escape hatch.** Each platform implements the
105
+ contract. 80% of cases **declaratively** (command template + output normalization
106
+ spec); the hard 20% (busybox, PowerShell) with a **code hook**.
107
+
108
+ **Rules that hold it together:**
109
+
110
+ - Apps **never** parse raw output; they only consume contract types. Parsing and its
111
+ _fallback_ live in the adapter (reinforces Art. 7).
112
+ - **Normalize at the source**: request structured output (`stat -c`, `ps -eo`,
113
+ `ConvertTo-Json`…) instead of parsing human format.
114
+ - **Capability gaps**: if a platform doesn't support an operation (e.g. `chmod` on
115
+ Windows), the adapter declares it _unsupported_ and the UI degrades gracefully
116
+ instead of pretending.
117
+
118
+ #### Non-interactive primitives > driving TUIs
119
+
120
+ A corollary of the contract (and the answer to "how to emulate nano"): DeskSSH
121
+ **does not drive remote interactive tools** (nano, vim, top…) by sending keystrokes
122
+ over a PTY —it would be fragile and impossible to normalize—. It uses
123
+ **non-interactive, structured primitives** and **emulates the experience on the
124
+ client**:
125
+
126
+ - **Editor** = `readFile` + `writeFile` + a custom GUI editor (no remote nano is
127
+ launched).
128
+ - **Monitor** = `listProcesses`/`systemMetrics` by _polling_, not a live `top`.
129
+ - The **only** deliberate exception is the **terminal** app, which does expose the
130
+ raw shell (there the user sees `bash`/`PowerShell`/`csh`).
131
+
132
+ ### OS adapters
133
+
134
+ - The contract above exposes a **uniform interface** (`listDir`, `stat`,
135
+ `listProcesses`, `listServices`, `serviceAction`, `systemMetrics`, …).
136
+ - Each **OS family** is an adapter implementing that contract, declaratively when
137
+ possible. **v1 covers only Debian/Ubuntu/Mint** (see host roadmap below).
138
+ - Detection on connect (`/etc/os-release`, `uname`), with a generic POSIX fallback
139
+ adapter for not-yet-supported Unix-likes.
140
+ - Prefer machine-readable output (`stat -c '%n|%s|%a|...'`, `ps -eo ...`, `--json`
141
+ flags where available) over parsing human format.
142
+
143
+ #### Supported-host roadmap
144
+
145
+ The tier number indicates **roadmap priority, NOT difficulty** (see the _Effort_
146
+ column). Windows is prioritized for popularity despite being the most costly.
147
+
148
+ | Tier | Hosts | Effort | Notes |
149
+ | ---------- | ------------------------------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
150
+ | **1** (v1) | Debian / Ubuntu / Mint | base | POSIX + GNU coreutils + systemd |
151
+ | **2** | Windows | **high** | Non-POSIX: its own PowerShell adapter family. Still agentless (PowerShell ships with the OS). Prioritized for popularity, not ease. |
152
+ | **3** | Rest of mainstream Linux (RHEL/Fedora/Rocky, Arch, openSUSE) | low | Same paradigm as v1 (systemd + GNU); differ mainly in the package manager |
153
+ | **4** | macOS, FreeBSD | medium | BSD userland; init `launchd` (macOS) / `rc.d` (FreeBSD), not systemd |
154
+ | **5** | Alpine | medium | `busybox` (trimmed flags), OpenRC, musl |
155
+
156
+ > Constitution note: when Tier 2 arrives, the **"POSIX utilities" wording of Art. 2
157
+ > will need generalizing** (still agentless, but no longer POSIX).
158
+
159
+ ### File opening: handlers and routes (FR-025)
160
+
161
+ - **File-type handler registry:** each DeskSSH app declares which types it can open.
162
+ It is the basis of "Open" (default handler) and "Open with" (choose handler), and
163
+ the seed of extensibility for future apps.
164
+ - **Two opening routes:**
165
+ - **(A) Render in DeskSSH:** the contract's `readFile` → painted by a GUI handler.
166
+ With a **size limit**/warning (Art. 8); no handler for the type → route B is
167
+ offered.
168
+ - **(B) _Handoff_ to the client:** download over **streaming SFTP** (don't load
169
+ into memory) to the user's local machine.
170
+ - **Browser limitation (`[NEEDS DECISION]`):** DeskSSH always runs in a browser
171
+ (hosted or self-hosted), so "Open on the client" can only **download** (and inline
172
+ depending on type); it cannot force the OS's default app. Resolve as plain
173
+ download, inline-by-type, or both.
174
+
175
+ ### Parsers and resilience
176
+
177
+ - Each parser receives output + exit code; on unexpected format it returns a
178
+ "degraded" result with the raw output (Art. 7), never throwing and breaking.
179
+
180
+ ### Transparency
181
+
182
+ - Every execution goes through a single point that logs `{command, host, timestamp,
183
+ exitCode}` and makes it queryable from the UI (FR-013, Art. 3).
184
+
185
+ ### Performance (Art. 8)
186
+
187
+ - VFS listing cache with per-action invalidation.
188
+ - Batching related commands into a single invocation when possible.
189
+ - Optimistic UI on file operations, with reconciliation.
190
+
191
+ ## 5. Security (Art. 4)
192
+
193
+ - The backend is the critical surface: gateway user authentication, strict per-user
194
+ session isolation, rate limiting.
195
+ - Secrets: never in plain text nor in logs. **v1 does not persist** (ask each
196
+ session, in-memory only); an encrypted store (OS keychain / local encryption with
197
+ a derived key) is post-v1 (FR-005).
198
+ - Destructive actions: mandatory confirmation at the app layer (FR-090).
199
+ - SSH host key verification (avoid MITM); `known_hosts` policy.
200
+ - Auditing: the transparency log also serves as an audit trail.
201
+
202
+ ## 6. Phases / milestones
203
+
204
+ **v1 = focused cut, accessibility first.** v1 app set: connection/hosts, desktop
205
+ shell, file manager, text editor, terminal and **system monitor**. Processes,
206
+ services, log viewer and packages are **post-v1**.
207
+
208
+ - **M0 — Scaffolding:** monorepo, empty packages, basic CI, license, contributing.
209
+ - **M1 — Connection core:** SSH session (exec/PTY/SFTP) + OS detection +
210
+ Debian/Ubuntu adapter + capability-contract base. Demonstrable via tests/minimal
211
+ CLI.
212
+ - **M2 — Shell + Terminal + File manager:** first usable desktop.
213
+ - **M3 — Text editor + System monitor + transparency in the UI:** **completes v1.**
214
+ - **── 🚀 v1 release ──**
215
+ - **Post-v1 (admin apps):** Processes + Services + Log viewer + Packages.
216
+ - **Post-v1 (hosts):** go down the tier roadmap (Windows → rest of Linux → …).
217
+ - **Post-v1 (hosted):** a hosted web deployment for internet-open servers
218
+ (self-hosted npm is available from v1).
219
+
220
+ ## 7. Risks and mitigations
221
+
222
+ | Risk | Impact | Mitigation |
223
+ | ------------------------------------------ | --------------------- | --------------------------------------------------- |
224
+ | Command output varies by OS/locale/version | Fragile parsing | Adapters + machine output + raw fallback (Art. 6/7) |
225
+ | Latency from round trips | Slow UX | Cache, batching, optimistic UI (Art. 8) |
226
+ | Backend = exposed SSH gateway | High security risk | Auth, isolation, host keys, auditing (§5) |
227
+ | App over-scope | v1 never ships | Lock a minimal app subset per milestone |
228
+ | Coupling logic to the UI | Breaks future desktop | Strict agnostic core (Art. 5) |
229
+
230
+ ## 8. Decisions
231
+
232
+ **Closed (2026-06-25):**
233
+
234
+ - **Web-first** + agnostic core as the v1 architecture.
235
+ - **License AGPL-3.0-or-later** (see `constitution.md` and `LICENSE`).
236
+ - **User #1 = accessibility**; **focused v1** (app set in §6).
237
+
238
+ **Closed (2026-06-26):**
239
+
240
+ - **Core stack: TypeScript + Node + `ssh2` + React.**
241
+ - **UI: Tailwind CSS + Radix UI (via shadcn/ui)** — all MIT.
242
+ - **Icon set: Lucide** (ISC — AGPL-compatible, no attribution required).
243
+ - **No native desktop app:** local/LAN use = self-hosted **npm** web app.
244
+ - **v1 credentials: not persisted** — asked per session (FR-005).
245
+
246
+ **Open:** product-level decisions tracked in spec §9 (`ssh-agent`, i18n scope).
@@ -0,0 +1,206 @@
1
+ # Functional specification — 001 Core (base experience)
2
+
3
+ Defines **what** DeskSSH does and **why**, without going into how it is implemented
4
+ (that's in `plan.md`). Open decisions are marked `[NEEDS DECISION]` and listed at
5
+ the end.
6
+
7
+ Related: [`../constitution.md`](../constitution.md) · [`../glossary.md`](../glossary.md)
8
+
9
+ ---
10
+
11
+ ## 1. Problem
12
+
13
+ Administering a Linux server with no graphical environment forces you to know the
14
+ command line. Existing graphical alternatives:
15
+
16
+ - **Remote desktop (VNC/RDP/X)**: requires an installed graphical environment and
17
+ consumes bandwidth transmitting pixels; unviable on headless servers.
18
+ - **Web panels (Cockpit, Webmin)**: require installing and maintaining an agent on
19
+ the server, and offer a "web form" UX, not a desktop one.
20
+
21
+ There is no tool that gives a **real desktop experience** over **any server with
22
+ just SSH**, installing nothing and transmitting no pixels.
23
+
24
+ ## 2. Value proposition
25
+
26
+ DeskSSH presents a **familiar desktop** (windows, file manager, terminal, monitor,
27
+ editor, services) whose backend is **plain SSH**: every interaction is translated
28
+ into commands executed on the host. No agents, no streaming, and with
29
+ **transparency**: you can always see the command behind the click.
30
+
31
+ ## 3. Goals (v1)
32
+
33
+ - G1. Connect to remote hosts over SSH (key or password) and manage them.
34
+ - G2. Offer a desktop shell with windows and basic multitasking.
35
+ - G3. Include an initial set of useful apps (see §6).
36
+ - G4. Work **agentless** on common Linux servers.
37
+ - G5. Make the equivalent command of each action visible (transparency).
38
+
39
+ ## 4. Non-goals (v1)
40
+
41
+ - Desktop streaming or remote graphical applications (forbidden by the
42
+ constitution).
43
+ - Windows, macOS, \*BSD and other Linux distros as remote host: **out of v1**,
44
+ planned in the host roadmap (Tier 2+, see `plan.md §4`).
45
+ - Multi-user / real-time collaboration over the same session.
46
+ - Fleet orchestration (managing many servers at once).
47
+ - Third-party app/plugin store (the architecture will allow it, but not in v1).
48
+
49
+ ## 5. Personas and use cases
50
+
51
+ > **v1 primary persona: "User with low CLI fluency".** v1 prioritizes
52
+ > **accessibility**: safe, guided defaults, plain language, the terminal as a last
53
+ > resort and transparency (Art. 3) presented in an _educational, on-demand_ way,
54
+ > not as the protagonist. Other personas are served, but they do not steer the
55
+ > design.
56
+
57
+ - **⭐ User with a VPS but low CLI fluency (PRIMARY)** — manages their server with a
58
+ GUI without having to master the terminal. _"I want to manage my server without
59
+ the console intimidating me."_
60
+ - **Sysadmin / DevOps** — manages VPSs and servers; wants speed and to see what
61
+ runs. _"I review services and logs without memorizing flags."_
62
+ - **Developer** — deploys to a VPS; wants to manage files and processes
63
+ comfortably. _"I upload a build and restart the service without opening 3
64
+ terminals."_
65
+ - **Person learning Linux** — command transparency teaches them. _"I see what
66
+ command each thing I click does."_
67
+
68
+ ### Main journey
69
+
70
+ 1. The user adds a host (address, user, authentication method).
71
+ 2. They connect; DeskSSH detects the OS and opens the **desktop**.
72
+ 3. They open the **file manager**, browse, copy a file (seeing the confirmation
73
+ and, optionally, the command).
74
+ 4. They open the **system monitor** and check CPU/memory/disk at a glance.
75
+ (Service/process management arrives _post-v1_.)
76
+ 5. They open a real **terminal** for something specific. They close the session.
77
+
78
+ ## 6. Functional requirements
79
+
80
+ > **v1 app scope (focused cut):** Connection/hosts, Desktop shell, File manager,
81
+ > Text editor, Terminal and **System monitor**. The apps marked _(post-v1)_ below
82
+ > —Processes, Services, Log viewer, Packages— are specified here but implemented
83
+ > after v1 (see `plan.md §6`).
84
+
85
+ ### Connection and hosts
86
+
87
+ - **FR-001** Add, edit and remove hosts (name, address, port, user).
88
+ - **FR-002** Authentication via: (a) **SSH private key** —the user provides the key
89
+ file (**PEM / OpenSSH / PKCS#8** formats), with **optional passphrase**—, and
90
+ (b) **password**. (Key protection/storage is governed by FR-005 and constitution
91
+ Art. 4.) `ssh-agent` support is **post-v1**.
92
+ - **FR-003** Connect/disconnect; show session state (connecting, alive, dropped,
93
+ error).
94
+ - **FR-004** Detect the host's OS family to choose the adapter (Art. 6).
95
+ - **FR-005** Never persist secrets in plain text (Art. 4). **v1: do not persist
96
+ credentials** — entered per session and kept only in memory while connected; an
97
+ encrypted store (OS keychain / local encryption) may come post-v1.
98
+
99
+ ### Desktop shell
100
+
101
+ - **FR-010** Show a desktop with movable/resizable windows and a taskbar.
102
+ - **FR-011** App launcher ("start menu") to open the available apps.
103
+ - **FR-012** Support multiple windows/apps open simultaneously over one session.
104
+ - **FR-013** Visible indicator to inspect the command of the last action
105
+ (transparency, Art. 3).
106
+
107
+ ### App: File manager
108
+
109
+ - **FR-020** Browse the remote directory tree with icons and details.
110
+ - **FR-021** Create folder, rename, copy, move and delete (delete/overwrite require
111
+ confirmation, Art. 4).
112
+ - **FR-022** View properties (size, permissions, owner, dates).
113
+ - **FR-023** Upload and download files between the user's machine and the host.
114
+ - **FR-024** Drag & drop in the file manager — **post-v1** (not in the focused v1
115
+ cut).
116
+ - **FR-025** When opening a file, the user chooses between two **execution
117
+ locations**:
118
+ - **(A) In DeskSSH** — _Open_ (default DeskSSH app for that type) or _Open with_
119
+ (choose among DeskSSH apps/viewers). The file is read via `readFile` and
120
+ rendered in the GUI; **nothing runs on the remote** (Art. 10). Requires a
121
+ DeskSSH _handler_ for that type; if none, option (B) is offered.
122
+ - **(B) On the client** — _Open on the client_ (downloaded over SFTP, streaming,
123
+ to the **user's local machine** and opened with their OS's default program) or
124
+ _Download_ (just save locally).
125
+
126
+ Vocabulary note: **"the client" = the user's local machine** (their browser/OS in
127
+ the web model), not the DeskSSH server.
128
+ Since DeskSSH always runs in a browser (hosted or self-hosted npm), it cannot
129
+ force opening with the OS's default program. `[NEEDS DECISION]` does "Open on the
130
+ client" resolve as a **plain download**, as **inline opening** depending on type,
131
+ or both?
132
+
133
+ ### App: Terminal
134
+
135
+ - **FR-030** Real interactive terminal (PTY over SSH) with resizing.
136
+ - **FR-031** Reuse the already-connected host's SSH session.
137
+
138
+ ### App: Processes / Task manager _(post-v1)_
139
+
140
+ - **FR-040** List processes (CPU/mem usage, PID, user, command).
141
+ - **FR-041** Terminate a process (mandatory confirmation).
142
+
143
+ ### App: System monitor
144
+
145
+ - **FR-050** Show CPU, memory, disk and uptime, with periodic refresh.
146
+
147
+ ### App: Service manager _(post-v1)_
148
+
149
+ - **FR-060** List services (systemd first) and their state.
150
+ - **FR-061** Start, stop and restart services (mandatory confirmation).
151
+ - **FR-062** View a service's recent state/log.
152
+
153
+ ### App: Text editor
154
+
155
+ - **FR-070** Open, edit and save remote text files.
156
+ - **FR-071** Warn about unsaved edits on close.
157
+
158
+ ### App: Log viewer _(post-v1)_
159
+
160
+ - **FR-080** View and follow (`tail -f`/`journalctl -f`) logs in streaming.
161
+
162
+ ### Cross-cutting
163
+
164
+ - **FR-090** Every destructive action asks for explicit confirmation (Art. 4).
165
+ - **FR-091** If parsing output fails, show the raw output without breaking the app
166
+ (Art. 7).
167
+ - **FR-092** Show latency/state of in-flight network operations (Art. 8).
168
+
169
+ ## 7. Non-functional requirements
170
+
171
+ - **NFR-Security** — Fully comply with Article 4 of the constitution.
172
+ - **NFR-Portability** — v1 covers **Debian/Ubuntu/Mint** as remote host; support for
173
+ more OSes is added via adapters (Art. 6) following the **host roadmap**
174
+ (`plan.md §4`).
175
+ - **NFR-Performance** — Common operations (list a folder, refresh the monitor)
176
+ perceptibly fluid at typical network latencies; minimize round trips.
177
+ - **NFR-Resilience** — No parsing/network failure crashes the app (Art. 7).
178
+ - **NFR-Accessibility / i18n** — Keyboard-navigable UI; **i18n-ready from day 1**
179
+ (all strings externalized) and **v1 ships EN + ES**.
180
+ - **NFR-Openness** — Stack and dependencies 100% open source (Art. 9).
181
+
182
+ ## 8. Acceptance criteria (v1, high level)
183
+
184
+ - A user can add a real Linux host, connect and open the desktop.
185
+ - They can browse files, open a working terminal, view processes/services and edit a
186
+ file, all agentless.
187
+ - Every destructive action asks for confirmation; no secret is stored in plain text.
188
+ - A parsing failure on an "odd" host shows raw output without crashing.
189
+
190
+ ## 9. Open decisions `[NEEDS DECISION]`
191
+
192
+ 1. ~~Open source license~~ → **Resolved (2026-06-25): AGPL-3.0-or-later** (see
193
+ `constitution.md` and `LICENSE`).
194
+ 2. ~~Confirm web-first~~ → **Resolved: web-first** (2026-06-25), one web app
195
+ delivered hosted or self-hosted via npm; **no native desktop** (2026-06-26). See
196
+ `plan.md §1`.
197
+ 3. ~~`ssh-agent`~~ → **Resolved (2026-06-26): post-v1** (v1 = key PEM/passphrase +
198
+ password) (FR-002).
199
+ 4. ~~Credential store~~ → **Resolved (2026-06-26): v1 does not persist** (ask each
200
+ session); encrypted store post-v1 (FR-005).
201
+ 5. ~~Drag & drop~~ → **Resolved (2026-06-26): post-v1** (FR-024).
202
+ 6. ~~i18n scope~~ → **Resolved (2026-06-26): i18n-ready from day 1, v1 ships EN +
203
+ ES** (NFR-Accessibility/i18n).
204
+ 7. ~~v1 app set~~ → **Resolved (2026-06-25):** focused cut = Connection/hosts,
205
+ Shell, File manager, Editor, Terminal and System monitor; the rest _(post-v1)_
206
+ (see §6 and `plan.md §6`).
@@ -0,0 +1,110 @@
1
+ # Tasks — 001 Core
2
+
3
+ > Breakdown of the work, derived from [`spec.md`](./spec.md) and
4
+ > [`plan.md`](./plan.md). **No code is written for a task that doesn't exist here**
5
+ > (see `CLAUDE.md`). Each task references the `FR-`/Article it serves.
6
+ > Detailed since: 2026-06-26. M0–M1 are detailed; M2–M3 are outlined and broken
7
+ > down when tackled.
8
+
9
+ ## Preconditions (definition phase) — DONE
10
+
11
+ - [x] **web-first + agnostic core** confirmed (plan §1).
12
+ - [x] **License** chosen — AGPL-3.0-or-later (spec §9.1).
13
+ - [x] **Stack** confirmed — TS + Node + ssh2 + React, Tailwind + Radix (shadcn/ui),
14
+ Lucide, xterm.js (plan §3).
15
+ - [x] **v1 app set** agreed — connection, shell, files, editor, terminal, monitor
16
+ (spec §9.7).
17
+ - [x] Credentials (no persistence), ssh-agent (post-v1), drag&drop (post-v1), i18n
18
+ (EN+ES, ready day 1) resolved.
19
+
20
+ > Only minor open item: browser behavior of "Open on the client" (FR-025) — decided
21
+ > during M2 coding.
22
+
23
+ ---
24
+
25
+ ## M0 — Scaffolding
26
+
27
+ Goal: an empty but coherent monorepo that builds, lints, tests and publishes.
28
+
29
+ - [x] **M0.1** Initialize pnpm-workspace monorepo with packages `core`, `server`,
30
+ `web`. → plan §2
31
+ - [x] **M0.2** Root TypeScript config (strict) shared across packages.
32
+ - [x] **M0.3** Linting/formatting (ESLint + Prettier) and a `test` runner
33
+ (Vitest) wired at the root.
34
+ - [x] **M0.4** Basic CI (GitHub Actions): install, lint, typecheck, test, build on
35
+ push/PR. → plan §6
36
+ - [x] **M0.5** Add `CONTRIBUTING.md` (SDD flow, English-only repo, how to propose
37
+ via `specs/`). `LICENSE` already present. → Art. 9
38
+ - [x] **M0.6** Minimal i18n scaffolding decision/setup so strings are externalized
39
+ from day 1 (EN + ES catalogs stub). → NFR-i18n
40
+ - [ ] **M0.7** Publish `deskssh` `0.0.1` placeholder to npm (claims the name;
41
+ `AGPL-3.0-or-later`, public access). **Needs maintainer npm login.** → vision
42
+ (distribution)
43
+
44
+ **M0 done when:** `pnpm install && pnpm -r build && pnpm -r test` pass in CI and the
45
+ `deskssh` name is reserved on npm.
46
+
47
+ ---
48
+
49
+ ## M1 — Connection core
50
+
51
+ Goal: the agnostic `core` can open an SSH session to a Debian/Ubuntu host, run typed
52
+ capabilities through an adapter, and log every command. Demonstrable via tests +
53
+ a minimal CLI (no web UI yet).
54
+
55
+ ### Session layer
56
+
57
+ - [ ] **M1.1** `SshSession` over `ssh2`: connect with **private key (PEM/OpenSSH/
58
+ PKCS#8, optional passphrase)** and **password**; never persist secrets. → FR-002,
59
+ FR-005, Art. 4
60
+ - [ ] **M1.2** Session lifecycle + state (connecting / alive / dropped / error) with
61
+ events. → FR-003
62
+ - [ ] **M1.3** Channels: `exec` (run command), **PTY** (interactive shell), **SFTP**
63
+ (file ops); SFTP-missing → fall back to `exec`. → FR-030/031, Art. 7
64
+ - [ ] **M1.4** SSH host key verification + `known_hosts` policy. → plan §5, Art. 4
65
+
66
+ ### Capability contract (IR)
67
+
68
+ - [ ] **M1.5** Define the typed contract + core types (`FileEntry`, `Process`,
69
+ `ServiceState`, `SystemMetrics`, capability-unsupported result). → plan §4
70
+ - [ ] **M1.6** Single **execution point**: every command flows through it and is
71
+ logged `{command, host, timestamp, exitCode}` (transparency + audit). → FR-013,
72
+ Art. 3
73
+ - [ ] **M1.7** Parser harness: parse → typed result, with **raw-output fallback**
74
+ that never throws. → FR-091, Art. 7
75
+
76
+ ### Adapter layer
77
+
78
+ - [ ] **M1.8** OS detection on connect (`/etc/os-release`, `uname`) + adapter
79
+ selection, with generic POSIX fallback. → FR-004, Art. 6
80
+ - [ ] **M1.9** **Debian/Ubuntu adapter** (Tier 1) implementing the v1-needed
81
+ capabilities (`listDir`, `stat`, `readFile`, `writeFile`, `systemMetrics`),
82
+ preferring machine-readable output. → plan §4, Art. 6/10
83
+ - [ ] **M1.10** Minimal CLI/harness to exercise the core against a real host
84
+ (manual and automated tests). → plan §6 ("demonstrable")
85
+
86
+ **M1 done when:** connecting to a real Debian/Ubuntu host, listing a directory and
87
+ reading system metrics return typed results, every command is in the transparency
88
+ log, and a forced parse failure shows raw output without crashing.
89
+
90
+ ---
91
+
92
+ ## M2 — Shell + Terminal + File manager _(outline)_
93
+
94
+ Desktop shell (windows, taskbar, launcher; FR-010/011/012), Terminal app
95
+ (`xterm.js`, FR-030/031), File manager (browse/CRUD/properties/upload-download,
96
+ FR-020..023), file-open routes + handler registry (FR-025), destructive-action
97
+ confirmations (FR-090). Broken down at the start of M2.
98
+
99
+ ## M3 — Editor + System monitor + transparency UI _(outline)_ → completes v1
100
+
101
+ Text editor via `readFile`/`writeFile` with unsaved-changes guard (FR-070/071),
102
+ System monitor with periodic refresh (FR-050), transparency surfaced in the UI
103
+ (FR-013), in-flight latency/state indicators (FR-092). Broken down at the start of
104
+ M3. → **🚀 v1 release** after M3.
105
+
106
+ ## Post-v1 _(pointer)_
107
+
108
+ Admin apps (Processes/Services/Logs/Packages), host tiers (Windows → rest of Linux →
109
+ macOS/FreeBSD → Alpine), encrypted credential store, ssh-agent, drag & drop, hosted
110
+ deployment. See `plan.md §6` and `vision.md`.