CLI
Cliente de linha de comando rápido para execução de código e sessões interativas. Mais de 42 linguagens, mais de 30 shells/REPLs.
Documentação Oficial OpenAPI Swagger ↗Início Rápido — Clojure
# Download + setup
curl -O https://git.unturf.com/engineering/unturf/un-inception/-/raw/main/clients/clojure/sync/src/un.clj && chmod +x un.clj && ln -sf un.clj un
export UNSANDBOX_PUBLIC_KEY="unsb-pk-xxxx-xxxx-xxxx-xxxx"
export UNSANDBOX_SECRET_KEY="unsb-sk-xxxxx-xxxxx-xxxxx-xxxxx"
# Run code
./un script.clj
Baixar
Guia de Instalação →Características:
- 42+ languages - Python, JS, Go, Rust, C++, Java...
- Sessions - 30+ shells/REPLs, tmux persistence
- Files - Upload files, collect artifacts
- Services - Persistent containers with domains
- Snapshots - Point-in-time backups
- Images - Publish, share, transfer
Início Rápido de Integração ⚡
Adicione superpoderes unsandbox ao seu app Clojure existente:
curl -O https://git.unturf.com/engineering/unturf/un-inception/-/raw/main/clients/clojure/sync/src/un.clj
# Option A: Environment variables
export UNSANDBOX_PUBLIC_KEY="unsb-pk-xxxx-xxxx-xxxx-xxxx"
export UNSANDBOX_SECRET_KEY="unsb-sk-xxxxx-xxxxx-xxxxx-xxxxx"
# Option B: Config file (persistent)
mkdir -p ~/.unsandbox
echo "unsb-pk-xxxx-xxxx-xxxx-xxxx,unsb-sk-xxxxx-xxxxx-xxxxx-xxxxx" > ~/.unsandbox/accounts.csv
;; In your Clojure app:
(require '[un :refer [execute-code]])
(let [result (execute-code "clojure" "(println \"Hello from Clojure running on unsandbox!\")")]
(println (:stdout result))) ; Hello from Clojure running on unsandbox!
clj -M myapp.clj
7485bc48cc589a65b0f642541398bd20
SHA256: a571495ea3d88162af935bb4798d05ea8f360e3f0c304a35aa50bbcb93558867
;; PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
;;
;; This is free public domain software for the public good of a permacomputer hosted
;; at permacomputer.com - an always-on computer by the people, for the people. One
;; which is durable, easy to repair, and distributed like tap water for machine
;; learning intelligence.
;;
;; The permacomputer is community-owned infrastructure optimized around four values:
;;
;; TRUTH - First principles, math & science, open source code freely distributed
;; FREEDOM - Voluntary partnerships, freedom from tyranny & corporate control
;; HARMONY - Minimal waste, self-renewing systems with diverse thriving connections
;; LOVE - Be yourself without hurting others, cooperation through natural law
;;
;; This software contributes to that vision by enabling code execution across 42+
;; programming languages through a unified interface, accessible to all. Code is
;; seeds to sprout on any abandoned technology.
;;
;; Learn more: https://www.permacomputer.com
;;
;; Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
;; software, either in source code form or as a compiled binary, for any purpose,
;; commercial or non-commercial, and by any means.
;;
;; NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
;;
;; That said, our permacomputer's digital membrane stratum continuously runs unit,
;; integration, and functional tests on all of it's own software - with our
;; permacomputer monitoring itself, repairing itself, with minimal human in the
;; loop guidance. Our agents do their best.
;;
;; Copyright 2025 TimeHexOn & foxhop & russell@unturf
;; https://www.timehexon.com
;; https://www.foxhop.net
;; https://www.unturf.com/software
#!/usr/bin/env bb
;; Clojure UN CLI - Unsandbox CLI Client
;;
;; Full-featured CLI matching un.py capabilities:
;; - Execute code with env vars, input files, artifacts
;; - Interactive sessions with shell/REPL support
;; - Persistent services with domains and ports
;;
;; Usage:
;; chmod +x un.clj
;; export UNSANDBOX_API_KEY="your_key_here"
;; ./un.clj [options] <source_file>
;; ./un.clj session [options]
;; ./un.clj service [options]
;;
;; Uses curl for HTTP (no external dependencies)
(require '[clojure.java.io :as io]
'[clojure.string :as str]
'[clojure.java.shell :refer [sh]])
(def blue "\u001b[34m")
(def red "\u001b[31m")
(def green "\u001b[32m")
(def yellow "\u001b[33m")
(def reset "\u001b[0m")
(def portal-base "https://unsandbox.com")
(def languages-cache-ttl 3600) ;; 1 hour in seconds
(def ext-map
{".hs" "haskell" ".ml" "ocaml" ".clj" "clojure" ".scm" "scheme"
".lisp" "commonlisp" ".erl" "erlang" ".ex" "elixir" ".exs" "elixir"
".py" "python" ".js" "javascript" ".ts" "typescript" ".rb" "ruby"
".go" "go" ".rs" "rust" ".c" "c" ".cpp" "cpp" ".cc" "cpp"
".cxx" "cpp" ".java" "java" ".kt" "kotlin" ".cs" "csharp"
".fs" "fsharp" ".jl" "julia" ".r" "r" ".cr" "crystal"
".d" "d" ".nim" "nim" ".zig" "zig" ".v" "v" ".dart" "dart"
".groovy" "groovy" ".scala" "scala" ".sh" "bash" ".pl" "perl"
".lua" "lua" ".php" "php"})
(defn get-extension [filename]
(let [dot-pos (str/last-index-of filename ".")]
(if dot-pos (subs filename dot-pos) "")))
(defn escape-json [s]
(-> s
(str/replace "\\" "\\\\")
(str/replace "\"" "\\\"")
(str/replace "\n" "\\n")
(str/replace "\r" "\\r")
(str/replace "\t" "\\t")))
(defn unescape-json [s]
(-> s
(str/replace "\\n" "\n")
(str/replace "\\t" "\t")
(str/replace "\\\"" "\"")
(str/replace "\\\\" "\\")))
(defn read-and-base64 [filepath]
(let [content (slurp filepath)
bytes (.getBytes content "UTF-8")]
(.encodeToString (java.util.Base64/getEncoder) bytes)))
(defn build-input-files-json [files]
(if (empty? files)
""
(let [file-jsons (map (fn [f]
(let [basename (-> (io/file f) .getName)
b64 (read-and-base64 f)]
(str "{\"filename\":\"" (escape-json basename) "\",\"content\":\"" b64 "\"}")))
files)]
(str ",\"input_files\":[" (str/join "," file-jsons) "]"))))
(defn extract-field [field json-str]
(let [pattern-str (re-pattern (str "\"" field "\":\"([^\"]*)\""))
pattern-num (re-pattern (str "\"" field "\":(\\d+)"))]
(or (second (re-find pattern-str json-str))
(second (re-find pattern-num json-str)))))
(def ^:dynamic *account-index* nil)
(defn load-accounts-csv [path index]
(when (.exists (io/file path))
(try
(let [lines (str/split-lines (slurp path))
rows (->> lines
(filter #(and (> (count %) 0)
(not (str/starts-with? % "#"))))
(map #(str/split % #"," 2))
(filter #(>= (count %) 2)))]
(when (< index (count rows))
(let [row (nth rows index)]
[(str/trim (first row)) (str/trim (second row))])))
(catch Exception _ nil))))
(defn get-api-keys []
(let [public-key (System/getenv "UNSANDBOX_PUBLIC_KEY")
secret-key (System/getenv "UNSANDBOX_SECRET_KEY")
api-key (System/getenv "UNSANDBOX_API_KEY")
home (System/getenv "HOME")
account-idx (or *account-index* 0)]
(cond
;; --account N: load row N from accounts.csv, bypasses env vars
(some? *account-index*)
(or (load-accounts-csv (str home "/.unsandbox/accounts.csv") account-idx)
(load-accounts-csv "./accounts.csv" account-idx)
(do
(binding [*out* *err*]
(println (str "Error: account " account-idx " not found in accounts.csv")))
(System/exit 1)))
;; env vars
(and public-key secret-key) [public-key secret-key]
api-key [api-key nil]
;; accounts.csv fallback (row 0 or UNSANDBOX_ACCOUNT)
:else
(let [row-idx (if-let [acc (System/getenv "UNSANDBOX_ACCOUNT")]
(try (Integer/parseInt acc) (catch Exception _ 0))
0)]
(or (load-accounts-csv (str home "/.unsandbox/accounts.csv") row-idx)
(load-accounts-csv "./accounts.csv" row-idx)
(do
(binding [*out* *err*]
(println "Error: UNSANDBOX_PUBLIC_KEY and UNSANDBOX_SECRET_KEY not set (or UNSANDBOX_API_KEY for backwards compat)"))
(System/exit 1)))))))
(defn get-api-key []
(first (get-api-keys)))
(defn hmac-sha256 [secret message]
(let [mac (javax.crypto.Mac/getInstance "HmacSHA256")
secret-key (javax.crypto.spec.SecretKeySpec. (.getBytes secret "UTF-8") "HmacSHA256")]
(.init mac secret-key)
(let [bytes (.doFinal mac (.getBytes message "UTF-8"))]
(apply str (map #(format "%02x" %) bytes)))))
(defn make-signature [secret-key timestamp method path body]
(let [message (str timestamp ":" method ":" path ":" body)]
(hmac-sha256 secret-key message)))
(defn check-clock-drift-error [response]
(let [has-timestamp (or (str/includes? response "timestamp")
(str/includes? response "\"timestamp\""))
has-401 (str/includes? response "401")
has-expired (str/includes? response "expired")
has-invalid (str/includes? response "invalid")]
(when (and has-timestamp (or has-401 has-expired has-invalid))
(binding [*out* *err*]
(println (str red "Error: Request timestamp expired (must be within 5 minutes of server time)" reset))
(println (str yellow "Your computer's clock may have drifted." reset))
(println "Check your system time and sync with NTP if needed:")
(println " Linux: sudo ntpdate -s time.nist.gov")
(println " macOS: sudo sntp -sS time.apple.com")
(println " Windows: w32tm /resync"))
(System/exit 1))))
(defn build-auth-headers [public-key secret-key method path body]
(if secret-key
(let [timestamp (str (quot (System/currentTimeMillis) 1000))
signature (make-signature secret-key timestamp method path body)]
["-H" (str "Authorization: Bearer " public-key)
"-H" (str "X-Timestamp: " timestamp)
"-H" (str "X-Signature: " signature)])
["-H" (str "Authorization: Bearer " public-key)]))
(defn curl-post [api-key endpoint json-data]
(let [tmp-file (str "/tmp/un_clj_" (rand-int 999999) ".json")
[public-key secret-key] (get-api-keys)
auth-headers (build-auth-headers public-key secret-key "POST" endpoint json-data)]
(spit tmp-file json-data)
(let [args (concat ["curl" "-s" "-X" "POST"
(str "https://api.unsandbox.com" endpoint)
"-H" "Content-Type: application/json"]
auth-headers
["-d" (str "@" tmp-file)])
{:keys [out]} (apply sh args)]
(io/delete-file tmp-file true)
(check-clock-drift-error out)
out)))
(defn curl-get [api-key endpoint]
(let [[public-key secret-key] (get-api-keys)
auth-headers (build-auth-headers public-key secret-key "GET" endpoint "")
args (concat ["curl" "-s"
(str "https://api.unsandbox.com" endpoint)]
auth-headers)
result (:out (apply sh args))]
(check-clock-drift-error result)
result))
(defn curl-delete [api-key endpoint]
(let [[public-key secret-key] (get-api-keys)
auth-headers (build-auth-headers public-key secret-key "DELETE" endpoint "")
args (concat ["curl" "-s" "-w" "\n%{http_code}" "-X" "DELETE"
(str "https://api.unsandbox.com" endpoint)]
auth-headers)
result (:out (apply sh args))]
(check-clock-drift-error result)
result))
(defn extract-http-code [response]
"Extract HTTP code from response with status code appended"
(let [lines (str/split response #"\n")
last-line (last lines)]
(try
(Integer/parseInt (str/trim last-line))
(catch Exception _ 0))))
(defn extract-body [response]
"Extract body from response (everything except last line which is status code)"
(let [lines (str/split response #"\n")]
(str/join "\n" (butlast lines))))
(defn handle-sudo-challenge
"Handle 428 sudo OTP challenge - prompts user for OTP and retries the request"
[response-data public-key secret-key method endpoint body]
(let [challenge-id (extract-field "challenge_id" response-data)]
(binding [*out* *err*]
(println (str yellow "Confirmation required. Check your email for a one-time code." reset)))
(print "Enter OTP: ")
(flush)
(let [otp (str/trim (or (read-line) ""))]
(when (empty? otp)
(binding [*out* *err*]
(println "Error: Operation cancelled"))
(System/exit 1))
;; Retry the request with sudo headers
(let [auth-headers (build-auth-headers public-key secret-key method endpoint (or body ""))
sudo-headers ["-H" (str "X-Sudo-OTP: " otp)
"-H" (str "X-Sudo-Challenge: " (or challenge-id ""))]
content-type-headers (if body ["-H" "Content-Type: application/json"] [])
body-args (if body ["-d" body] [])
method-args (cond
(= method "DELETE") ["-X" "DELETE"]
(= method "POST") ["-X" "POST"]
:else ["-X" method])
args (concat ["curl" "-s"]
method-args
[(str "https://api.unsandbox.com" endpoint)]
auth-headers
sudo-headers
content-type-headers
body-args)
{:keys [out]} (apply sh args)
http-code (extract-http-code out)]
(if (and (>= http-code 200) (< http-code 300))
{:success true :response (extract-body out)}
(do
(binding [*out* *err*]
(println (str red "Error: HTTP " http-code reset))
(println (extract-body out)))
{:success false}))))))
(defn curl-delete-with-sudo [api-key endpoint]
"DELETE request that handles 428 sudo OTP challenge"
(let [[public-key secret-key] (get-api-keys)
auth-headers (build-auth-headers public-key secret-key "DELETE" endpoint "")
args (concat ["curl" "-s" "-w" "\n%{http_code}" "-X" "DELETE"
(str "https://api.unsandbox.com" endpoint)]
auth-headers)
result (:out (apply sh args))
http-code (extract-http-code result)
body (extract-body result)]
(check-clock-drift-error body)
(if (= http-code 428)
(handle-sudo-challenge body public-key secret-key "DELETE" endpoint nil)
{:success (and (>= http-code 200) (< http-code 300)) :response body :http-code http-code})))
(defn curl-post-with-sudo [api-key endpoint json-data]
"POST request that handles 428 sudo OTP challenge"
(let [tmp-file (str "/tmp/un_clj_" (rand-int 999999) ".json")
[public-key secret-key] (get-api-keys)
auth-headers (build-auth-headers public-key secret-key "POST" endpoint json-data)]
(spit tmp-file json-data)
(let [args (concat ["curl" "-s" "-w" "\n%{http_code}" "-X" "POST"
(str "https://api.unsandbox.com" endpoint)
"-H" "Content-Type: application/json"]
auth-headers
["-d" (str "@" tmp-file)])
{:keys [out]} (apply sh args)]
(io/delete-file tmp-file true)
(let [http-code (extract-http-code out)
body (extract-body out)]
(check-clock-drift-error body)
(if (= http-code 428)
(handle-sudo-challenge body public-key secret-key "POST" endpoint json-data)
{:success (and (>= http-code 200) (< http-code 300)) :response body :http-code http-code})))))
(defn curl-put-text [endpoint body]
(let [tmp-file (str "/tmp/un_clj_" (rand-int 999999) ".txt")
[public-key secret-key] (get-api-keys)
auth-headers (build-auth-headers public-key secret-key "PUT" endpoint body)]
(spit tmp-file body)
(let [args (concat ["curl" "-s" "-o" "/dev/null" "-w" "%{http_code}" "-X" "PUT"
(str "https://api.unsandbox.com" endpoint)
"-H" "Content-Type: text/plain"]
auth-headers
["-d" (str "@" tmp-file)])
{:keys [out]} (apply sh args)]
(io/delete-file tmp-file true)
(let [status (Integer/parseInt (str/trim out))]
(and (>= status 200) (< status 300))))))
(defn curl-patch [api-key endpoint json-data]
(let [tmp-file (str "/tmp/un_clj_" (rand-int 999999) ".json")
[public-key secret-key] (get-api-keys)
auth-headers (build-auth-headers public-key secret-key "PATCH" endpoint json-data)]
(spit tmp-file json-data)
(let [args (concat ["curl" "-s" "-X" "PATCH"
(str "https://api.unsandbox.com" endpoint)
"-H" "Content-Type: application/json"]
auth-headers
["-d" (str "@" tmp-file)])
{:keys [out]} (apply sh args)]
(io/delete-file tmp-file true)
(check-clock-drift-error out)
out)))
(defn set-unfreeze-on-demand [service-id enabled]
(let [api-key (get-api-key)
json (str "{\"unfreeze_on_demand\":" (if enabled "true" "false") "}")]
(curl-patch api-key (str "/services/" service-id) json)
(println (str green "Service unfreeze_on_demand set to " (if enabled "true" "false") reset))))
(def max-env-content-size 65536)
(defn read-env-file [path]
(if (.exists (io/file path))
(slurp path)
(do
(binding [*out* *err*]
(println (str red "Error: Env file not found: " path reset)))
(System/exit 1))))
(defn build-env-content [envs env-file]
(let [file-lines (if env-file
(->> (str/split (read-env-file env-file) #"\n")
(map str/trim)
(filter #(and (> (count %) 0) (not (.startsWith % "#")))))
[])]
(str/join "\n" (concat envs file-lines))))
(defn service-env-status [service-id]
(let [api-key (get-api-key)]
(curl-get api-key (str "/services/" service-id "/env"))))
(defn service-env-set [service-id env-content]
(if (> (count env-content) max-env-content-size)
(do
(binding [*out* *err*]
(println (str red "Error: Env content exceeds maximum size of 64KB" reset)))
false)
(curl-put-text (str "/services/" service-id "/env") env-content)))
(defn service-env-export [service-id]
(let [api-key (get-api-key)]
(curl-post api-key (str "/services/" service-id "/env/export") "{}")))
(defn service-env-delete [service-id]
(let [api-key (get-api-key)]
(try
(curl-delete api-key (str "/services/" service-id "/env"))
true
(catch Exception _ false))))
(defn service-env-command [action target envs env-file]
(case action
"status" (if target
(let [response (service-env-status target)
has-vault (= (extract-field "has_vault" response) "true")]
(if has-vault
(do
(println (str green "Vault: configured" reset))
(when-let [env-count (extract-field "env_count" response)]
(println (str "Variables: " env-count)))
(when-let [updated-at (extract-field "updated_at" response)]
(println (str "Updated: " updated-at))))
(println (str yellow "Vault: not configured" reset))))
(do
(binding [*out* *err*]
(println (str red "Error: service env status requires service ID" reset)))
(System/exit 1)))
"set" (if target
(if (and (empty? envs) (nil? env-file))
(do
(binding [*out* *err*]
(println (str red "Error: service env set requires -e or --env-file" reset)))
(System/exit 1))
(let [env-content (build-env-content envs env-file)]
(if (service-env-set target env-content)
(println (str green "Vault updated for service " target reset))
(do
(binding [*out* *err*]
(println (str red "Error: Failed to update vault" reset)))
(System/exit 1)))))
(do
(binding [*out* *err*]
(println (str red "Error: service env set requires service ID" reset)))
(System/exit 1)))
"export" (if target
(let [response (service-env-export target)
content (extract-field "content" response)]
(when content (print (unescape-json content))))
(do
(binding [*out* *err*]
(println (str red "Error: service env export requires service ID" reset)))
(System/exit 1)))
"delete" (if target
(if (service-env-delete target)
(println (str green "Vault deleted for service " target reset))
(do
(binding [*out* *err*]
(println (str red "Error: Failed to delete vault" reset)))
(System/exit 1)))
(do
(binding [*out* *err*]
(println (str red "Error: service env delete requires service ID" reset)))
(System/exit 1)))
(do
(binding [*out* *err*]
(println (str red "Error: Unknown env action: " action reset))
(println "Usage: un.clj service env <status|set|export|delete> <service_id>"))
(System/exit 1))))
(defn curl-portal-post [api-key endpoint json-data]
(let [tmp-file (str "/tmp/un_clj_portal_" (rand-int 999999) ".json")
[public-key secret-key] (get-api-keys)
auth-headers (build-auth-headers public-key secret-key "POST" endpoint json-data)]
(spit tmp-file json-data)
(let [args (concat ["curl" "-s" "-X" "POST"
(str portal-base endpoint)
"-H" "Content-Type: application/json"]
auth-headers
["-d" (str "@" tmp-file)])
{:keys [out]} (apply sh args)]
(io/delete-file tmp-file true)
(check-clock-drift-error out)
out)))
(defn execute-command [file env-vars artifacts out-dir network vcpu]
(let [api-key (get-api-key)
ext (get-extension file)
language (get ext-map ext)]
(when-not language
(binding [*out* *err*]
(println (str "Error: Unknown extension: " ext)))
(System/exit 1))
(let [code (slurp file)
env-json (if (empty? env-vars) ""
(str ",\"env\":{"
(str/join "," (map (fn [[k v]]
(str "\"" k "\":\"" (escape-json v) "\""))
env-vars))
"}"))
artifacts-json (if artifacts ",\"return_artifacts\":true" "")
network-json (if network (str ",\"network\":\"" network "\"") "")
vcpu-json (if vcpu (str ",\"vcpu\":" vcpu) "")
json (str "{\"language\":\"" language "\",\"code\":\"" (escape-json code) "\""
env-json artifacts-json network-json vcpu-json "}")]
(let [response (curl-post api-key "/execute" json)
stdout-val (extract-field "stdout" response)
stderr-val (extract-field "stderr" response)
exit-code (or (some-> (extract-field "exit_code" response) Integer/parseInt) 0)]
(when stdout-val
(print (str blue (unescape-json stdout-val) reset))
(flush))
(when stderr-val
(binding [*out* *err*]
(print (str red (unescape-json stderr-val) reset))
(flush)))
(System/exit exit-code)))))
(defn session-command [action sid shell network vcpu input-files]
(let [api-key (get-api-key)]
(case action
:list (println (curl-get api-key "/sessions"))
:kill (do
(curl-delete api-key (str "/sessions/" sid))
(println (str green "Session terminated: " sid reset)))
:create (let [sh (or shell "bash")
network-json (if network (str ",\"network\":\"" network "\"") "")
vcpu-json (if vcpu (str ",\"vcpu\":" vcpu) "")
input-files-json (build-input-files-json input-files)
json (str "{\"shell\":\"" sh "\"" network-json vcpu-json input-files-json "}")]
(println (str yellow "Session created (WebSocket required)" reset))
(println (curl-post api-key "/sessions" json))))))
(defn service-command [action sid name ports bootstrap bootstrap-file service-type network vcpu input-files envs env-file unfreeze-on-demand]
(let [api-key (get-api-key)]
(case action
:env (service-env-command sid name envs env-file)
:list (println (curl-get api-key "/services"))
:info (println (curl-get api-key (str "/services/" sid)))
:logs (println (curl-get api-key (str "/services/" sid "/logs")))
:sleep (do
(curl-post api-key (str "/services/" sid "/freeze") "{}")
(println (str green "Service frozen: " sid reset)))
:wake (do
(curl-post api-key (str "/services/" sid "/unfreeze") "{}")
(println (str green "Service unfreezing: " sid reset)))
:destroy (let [result (curl-delete-with-sudo api-key (str "/services/" sid))]
(if (:success result)
(println (str green "Service destroyed: " sid reset))
(do
(binding [*out* *err*]
(println (str red "Error destroying service" reset)))
(System/exit 1))))
:resize (when sid
(if (or (nil? vcpu) (< vcpu 1) (> vcpu 8))
(do
(binding [*out* *err*]
(println (str red "Error: --resize requires -v N (1-8)" reset)))
(System/exit 1))
(let [json (str "{\"vcpu\":" vcpu "}")
_ (curl-patch api-key (str "/services/" sid) json)
ram (* vcpu 2)]
(println (str green "Service resized to " vcpu " vCPU, " ram " GB RAM" reset)))))
:set-unfreeze-on-demand (when sid
(set-unfreeze-on-demand sid unfreeze-on-demand))
:execute (when (and sid bootstrap)
(let [json (str "{\"command\":\"" (escape-json bootstrap) "\"}")
response (curl-post api-key (str "/services/" sid "/execute") json)
stdout-val (extract-field "stdout" response)]
(when stdout-val
(print (str blue (unescape-json stdout-val) reset))
(flush))))
:dump-bootstrap (when sid
(binding [*out* *err*]
(println (str "Fetching bootstrap script from " sid "...")))
(let [json "{\"command\":\"cat /tmp/bootstrap.sh\"}"
response (curl-post api-key (str "/services/" sid "/execute") json)
stdout-val (extract-field "stdout" response)]
(if stdout-val
(let [script (unescape-json stdout-val)]
(if service-type
(do
(spit service-type script)
(sh "chmod" "755" service-type)
(println (str "Bootstrap saved to " service-type)))
(print script)))
(do
(binding [*out* *err*]
(println (str red "Error: Failed to fetch bootstrap (service not running or no bootstrap file)" reset)))
(System/exit 1)))))
:create (when name
(let [ports-json (if ports (str ",\"ports\":[" ports "]") "")
bootstrap-json (if bootstrap (str ",\"bootstrap\":\"" (escape-json bootstrap) "\"") "")
bootstrap-content-json (if bootstrap-file
(str ",\"bootstrap_content\":\"" (escape-json (slurp bootstrap-file)) "\"")
"")
service-type-json (if service-type (str ",\"service_type\":\"" service-type "\"") "")
network-json (if network (str ",\"network\":\"" network "\"") "")
vcpu-json (if vcpu (str ",\"vcpu\":" vcpu) "")
unfreeze-on-demand-json (if (some? unfreeze-on-demand) (str ",\"unfreeze_on_demand\":" (if unfreeze-on-demand "true" "false")) "")
input-files-json (build-input-files-json input-files)
json (str "{\"name\":\"" name "\"" ports-json bootstrap-json bootstrap-content-json service-type-json network-json vcpu-json unfreeze-on-demand-json input-files-json "}")
response (curl-post api-key "/services" json)
service-id (extract-field "id" response)]
(println (str green "Service created" reset))
(println response)
;; Auto-set vault if env vars were provided
(when (and service-id (or (seq envs) env-file))
(let [env-content (build-env-content envs env-file)]
(when (> (count env-content) 0)
(if (service-env-set service-id env-content)
(println (str green "Vault configured with environment variables" reset))
(println (str yellow "Warning: Failed to set vault" reset))))))))))))
(defn validate-key [api-key extend?]
(let [response (curl-portal-post api-key "/keys/validate" "{}")
status (extract-field "status" response)
public-key (extract-field "public_key" response)
tier (extract-field "tier" response)
valid-through (extract-field "valid_through_datetime" response)
valid-for (extract-field "valid_for_human" response)
rate-limit (extract-field "rate_per_minute" response)
burst (extract-field "burst" response)
concurrency (extract-field "concurrency" response)
expired-at (extract-field "expired_at_datetime" response)]
(cond
(= status "valid")
(do
(println (str green "Valid" reset "\n"))
(when public-key (println (str "Public Key: " public-key)))
(when tier (println (str "Tier: " tier)))
(println "Status: valid")
(when valid-through (println (str "Expires: " valid-through)))
(when valid-for (println (str "Time Remaining: " valid-for)))
(when rate-limit (println (str "Rate Limit: " rate-limit "/min")))
(when burst (println (str "Burst: " burst)))
(when concurrency (println (str "Concurrency: " concurrency)))
(when extend?
(let [url (str portal-base "/keys/extend?pk=" public-key)]
(println (str blue "Opening browser to extend key..." reset))
(sh "xdg-open" url))))
(= status "expired")
(do
(println (str red "Expired" reset "\n"))
(when public-key (println (str "Public Key: " public-key)))
(when tier (println (str "Tier: " tier)))
(when expired-at (println (str "Expired: " expired-at)))
(println (str "\n" yellow "To renew:" reset " Visit " portal-base "/keys/extend"))
(when extend?
(let [url (str portal-base "/keys/extend?pk=" public-key)]
(println (str blue "Opening browser..." reset))
(sh "xdg-open" url))))
:else
(do
(println (str red "Invalid" reset))
(println response)))))
(defn key-command [extend?]
(let [api-key (get-api-key)]
(validate-key api-key extend?)))
(defn get-languages-cache-path []
(let [home (System/getenv "HOME")]
(str home "/.unsandbox/languages.json")))
(defn load-languages-cache []
(let [cache-path (get-languages-cache-path)]
(when (.exists (io/file cache-path))
(try
(let [content (slurp cache-path)
timestamp (extract-field "timestamp" content)]
(when timestamp
(let [cache-time (Long/parseLong timestamp)
now (quot (System/currentTimeMillis) 1000)]
(when (< (- now cache-time) languages-cache-ttl)
content))))
(catch Exception _ nil)))))
(defn save-languages-cache [languages-json]
(let [cache-path (get-languages-cache-path)
cache-dir (.getParent (io/file cache-path))]
(.mkdirs (io/file cache-dir))
(let [timestamp (quot (System/currentTimeMillis) 1000)
cache-content (str "{\"languages\":" languages-json ",\"timestamp\":" timestamp "}")]
(spit cache-path cache-content))))
(defn languages-command [json-output?]
(let [api-key (get-api-key)
;; Try cache first
cached (load-languages-cache)
response (if cached
cached
(let [resp (curl-get api-key "/languages")]
;; Extract and cache the languages array
(when-let [match (re-find #"\"languages\":\s*\[([^\]]*)\]" resp)]
(save-languages-cache (str "[" (second match) "]")))
resp))]
(if json-output?
;; Extract language names and output as JSON array
(let [langs (re-seq #"\"name\":\"([^\"]+)\"" response)
names (map second langs)]
(println (str "[" (str/join "," (map #(str "\"" % "\"") names)) "]")))
;; Output one language per line
(let [langs (re-seq #"\"name\":\"([^\"]+)\"" response)
names (map second langs)]
(doseq [name names]
(println name))))))
;; Image command functions
(defn image-list []
(let [api-key (get-api-key)]
(println (curl-get api-key "/images"))))
(defn image-info [id]
(let [api-key (get-api-key)]
(println (curl-get api-key (str "/images/" id)))))
(defn image-delete [id]
(let [api-key (get-api-key)
result (curl-delete-with-sudo api-key (str "/images/" id))]
(if (:success result)
(println (str green "Image deleted: " id reset))
(do
(binding [*out* *err*]
(println (str red "Error deleting image" reset)))
(System/exit 1)))))
(defn image-lock [id]
(let [api-key (get-api-key)]
(curl-post api-key (str "/images/" id "/lock") "{}")
(println (str green "Image locked: " id reset))))
(defn image-unlock [id]
(let [api-key (get-api-key)
result (curl-post-with-sudo api-key (str "/images/" id "/unlock") "{}")]
(if (:success result)
(println (str green "Image unlocked: " id reset))
(do
(binding [*out* *err*]
(println (str red "Error unlocking image" reset)))
(System/exit 1)))))
(defn image-publish [source-id source-type name]
(let [api-key (get-api-key)
json (str "{\"source_type\":\"" source-type "\",\"source_id\":\"" source-id "\""
(if name (str ",\"name\":\"" (escape-json name) "\"") "")
"}")]
(println (str green "Image published" reset))
(println (curl-post api-key "/images/publish" json))))
(defn image-visibility [id visibility]
(let [api-key (get-api-key)
json (str "{\"visibility\":\"" visibility "\"}")]
(curl-post api-key (str "/images/" id "/visibility") json)
(println (str green "Image visibility set to: " visibility reset))))
(defn image-spawn [id name ports]
(let [api-key (get-api-key)
json (str "{"
(if name (str "\"name\":\"" (escape-json name) "\"") "")
(if (and name ports) "," "")
(if ports (str "\"ports\":[" ports "]") "")
"}")]
(println (str green "Service spawned from image" reset))
(println (curl-post api-key (str "/images/" id "/spawn") json))))
(defn image-clone [id name]
(let [api-key (get-api-key)
json (str "{" (if name (str "\"name\":\"" (escape-json name) "\"") "") "}")]
(println (str green "Image cloned" reset))
(println (curl-post api-key (str "/images/" id "/clone") json))))
;; Image access management functions
(defn image-grant-access [id trusted-key]
(let [api-key (get-api-key)
json (str "{\"trusted_api_key\":\"" trusted-key "\"}")]
(curl-post api-key (str "/images/" id "/grant-access") json)
(println (str green "Access granted to: " trusted-key reset))))
(defn image-revoke-access [id trusted-key]
(let [api-key (get-api-key)
json (str "{\"trusted_api_key\":\"" trusted-key "\"}")]
(curl-post api-key (str "/images/" id "/revoke-access") json)
(println (str green "Access revoked from: " trusted-key reset))))
(defn image-list-trusted [id]
(let [api-key (get-api-key)]
(println (curl-get api-key (str "/images/" id "/trusted")))))
(defn image-transfer [id to-key]
(let [api-key (get-api-key)
json (str "{\"to_api_key\":\"" to-key "\"}")]
(curl-post api-key (str "/images/" id "/transfer") json)
(println (str green "Image transferred to: " to-key reset))))
;; Snapshot functions
(defn snapshot-list []
(let [api-key (get-api-key)]
(println (curl-get api-key "/snapshots"))))
(defn snapshot-info [id]
(let [api-key (get-api-key)]
(println (curl-get api-key (str "/snapshots/" id)))))
(defn snapshot-session [session-id name hot]
(let [api-key (get-api-key)
json (str "{\"session_id\":\"" session-id "\""
(if name (str ",\"name\":\"" (escape-json name) "\"") "")
(if hot ",\"hot\":true" "")
"}")]
(println (str green "Snapshot created" reset))
(println (curl-post api-key "/snapshots" json))))
(defn snapshot-service [service-id name hot]
(let [api-key (get-api-key)
json (str "{\"service_id\":\"" service-id "\""
(if name (str ",\"name\":\"" (escape-json name) "\"") "")
(if hot ",\"hot\":true" "")
"}")]
(println (str green "Snapshot created" reset))
(println (curl-post api-key "/snapshots" json))))
(defn snapshot-restore [id]
(let [api-key (get-api-key)]
(curl-post api-key (str "/snapshots/" id "/restore") "{}")
(println (str green "Snapshot restored: " id reset))))
(defn snapshot-delete [id]
(let [api-key (get-api-key)
result (curl-delete-with-sudo api-key (str "/snapshots/" id))]
(if (:success result)
(println (str green "Snapshot deleted: " id reset))
(do
(binding [*out* *err*]
(println (str red "Error deleting snapshot" reset)))
(System/exit 1)))))
(defn snapshot-lock [id]
(let [api-key (get-api-key)]
(curl-post api-key (str "/snapshots/" id "/lock") "{}")
(println (str green "Snapshot locked: " id reset))))
(defn snapshot-unlock [id]
(let [api-key (get-api-key)
result (curl-post-with-sudo api-key (str "/snapshots/" id "/unlock") "{}")]
(if (:success result)
(println (str green "Snapshot unlocked: " id reset))
(do
(binding [*out* *err*]
(println (str red "Error unlocking snapshot" reset)))
(System/exit 1)))))
(defn snapshot-clone [id clone-type name ports shell]
(let [api-key (get-api-key)
json (str "{\"clone_type\":\"" clone-type "\""
(if name (str ",\"name\":\"" (escape-json name) "\"") "")
(if ports (str ",\"ports\":[" ports "]") "")
(if shell (str ",\"shell\":\"" shell "\"") "")
"}")]
(println (str green "Snapshot cloned" reset))
(println (curl-post api-key (str "/snapshots/" id "/clone") json))))
(defn snapshot-command [action id name ports shell hot]
(case action
:list (snapshot-list)
:info (snapshot-info id)
:session (snapshot-session id name hot)
:service (snapshot-service id name hot)
:restore (snapshot-restore id)
:delete (snapshot-delete id)
:lock (snapshot-lock id)
:unlock (snapshot-unlock id)
:clone (snapshot-clone id "session" name ports shell)))
;; Session additional functions
(defn session-info [id]
(let [api-key (get-api-key)]
(println (curl-get api-key (str "/sessions/" id)))))
(defn session-boost [id vcpu]
(let [api-key (get-api-key)
json (str "{\"vcpu\":" vcpu "}")]
(curl-patch api-key (str "/sessions/" id) json)
(println (str green "Session boosted to " vcpu " vCPU" reset))))
(defn session-unboost [id]
(let [api-key (get-api-key)
json "{\"vcpu\":1}"]
(curl-patch api-key (str "/sessions/" id) json)
(println (str green "Session unboosted to 1 vCPU" reset))))
(defn session-execute [id command]
(let [api-key (get-api-key)
json (str "{\"command\":\"" (escape-json command) "\"}")
response (curl-post api-key (str "/sessions/" id "/execute") json)
stdout-val (extract-field "stdout" response)]
(when stdout-val
(print (str blue (unescape-json stdout-val) reset))
(flush))))
;; Service additional functions
(defn service-lock [id]
(let [api-key (get-api-key)]
(curl-post api-key (str "/services/" id "/lock") "{}")
(println (str green "Service locked: " id reset))))
(defn service-unlock [id]
(let [api-key (get-api-key)
result (curl-post-with-sudo api-key (str "/services/" id "/unlock") "{}")]
(if (:success result)
(println (str green "Service unlocked: " id reset))
(do
(binding [*out* *err*]
(println (str red "Error unlocking service" reset)))
(System/exit 1)))))
(defn service-redeploy [id bootstrap]
(let [api-key (get-api-key)
json (if bootstrap
(str "{\"bootstrap\":\"" (escape-json bootstrap) "\"}")
"{}")]
(curl-post api-key (str "/services/" id "/redeploy") json)
(println (str green "Service redeploying: " id reset))))
;; PaaS logs functions
(defn logs-fetch [source lines since grep-pattern]
(let [api-key (get-api-key)
params (str "?source=" (or source "all")
"&lines=" (or lines 100)
(if since (str "&since=" since) "")
(if grep-pattern (str "&grep=" (java.net.URLEncoder/encode grep-pattern "UTF-8")) ""))]
(println (curl-get api-key (str "/logs" params)))))
;; Utility functions
(defn health-check []
(try
(let [result (:out (sh "curl" "-s" "https://api.unsandbox.com/health"))]
(println result)
(str/includes? result "ok"))
(catch Exception _ false)))
(defn version []
"4.2.0")
(defn image-command [action id source-type visibility name ports]
(case action
:list (image-list)
:info (image-info id)
:delete (image-delete id)
:lock (image-lock id)
:unlock (image-unlock id)
:publish (if source-type
(image-publish id source-type name)
(do
(binding [*out* *err*]
(println (str red "Error: --publish requires --source-type (service or snapshot)" reset)))
(System/exit 1)))
:visibility (if visibility
(image-visibility id visibility)
(do
(binding [*out* *err*]
(println (str red "Error: --visibility requires visibility mode (private, unlisted, public)" reset)))
(System/exit 1)))
:spawn (image-spawn id name ports)
:clone (image-clone id name)))
(defn parse-args [args]
(loop [args args
file nil
env-vars []
artifacts false
out-dir nil
network nil
vcpu nil
session-action nil
session-id nil
session-shell nil
session-input-files []
service-action nil
service-id nil
service-name nil
service-ports nil
service-bootstrap nil
service-bootstrap-file nil
service-type nil
service-input-files []
service-envs []
service-env-file nil
service-unfreeze-on-demand nil
key-extend false
image-action nil
image-id nil
image-source-type nil
image-visibility nil
image-name nil
image-ports nil
mode :execute]
(cond
(empty? args)
(case mode
:session (session-command (or session-action :create) session-id session-shell network vcpu session-input-files)
:service (service-command (or service-action :create) service-id service-name service-ports service-bootstrap service-bootstrap-file service-type network vcpu service-input-files service-envs service-env-file service-unfreeze-on-demand)
:key (key-command key-extend)
:languages (languages-command false)
:image (image-command (or image-action :list) image-id image-source-type image-visibility image-name image-ports)
:execute (if file
(execute-command file env-vars artifacts out-dir network vcpu)
(do (println "Usage: un.clj [options] <source_file>")
(println " un.clj languages [--json]")
(println " un.clj session [options]")
(println " un.clj service [options]")
(println " un.clj service env <action> <service_id>")
(println " un.clj image [options]")
(println " un.clj key [options]")
(System/exit 1))))
(= (first args) "session")
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :session)
(= (first args) "service")
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :service)
(= (first args) "key")
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :key)
(= (first args) "languages")
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :languages)
(= (first args) "image")
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :image)
(= (first args) "snapshot")
(let [rest-args (rest args)]
(cond
(empty? rest-args) (do (snapshot-list) (System/exit 0))
(= (first rest-args) "--list") (do (snapshot-list) (System/exit 0))
(= (first rest-args) "-l") (do (snapshot-list) (System/exit 0))
(= (first rest-args) "--info") (do (snapshot-info (second rest-args)) (System/exit 0))
(= (first rest-args) "--session") (do (snapshot-session (second rest-args) nil false) (System/exit 0))
(= (first rest-args) "--service") (do (snapshot-service (second rest-args) nil false) (System/exit 0))
(= (first rest-args) "--restore") (do (snapshot-restore (second rest-args)) (System/exit 0))
(= (first rest-args) "--delete") (do (snapshot-delete (second rest-args)) (System/exit 0))
(= (first rest-args) "--lock") (do (snapshot-lock (second rest-args)) (System/exit 0))
(= (first rest-args) "--unlock") (do (snapshot-unlock (second rest-args)) (System/exit 0))
(= (first rest-args) "--clone") (do (snapshot-clone (second rest-args) "session" nil nil nil) (System/exit 0))
:else (do (println "Error: Unknown snapshot action") (System/exit 1))))
;; Image options
(and (= mode :image) (or (= (first args) "--list") (= (first args) "-l")))
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :list image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--info"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :info (second args) image-source-type image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--delete"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :delete (second args) image-source-type image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--lock"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :lock (second args) image-source-type image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--unlock"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :unlock (second args) image-source-type image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--publish"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :publish (second args) image-source-type image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--source-type"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id (second args) image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--visibility"))
(recur (rest (rest (rest args))) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :visibility (second args) image-source-type (nth args 2) image-name image-ports mode)
(and (= mode :image) (= (first args) "--spawn"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :spawn (second args) image-source-type image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--clone"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :clone (second args) image-source-type image-visibility image-name image-ports mode)
(and (= mode :image) (= (first args) "--name"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility (second args) image-ports mode)
(and (= mode :image) (= (first args) "--ports"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name (second args) mode)
;; Key options
(and (= mode :key) (= (first args) "--extend"))
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand true image-action image-id image-source-type image-visibility image-name image-ports mode)
;; Languages options
(and (= mode :languages) (= (first args) "--json"))
(do
(languages-command true)
(System/exit 0))
;; Session options
(and (= mode :session) (= (first args) "--list"))
(recur (rest args) file env-vars artifacts out-dir network vcpu :list session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :session) (= (first args) "--kill"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu :kill (second args) session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :session) (or (= (first args) "--shell") (= (first args) "-s")))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id (second args) session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :session) (= (first args) "-f"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell (conj session-input-files (second args))
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
;; Service options
(and (= mode :service) (= (first args) "--list"))
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:list service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--info"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:info (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--logs"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:logs (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--freeze"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:sleep (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--unfreeze"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:wake (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--destroy"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:destroy (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--resize"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:resize (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--set-unfreeze-on-demand"))
(recur (rest (rest (rest args))) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:set-unfreeze-on-demand (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file (= (nth args 2) "true") key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--execute"))
(recur (rest (rest (rest args))) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:execute (second args) service-name service-ports (nth args 2) service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--dump-bootstrap") (>= (count args) 3) (not (.startsWith (nth args 2) "-")))
(recur (rest (rest (rest args))) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:dump-bootstrap (second args) service-name service-ports service-bootstrap (nth args 2) service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--dump-bootstrap"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:dump-bootstrap (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--name"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:create service-id (second args) service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--ports"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name (second args) service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--bootstrap"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports (second args) service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--bootstrap-file"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap (second args) service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--type"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file (second args) service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "-f"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type (conj service-input-files (second args)) service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "env") (>= (count args) 2))
(let [env-action (second args)
env-target (when (and (>= (count args) 3) (not (.startsWith (nth args 2) "-"))) (nth args 2))
rest-args (if env-target (drop 3 args) (drop 2 args))]
(recur rest-args file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
:env env-action env-target service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode))
(and (= mode :service) (= (first args) "-e"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files (conj service-envs (second args)) service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--env-file"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs (second args) service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(and (= mode :service) (= (first args) "--unfreeze-on-demand"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file (= (second args) "true") key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
;; Execute options
(= (first args) "-e")
(let [[k v] (str/split (second args) #"=" 2)]
(recur (rest (rest args)) file (conj env-vars [k v]) artifacts out-dir network vcpu
session-action session-id session-shell session-input-files service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode))
(= (first args) "-a")
(recur (rest args) file env-vars true out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(= (first args) "-o")
(recur (rest (rest args)) file env-vars artifacts (second args) network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(= (first args) "-n")
(recur (rest (rest args)) file env-vars artifacts out-dir (second args) vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
(= (first args) "-v")
(recur (rest (rest args)) file env-vars artifacts out-dir network (Integer/parseInt (second args)) session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
;; Source file
(and (= mode :execute) (not (.startsWith (first args) "-")) (nil? file))
(recur (rest args) (first args) env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
;; Unknown option check
(and (= mode :session) (.startsWith (first args) "-"))
(do
(println (str red "Unknown option: " (first args) reset) *err*)
(println "Usage: un.clj session [options]")
(println "Options: --list, --kill ID, --shell SHELL, -s SHELL, -f FILE, -n NETWORK, -v VCPU")
(System/exit 1))
:else
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode))))
(let [raw-args *command-line-args*
account-val (second (drop-while #(not= % "--account") raw-args))
account-idx (when account-val
(try (Integer/parseInt account-val) (catch Exception _ nil)))
filtered-args (loop [in raw-args out []]
(cond
(empty? in) out
(= (first in) "--account") (recur (drop 2 in) out)
:else (recur (rest in) (conj out (first in)))))]
(binding [*account-index* account-idx]
(parse-args filtered-args)))
Esclarecimentos de documentação
Dependências
C Binary (un1) — requer libcurl e libwebsockets:
sudo apt install build-essential libcurl4-openssl-dev libwebsockets-dev
wget unsandbox.com/downloads/un.c && gcc -O2 -o un un.c -lcurl -lwebsockets
Implementações SDK — a maioria usa apenas stdlib (Ruby, JS, Go, etc). Alguns requerem dependências mínimas:
pip install requests # Python
Executar Código
Executar um script
./un hello.py
./un app.js
./un main.rs
Com variáveis de ambiente
./un -e DEBUG=1 -e NAME=World script.py
Com arquivos de entrada (teletransportar arquivos para sandbox)
./un -f data.csv -f config.json process.py
Obter binário compilado
./un -a -o ./bin main.c
Sessões interativas
Iniciar uma sessão de shell
# Default bash shell
./un session
# Choose your shell
./un session --shell zsh
./un session --shell fish
# Jump into a REPL
./un session --shell python3
./un session --shell node
./un session --shell julia
Sessão com acesso à rede
./un session -n semitrusted
Auditoria de sessão (gravação completa do terminal)
# Record everything (including vim, interactive programs)
./un session --audit -o ./logs
# Replay session later
zcat session.log*.gz | less -R
Coletar artefatos da sessão
# Files in /tmp/artifacts/ are collected on exit
./un session -a -o ./outputs
Persistência de sessão (tmux/screen)
# Default: session terminates on disconnect (clean exit)
./un session
# With tmux: session persists, can reconnect later
./un session --tmux
# Press Ctrl+b then d to detach
# With screen: alternative multiplexer
./un session --screen
# Press Ctrl+a then d to detach
Listar Trabalhos Ativos
./un session --list
# Output:
# Active sessions: 2
#
# SESSION ID CONTAINER SHELL TTL STATUS
# abc123... unsb-vm-12345 python3 45m30s active
# def456... unsb-vm-67890 bash 1h2m active
Reconectar à sessão existente
# Reconnect by container name (requires --tmux or --screen)
./un session --attach unsb-vm-12345
# Use exit to terminate session, or detach to keep it running
Encerrar uma sessão
./un session --kill unsb-vm-12345
Shells e REPLs disponíveis
Shells: bash, dash, sh, zsh, fish, ksh, tcsh, csh, elvish, xonsh, ash
REPLs: python3, bpython, ipython # Python
node # JavaScript
ruby, irb # Ruby
lua # Lua
php # PHP
perl # Perl
guile, scheme # Scheme
ghci # Haskell
erl, iex # Erlang/Elixir
sbcl, clisp # Common Lisp
r # R
julia # Julia
clojure # Clojure
Gerenciamento de Chave API
Verificar Status do Pagamento
# Check if your API key is valid
./un key
# Output:
# Valid: key expires in 30 days
Estender Chave Expirada
# Open the portal to extend an expired key
./un key --extend
# This opens the unsandbox.com portal where you can
# add more credits to extend your key's expiration
Autenticação
As credenciais são carregadas em ordem de prioridade (maior primeiro):
# 1. CLI flags (highest priority)
./un -p unsb-pk-xxxx -k unsb-sk-xxxxx script.py
# 2. Environment variables
export UNSANDBOX_PUBLIC_KEY=unsb-pk-xxxx-xxxx-xxxx-xxxx
export UNSANDBOX_SECRET_KEY=unsb-sk-xxxxx-xxxxx-xxxxx-xxxxx
./un script.py
# 3. Config file (lowest priority)
# ~/.unsandbox/accounts.csv format: public_key,secret_key
mkdir -p ~/.unsandbox
echo "unsb-pk-xxxx-xxxx-xxxx-xxxx,unsb-sk-xxxxx-xxxxx-xxxxx-xxxxx" > ~/.unsandbox/accounts.csv
./un script.py
As requisições são assinadas com HMAC-SHA256. O token bearer contém apenas a chave pública; a chave secreta calcula a assinatura (nunca é transmitida).
Escalonamento de Recursos
Definir Quantidade de vCPU
# Default: 1 vCPU, 2GB RAM
./un script.py
# Scale up: 4 vCPUs, 8GB RAM
./un -v 4 script.py
# Maximum: 8 vCPUs, 16GB RAM
./un --vcpu 8 heavy_compute.py
Reforço de Sessão Ao Vivo
# Boost a running session to 2 vCPU, 4GB RAM
./un session --boost sandbox-abc
# Boost to specific vCPU count (4 vCPU, 8GB RAM)
./un session --boost sandbox-abc --boost-vcpu 4
# Return to base resources (1 vCPU, 2GB RAM)
./un session --unboost sandbox-abc
Congelar/Descongelar Sessão
Congelar e Descongelar Sessões
# Freeze a session (stop billing, preserve state)
./un session --freeze sandbox-abc
# Unfreeze a frozen session
./un session --unfreeze sandbox-abc
# Note: Requires --tmux or --screen for persistence
Serviços Persistentes
Criar um Serviço
# Web server with ports
./un service --name web --ports 80,443 --bootstrap "python -m http.server 80"
# With custom domains
./un service --name blog --ports 8000 --domains blog.example.com
# Game server with SRV records
./un service --name mc --type minecraft --bootstrap ./setup.sh
# Deploy app tarball with bootstrap script
./un service --name app --ports 8000 -f app.tar.gz --bootstrap-file ./setup.sh
# setup.sh: cd /tmp && tar xzf app.tar.gz && ./app/start.sh
Gerenciar Serviços
# List all services
./un service --list
# Get service details
./un service --info abc123
# View bootstrap logs
./un service --logs abc123
./un service --tail abc123 # last 9000 lines
# Execute command in running service
./un service --execute abc123 'journalctl -u myapp -n 50'
# Dump bootstrap script (for migrations)
./un service --dump-bootstrap abc123
./un service --dump-bootstrap abc123 backup.sh
# Freeze/unfreeze service
./un service --freeze abc123
./un service --unfreeze abc123
# Service settings (auto-wake, freeze page display)
./un service --auto-unfreeze abc123 # enable auto-wake on HTTP
./un service --no-auto-unfreeze abc123 # disable auto-wake
./un service --show-freeze-page abc123 # show HTML payment page (default)
./un service --no-show-freeze-page abc123 # return JSON error instead
# Redeploy with new bootstrap
./un service --redeploy abc123 --bootstrap ./new-setup.sh
# Destroy service
./un service --destroy abc123
Snapshots
Listar Snapshots
./un snapshot --list
# Output:
# Snapshots: 3
#
# SNAPSHOT ID NAME SOURCE SIZE CREATED
# unsb-snapshot-a1b2-c3d4-e5f6-g7h8 before-upgrade session 512 MB 2h ago
# unsb-snapshot-i9j0-k1l2-m3n4-o5p6 stable-v1.0 service 1.2 GB 1d ago
Criar Snapshot da Sessão
# Snapshot with name
./un session --snapshot unsb-vm-12345 --name "before upgrade"
# Quick snapshot (auto-generated name)
./un session --snapshot unsb-vm-12345
Criar Snapshot do Serviço
# Standard snapshot (pauses container briefly)
./un service --snapshot unsb-service-abc123 --name "stable v1.0"
# Hot snapshot (no pause, may be inconsistent)
./un service --snapshot unsb-service-abc123 --hot
Restaurar a partir do Snapshot
# Restore session from snapshot
./un session --restore unsb-snapshot-a1b2-c3d4-e5f6-g7h8
# Restore service from snapshot
./un service --restore unsb-snapshot-i9j0-k1l2-m3n4-o5p6
Excluir Snapshot
./un snapshot --delete unsb-snapshot-a1b2-c3d4-e5f6-g7h8
Imagens
Imagens são imagens de container independentes e transferíveis que sobrevivem à exclusão do container. Diferente dos snapshots (que permanecem com seu container), imagens podem ser compartilhadas com outros usuários, transferidas entre chaves de API ou tornadas públicas no marketplace.
Listar Imagens
# List all images (owned + shared + public)
./un image --list
# List only your images
./un image --list owned
# List images shared with you
./un image --list shared
# List public marketplace images
./un image --list public
# Get image details
./un image --info unsb-image-xxxx-xxxx-xxxx-xxxx
Publicar Imagens
# Publish from a stopped or frozen service
./un image --publish-service unsb-service-abc123 \
--name "My App v1.0" --description "Production snapshot"
# Publish from a snapshot
./un image --publish-snapshot unsb-snapshot-xxxx-xxxx-xxxx-xxxx \
--name "Stable Release"
# Note: Cannot publish from running containers - stop or freeze first
Criar Serviços a partir de Imagens
# Spawn a new service from an image
./un image --spawn unsb-image-xxxx-xxxx-xxxx-xxxx \
--name new-service --ports 80,443
# Clone an image (creates a copy you own)
./un image --clone unsb-image-xxxx-xxxx-xxxx-xxxx
Proteção de Imagem
# Lock image to prevent accidental deletion
./un image --lock unsb-image-xxxx-xxxx-xxxx-xxxx
# Unlock image to allow deletion
./un image --unlock unsb-image-xxxx-xxxx-xxxx-xxxx
# Delete image (must be unlocked)
./un image --delete unsb-image-xxxx-xxxx-xxxx-xxxx
Visibilidade e Compartilhamento
# Set visibility level
./un image --visibility unsb-image-xxxx-xxxx-xxxx-xxxx private # owner only (default)
./un image --visibility unsb-image-xxxx-xxxx-xxxx-xxxx unlisted # can be shared
./un image --visibility unsb-image-xxxx-xxxx-xxxx-xxxx public # marketplace
# Share with specific user
./un image --grant unsb-image-xxxx-xxxx-xxxx-xxxx \
--key unsb-pk-friend-friend-friend-friend
# Revoke access
./un image --revoke unsb-image-xxxx-xxxx-xxxx-xxxx \
--key unsb-pk-friend-friend-friend-friend
# List who has access
./un image --trusted unsb-image-xxxx-xxxx-xxxx-xxxx
Transferir Propriedade
# Transfer image to another API key
./un image --transfer unsb-image-xxxx-xxxx-xxxx-xxxx \
--to unsb-pk-newowner-newowner-newowner-newowner
Referência de uso
Usage: ./un [options] <source_file>
./un session [options]
./un service [options]
./un snapshot [options]
./un image [options]
./un key
Commands:
(default) Execute source file in sandbox
session Open interactive shell/REPL session
service Manage persistent services
snapshot Manage container snapshots
image Manage container images (publish, share, transfer)
key Check API key validity and expiration
Options:
-e KEY=VALUE Set environment variable (can use multiple times)
-f FILE Add input file (can use multiple times)
-a Return and save artifacts from /tmp/artifacts/
-o DIR Output directory for artifacts (default: current dir)
-p KEY Public key (or set UNSANDBOX_PUBLIC_KEY env var)
-k KEY Secret key (or set UNSANDBOX_SECRET_KEY env var)
-n MODE Network mode: zerotrust (default) or semitrusted
-v N, --vcpu N vCPU count 1-8, each vCPU gets 2GB RAM (default: 1)
-y Skip confirmation for large uploads (>1GB)
-h Show this help
Authentication (priority order):
1. -p and -k flags (public and secret key)
2. UNSANDBOX_PUBLIC_KEY + UNSANDBOX_SECRET_KEY env vars
3. ~/.unsandbox/accounts.csv (format: public_key,secret_key per line)
Session options:
-s, --shell SHELL Shell/REPL to use (default: bash)
-l, --list List active sessions
--attach ID Reconnect to existing session (ID or container name)
--kill ID Terminate a session (ID or container name)
--freeze ID Freeze a session (requires --tmux/--screen)
--unfreeze ID Unfreeze a frozen session
--boost ID Boost session resources (2 vCPU, 4GB RAM)
--boost-vcpu N Specify vCPU count for boost (1-8)
--unboost ID Return to base resources
--audit Record full session for auditing
--tmux Enable session persistence with tmux (allows reconnect)
--screen Enable session persistence with screen (allows reconnect)
Service options:
--name NAME Service name (creates new service)
--ports PORTS Comma-separated ports (e.g., 80,443)
--domains DOMAINS Custom domains (e.g., example.com,www.example.com)
--type TYPE Service type: minecraft, mumble, teamspeak, source, tcp, udp
--bootstrap CMD Bootstrap command/file/URL to run on startup
-f FILE Upload file to /tmp/ (can use multiple times)
-l, --list List all services
--info ID Get service details
--tail ID Get last 9000 lines of bootstrap logs
--logs ID Get all bootstrap logs
--freeze ID Freeze a service
--unfreeze ID Unfreeze a service
--auto-unfreeze ID Enable auto-wake on HTTP request
--no-auto-unfreeze ID Disable auto-wake on HTTP request
--show-freeze-page ID Show HTML payment page when frozen (default)
--no-show-freeze-page ID Return JSON error when frozen
--destroy ID Destroy a service
--redeploy ID Re-run bootstrap script (requires --bootstrap)
--execute ID CMD Run a command in a running service
--dump-bootstrap ID [FILE] Dump bootstrap script (for migrations)
--snapshot ID Create snapshot of session or service
--snapshot-name User-friendly name for snapshot
--hot Create snapshot without pausing (may be inconsistent)
--restore ID Restore session/service from snapshot ID
Snapshot options:
-l, --list List all snapshots
--info ID Get snapshot details
--delete ID Delete a snapshot permanently
Image options:
-l, --list [owned|shared|public] List images (all, owned, shared, or public)
--info ID Get image details
--publish-service ID Publish image from stopped/frozen service
--publish-snapshot ID Publish image from snapshot
--name NAME Name for published image
--description DESC Description for published image
--delete ID Delete image (must be unlocked)
--clone ID Clone image (creates copy you own)
--spawn ID Create service from image (requires --name)
--lock ID Lock image to prevent deletion
--unlock ID Unlock image to allow deletion
--visibility ID LEVEL Set visibility (private|unlisted|public)
--grant ID --key KEY Grant access to another API key
--revoke ID --key KEY Revoke access from API key
--transfer ID --to KEY Transfer ownership to API key
--trusted ID List API keys with access
Key options:
(no options) Check API key validity
--extend Open portal to extend an expired key
Examples:
./un script.py # execute Python script
./un -e DEBUG=1 script.py # with environment variable
./un -f data.csv process.py # with input file
./un -a -o ./bin main.c # save compiled artifacts
./un -v 4 heavy.py # with 4 vCPUs, 8GB RAM
./un session # interactive bash session
./un session --tmux # bash with reconnect support
./un session --list # list active sessions
./un session --attach unsb-vm-12345 # reconnect to session
./un session --kill unsb-vm-12345 # terminate a session
./un session --freeze unsb-vm-12345 # freeze session
./un session --unfreeze unsb-vm-12345 # unfreeze session
./un session --boost unsb-vm-12345 # boost resources
./un session --unboost unsb-vm-12345 # return to base
./un session --shell python3 # Python REPL
./un session --shell node # Node.js REPL
./un session -n semitrusted # session with network access
./un session --audit -o ./logs # record session for auditing
./un service --name web --ports 80 # create web service
./un service --list # list all services
./un service --logs abc123 # view bootstrap logs
./un key # check API key
./un key --extend # extend expired key
./un snapshot --list # list all snapshots
./un session --snapshot unsb-vm-123 # snapshot a session
./un service --snapshot abc123 # snapshot a service
./un session --restore unsb-snapshot-xxxx # restore from snapshot
./un image --list # list all images
./un image --list owned # list your images
./un image --publish-service abc # publish image from service
./un image --spawn img123 --name x # create service from image
./un image --grant img --key pk # share image with user
CLI Inception
O UN CLI foi implementado em 42 linguagens de programação, demonstrando que a API do unsandbox pode ser acessada de praticamente qualquer ambiente.
Ver Todas as 42 Implementações →
Licença
DOMÍNIO PÚBLICO - SEM LICENÇA, SEM GARANTIA
Este é software gratuito de domínio público para o bem público de um permacomputador hospedado
em permacomputer.com - um computador sempre ativo pelo povo, para o povo. Um que é
durável, fácil de reparar e distribuído como água da torneira para inteligência de
aprendizado de máquina.
O permacomputador é infraestrutura de propriedade comunitária otimizada em torno de quatro valores:
VERDADE - Primeiros princípios, matemática & ciência, código aberto distribuído livremente
LIBERDADE - Parcerias voluntárias, liberdade da tirania e controle corporativo
HARMONIA - Desperdício mínimo, sistemas auto-renováveis com diversas conexões prósperas
AMOR - Seja você mesmo sem ferir os outros, cooperação através da lei natural
Este software contribui para essa visão ao permitir a execução de código em mais de 42
linguagens de programação através de uma interface unificada, acessível a todos. Código são
sementes que brotam em qualquer tecnologia abandonada.
Saiba mais: https://www.permacomputer.com
Qualquer pessoa é livre para copiar, modificar, publicar, usar, compilar, vender ou distribuir
este software, seja em forma de código-fonte ou como binário compilado, para qualquer propósito,
comercial ou não comercial, e por qualquer meio.
SEM GARANTIA. O SOFTWARE É FORNECIDO "COMO ESTÁ" SEM GARANTIA DE QUALQUER TIPO.
Dito isso, a camada de membrana digital do nosso permacomputador executa continuamente testes
unitários, de integração e funcionais em todo o seu próprio software - com nosso permacomputador
monitorando a si mesmo, reparando a si mesmo, com orientação humana mínima no ciclo.
Nossos agentes fazem o seu melhor.
Copyright 2025 TimeHexOn & foxhop & russell@unturf
https://www.timehexon.com
https://www.foxhop.net
https://www.unturf.com/software