unsandboxunsandbox.com
Anonymous remote code, compile, & execution API for humans & machine learning agents.
Docs 📚 View Pricing →
Elixir
UN CLI
un.ex
Usage
# Run this implementation to execute a Python script
elixir cli/inception/un.ex test/fib.py
Integration Quickstart ⚡
Add unsandbox superpowers to your existing Elixir app:
1
Download
curl -O https://git.unturf.com/engineering/unturf/un-inception/-/raw/main/un.ex
2
Set API Keys
export UNSANDBOX_PUBLIC_KEY="unsb-pk-xxxx-xxxx-xxxx-xxxx"
export UNSANDBOX_SECRET_KEY="unsb-sk-xxxx-xxxx-xxxx-xxxx"
3
Hello World
# In your Elixir app:
result = Un.execute_code("elixir", ~s(IO.puts "Hello!"))
IO.puts(result["stdout"]) # Hello!
What you can do
execute_code(lang, code)
Run code in 42+ languages
create_session()
Interactive shells & REPLs
create_service()
Deploy persistent HTTPS apps
snapshot_session()
Save & restore container state
Source Code 📄
#!/usr/bin/env elixir
# 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.ex - Unsandbox CLI client in Elixir
#
# 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.ex
# export UNSANDBOX_API_KEY="your_key_here"
# ./un.ex [options] <source_file>
# ./un.ex session [options]
# ./un.ex service [options]
#
# Uses curl for HTTP (no external dependencies)
defmodule Un do
@blue "\e[34m"
@red "\e[31m"
@green "\e[32m"
@yellow "\e[33m"
@reset "\e[0m"
@portal_base "https://unsandbox.com"
@ext_map %{
".ex" => "elixir", ".exs" => "elixir", ".erl" => "erlang",
".py" => "python", ".js" => "javascript", ".ts" => "typescript",
".rb" => "ruby", ".go" => "go", ".rs" => "rust", ".c" => "c",
".cpp" => "cpp", ".cc" => "cpp", ".java" => "java", ".kt" => "kotlin",
".cs" => "csharp", ".fs" => "fsharp", ".hs" => "haskell",
".ml" => "ocaml", ".clj" => "clojure", ".scm" => "scheme",
".lisp" => "commonlisp", ".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",
".f90" => "fortran", ".cob" => "cobol", ".pro" => "prolog",
".forth" => "forth", ".tcl" => "tcl", ".raku" => "raku"
}
def main([]), do: print_usage()
def main(["session" | rest]), do: session_command(rest)
def main(["service" | rest]), do: service_command(rest)
def main(["snapshot" | rest]), do: snapshot_command(rest)
def main(["key" | rest]), do: key_command(rest)
def main(args), do: execute_command(args)
defp print_usage do
IO.puts("Usage: un.ex [options] <source_file>")
IO.puts(" un.ex session [options]")
IO.puts(" un.ex service [options]")
IO.puts(" un.ex service env <action> <service_id>")
IO.puts(" un.ex snapshot [options]")
IO.puts(" un.ex key [--extend]")
IO.puts("")
IO.puts("Service options: --name, --ports, --bootstrap, -e KEY=VALUE, --env-file FILE")
IO.puts("Service env commands: status, set, export, delete")
System.halt(1)
end
# Execute command
defp execute_command(args) do
api_key = get_api_key()
{file, opts} = parse_exec_args(args)
if is_nil(file) do
IO.puts(:stderr, "Error: No source file specified")
System.halt(1)
end
ext = Path.extname(file)
language = Map.get(@ext_map, ext)
if is_nil(language) do
IO.puts(:stderr, "Error: Unknown extension: #{ext}")
System.halt(1)
end
case File.read(file) do
{:ok, code} ->
json = build_execute_json(language, code, opts)
response = curl_post(api_key, "/execute", json)
IO.puts(response)
{:error, reason} ->
IO.puts(:stderr, "Error reading file: #{reason}")
System.halt(1)
end
end
# Session command
defp session_command(["--list" | _]) do
api_key = get_api_key()
response = curl_get(api_key, "/sessions")
IO.puts(response)
end
defp session_command(["--kill", session_id | _]) do
api_key = get_api_key()
curl_delete(api_key, "/sessions/#{session_id}")
IO.puts("#{@green}Session terminated: #{session_id}#{@reset}")
end
defp session_command(["--snapshot", session_id | rest]) do
api_key = get_api_key()
name = get_opt(rest, "--snapshot-name", nil, nil)
hot = "--hot" in rest
name_json = if name, do: ",\"name\":\"#{escape_json(name)}\"", else: ""
hot_json = if hot, do: ",\"hot\":true", else: ""
json = "{#{String.slice(name_json <> hot_json, 1..-1)}}"
response = curl_post(api_key, "/sessions/#{session_id}/snapshot", json)
IO.puts("#{@green}Snapshot created#{@reset}")
IO.puts(response)
end
defp session_command(["--restore", snapshot_id | _rest]) do
# --restore takes snapshot ID directly, calls /snapshots/:id/restore
api_key = get_api_key()
response = curl_post(api_key, "/snapshots/#{snapshot_id}/restore", "{}")
IO.puts("#{@green}Session restored from snapshot#{@reset}")
IO.puts(response)
end
defp session_command(args) do
validate_session_args(args)
api_key = get_api_key()
shell = get_opt(args, "--shell", "-s", "bash")
network = get_opt(args, "-n", nil, nil)
vcpu = get_opt(args, "-v", nil, nil)
input_files = get_all_opts(args, "-f")
network_json = if network, do: ",\"network\":\"#{network}\"", else: ""
vcpu_json = if vcpu, do: ",\"vcpu\":#{vcpu}", else: ""
input_files_json = build_input_files_json(input_files)
json = "{\"shell\":\"#{shell}\"#{network_json}#{vcpu_json}#{input_files_json}}"
response = curl_post(api_key, "/sessions", json)
IO.puts("#{@yellow}Session created (WebSocket required)#{@reset}")
IO.puts(response)
end
defp validate_session_args([]), do: :ok
defp validate_session_args(["--shell", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["-s", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["-f", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["-n", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["-v", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["--snapshot", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["--restore", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["--from", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["--snapshot-name", _ | rest]), do: validate_session_args(rest)
defp validate_session_args(["--hot" | rest]), do: validate_session_args(rest)
defp validate_session_args([arg | _]) do
if String.starts_with?(arg, "-") do
IO.puts(:stderr, "Unknown option: #{arg}")
IO.puts(:stderr, "Usage: un.ex session [options]")
System.halt(1)
else
validate_session_args([])
end
end
# Service command
defp service_command(["--list" | _]) do
api_key = get_api_key()
response = curl_get(api_key, "/services")
IO.puts(response)
end
defp service_command(["--info", service_id | _]) do
api_key = get_api_key()
response = curl_get(api_key, "/services/#{service_id}")
IO.puts(response)
end
defp service_command(["--logs", service_id | _]) do
api_key = get_api_key()
response = curl_get(api_key, "/services/#{service_id}/logs")
IO.puts(response)
end
defp service_command(["--freeze", service_id | _]) do
api_key = get_api_key()
curl_post(api_key, "/services/#{service_id}/sleep", "{}")
IO.puts("#{@green}Service sleeping: #{service_id}#{@reset}")
end
defp service_command(["--unfreeze", service_id | _]) do
api_key = get_api_key()
curl_post(api_key, "/services/#{service_id}/wake", "{}")
IO.puts("#{@green}Service waking: #{service_id}#{@reset}")
end
defp service_command(["--destroy", service_id | _]) do
api_key = get_api_key()
curl_delete(api_key, "/services/#{service_id}")
IO.puts("#{@green}Service destroyed: #{service_id}#{@reset}")
end
defp service_command(["--resize", service_id | rest]) do
vcpu = get_opt(rest, "--vcpu", "-v", nil)
if is_nil(vcpu) do
IO.puts(:stderr, "#{@red}Error: --resize requires --vcpu N#{@reset}")
System.halt(1)
end
vcpu_int = String.to_integer(vcpu)
if vcpu_int < 1 or vcpu_int > 8 do
IO.puts(:stderr, "#{@red}Error: --vcpu must be between 1 and 8#{@reset}")
System.halt(1)
end
api_key = get_api_key()
json = "{\"vcpu\":#{vcpu_int}}"
curl_patch(api_key, "/services/#{service_id}", json)
ram = vcpu_int * 2
IO.puts("#{@green}Service resized to #{vcpu_int} vCPU, #{ram} GB RAM#{@reset}")
end
defp service_command(["--snapshot", service_id | rest]) do
api_key = get_api_key()
name = get_opt(rest, "--snapshot-name", nil, nil)
hot = "--hot" in rest
name_json = if name, do: ",\"name\":\"#{escape_json(name)}\"", else: ""
hot_json = if hot, do: ",\"hot\":true", else: ""
json = "{#{String.slice(name_json <> hot_json, 1..-1)}}"
response = curl_post(api_key, "/services/#{service_id}/snapshot", json)
IO.puts("#{@green}Snapshot created#{@reset}")
IO.puts(response)
end
defp service_command(["--restore", snapshot_id | _rest]) do
# --restore takes snapshot ID directly, calls /snapshots/:id/restore
api_key = get_api_key()
response = curl_post(api_key, "/snapshots/#{snapshot_id}/restore", "{}")
IO.puts("#{@green}Service restored from snapshot#{@reset}")
IO.puts(response)
end
defp service_command(["--execute", service_id, "--command", command | _]) do
api_key = get_api_key()
json = "{\"command\":\"#{escape_json(command)}\"}"
response = curl_post(api_key, "/services/#{service_id}/execute", json)
case extract_json_value(response, "stdout") do
nil -> :ok
stdout -> IO.write("#{@blue}#{stdout}#{@reset}")
end
end
defp service_command(["--dump-bootstrap", service_id, file | _]) do
api_key = get_api_key()
IO.puts(:stderr, "Fetching bootstrap script from #{service_id}...")
json = "{\"command\":\"cat /tmp/bootstrap.sh\"}"
response = curl_post(api_key, "/services/#{service_id}/execute", json)
case extract_json_value(response, "stdout") do
nil ->
IO.puts(:stderr, "#{@red}Error: Failed to fetch bootstrap (service not running or no bootstrap file)#{@reset}")
System.halt(1)
script ->
File.write!(file, script)
System.cmd("chmod", ["755", file])
IO.puts("Bootstrap saved to #{file}")
end
end
defp service_command(["--dump-bootstrap", service_id | _]) do
api_key = get_api_key()
IO.puts(:stderr, "Fetching bootstrap script from #{service_id}...")
json = "{\"command\":\"cat /tmp/bootstrap.sh\"}"
response = curl_post(api_key, "/services/#{service_id}/execute", json)
case extract_json_value(response, "stdout") do
nil ->
IO.puts(:stderr, "#{@red}Error: Failed to fetch bootstrap (service not running or no bootstrap file)#{@reset}")
System.halt(1)
script ->
IO.write(script)
end
end
defp service_command(["env", "status", service_id | _]) do
response = service_env_status(service_id)
has_vault = extract_json_value(response, "has_vault") == "true"
if has_vault do
IO.puts("#{@green}Vault: configured#{@reset}")
env_count = extract_json_value(response, "env_count")
if env_count, do: IO.puts("Variables: #{env_count}")
updated_at = extract_json_value(response, "updated_at")
if updated_at, do: IO.puts("Updated: #{updated_at}")
else
IO.puts("#{@yellow}Vault: not configured#{@reset}")
end
end
defp service_command(["env", "set", service_id | rest]) do
envs = get_all_opts(rest, "-e")
env_file = get_opt(rest, "--env-file", nil, nil)
if Enum.empty?(envs) and is_nil(env_file) do
IO.puts(:stderr, "#{@red}Error: service env set requires -e or --env-file#{@reset}")
System.halt(1)
end
env_content = build_env_content(envs, env_file)
if service_env_set(service_id, env_content) do
IO.puts("#{@green}Vault updated for service #{service_id}#{@reset}")
else
IO.puts(:stderr, "#{@red}Error: Failed to update vault#{@reset}")
System.halt(1)
end
end
defp service_command(["env", "export", service_id | _]) do
response = service_env_export(service_id)
content = extract_json_value(response, "content")
if content, do: IO.write(content)
end
defp service_command(["env", "delete", service_id | _]) do
if service_env_delete(service_id) do
IO.puts("#{@green}Vault deleted for service #{service_id}#{@reset}")
else
IO.puts(:stderr, "#{@red}Error: Failed to delete vault#{@reset}")
System.halt(1)
end
end
defp service_command(["env" | _]) do
IO.puts(:stderr, "#{@red}Error: Usage: un.ex service env <status|set|export|delete> <service_id>#{@reset}")
System.halt(1)
end
defp service_command(args) do
name = get_opt(args, "--name", nil, nil)
if is_nil(name) do
IO.puts(:stderr, "Error: --name required to create service")
System.halt(1)
end
api_key = get_api_key()
ports = get_opt(args, "--ports", nil, nil)
bootstrap = get_opt(args, "--bootstrap", nil, nil)
bootstrap_file = get_opt(args, "--bootstrap-file", nil, nil)
network = get_opt(args, "-n", nil, nil)
vcpu = get_opt(args, "-v", nil, nil)
service_type = get_opt(args, "--type", nil, nil)
input_files = get_all_opts(args, "-f")
envs = get_all_opts(args, "-e")
env_file = get_opt(args, "--env-file", nil, nil)
ports_json = if ports, do: ",\"ports\":[#{ports}]", else: ""
bootstrap_json = if bootstrap, do: ",\"bootstrap\":\"#{escape_json(bootstrap)}\"", else: ""
bootstrap_content_json = if bootstrap_file do
case File.read(bootstrap_file) do
{:ok, content} -> ",\"bootstrap_content\":\"#{escape_json(content)}\""
{:error, _} ->
IO.puts(:stderr, "#{@red}Error: Bootstrap file not found: #{bootstrap_file}#{@reset}")
System.halt(1)
end
else
""
end
network_json = if network, do: ",\"network\":\"#{network}\"", else: ""
vcpu_json = if vcpu, do: ",\"vcpu\":#{vcpu}", else: ""
type_json = if service_type, do: ",\"service_type\":\"#{service_type}\"", else: ""
input_files_json = build_input_files_json(input_files)
json = "{\"name\":\"#{name}\"#{ports_json}#{bootstrap_json}#{bootstrap_content_json}#{network_json}#{vcpu_json}#{type_json}#{input_files_json}}"
response = curl_post(api_key, "/services", json)
IO.puts("#{@green}Service created#{@reset}")
IO.puts(response)
# Auto-set vault if env vars were provided
service_id = extract_json_value(response, "id")
if service_id and (not Enum.empty?(envs) or env_file) do
env_content = build_env_content(envs, env_file)
if String.length(env_content) > 0 do
if service_env_set(service_id, env_content) do
IO.puts("#{@green}Vault configured with environment variables#{@reset}")
else
IO.puts("#{@yellow}Warning: Failed to set vault#{@reset}")
end
end
end
end
# Snapshot command
defp snapshot_command(["--list" | _]) do
snapshot_command(["-l"])
end
defp snapshot_command(["-l" | _]) do
api_key = get_api_key()
response = curl_get(api_key, "/snapshots")
IO.puts(response)
end
defp snapshot_command(["--info", snapshot_id | _]) do
api_key = get_api_key()
response = curl_get(api_key, "/snapshots/#{snapshot_id}")
IO.puts(response)
end
defp snapshot_command(["--delete", snapshot_id | _]) do
api_key = get_api_key()
curl_delete(api_key, "/snapshots/#{snapshot_id}")
IO.puts("#{@green}Snapshot deleted: #{snapshot_id}#{@reset}")
end
defp snapshot_command(["--clone", snapshot_id | rest]) do
api_key = get_api_key()
clone_type = get_opt(rest, "--type", nil, nil)
name = get_opt(rest, "--name", nil, nil)
shell = get_opt(rest, "--shell", nil, nil)
ports = get_opt(rest, "--ports", nil, nil)
if !clone_type do
IO.puts(:stderr, "#{@red}Error: --type required (session or service)#{@reset}")
System.halt(1)
end
type_json = "\"type\":\"#{clone_type}\""
name_json = if name, do: ",\"name\":\"#{escape_json(name)}\"", else: ""
shell_json = if shell, do: ",\"shell\":\"#{shell}\"", else: ""
ports_json = if ports, do: ",\"ports\":[#{ports}]", else: ""
json = "{#{type_json}#{name_json}#{shell_json}#{ports_json}}"
response = curl_post(api_key, "/snapshots/#{snapshot_id}/clone", json)
IO.puts("#{@green}Created from snapshot#{@reset}")
IO.puts(response)
end
defp snapshot_command(_) do
IO.puts(:stderr, "Error: Use --list, --info ID, --delete ID, or --clone ID --type TYPE")
System.halt(1)
end
# Key command
defp key_command(args) do
api_key = get_api_key()
if "--extend" in args do
validate_key(api_key, extend: true)
else
validate_key(api_key, extend: false)
end
end
defp validate_key(api_key, extend: extend) do
json = "{}"
response = portal_curl_post(api_key, "/keys/validate", json)
# Try to use Jason if available, otherwise fall back to manual parsing
try do
case Jason.decode(response) do
{:ok, data} ->
display_key_info(data, extend)
{:error, _} ->
# Fallback if Jason is not available, parse manually
display_key_info_manual(response, extend)
end
rescue
UndefinedFunctionError ->
# If Jason module doesn't exist, use manual parsing
display_key_info_manual(response, extend)
end
end
defp display_key_info(data, extend) do
status = Map.get(data, "status")
public_key = Map.get(data, "public_key")
tier = Map.get(data, "tier")
expires_at = Map.get(data, "expires_at")
time_remaining = Map.get(data, "time_remaining")
rate_limit = Map.get(data, "rate_limit")
burst = Map.get(data, "burst")
concurrency = Map.get(data, "concurrency")
case status do
"valid" ->
IO.puts("#{@green}Valid#{@reset}")
IO.puts("Public Key: #{public_key}")
IO.puts("Tier: #{tier}")
IO.puts("Status: #{status}")
IO.puts("Expires: #{expires_at}")
if time_remaining, do: IO.puts("Time Remaining: #{time_remaining}")
if rate_limit, do: IO.puts("Rate Limit: #{rate_limit}")
if burst, do: IO.puts("Burst: #{burst}")
if concurrency, do: IO.puts("Concurrency: #{concurrency}")
if extend do
open_browser("#{@portal_base}/keys/extend?pk=#{public_key}")
end
"expired" ->
IO.puts("#{@red}Expired#{@reset}")
IO.puts("Public Key: #{public_key}")
IO.puts("Tier: #{tier}")
IO.puts("Expired: #{expires_at}")
IO.puts("#{@yellow}To renew: Visit #{@portal_base}/keys/extend#{@reset}")
if extend do
open_browser("#{@portal_base}/keys/extend?pk=#{public_key}")
end
"invalid" ->
IO.puts("#{@red}Invalid#{@reset}")
_ ->
IO.puts("#{@red}Unknown status: #{status}#{@reset}")
end
end
defp display_key_info_manual(response, extend) do
# Simple manual parsing for JSON response
status = extract_json_value(response, "status")
public_key = extract_json_value(response, "public_key")
tier = extract_json_value(response, "tier")
expires_at = extract_json_value(response, "expires_at")
time_remaining = extract_json_value(response, "time_remaining")
rate_limit = extract_json_value(response, "rate_limit")
burst = extract_json_value(response, "burst")
concurrency = extract_json_value(response, "concurrency")
case status do
"valid" ->
IO.puts("#{@green}Valid#{@reset}")
IO.puts("Public Key: #{public_key}")
IO.puts("Tier: #{tier}")
IO.puts("Status: #{status}")
IO.puts("Expires: #{expires_at}")
if time_remaining, do: IO.puts("Time Remaining: #{time_remaining}")
if rate_limit, do: IO.puts("Rate Limit: #{rate_limit}")
if burst, do: IO.puts("Burst: #{burst}")
if concurrency, do: IO.puts("Concurrency: #{concurrency}")
if extend do
open_browser("#{@portal_base}/keys/extend?pk=#{public_key}")
end
"expired" ->
IO.puts("#{@red}Expired#{@reset}")
IO.puts("Public Key: #{public_key}")
IO.puts("Tier: #{tier}")
IO.puts("Expired: #{expires_at}")
IO.puts("#{@yellow}To renew: Visit #{@portal_base}/keys/extend#{@reset}")
if extend do
open_browser("#{@portal_base}/keys/extend?pk=#{public_key}")
end
"invalid" ->
IO.puts("#{@red}Invalid#{@reset}")
_ ->
IO.puts("#{@red}Unknown status: #{status}#{@reset}")
IO.puts(response)
end
end
defp extract_json_value(json_str, key) do
case Regex.run(~r/"#{key}"\s*:\s*"([^"]*)"/, json_str) do
[_, value] -> value
_ -> nil
end
end
defp open_browser(url) do
IO.puts("#{@blue}Opening browser: #{url}#{@reset}")
case :os.type() do
{:unix, :linux} ->
System.cmd("xdg-open", [url], stderr_to_stdout: true)
{:unix, :darwin} ->
System.cmd("open", [url], stderr_to_stdout: true)
{:win32, _} ->
System.cmd("cmd", ["/c", "start", url], stderr_to_stdout: true)
_ ->
IO.puts("#{@yellow}Please open manually: #{url}#{@reset}")
end
end
# Helpers
defp get_api_keys do
public_key = System.get_env("UNSANDBOX_PUBLIC_KEY")
secret_key = System.get_env("UNSANDBOX_SECRET_KEY")
# Fall back to UNSANDBOX_API_KEY for backwards compatibility
api_key = System.get_env("UNSANDBOX_API_KEY")
cond do
public_key && secret_key ->
{public_key, secret_key}
api_key ->
{api_key, nil}
true ->
IO.puts(:stderr, "Error: UNSANDBOX_PUBLIC_KEY and UNSANDBOX_SECRET_KEY not set (or UNSANDBOX_API_KEY for backwards compat)")
System.halt(1)
end
end
defp get_api_key do
{public_key, _} = get_api_keys()
public_key
end
defp hmac_sha256(secret, message) do
:crypto.mac(:hmac, :sha256, secret, message)
|> Base.encode16(case: :lower)
end
defp make_signature(secret_key, timestamp, method, path, body) do
message = "#{timestamp}:#{method}:#{path}:#{body}"
hmac_sha256(secret_key, message)
end
defp escape_json(s) do
s
|> String.replace("\\", "\\\\")
|> String.replace("\"", "\\\"")
|> String.replace("\n", "\\n")
|> String.replace("\r", "\\r")
|> String.replace("\t", "\\t")
end
defp read_and_base64(filepath) do
case File.read(filepath) do
{:ok, content} -> Base.encode64(content)
{:error, _} -> ""
end
end
defp build_input_files_json([]), do: ""
defp build_input_files_json(files) do
file_jsons = files
|> Enum.map(fn f ->
b64 = read_and_base64(f)
basename = Path.basename(f)
"{\"filename\":\"#{escape_json(basename)}\",\"content\":\"#{b64}\"}"
end)
|> Enum.join(",")
",\"input_files\":[#{file_jsons}]"
end
defp build_execute_json(language, code, _opts) do
"{\"language\":\"#{language}\",\"code\":\"#{escape_json(code)}\"}"
end
defp curl_post(api_key, endpoint, json) do
tmp_file = "/tmp/un_ex_#{:rand.uniform(999999)}.json"
File.write!(tmp_file, json)
{public_key, secret_key} = get_api_keys()
headers = build_auth_headers(public_key, secret_key, "POST", endpoint, json)
args = [
"-s", "-X", "POST",
"https://api.unsandbox.com#{endpoint}",
"-H", "Content-Type: application/json"
] ++ headers ++ ["-d", "@#{tmp_file}"]
{output, _exit} = System.cmd("curl", args, stderr_to_stdout: true)
File.rm(tmp_file)
check_clock_drift(output)
output
end
defp build_auth_headers(public_key, secret_key, method, path, body) do
if secret_key do
timestamp = System.system_time(:second) |> Integer.to_string()
signature = make_signature(secret_key, timestamp, method, path, body)
[
"-H", "Authorization: Bearer #{public_key}",
"-H", "X-Timestamp: #{timestamp}",
"-H", "X-Signature: #{signature}"
]
else
# Backwards compatibility: use simple bearer token
["-H", "Authorization: Bearer #{public_key}"]
end
end
defp portal_curl_post(api_key, endpoint, json) do
tmp_file = "/tmp/un_ex_#{:rand.uniform(999999)}.json"
File.write!(tmp_file, json)
{public_key, secret_key} = get_api_keys()
headers = build_auth_headers(public_key, secret_key, "POST", endpoint, json)
args = [
"-s", "-X", "POST",
"#{@portal_base}#{endpoint}",
"-H", "Content-Type: application/json"
] ++ headers ++ ["-d", "@#{tmp_file}"]
{output, _exit} = System.cmd("curl", args, stderr_to_stdout: true)
File.rm(tmp_file)
check_clock_drift(output)
output
end
defp curl_get(api_key, endpoint) do
{public_key, secret_key} = get_api_keys()
headers = build_auth_headers(public_key, secret_key, "GET", endpoint, "")
args = [
"-s",
"https://api.unsandbox.com#{endpoint}"
] ++ headers
{output, _exit} = System.cmd("curl", args, stderr_to_stdout: true)
check_clock_drift(output)
output
end
defp curl_delete(api_key, endpoint) do
{public_key, secret_key} = get_api_keys()
headers = build_auth_headers(public_key, secret_key, "DELETE", endpoint, "")
args = [
"-s", "-X", "DELETE",
"https://api.unsandbox.com#{endpoint}"
] ++ headers
{output, _exit} = System.cmd("curl", args, stderr_to_stdout: true)
check_clock_drift(output)
output
end
defp curl_patch(api_key, endpoint, json) do
tmp_file = "/tmp/un_ex_#{:rand.uniform(999999)}.json"
File.write!(tmp_file, json)
{public_key, secret_key} = get_api_keys()
headers = build_auth_headers(public_key, secret_key, "PATCH", endpoint, json)
args = [
"-s", "-X", "PATCH",
"https://api.unsandbox.com#{endpoint}",
"-H", "Content-Type: application/json"
] ++ headers ++ ["-d", "@#{tmp_file}"]
{output, _exit} = System.cmd("curl", args, stderr_to_stdout: true)
File.rm(tmp_file)
check_clock_drift(output)
output
end
defp curl_put_text(endpoint, body) do
tmp_file = "/tmp/un_ex_#{:rand.uniform(999999)}.txt"
File.write!(tmp_file, body)
{public_key, secret_key} = get_api_keys()
headers = build_auth_headers(public_key, secret_key, "PUT", endpoint, body)
args = [
"-s", "-o", "/dev/null", "-w", "%{http_code}",
"-X", "PUT",
"https://api.unsandbox.com#{endpoint}",
"-H", "Content-Type: text/plain"
] ++ headers ++ ["-d", "@#{tmp_file}"]
{output, _exit} = System.cmd("curl", args, stderr_to_stdout: true)
File.rm(tmp_file)
status_code = String.trim(output) |> String.to_integer()
status_code >= 200 and status_code < 300
end
@max_env_content_size 65536
defp read_env_file(path) do
case File.read(path) do
{:ok, content} -> content
{:error, _} ->
IO.puts(:stderr, "#{@red}Error: Env file not found: #{path}#{@reset}")
System.halt(1)
end
end
defp build_env_content(envs, env_file) do
file_lines = if env_file do
content = read_env_file(env_file)
content
|> String.split("\n")
|> Enum.map(&String.trim/1)
|> Enum.filter(fn line ->
String.length(line) > 0 and not String.starts_with?(line, "#")
end)
else
[]
end
(envs ++ file_lines) |> Enum.join("\n")
end
defp service_env_status(service_id) do
api_key = get_api_key()
curl_get(api_key, "/services/#{service_id}/env")
end
defp service_env_set(service_id, env_content) do
if String.length(env_content) > @max_env_content_size do
IO.puts(:stderr, "#{@red}Error: Env content exceeds maximum size of 64KB#{@reset}")
false
else
curl_put_text("/services/#{service_id}/env", env_content)
end
end
defp service_env_export(service_id) do
api_key = get_api_key()
curl_post(api_key, "/services/#{service_id}/env/export", "{}")
end
defp service_env_delete(service_id) do
api_key = get_api_key()
curl_delete(api_key, "/services/#{service_id}/env")
true
end
defp parse_exec_args(args) do
parse_exec_args(args, nil, %{})
end
defp parse_exec_args([], file, opts), do: {file, opts}
defp parse_exec_args([arg | rest], file, opts) do
cond do
String.starts_with?(arg, "-") ->
parse_exec_args(rest, file, opts)
is_nil(file) ->
parse_exec_args(rest, arg, opts)
true ->
parse_exec_args(rest, file, opts)
end
end
defp get_opt([], _long, _short, default), do: default
defp get_opt([arg, value | rest], long, short, _default) when arg == long or arg == short do
value
end
defp get_opt([_arg | rest], long, short, default) do
get_opt(rest, long, short, default)
end
defp get_all_opts(args, flag), do: get_all_opts(args, flag, [])
defp get_all_opts([], _flag, acc), do: Enum.reverse(acc)
defp get_all_opts([arg, value | rest], flag, acc) when arg == flag do
get_all_opts(rest, flag, [value | acc])
end
defp get_all_opts([_arg | rest], flag, acc) do
get_all_opts(rest, flag, acc)
end
defp check_clock_drift(response) do
response_lower = String.downcase(response)
# Check if response contains "timestamp" and error indicators
has_timestamp = String.contains?(response_lower, "timestamp")
has_error = String.contains?(response_lower, "401") or
String.contains?(response_lower, "expired") or
String.contains?(response_lower, "invalid")
if has_timestamp and has_error do
IO.puts(:stderr, "#{@red}Error: Request timestamp expired (must be within 5 minutes of server time)#{@reset}")
IO.puts(:stderr, "#{@yellow}Your computer's clock may have drifted.")
IO.puts(:stderr, "Check your system time and sync with NTP if needed:")
IO.puts(:stderr, " Linux: sudo ntpdate -s time.nist.gov")
IO.puts(:stderr, " macOS: sudo sntp -sS time.apple.com")
IO.puts(:stderr, " Windows: w32tm /resync#{@reset}")
System.halt(1)
end
end
end
Un.main(System.argv())
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