unsandbox.com
Anonymous remote code, compile, & execution API for humans & machine learning agents.
Docs 📚 View Pricing →
F#
UN CLI
un.fs
Usage
# Run this implementation to execute a Python script
dotnet fsi cli/inception/un.fs test/fib.py test/fib.py
Source Code 📄
// 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
// un.fs - Unsandbox CLI Client (F# Implementation)
// Compile: fsharpc un.fs
// Run: mono un.exe [options] <source_file>
// Requires: UNSANDBOX_API_KEY environment variable
open System
open System.IO
open System.Net
open System.Text
let apiBase = "https://api.unsandbox.com"
let blue = "\x1B[34m"
let red = "\x1B[31m"
let green = "\x1B[32m"
let yellow = "\x1B[33m"
let reset = "\x1B[0m"
let extMap =
Map.ofList [
(".py", "python"); (".js", "javascript"); (".ts", "typescript")
(".rb", "ruby"); (".php", "php"); (".pl", "perl"); (".lua", "lua")
(".sh", "bash"); (".go", "go"); (".rs", "rust"); (".c", "c")
(".cpp", "cpp"); (".cc", "cpp"); (".cxx", "cpp")
(".java", "java"); (".kt", "kotlin"); (".cs", "csharp"); (".fs", "fsharp")
(".hs", "haskell"); (".ml", "ocaml"); (".clj", "clojure"); (".scm", "scheme")
(".lisp", "commonlisp"); (".erl", "erlang"); (".ex", "elixir"); (".exs", "elixir")
(".jl", "julia"); (".r", "r"); (".R", "r"); (".cr", "crystal")
(".d", "d"); (".nim", "nim"); (".zig", "zig"); (".v", "v")
(".dart", "dart"); (".groovy", "groovy"); (".scala", "scala")
(".f90", "fortran"); (".f95", "fortran"); (".cob", "cobol")
(".pro", "prolog"); (".forth", "forth"); (".4th", "forth")
(".tcl", "tcl"); (".raku", "raku"); (".m", "objc")
]
type Args = {
mutable Command: string option
mutable SourceFile: string option
mutable ApiKey: string option
mutable Network: string option
mutable Vcpu: int
Env: ResizeArray<string>
Files: ResizeArray<string>
mutable Artifacts: bool
mutable OutputDir: string option
mutable SessionList: bool
mutable SessionShell: string option
mutable SessionKill: string option
mutable ServiceList: bool
mutable ServiceName: string option
mutable ServicePorts: string option
mutable ServiceType: string option
mutable ServiceBootstrap: string option
mutable ServiceInfo: string option
mutable ServiceLogs: string option
mutable ServiceTail: string option
mutable ServiceSleep: string option
mutable ServiceWake: string option
mutable ServiceDestroy: string option
}
let getApiKey (argsKey: string option) =
let key = match argsKey with | Some k -> k | None -> Environment.GetEnvironmentVariable("UNSANDBOX_API_KEY")
if String.IsNullOrEmpty(key) then
eprintfn "%sError: UNSANDBOX_API_KEY not set%s" red reset
exit 1
key
let detectLanguage (filename: string) =
let dotIndex = filename.LastIndexOf('.')
if dotIndex = -1 then
failwith "Cannot detect language: no file extension"
let ext = filename.Substring(dotIndex).ToLower()
match Map.tryFind ext extMap with
| Some lang -> lang
| None -> failwithf "Unsupported file extension: %s" ext
let jsonEscape (s: string) =
let sb = StringBuilder("\"")
for c in s do
match c with
| '"' -> sb.Append("\\\"") |> ignore
| '\\' -> sb.Append("\\\\") |> ignore
| '\n' -> sb.Append("\\n") |> ignore
| '\r' -> sb.Append("\\r") |> ignore
| '\t' -> sb.Append("\\t") |> ignore
| _ -> sb.Append(c) |> ignore
sb.Append("\"") |> ignore
sb.ToString()
let rec toJson (obj: obj) =
match obj with
| null -> "null"
| :? string as s -> jsonEscape s
| :? int as i -> i.ToString()
| :? float as f -> f.ToString()
| :? bool as b -> b.ToString().ToLower()
| :? Map<string, obj> as m ->
let entries = m |> Map.toSeq |> Seq.map (fun (k, v) -> sprintf "\"%s\":%s" k (toJson v)) |> String.concat ","
sprintf "{%s}" entries
| :? ResizeArray<obj> as lst ->
let items = lst |> Seq.map toJson |> String.concat ","
sprintf "[%s]" items
| :? (string * obj) list as lst ->
let entries = lst |> List.map (fun (k, v) -> sprintf "\"%s\":%s" k (toJson v)) |> String.concat ","
sprintf "{%s}" entries
| _ -> jsonEscape (obj.ToString())
let extractJsonValue (json: string) (key: string) =
let pattern = sprintf "\"%s\":" key
let startIndex = json.IndexOf(pattern)
if startIndex = -1 then None
else
let mutable idx = startIndex + pattern.Length
while idx < json.Length && Char.IsWhiteSpace(json.[idx]) do
idx <- idx + 1
if json.[idx] = '"' then
idx <- idx + 1
let sb = StringBuilder()
let mutable escaped = false
let mutable found = false
let mutable i = idx
while i < json.Length && not found do
let c = json.[i]
if escaped then
match c with
| 'n' -> sb.Append('\n') |> ignore
| 'r' -> sb.Append('\r') |> ignore
| 't' -> sb.Append('\t') |> ignore
| '"' -> sb.Append('"') |> ignore
| '\\' -> sb.Append('\\') |> ignore
| _ -> sb.Append(c) |> ignore
escaped <- false
else if c = '\\' then
escaped <- true
else if c = '"' then
found <- true
else
sb.Append(c) |> ignore
i <- i + 1
Some (sb.ToString())
else
let sb = StringBuilder()
let mutable i = idx
while i < json.Length && (Char.IsDigit(json.[i]) || json.[i] = '-') do
sb.Append(json.[i]) |> ignore
i <- i + 1
Some (sb.ToString())
let parseJson (json: string) =
let trimmed = json.Trim()
if not (trimmed.StartsWith("{")) then Map.empty
else
let result = ResizeArray<string * obj>()
let mutable i = 1
while i < trimmed.Length do
while i < trimmed.Length && Char.IsWhiteSpace(trimmed.[i]) do i <- i + 1
if trimmed.[i] = '}' then i <- trimmed.Length
elif trimmed.[i] = '"' then
let keyStart = i + 1
i <- i + 1
while i < trimmed.Length && trimmed.[i] <> '"' do
if trimmed.[i] = '\\' then i <- i + 1
i <- i + 1
let key = trimmed.Substring(keyStart, i - keyStart).Replace("\\\"", "\"").Replace("\\\\", "\\")
i <- i + 1
while i < trimmed.Length && (Char.IsWhiteSpace(trimmed.[i]) || trimmed.[i] = ':') do i <- i + 1
let value = extractJsonValue trimmed key
match value with
| Some v -> result.Add((key, box v))
| None -> ()
while i < trimmed.Length && (Char.IsWhiteSpace(trimmed.[i]) || trimmed.[i] = ',' || trimmed.[i] = '"' || Char.IsLetterOrDigit(trimmed.[i]) || trimmed.[i] = '\\') do i <- i + 1
else
i <- i + 1
result |> Seq.map (fun (k, v) -> k, v) |> Map.ofSeq
let apiRequest (endpoint: string) (method: string) (data: (string * obj) list option) (apiKey: string) =
ServicePointManager.SecurityProtocol <- SecurityProtocolType.Tls12 ||| SecurityProtocolType.Tls11 ||| SecurityProtocolType.Tls
let request = WebRequest.Create(apiBase + endpoint) :?> HttpWebRequest
request.Method <- method
request.ContentType <- "application/json"
request.Headers.Add("Authorization", sprintf "Bearer %s" apiKey)
request.Timeout <- 300000
match data with
| Some d ->
let json = toJson (box d)
let bytes = Encoding.UTF8.GetBytes(json)
request.ContentLength <- int64 bytes.Length
use stream = request.GetRequestStream()
stream.Write(bytes, 0, bytes.Length)
| None -> ()
try
use response = request.GetResponse() :?> HttpWebResponse
if response.StatusCode <> HttpStatusCode.OK then
failwithf "HTTP %A" response.StatusCode
use reader = new StreamReader(response.GetResponseStream())
let responseText = reader.ReadToEnd()
parseJson responseText
with
| :? WebException as ex ->
let errorMsg =
if ex.Response <> null then
use reader = new StreamReader(ex.Response.GetResponseStream())
reader.ReadToEnd()
else
ex.Message
failwithf "HTTP error - %s" errorMsg
let cmdExecute (args: Args) =
let apiKey = getApiKey args.ApiKey
let code = File.ReadAllText(args.SourceFile.Value)
let language = detectLanguage args.SourceFile.Value
let mutable payload = [("language", box language); ("code", box code)]
if args.Env.Count > 0 then
let envVars = args.Env |> Seq.choose (fun e ->
let parts = e.Split([|'='|], 2)
if parts.Length = 2 then Some (parts.[0], box parts.[1]) else None
) |> Seq.toList
if not (List.isEmpty envVars) then
payload <- payload @ [("env", box envVars)]
if args.Files.Count > 0 then
let inputFiles = args.Files |> Seq.map (fun filepath ->
let content = File.ReadAllBytes(filepath)
[("filename", box (Path.GetFileName(filepath))); ("content_base64", box (Convert.ToBase64String(content)))]
) |> Seq.toList
payload <- payload @ [("input_files", box inputFiles)]
if args.Artifacts then
payload <- payload @ [("return_artifacts", box true)]
if args.Network.IsSome then
payload <- payload @ [("network", box args.Network.Value)]
if args.Vcpu > 0 then
payload <- payload @ [("vcpu", box args.Vcpu)]
let result = apiRequest "/execute" "POST" (Some payload) apiKey
match result.TryFind "stdout" with
| Some stdout when not (String.IsNullOrEmpty(stdout.ToString())) ->
printf "%s%s%s" blue (stdout.ToString()) reset
| _ -> ()
match result.TryFind "stderr" with
| Some stderr when not (String.IsNullOrEmpty(stderr.ToString())) ->
eprintf "%s%s%s" red (stderr.ToString()) reset
| _ -> ()
if args.Artifacts && result.ContainsKey("artifacts") then
let outDir = match args.OutputDir with | Some d -> d | None -> "."
Directory.CreateDirectory(outDir) |> ignore
eprintfn "%sSaved artifacts to %s%s" green outDir reset
let exitCode = match result.TryFind "exit_code" with | Some ec -> int (ec.ToString()) | None -> 0
exit exitCode
let cmdSession (args: Args) =
let apiKey = getApiKey args.ApiKey
if args.SessionList then
let result = apiRequest "/sessions" "GET" None apiKey
printfn "%-40s %-10s %-10s %s" "ID" "Shell" "Status" "Created"
printfn "No sessions (list parsing not implemented)"
elif args.SessionKill.IsSome then
let result = apiRequest (sprintf "/sessions/%s" args.SessionKill.Value) "DELETE" None apiKey
printfn "%sSession terminated: %s%s" green args.SessionKill.Value reset
else
let mutable payload = [("shell", box (match args.SessionShell with | Some s -> s | None -> "bash"))]
if args.Network.IsSome then
payload <- payload @ [("network", box args.Network.Value)]
if args.Vcpu > 0 then
payload <- payload @ [("vcpu", box args.Vcpu)]
printfn "%sCreating session...%s" yellow reset
let result = apiRequest "/sessions" "POST" (Some payload) apiKey
match result.TryFind "id" with
| Some id -> printfn "%sSession created: %s%s" green (id.ToString()) reset
| None -> printfn "%sSession created%s" green reset
printfn "%s(Interactive sessions require WebSocket - use un2 for full support)%s" yellow reset
let cmdService (args: Args) =
let apiKey = getApiKey args.ApiKey
if args.ServiceList then
let result = apiRequest "/services" "GET" None apiKey
printfn "%-20s %-15s %-10s %-15s %s" "ID" "Name" "Status" "Ports" "Domains"
printfn "No services (list parsing not implemented)"
elif args.ServiceInfo.IsSome then
let result = apiRequest (sprintf "/services/%s" args.ServiceInfo.Value) "GET" None apiKey
printfn "%s" (toJson (box result))
elif args.ServiceLogs.IsSome then
let result = apiRequest (sprintf "/services/%s/logs" args.ServiceLogs.Value) "GET" None apiKey
match result.TryFind "logs" with
| Some logs -> printfn "%s" (logs.ToString())
| None -> ()
elif args.ServiceTail.IsSome then
let result = apiRequest (sprintf "/services/%s/logs?lines=9000" args.ServiceTail.Value) "GET" None apiKey
match result.TryFind "logs" with
| Some logs -> printfn "%s" (logs.ToString())
| None -> ()
elif args.ServiceSleep.IsSome then
let result = apiRequest (sprintf "/services/%s/sleep" args.ServiceSleep.Value) "POST" None apiKey
printfn "%sService sleeping: %s%s" green args.ServiceSleep.Value reset
elif args.ServiceWake.IsSome then
let result = apiRequest (sprintf "/services/%s/wake" args.ServiceWake.Value) "POST" None apiKey
printfn "%sService waking: %s%s" green args.ServiceWake.Value reset
elif args.ServiceDestroy.IsSome then
let result = apiRequest (sprintf "/services/%s" args.ServiceDestroy.Value) "DELETE" None apiKey
printfn "%sService destroyed: %s%s" green args.ServiceDestroy.Value reset
elif args.ServiceName.IsSome then
let mutable payload = [("name", box args.ServiceName.Value)]
if args.ServicePorts.IsSome then
let ports = args.ServicePorts.Value.Split(',') |> Array.map (fun p -> box (int (p.Trim())))
payload <- payload @ [("ports", box ports)]
if args.ServiceType.IsSome then
payload <- payload @ [("service_type", box args.ServiceType.Value)]
if args.ServiceBootstrap.IsSome then
payload <- payload @ [("bootstrap", box args.ServiceBootstrap.Value)]
if args.Network.IsSome then
payload <- payload @ [("network", box args.Network.Value)]
if args.Vcpu > 0 then
payload <- payload @ [("vcpu", box args.Vcpu)]
let result = apiRequest "/services" "POST" (Some payload) apiKey
match result.TryFind "id" with
| Some id -> printfn "%sService created: %s%s" green (id.ToString()) reset
| None -> printfn "%sService created%s" green reset
match result.TryFind "name" with
| Some name -> printfn "Name: %s" (name.ToString())
| None -> ()
match result.TryFind "url" with
| Some url -> printfn "URL: %s" (url.ToString())
| None -> ()
else
eprintfn "%sError: Specify --name to create a service, or use --list, --info, etc.%s" red reset
exit 1
let parseArgs (argv: string[]) =
let args = {
Command = None
SourceFile = None
ApiKey = None
Network = None
Vcpu = 0
Env = ResizeArray<string>()
Files = ResizeArray<string>()
Artifacts = false
OutputDir = None
SessionList = false
SessionShell = None
SessionKill = None
ServiceList = false
ServiceName = None
ServicePorts = None
ServiceType = None
ServiceBootstrap = None
ServiceInfo = None
ServiceLogs = None
ServiceTail = None
ServiceSleep = None
ServiceWake = None
ServiceDestroy = None
}
let mutable i = 0
while i < argv.Length do
match argv.[i] with
| "session" -> args.Command <- Some "session"
| "service" -> args.Command <- Some "service"
| "-k" | "--api-key" -> i <- i + 1; args.ApiKey <- Some argv.[i]
| "-n" | "--network" -> i <- i + 1; args.Network <- Some argv.[i]
| "-v" | "--vcpu" -> i <- i + 1; args.Vcpu <- int argv.[i]
| "-e" | "--env" -> i <- i + 1; args.Env.Add(argv.[i])
| "-f" | "--files" -> i <- i + 1; args.Files.Add(argv.[i])
| "-a" | "--artifacts" -> args.Artifacts <- true
| "-o" | "--output-dir" -> i <- i + 1; args.OutputDir <- Some argv.[i]
| "-l" | "--list" ->
match args.Command with
| Some "session" -> args.SessionList <- true
| Some "service" -> args.ServiceList <- true
| _ -> ()
| "-s" | "--shell" -> i <- i + 1; args.SessionShell <- Some argv.[i]
| "--kill" -> i <- i + 1; args.SessionKill <- Some argv.[i]
| "--name" -> i <- i + 1; args.ServiceName <- Some argv.[i]
| "--ports" -> i <- i + 1; args.ServicePorts <- Some argv.[i]
| "--type" -> i <- i + 1; args.ServiceType <- Some argv.[i]
| "--bootstrap" -> i <- i + 1; args.ServiceBootstrap <- Some argv.[i]
| "--info" -> i <- i + 1; args.ServiceInfo <- Some argv.[i]
| "--logs" -> i <- i + 1; args.ServiceLogs <- Some argv.[i]
| "--tail" -> i <- i + 1; args.ServiceTail <- Some argv.[i]
| "--sleep" -> i <- i + 1; args.ServiceSleep <- Some argv.[i]
| "--wake" -> i <- i + 1; args.ServiceWake <- Some argv.[i]
| "--destroy" -> i <- i + 1; args.ServiceDestroy <- Some argv.[i]
| arg when not (arg.StartsWith("-")) -> args.SourceFile <- Some arg
| _ -> ()
i <- i + 1
args
let printHelp () =
printfn "Usage: un [options] <source_file>"
printfn " un session [options]"
printfn " un service [options]"
printfn ""
printfn "Execute options:"
printfn " -e KEY=VALUE Set environment variable"
printfn " -f FILE Add input file"
printfn " -a Return artifacts"
printfn " -o DIR Output directory for artifacts"
printfn " -n MODE Network mode (zerotrust/semitrusted)"
printfn " -v N vCPU count (1-8)"
printfn " -k KEY API key"
printfn ""
printfn "Session options:"
printfn " --list List active sessions"
printfn " --shell NAME Shell/REPL to use"
printfn " --kill ID Terminate session"
printfn ""
printfn "Service options:"
printfn " --list List services"
printfn " --name NAME Service name"
printfn " --ports PORTS Comma-separated ports"
printfn " --type TYPE Service type (minecraft/mumble/teamspeak/source/tcp/udp)"
printfn " --bootstrap CMD Bootstrap command"
printfn " --info ID Get service details"
printfn " --logs ID Get all logs"
printfn " --tail ID Get last 9000 lines"
printfn " --sleep ID Freeze service"
printfn " --wake ID Unfreeze service"
printfn " --destroy ID Destroy service"
[<EntryPoint>]
let main argv =
try
let args = parseArgs argv
match args.Command with
| Some "session" -> cmdSession args; 0
| Some "service" -> cmdService args; 0
| _ ->
match args.SourceFile with
| Some _ -> cmdExecute args; 0
| None -> printHelp(); 1
with ex ->
eprintfn "%sError: %s%s" red ex.Message reset
1
License
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
that 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 all 42
programming languages through a unified interface, accessible to everyone. 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 its own software - with our permacomputer
monitoring itself, repairing itself, with minimal human guidance in the loop.
Our agents do their best.
Copyright 2025 TimeHexOn & foxhop & russell@unturf
https://www.timehexon.com
https://www.foxhop.net
https://www.unturf.com/software