A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz
99
fork

Configure Feed

Select the types of activity you want to include in your feed.

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.

+49 -7
+3
.sandbox/cloudflare/Dockerfile
··· 1 1 FROM docker.io/cloudflare/sandbox:0.7.0 2 2 3 + RUN apt-get update && apt-get install -y --no-install-recommends \ 4 + openssh-client 5 + 3 6 # Required during local development to access exposed ports 4 7 EXPOSE 8080
+5
.sandbox/cloudflare/bun.lock
··· 4 4 "workspaces": { 5 5 "": { 6 6 "name": "@cloudflare/sandbox-minimal-example", 7 + "dependencies": { 8 + "consola": "^3.4.2", 9 + }, 7 10 "devDependencies": { 8 11 "@cloudflare/sandbox": "*", 9 12 "@types/node": "^24.10.11", ··· 156 159 "@types/node": ["@types/node@24.10.11", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-/Af7O8r1frCVgOz0I62jWUtMohJ0/ZQU/ZoketltOJPZpnb17yoNc9BSoVuV9qlaIXJiPNOpsfq4ByFajSArNQ=="], 157 160 158 161 "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], 162 + 163 + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], 159 164 160 165 "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], 161 166
+4 -1
.sandbox/cloudflare/package.json
··· 18 18 "wrangler": "^4.63.0" 19 19 }, 20 20 "author": "", 21 - "license": "MIT" 21 + "license": "MIT", 22 + "dependencies": { 23 + "consola": "^3.4.2" 24 + } 22 25 }
+28 -5
.sandbox/cloudflare/src/index.ts
··· 1 1 import { getSandbox } from "@cloudflare/sandbox"; 2 + import consola from "consola"; 2 3 3 4 export { Sandbox } from "@cloudflare/sandbox"; 4 5 ··· 11 12 12 13 // Execute a shell command 13 14 if (url.pathname === "/run") { 14 - const result = await sandbox.exec('echo "2 + 3 = $((2 + 3))"'); 15 + const HOME = "/root"; 16 + await sandbox.exec("mkdir -p $HOME/.ssh"); 17 + await sandbox.writeFile(`${HOME}/.ssh/id_rsa`, env.SSH_PRIVATE_KEY); 18 + await sandbox.writeFile(`${HOME}/.ssh/id_rsa.pub`, env.SSH_PUBLIC_KEY); 19 + await sandbox.exec("chmod 600 $HOME/.ssh/id_rsa"); 20 + await sandbox.exec( 21 + "ssh-keyscan -t rsa tangled.org >> $HOME/.ssh/known_hosts", 22 + ); 23 + await sandbox.exec("git config --global user.name 'Cloudflare Sandbox'"); 24 + await sandbox.exec( 25 + "git config --global user.email 'tsiry.sndr@rocksky.app'", 26 + ); 27 + consola.info("SSH keys uploaded to sandbox."); 28 + 29 + consola.info("Sandbox environment configured for Git operations."); 30 + consola.info("Cloning repository..."); 31 + const clone = await sandbox.exec( 32 + "git clone git@tangled.org:rocksky.app/rocksky rocksky -b main", 33 + ); 34 + consola.log(clone.stdout); 35 + const ls = await sandbox.exec("ls -la rocksky"); 15 36 return Response.json({ 16 - output: result.stdout, 17 - error: result.stderr, 18 - exitCode: result.exitCode, 19 - success: result.success, 37 + output: ls.stdout, 38 + error: ls.stderr, 39 + exitCode: ls.exitCode, 40 + success: ls.success, 20 41 }); 21 42 } 22 43 ··· 28 49 content: file.content, 29 50 }); 30 51 } 52 + 53 + sandbox.destroy(); 31 54 32 55 return new Response("Try /run or /file"); 33 56 },
+9 -1
.sandbox/cloudflare/worker-configuration.d.ts
··· 1 1 /* eslint-disable */ 2 - // Generated by Wrangler by running `wrangler types` (hash: 6ac4664e0b184e123d5c5ac50212c5b8) 2 + // Generated by Wrangler by running `wrangler types` (hash: fc7de8dc66857a5a298a86791b7745d0) 3 3 // Runtime types generated with workerd@1.20260205.0 2025-05-06 nodejs_compat 4 4 declare namespace Cloudflare { 5 5 interface GlobalProps { ··· 7 7 durableNamespaces: "Sandbox"; 8 8 } 9 9 interface Env { 10 + SSH_PRIVATE_KEY: string; 11 + SSH_PUBLIC_KEY: string; 10 12 Sandbox: DurableObjectNamespace<import("./src/index").Sandbox>; 11 13 } 12 14 } 13 15 interface Env extends Cloudflare.Env {} 16 + type StringifyValues<EnvType extends Record<string, unknown>> = { 17 + [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string; 18 + }; 19 + declare namespace NodeJS { 20 + interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "SSH_PRIVATE_KEY" | "SSH_PUBLIC_KEY">> {} 21 + } 14 22 15 23 // Begin runtime types 16 24 /*! *****************************************************************************