From cee0632922db82473d818c01f953fc15b6415bb5 Mon Sep 17 00:00:00 2001 From: Tsiry Sandratraina Date: Sat, 7 Feb 2026 18:39:24 +0300 Subject: [PATCH] Enable SSH and Git operations in Cloudflare sandbox Install openssh-client in the sandbox image and add consola for logging. Read SSH_PRIVATE_KEY/SSH_PUBLIC_KEY env vars to write SSH keys, set known_hosts and git config, then clone the repository in the /run handler. Expose SSH env types in worker typings and update ProcessEnv mapping. --- .sandbox/cloudflare/Dockerfile | 3 ++ .sandbox/cloudflare/bun.lock | 5 +++ .sandbox/cloudflare/package.json | 5 ++- .sandbox/cloudflare/src/index.ts | 33 ++++++++++++++++--- .sandbox/cloudflare/worker-configuration.d.ts | 10 +++++- 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/.sandbox/cloudflare/Dockerfile b/.sandbox/cloudflare/Dockerfile index 3254d395..dbcc12cc 100644 --- a/.sandbox/cloudflare/Dockerfile +++ b/.sandbox/cloudflare/Dockerfile @@ -1,4 +1,7 @@ FROM docker.io/cloudflare/sandbox:0.7.0 +RUN apt-get update && apt-get install -y --no-install-recommends \ + openssh-client + # Required during local development to access exposed ports EXPOSE 8080 diff --git a/.sandbox/cloudflare/bun.lock b/.sandbox/cloudflare/bun.lock index 1590b2ae..dc918732 100644 --- a/.sandbox/cloudflare/bun.lock +++ b/.sandbox/cloudflare/bun.lock @@ -4,6 +4,9 @@ "workspaces": { "": { "name": "@cloudflare/sandbox-minimal-example", + "dependencies": { + "consola": "^3.4.2", + }, "devDependencies": { "@cloudflare/sandbox": "*", "@types/node": "^24.10.11", @@ -157,6 +160,8 @@ "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], diff --git a/.sandbox/cloudflare/package.json b/.sandbox/cloudflare/package.json index 60860d30..7fc1ad04 100644 --- a/.sandbox/cloudflare/package.json +++ b/.sandbox/cloudflare/package.json @@ -18,5 +18,8 @@ "wrangler": "^4.63.0" }, "author": "", - "license": "MIT" + "license": "MIT", + "dependencies": { + "consola": "^3.4.2" + } } \ No newline at end of file diff --git a/.sandbox/cloudflare/src/index.ts b/.sandbox/cloudflare/src/index.ts index 3430787f..08c7b530 100644 --- a/.sandbox/cloudflare/src/index.ts +++ b/.sandbox/cloudflare/src/index.ts @@ -1,4 +1,5 @@ import { getSandbox } from "@cloudflare/sandbox"; +import consola from "consola"; export { Sandbox } from "@cloudflare/sandbox"; @@ -11,12 +12,32 @@ export default { // Execute a shell command if (url.pathname === "/run") { - const result = await sandbox.exec('echo "2 + 3 = $((2 + 3))"'); + const HOME = "/root"; + await sandbox.exec("mkdir -p $HOME/.ssh"); + await sandbox.writeFile(`${HOME}/.ssh/id_rsa`, env.SSH_PRIVATE_KEY); + await sandbox.writeFile(`${HOME}/.ssh/id_rsa.pub`, env.SSH_PUBLIC_KEY); + await sandbox.exec("chmod 600 $HOME/.ssh/id_rsa"); + await sandbox.exec( + "ssh-keyscan -t rsa tangled.org >> $HOME/.ssh/known_hosts", + ); + await sandbox.exec("git config --global user.name 'Cloudflare Sandbox'"); + await sandbox.exec( + "git config --global user.email 'tsiry.sndr@rocksky.app'", + ); + consola.info("SSH keys uploaded to sandbox."); + + consola.info("Sandbox environment configured for Git operations."); + consola.info("Cloning repository..."); + const clone = await sandbox.exec( + "git clone git@tangled.org:rocksky.app/rocksky rocksky -b main", + ); + consola.log(clone.stdout); + const ls = await sandbox.exec("ls -la rocksky"); return Response.json({ - output: result.stdout, - error: result.stderr, - exitCode: result.exitCode, - success: result.success, + output: ls.stdout, + error: ls.stderr, + exitCode: ls.exitCode, + success: ls.success, }); } @@ -29,6 +50,8 @@ export default { }); } + sandbox.destroy(); + return new Response("Try /run or /file"); }, }; diff --git a/.sandbox/cloudflare/worker-configuration.d.ts b/.sandbox/cloudflare/worker-configuration.d.ts index b3ce0037..6a089886 100644 --- a/.sandbox/cloudflare/worker-configuration.d.ts +++ b/.sandbox/cloudflare/worker-configuration.d.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// Generated by Wrangler by running `wrangler types` (hash: 6ac4664e0b184e123d5c5ac50212c5b8) +// Generated by Wrangler by running `wrangler types` (hash: fc7de8dc66857a5a298a86791b7745d0) // Runtime types generated with workerd@1.20260205.0 2025-05-06 nodejs_compat declare namespace Cloudflare { interface GlobalProps { @@ -7,10 +7,18 @@ declare namespace Cloudflare { durableNamespaces: "Sandbox"; } interface Env { + SSH_PRIVATE_KEY: string; + SSH_PUBLIC_KEY: string; Sandbox: DurableObjectNamespace; } } interface Env extends Cloudflare.Env {} +type StringifyValues> = { + [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string; +}; +declare namespace NodeJS { + interface ProcessEnv extends StringifyValues> {} +} // Begin runtime types /*! ***************************************************************************** -- 2.51.2