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 — COBOL
# Download + setup
curl -O https://git.unturf.com/engineering/unturf/un-inception/-/raw/main/clients/cobol/sync/src/un.cob
export UNSANDBOX_PUBLIC_KEY="unsb-pk-xxxx-xxxx-xxxx-xxxx"
export UNSANDBOX_SECRET_KEY="unsb-sk-xxxxx-xxxxx-xxxxx-xxxxx"
# Run code
./un script.cobol
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 COBOL existente:
curl -O https://git.unturf.com/engineering/unturf/un-inception/-/raw/main/clients/cobol/sync/src/un.cob
# 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 COBOL app:
IDENTIFICATION DIVISION.
PROGRAM-ID. MYAPP.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-RESULT PIC X(1000).
PROCEDURE DIVISION.
CALL 'EXECUTE_CODE' USING "cobol" "DISPLAY 'Hello from COBOL running on unsandbox!'"
RETURNING WS-RESULT.
DISPLAY WS-RESULT.
STOP RUN.
cobc -x -o myapp main.cob un.cob && ./myapp
9d9c4d82f09725d2b2eeb92a74ecb3b7
SHA256: 9cb8abb355caa8f3dafa0058e2acee56817107702f86a0fe120922be10a38d6f
* 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
IDENTIFICATION DIVISION.
PROGRAM-ID. UNSANDBOX-CLI.
AUTHOR. UNSANDBOX.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT SOURCE-FILE ASSIGN TO WS-FILENAME
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS.
SELECT CRED-FILE ASSIGN TO "/tmp/unsb_creds.txt"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-CRED-STATUS.
DATA DIVISION.
FILE SECTION.
FD SOURCE-FILE.
01 SOURCE-LINE PIC X(1024).
FD CRED-FILE.
01 CRED-LINE PIC X(512).
WORKING-STORAGE SECTION.
01 WS-FILENAME PIC X(256).
01 WS-FILE-STATUS PIC XX.
01 WS-CRED-STATUS PIC XX.
01 WS-API-KEY PIC X(256).
01 WS-PUBLIC-KEY PIC X(256).
01 WS-SECRET-KEY PIC X(256).
01 WS-LANGUAGE PIC X(32).
01 WS-EXTENSION PIC X(16).
01 WS-CURL-CMD PIC X(4096).
01 WS-EXIT-CODE PIC 9(4) VALUE 0.
01 WS-DOT-POS PIC 9(4) VALUE 0.
01 WS-LEN PIC 9(4) VALUE 0.
01 WS-I PIC 9(4) VALUE 0.
01 WS-ARG1 PIC X(256).
01 WS-ARG2 PIC X(256).
01 WS-ARG3 PIC X(256).
01 WS-COMMAND PIC X(32).
01 WS-OPERATION PIC X(32).
01 WS-ID PIC X(256).
01 WS-NAME PIC X(256).
01 WS-PORTS PIC X(256).
01 WS-DOMAINS PIC X(256).
01 WS-SERVICE-TYPE PIC X(64).
01 WS-BOOTSTRAP PIC X(2048).
01 WS-BOOTSTRAP-FILE PIC X(256).
01 WS-INPUT-FILES PIC X(1024).
01 WS-PORTAL-BASE PIC X(256) VALUE
"https://unsandbox.com".
01 WS-LANGUAGES-CACHE-TTL PIC 9(8) VALUE 3600.
01 WS-EXTEND-FLAG PIC X(8).
01 WS-SVC-ENVS PIC X(2048).
01 WS-SVC-ENV-FILE PIC X(256).
01 WS-ENV-ACTION PIC X(32).
01 WS-ENV-TARGET PIC X(256).
01 WS-VCPU PIC 9(2) VALUE 0.
01 WS-VCPU-STR PIC X(8).
01 WS-RAM PIC 9(4) VALUE 0.
01 WS-JSON-OUTPUT PIC X(8).
01 WS-IMAGE-SOURCE-TYPE PIC X(32).
01 WS-IMAGE-VISIBILITY PIC X(32).
01 WS-ARG4 PIC X(256).
01 WS-ARG5 PIC X(256).
01 WS-UNFREEZE-ON-DEMAND PIC X(8).
01 WS-UOD-ENABLED PIC X(8).
01 WS-TYPE PIC X(32).
01 WS-SHELL PIC X(32).
01 WS-ACCOUNT-INDEX PIC S9(4) VALUE -1.
01 WS-ACCOUNT-STR PIC X(16).
01 WS-ACCT-POS PIC 9(4) VALUE 0.
01 WS-ERROR-MSG PIC X(256).
PROCEDURE DIVISION.
MAIN-PROCEDURE.
* Get first command line argument
ACCEPT WS-ARG1 FROM COMMAND-LINE.
* Pre-scan: handle --account N global flag before subcommand
IF WS-ARG1 = "--account"
ACCEPT WS-ACCOUNT-STR FROM ARGUMENT-VALUE
MOVE FUNCTION NUMVAL(WS-ACCOUNT-STR) TO WS-ACCOUNT-INDEX
ACCEPT WS-ARG1 FROM ARGUMENT-VALUE
END-IF.
IF WS-ARG1 = SPACES
DISPLAY "Usage: un.cob <source_file>" UPON SYSERR
DISPLAY " un.cob session [options]" UPON SYSERR
DISPLAY " un.cob service [options]" UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
* Check for subcommands
IF WS-ARG1 = "session"
PERFORM HANDLE-SESSION
STOP RUN
END-IF.
IF WS-ARG1 = "service"
PERFORM HANDLE-SERVICE
STOP RUN
END-IF.
IF WS-ARG1 = "key"
PERFORM HANDLE-KEY
STOP RUN
END-IF.
IF WS-ARG1 = "languages"
PERFORM HANDLE-LANGUAGES
STOP RUN
END-IF.
IF WS-ARG1 = "image"
PERFORM HANDLE-IMAGE
STOP RUN
END-IF.
IF WS-ARG1 = "snapshot"
PERFORM HANDLE-SNAPSHOT
STOP RUN
END-IF.
* Default: execute command
MOVE WS-ARG1 TO WS-FILENAME.
PERFORM HANDLE-EXECUTE.
STOP RUN.
GET-CREDENTIALS.
* If already set, return immediately
IF WS-PUBLIC-KEY NOT = SPACES AND WS-SECRET-KEY NOT = SPACES
EXIT PARAGRAPH
END-IF.
* Build shell script to resolve credentials with full priority
IF WS-ACCOUNT-INDEX >= 0
MOVE WS-ACCOUNT-INDEX TO WS-ACCOUNT-STR
STRING "IDX=" FUNCTION TRIM(WS-ACCOUNT-STR) "; "
"PK=''; SK=''; CNT=-1; "
"for CSV in \"$HOME/.unsandbox/accounts.csv\" "
"\"./accounts.csv\"; do "
"[ -f \"$CSV\" ] || continue; "
"while IFS= read -r line || [ -n \"$line\" ]; do "
"case \"$line\" in \"#\"*|\"\"|\" \"*) continue ;; esac; "
"CNT=$((CNT+1)); "
"if [ \"$CNT\" -eq \"$IDX\" ]; then "
"PK=$(echo \"$line\" | cut -d',' -f1 | tr -d ' '); "
"SK=$(echo \"$line\" | cut -d',' -f2 | tr -d ' '); "
"break 2; fi; "
"done < \"$CSV\"; done; "
"if [ -z \"$PK\" ]; then "
"echo -e '\\x1b[31mError: Account index "
FUNCTION TRIM(WS-ACCOUNT-STR)
" not found in accounts.csv\\x1b[0m' >&2; exit 1; fi; "
"printf '%s\\n%s\\n' \"$PK\" \"$SK\" "
"> /tmp/unsb_creds.txt"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
ELSE
STRING "PK=\"$UNSANDBOX_PUBLIC_KEY\"; "
"SK=\"$UNSANDBOX_SECRET_KEY\"; "
"if [ -z \"$PK\" ]; then PK=\"$UNSANDBOX_API_KEY\"; SK=''; fi; "
"if [ -z \"$PK\" ]; then "
"IDX=\"${UNSANDBOX_ACCOUNT:-0}\"; "
"CNT=-1; "
"for CSV in \"$HOME/.unsandbox/accounts.csv\" "
"\"./accounts.csv\"; do "
"[ -f \"$CSV\" ] || continue; "
"while IFS= read -r line || [ -n \"$line\" ]; do "
"case \"$line\" in \"#\"*|\"\"|\" \"*) continue ;; esac; "
"CNT=$((CNT+1)); "
"if [ \"$CNT\" -eq \"$IDX\" ]; then "
"PK=$(echo \"$line\" | cut -d',' -f1 | tr -d ' '); "
"SK=$(echo \"$line\" | cut -d',' -f2 | tr -d ' '); "
"break 2; fi; "
"done < \"$CSV\"; done; fi; "
"if [ -z \"$PK\" ]; then "
"echo -e '\\x1b[31mError: No credentials found\\x1b[0m' "
">&2; exit 1; fi; "
"printf '%s\\n%s\\n' \"$PK\" \"$SK\" "
"> /tmp/unsb_creds.txt"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
CALL "SYSTEM" USING WS-CURL-CMD
RETURNING WS-EXIT-CODE.
IF WS-EXIT-CODE NOT = 0
MOVE WS-EXIT-CODE TO RETURN-CODE
STOP RUN
END-IF.
* Read resolved credentials from temp file
MOVE SPACES TO WS-PUBLIC-KEY.
MOVE SPACES TO WS-SECRET-KEY.
OPEN INPUT CRED-FILE.
IF WS-CRED-STATUS = "00"
READ CRED-FILE INTO WS-PUBLIC-KEY
READ CRED-FILE INTO WS-SECRET-KEY
CLOSE CRED-FILE
END-IF.
IF WS-PUBLIC-KEY = SPACES
DISPLAY "Error: Could not resolve credentials"
UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
HANDLE-EXECUTE.
* Get credentials
PERFORM GET-CREDENTIALS.
IF WS-API-KEY = SPACES
MOVE WS-PUBLIC-KEY TO WS-API-KEY
END-IF.
* Check if file exists
OPEN INPUT SOURCE-FILE.
IF WS-FILE-STATUS NOT = "00"
DISPLAY "Error: File not found: " WS-FILENAME
UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
CLOSE SOURCE-FILE.
* Detect language from extension
PERFORM DETECT-LANGUAGE.
IF WS-LANGUAGE = "unknown"
DISPLAY "Error: Unknown language for file: "
WS-FILENAME UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
* Use curl to make request
PERFORM MAKE-EXECUTE-REQUEST.
HANDLE-SESSION.
* Get credentials
PERFORM GET-CREDENTIALS.
IF WS-API-KEY = SPACES
MOVE WS-PUBLIC-KEY TO WS-API-KEY
END-IF.
* Initialize session parameters
MOVE SPACES TO WS-INPUT-FILES.
* Parse session arguments (simplified)
* For full implementation, would need to parse multiple args
ACCEPT WS-ARG2 FROM ARGUMENT-VALUE.
IF WS-ARG2 = "-l" OR WS-ARG2 = "--list"
PERFORM SESSION-LIST
ELSE
IF WS-ARG2 = "--kill"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SESSION-KILL
ELSE
PERFORM PARSE-SESSION-CREATE-ARGS
PERFORM SESSION-CREATE
END-IF
END-IF.
HANDLE-SERVICE.
* Get credentials
PERFORM GET-CREDENTIALS.
* Initialize service parameters
MOVE SPACES TO WS-NAME.
MOVE SPACES TO WS-PORTS.
MOVE SPACES TO WS-DOMAINS.
MOVE SPACES TO WS-SERVICE-TYPE.
MOVE SPACES TO WS-BOOTSTRAP.
MOVE SPACES TO WS-BOOTSTRAP-FILE.
MOVE SPACES TO WS-INPUT-FILES.
MOVE SPACES TO WS-SVC-ENVS.
MOVE SPACES TO WS-SVC-ENV-FILE.
MOVE SPACES TO WS-ENV-ACTION.
MOVE SPACES TO WS-ENV-TARGET.
* Parse service arguments
ACCEPT WS-ARG2 FROM ARGUMENT-VALUE.
IF WS-ARG2 = "-l" OR WS-ARG2 = "--list"
PERFORM SERVICE-LIST
ELSE IF WS-ARG2 = "env"
ACCEPT WS-ENV-ACTION FROM ARGUMENT-VALUE
ACCEPT WS-ENV-TARGET FROM ARGUMENT-VALUE
PERFORM PARSE-SERVICE-ENV-ARGS
PERFORM SERVICE-ENV
ELSE IF WS-ARG2 = "--info"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SERVICE-INFO
ELSE IF WS-ARG2 = "--logs"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SERVICE-LOGS
ELSE IF WS-ARG2 = "--freeze"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SERVICE-SLEEP
ELSE IF WS-ARG2 = "--unfreeze"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SERVICE-WAKE
ELSE IF WS-ARG2 = "--destroy"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SERVICE-DESTROY
ELSE IF WS-ARG2 = "--dump-bootstrap"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SERVICE-DUMP-BOOTSTRAP
ELSE IF WS-ARG2 = "--resize"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM PARSE-SERVICE-RESIZE-ARGS
PERFORM SERVICE-RESIZE
ELSE IF WS-ARG2 = "--set-unfreeze-on-demand"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM PARSE-SERVICE-UOD-ARGS
PERFORM SERVICE-SET-UNFREEZE-ON-DEMAND
ELSE IF WS-ARG2 = "--name"
ACCEPT WS-NAME FROM ARGUMENT-VALUE
PERFORM PARSE-SERVICE-CREATE-ARGS
PERFORM SERVICE-CREATE
ELSE
DISPLAY "Error: Use --list, --info, --logs, "
"--freeze, --unfreeze, --destroy, --dump-bootstrap, "
"--resize, --set-unfreeze-on-demand, --name, or env"
UPON SYSERR
MOVE 1 TO RETURN-CODE
END-IF.
DETECT-LANGUAGE.
* Find last dot in filename
MOVE FUNCTION LENGTH(FUNCTION TRIM(WS-FILENAME)) TO WS-LEN.
MOVE 0 TO WS-DOT-POS.
PERFORM VARYING WS-I FROM WS-LEN BY -1
UNTIL WS-I < 1 OR WS-DOT-POS > 0
IF WS-FILENAME(WS-I:1) = "."
MOVE WS-I TO WS-DOT-POS
END-IF
END-PERFORM.
IF WS-DOT-POS = 0
MOVE "unknown" TO WS-LANGUAGE
ELSE
COMPUTE WS-I = WS-LEN - WS-DOT-POS + 1
MOVE WS-FILENAME(WS-DOT-POS:WS-I) TO WS-EXTENSION
EVALUATE WS-EXTENSION
WHEN ".jl" MOVE "julia" TO WS-LANGUAGE
WHEN ".r" MOVE "r" TO WS-LANGUAGE
WHEN ".cr" MOVE "crystal" TO WS-LANGUAGE
WHEN ".f90" MOVE "fortran" TO WS-LANGUAGE
WHEN ".cob" MOVE "cobol" TO WS-LANGUAGE
WHEN ".pro" MOVE "prolog" TO WS-LANGUAGE
WHEN ".forth" MOVE "forth" TO WS-LANGUAGE
WHEN ".4th" MOVE "forth" TO WS-LANGUAGE
WHEN ".py" MOVE "python" TO WS-LANGUAGE
WHEN ".js" MOVE "javascript" TO WS-LANGUAGE
WHEN ".rb" MOVE "ruby" TO WS-LANGUAGE
WHEN ".go" MOVE "go" TO WS-LANGUAGE
WHEN ".rs" MOVE "rust" TO WS-LANGUAGE
WHEN ".c" MOVE "c" TO WS-LANGUAGE
WHEN ".cpp" MOVE "cpp" TO WS-LANGUAGE
WHEN ".java" MOVE "java" TO WS-LANGUAGE
WHEN ".sh" MOVE "bash" TO WS-LANGUAGE
WHEN OTHER MOVE "unknown" TO WS-LANGUAGE
END-EVALUATE
END-IF.
MAKE-EXECUTE-REQUEST.
* Credentials already resolved by caller (GET-CREDENTIALS)
* Build curl command using shell with HMAC signature
STRING "TS=$(date +%s); "
"BODY=$(jq -Rs '{language: """
FUNCTION TRIM(WS-LANGUAGE)
""", code: .}' < '"
FUNCTION TRIM(WS-FILENAME)
"'); "
"SIG=$(echo -n \"$TS:POST:/execute:$BODY\" | "
"openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -w '\n%{http_code}' -X POST "
"https://api.unsandbox.com/execute "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"--data-binary \"$BODY\"); "
"HTTP_CODE=$(echo \"$RESP\" | tail -n1); "
"BODY=$(echo \"$RESP\" | sed '$d'); "
"echo \"$BODY\" > /tmp/unsandbox_resp.json; "
"if echo \"$BODY\" | grep -q '\"timestamp\"' && "
"(echo \"$HTTP_CODE\" | grep -q '401' || "
"echo \"$BODY\" | grep -qi 'expired' || "
"echo \"$BODY\" | grep -qi 'invalid'); then "
"echo -e '\x1b[31mError: Request timestamp expired "
"(must be within 5 minutes of server time)\x1b[0m' >&2; "
"echo -e '\x1b[33mYour computer'"'"'s clock may have "
"drifted.\x1b[0m' >&2; "
"echo 'Check your system time and sync with NTP if "
"needed:' >&2; "
"echo ' Linux: sudo ntpdate -s time.nist.gov' >&2; "
"echo ' macOS: sudo sntp -sS time.apple.com' >&2; "
"echo ' Windows: w32tm /resync' >&2; "
"rm -f /tmp/unsandbox_resp.json; exit 1; fi; "
"jq -r '.stdout // empty' /tmp/unsandbox_resp.json | "
"sed 's/^/\x1b[34m/' | sed 's/$/\x1b[0m/'; "
"jq -r '.stderr // empty' /tmp/unsandbox_resp.json | "
"sed 's/^/\x1b[31m/' | sed 's/$/\x1b[0m/' >&2; "
"rm -f /tmp/unsandbox_resp.json"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD
RETURNING WS-EXIT-CODE.
MOVE WS-EXIT-CODE TO RETURN-CODE.
SESSION-LIST.
STRING "curl -s -X GET https://api.unsandbox.com/sessions "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' | jq -r '.sessions[] | "
'"\(.id) \(.shell) \(.status) \(.created_at)"'' "
"2>/dev/null || echo 'No active sessions'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SESSION-KILL.
STRING "curl -s -X DELETE "
"https://api.unsandbox.com/sessions/"
FUNCTION TRIM(WS-ID) " "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' >/dev/null && "
"echo -e '\x1b[32mSession terminated: "
FUNCTION TRIM(WS-ID) "\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-SESSION-CREATE-ARGS.
* Parse arguments for session creation
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "-f"
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
IF WS-INPUT-FILES NOT = SPACES
STRING FUNCTION TRIM(WS-INPUT-FILES) ","
FUNCTION TRIM(WS-ARG3)
DELIMITED BY SIZE INTO WS-INPUT-FILES
END-STRING
ELSE
MOVE WS-ARG3 TO WS-INPUT-FILES
END-IF
ELSE
IF WS-ARG3(1:1) = "-"
STRING "Unknown option: " FUNCTION TRIM(WS-ARG3)
DELIMITED BY SIZE INTO WS-ERROR-MSG
END-STRING
DISPLAY WS-ERROR-MSG UPON SYSERR
DISPLAY "Usage: un.cob session [options]" UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
SESSION-CREATE.
* Build curl command for session creation with input_files support
STRING "INPUT_FILES=''; "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
IF WS-INPUT-FILES NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
"IFS=',' read -ra FILES <<< '"
FUNCTION TRIM(WS-INPUT-FILES)
"'; "
"for f in \"${FILES[@]}\"; do "
"b64=$(base64 -w0 \"$f\" 2>/dev/null || base64 \"$f\"); "
"name=$(basename \"$f\"); "
"if [ -n \"$INPUT_FILES\" ]; then INPUT_FILES=\"$INPUT_FILES,\"; fi; "
"INPUT_FILES=\"$INPUT_FILES{\\\"filename\\\":\\\"$name\\\",\\\"content\\\":\\\"$b64\\\"}\"; "
"done; "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
STRING FUNCTION TRIM(WS-CURL-CMD)
"if [ -n \"$INPUT_FILES\" ]; then "
"JSON='{\"shell\":\"bash\",\"input_files\":['\"$INPUT_FILES\"']}'; "
"else JSON='{\"shell\":\"bash\"}'; fi; "
"curl -s -X POST https://api.unsandbox.com/sessions "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' -d \"$JSON\" && "
"echo -e '\x1b[33mSession created (WebSocket required)\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-LIST.
STRING "curl -s -X GET https://api.unsandbox.com/services "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' | jq -r '.services[] | "
'"\(.id) \(.name) \(.status)"'' "
"2>/dev/null || echo 'No services'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-INFO.
STRING "curl -s -X GET "
"https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID) " "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' | jq ."
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-LOGS.
STRING "curl -s -X GET "
"https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID) "/logs "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' | jq -r '.logs'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-SLEEP.
STRING "curl -s -X POST "
"https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID) "/freeze "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' >/dev/null && "
"echo -e '\x1b[32mService frozen: "
FUNCTION TRIM(WS-ID) "\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-WAKE.
STRING "curl -s -X POST "
"https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID) "/unfreeze "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' >/dev/null && "
"echo -e '\x1b[32mService unfreezing: "
FUNCTION TRIM(WS-ID) "\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-DESTROY.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:DELETE:/services/"
FUNCTION TRIM(WS-ID)
":\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -w '\\n%{http_code}' -X DELETE "
"'https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG); "
"HTTP_CODE=$(echo \"$RESP\" | tail -n1); "
"BODY=$(echo \"$RESP\" | sed '$d'); "
"if [ \"$HTTP_CODE\" = \"428\" ]; then "
"CHALLENGE_ID=$(echo \"$BODY\" | jq -r '.challenge_id // empty'); "
"echo -e '\\x1b[33mConfirmation required. Check your email "
"for a one-time code.\\x1b[0m' >&2; "
"echo -n 'Enter OTP: ' >&2; read OTP; "
"if [ -z \"$OTP\" ]; then "
"echo -e '\\x1b[31mError: Operation cancelled\\x1b[0m' >&2; "
"exit 1; fi; "
"TS2=$(date +%s); "
"SIG2=$(echo -n \"$TS2:DELETE:/services/"
FUNCTION TRIM(WS-ID)
":\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP2=$(curl -s -w '\\n%{http_code}' -X DELETE "
"'https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS2 "
"-H 'X-Signature: '$SIG2 "
"-H 'X-Sudo-OTP: '$OTP "
"-H 'X-Sudo-Challenge: '$CHALLENGE_ID); "
"HTTP_CODE2=$(echo \"$RESP2\" | tail -n1); "
"if [ \"$HTTP_CODE2\" = \"200\" ] || "
"[ \"$HTTP_CODE2\" = \"204\" ]; then "
"echo -e '\\x1b[32mService destroyed: "
FUNCTION TRIM(WS-ID) "\\x1b[0m'; "
"else echo \"$RESP2\" | sed '$d' | jq . 2>/dev/null || "
"echo \"$RESP2\" | sed '$d'; exit 1; fi; "
"elif [ \"$HTTP_CODE\" = \"200\" ] || "
"[ \"$HTTP_CODE\" = \"204\" ]; then "
"echo -e '\\x1b[32mService destroyed: "
FUNCTION TRIM(WS-ID) "\\x1b[0m'; "
"else echo \"$BODY\" | jq . 2>/dev/null || "
"echo \"$BODY\"; exit 1; fi"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-DUMP-BOOTSTRAP.
* Check if WS-ARG3 contains --dump-file argument
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
MOVE SPACES TO WS-BOOTSTRAP.
IF WS-ARG3 = "--dump-file"
ACCEPT WS-BOOTSTRAP FROM ARGUMENT-VALUE
END-IF.
STRING "echo 'Fetching bootstrap script from "
FUNCTION TRIM(WS-ID) "...' >&2; "
"RESP=$(curl -s -X POST "
"https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID) "/execute "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' -d '{\"command\":\"cat /tmp/bootstrap.sh\"}'); "
"STDOUT=$(echo \"$RESP\" | jq -r '.stdout // empty'); "
"if [ -n \"$STDOUT\" ]; then "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
IF WS-BOOTSTRAP NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
"echo \"$STDOUT\" > '"
FUNCTION TRIM(WS-BOOTSTRAP)
"' && chmod 755 '"
FUNCTION TRIM(WS-BOOTSTRAP)
"' && echo 'Bootstrap saved to "
FUNCTION TRIM(WS-BOOTSTRAP) "'; "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
ELSE
STRING FUNCTION TRIM(WS-CURL-CMD)
"echo \"$STDOUT\"; "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
STRING FUNCTION TRIM(WS-CURL-CMD)
"else echo -e '\x1b[31mError: Failed to fetch "
"bootstrap\x1b[0m' >&2; exit 1; fi"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-SERVICE-CREATE-ARGS.
* Parse remaining arguments for service creation
* This is a simplified parser that looks for specific flags
MOVE SPACES TO WS-UNFREEZE-ON-DEMAND.
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "--ports"
ACCEPT WS-PORTS FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--domains"
ACCEPT WS-DOMAINS FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--type"
ACCEPT WS-SERVICE-TYPE FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--bootstrap"
ACCEPT WS-BOOTSTRAP FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--bootstrap-file"
ACCEPT WS-BOOTSTRAP-FILE FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--unfreeze-on-demand"
ACCEPT WS-UNFREEZE-ON-DEMAND FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "-e"
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
IF WS-SVC-ENVS NOT = SPACES
STRING FUNCTION TRIM(WS-SVC-ENVS) X"0A"
FUNCTION TRIM(WS-ARG3)
DELIMITED BY SIZE INTO WS-SVC-ENVS
END-STRING
ELSE
MOVE WS-ARG3 TO WS-SVC-ENVS
END-IF
ELSE IF WS-ARG3 = "--env-file"
ACCEPT WS-SVC-ENV-FILE FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "-f"
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
IF WS-INPUT-FILES NOT = SPACES
STRING FUNCTION TRIM(WS-INPUT-FILES) ","
FUNCTION TRIM(WS-ARG3)
DELIMITED BY SIZE INTO WS-INPUT-FILES
END-STRING
ELSE
MOVE WS-ARG3 TO WS-INPUT-FILES
END-IF
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
PARSE-SERVICE-ENV-ARGS.
* Parse -e and --env-file for env set command
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "-e"
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
IF WS-SVC-ENVS NOT = SPACES
STRING FUNCTION TRIM(WS-SVC-ENVS) X"0A"
FUNCTION TRIM(WS-ARG3)
DELIMITED BY SIZE INTO WS-SVC-ENVS
END-STRING
ELSE
MOVE WS-ARG3 TO WS-SVC-ENVS
END-IF
ELSE IF WS-ARG3 = "--env-file"
ACCEPT WS-SVC-ENV-FILE FROM ARGUMENT-VALUE
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
SERVICE-ENV.
* Handle env subcommand (status/set/export/delete)
IF WS-ENV-ACTION = "status"
PERFORM SERVICE-ENV-STATUS
ELSE IF WS-ENV-ACTION = "set"
PERFORM SERVICE-ENV-SET
ELSE IF WS-ENV-ACTION = "export"
PERFORM SERVICE-ENV-EXPORT
ELSE IF WS-ENV-ACTION = "delete"
PERFORM SERVICE-ENV-DELETE
ELSE
DISPLAY "Error: Unknown env action: "
FUNCTION TRIM(WS-ENV-ACTION) UPON SYSERR
DISPLAY "Usage: un.cob service env "
"<status|set|export|delete> <service_id>" UPON SYSERR
MOVE 1 TO RETURN-CODE
END-IF.
SERVICE-ENV-STATUS.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:GET:/services/"
FUNCTION TRIM(WS-ENV-TARGET)
"/env:\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X GET 'https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ENV-TARGET)
"/env' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG | jq ."
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-ENV-SET.
STRING "ENV_CONTENT=''; "
"ENV_LINES='"
FUNCTION TRIM(WS-SVC-ENVS)
"'; "
"if [ -n \"$ENV_LINES\" ]; then "
"ENV_CONTENT=\"$ENV_LINES\"; fi; "
"ENV_FILE='"
FUNCTION TRIM(WS-SVC-ENV-FILE)
"'; "
"if [ -n \"$ENV_FILE\" ] && [ -f \"$ENV_FILE\" ]; then "
"while IFS= read -r line || [ -n \"$line\" ]; do "
"case \"$line\" in \"#\"*|\"\") continue ;; esac; "
"if [ -n \"$ENV_CONTENT\" ]; then "
"ENV_CONTENT=\"$ENV_CONTENT"
X"0A"
"\"; fi; "
"ENV_CONTENT=\"$ENV_CONTENT$line\"; "
"done < \"$ENV_FILE\"; fi; "
"if [ -z \"$ENV_CONTENT\" ]; then "
"echo -e '\x1b[31mError: No environment variables "
"to set\x1b[0m' >&2; exit 1; fi; "
"TS=$(date +%s); "
"SIG=$(echo -n \"$TS:PUT:/services/"
FUNCTION TRIM(WS-ENV-TARGET)
"/env:$ENV_CONTENT\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X PUT 'https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ENV-TARGET)
"/env' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-H 'Content-Type: text/plain' "
"--data-binary \"$ENV_CONTENT\" | jq ."
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-ENV-EXPORT.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:POST:/services/"
FUNCTION TRIM(WS-ENV-TARGET)
"/env/export:\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ENV-TARGET)
"/env/export' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG | jq -r '.content // empty'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-ENV-DELETE.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:DELETE:/services/"
FUNCTION TRIM(WS-ENV-TARGET)
"/env:\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X DELETE 'https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ENV-TARGET)
"/env' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG >/dev/null && "
"echo -e '\x1b[32mVault deleted for: "
FUNCTION TRIM(WS-ENV-TARGET)
"\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SERVICE-CREATE.
* Build service creation with HMAC auth and auto-vault
STRING "BODY='{\"name\":\"" FUNCTION TRIM(WS-NAME) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
* Add ports if provided
IF WS-PORTS NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"ports\":[" FUNCTION TRIM(WS-PORTS) "]"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Add domains if provided
IF WS-DOMAINS NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"domains\":[\"" FUNCTION TRIM(WS-DOMAINS) "\"]"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Add service_type if provided
IF WS-SERVICE-TYPE NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"service_type\":\"" FUNCTION TRIM(WS-SERVICE-TYPE) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Add bootstrap if provided
IF WS-BOOTSTRAP NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"bootstrap\":\"" FUNCTION TRIM(WS-BOOTSTRAP) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Add unfreeze_on_demand if provided
IF WS-UNFREEZE-ON-DEMAND NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"unfreeze_on_demand\":"
FUNCTION TRIM(WS-UNFREEZE-ON-DEMAND)
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Close JSON body
STRING FUNCTION TRIM(WS-CURL-CMD) "}'; "
"TS=$(date +%s); "
"SIG=$(echo -n \"$TS:POST:/services:$BODY\" | "
"openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -X POST https://api.unsandbox.com/services "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-PUBLIC-KEY) "' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\"); "
"SVC_ID=$(echo \"$RESP\" | jq -r '.id // empty'); "
"if [ -n \"$SVC_ID\" ]; then "
"echo -e '\x1b[32m'\"$SVC_ID\"' created\x1b[0m'; "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
* Add auto-vault logic
STRING FUNCTION TRIM(WS-CURL-CMD)
"ENV_CONTENT=''; "
"ENV_LINES='" FUNCTION TRIM(WS-SVC-ENVS) "'; "
"if [ -n \"$ENV_LINES\" ]; then ENV_CONTENT=\"$ENV_LINES\"; fi; "
"ENV_FILE='" FUNCTION TRIM(WS-SVC-ENV-FILE) "'; "
"if [ -n \"$ENV_FILE\" ] && [ -f \"$ENV_FILE\" ]; then "
"while IFS= read -r line || [ -n \"$line\" ]; do "
"case \"$line\" in \"#\"*|\"\") continue ;; esac; "
"if [ -n \"$ENV_CONTENT\" ]; then "
"ENV_CONTENT=\"$ENV_CONTENT" X"0A" "\"; fi; "
"ENV_CONTENT=\"$ENV_CONTENT$line\"; "
"done < \"$ENV_FILE\"; fi; "
"if [ -n \"$ENV_CONTENT\" ]; then "
"TS2=$(date +%s); "
"SIG2=$(echo -n \"$TS2:PUT:/services/$SVC_ID/env:$ENV_CONTENT\" | "
"openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X PUT \"https://api.unsandbox.com/services/$SVC_ID/env\" "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-PUBLIC-KEY) "' "
"-H 'X-Timestamp: '$TS2 "
"-H 'X-Signature: '$SIG2 "
"-H 'Content-Type: text/plain' "
"--data-binary \"$ENV_CONTENT\" >/dev/null && "
"echo -e '\x1b[32mVault configured\x1b[0m'; fi; "
"else echo \"$RESP\" | jq .; fi"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
HANDLE-KEY.
* Get credentials
PERFORM GET-CREDENTIALS.
IF WS-API-KEY = SPACES
MOVE WS-PUBLIC-KEY TO WS-API-KEY
END-IF.
* Parse key arguments
MOVE SPACES TO WS-EXTEND-FLAG.
ACCEPT WS-ARG2 FROM ARGUMENT-VALUE.
IF WS-ARG2 = "--extend"
MOVE "true" TO WS-EXTEND-FLAG
END-IF.
* Validate key
PERFORM VALIDATE-KEY.
VALIDATE-KEY.
* Build curl command to validate API key
STRING "curl -s -X POST "
FUNCTION TRIM(WS-PORTAL-BASE)
"/keys/validate "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' -o /tmp/unsandbox_key_resp.json; "
"STATUS=$?; "
"if [ $STATUS -ne 0 ]; then "
"echo -e '\x1b[31mInvalid\x1b[0m'; "
"exit 1; "
"fi; "
"EXPIRED=$(jq -r '.expired // false' "
"/tmp/unsandbox_key_resp.json); "
"PUBLIC_KEY=$(jq -r '.public_key // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
IF WS-EXTEND-FLAG = "true"
STRING FUNCTION TRIM(WS-CURL-CMD)
"xdg-open '"
FUNCTION TRIM(WS-PORTAL-BASE)
"/keys/extend?pk='\"$PUBLIC_KEY\" 2>/dev/null; "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
ELSE
STRING FUNCTION TRIM(WS-CURL-CMD)
"if [ \"$EXPIRED\" = \"true\" ]; then "
"echo -e '\x1b[31mExpired\x1b[0m'; "
"echo 'Public Key: '$PUBLIC_KEY; "
"echo 'Tier: '$(jq -r '.tier // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"echo 'Expired: '$(jq -r '.expires_at // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"echo -e '\x1b[33mTo renew: Visit "
"https://unsandbox.com/keys/extend\x1b[0m'; "
"rm -f /tmp/unsandbox_key_resp.json; "
"exit 1; "
"else "
"echo -e '\x1b[32mValid\x1b[0m'; "
"echo 'Public Key: '$PUBLIC_KEY; "
"echo 'Tier: '$(jq -r '.tier // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"echo 'Status: '$(jq -r '.status // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"echo 'Expires: '$(jq -r '.expires_at // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"echo 'Time Remaining: '$(jq -r "
"'.time_remaining // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"echo 'Rate Limit: '$(jq -r '.rate_limit // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"echo 'Burst: '$(jq -r '.burst // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"echo 'Concurrency: '$(jq -r '.concurrency // \"N/A\"' "
"/tmp/unsandbox_key_resp.json); "
"fi; "
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
STRING FUNCTION TRIM(WS-CURL-CMD)
"rm -f /tmp/unsandbox_key_resp.json"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-SERVICE-RESIZE-ARGS.
* Parse -v argument for vcpu
MOVE 0 TO WS-VCPU.
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "-v"
ACCEPT WS-VCPU-STR FROM ARGUMENT-VALUE
MOVE FUNCTION NUMVAL(WS-VCPU-STR) TO WS-VCPU
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
SERVICE-RESIZE.
* Validate vcpu
IF WS-VCPU < 1 OR WS-VCPU > 8
DISPLAY "Error: --resize requires -v N (1-8)"
UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
* Calculate RAM
COMPUTE WS-RAM = WS-VCPU * 2.
* Build and execute resize request with HMAC auth
STRING "TS=$(date +%s); "
"BODY='{\"vcpu\":" WS-VCPU "}'; "
"SIG=$(echo -n \"$TS:PATCH:/services/"
FUNCTION TRIM(WS-ID)
":$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X PATCH 'https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-PUBLIC-KEY) "' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" >/dev/null && "
"echo -e '\x1b[32mService resized to " WS-VCPU
" vCPU, " WS-RAM " GB RAM\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-SERVICE-UOD-ARGS.
* Parse --enabled argument for unfreeze_on_demand
MOVE SPACES TO WS-UOD-ENABLED.
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "--enabled"
ACCEPT WS-UOD-ENABLED FROM ARGUMENT-VALUE
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
SERVICE-SET-UNFREEZE-ON-DEMAND.
* Validate enabled value
IF WS-UOD-ENABLED NOT = "true" AND WS-UOD-ENABLED NOT = "false"
DISPLAY "Error: --set-unfreeze-on-demand requires "
"--enabled true|false" UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
* Build and execute set unfreeze_on_demand request with HMAC auth
STRING "TS=$(date +%s); "
"BODY='{\"unfreeze_on_demand\":"
FUNCTION TRIM(WS-UOD-ENABLED) "}'; "
"SIG=$(echo -n \"$TS:PATCH:/services/"
FUNCTION TRIM(WS-ID)
":$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X PATCH 'https://api.unsandbox.com/services/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-PUBLIC-KEY) "' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" >/dev/null && "
"echo -e '\x1b[32mService unfreeze_on_demand set to "
FUNCTION TRIM(WS-UOD-ENABLED) "\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
HANDLE-LANGUAGES.
* Get credentials
PERFORM GET-CREDENTIALS.
* Parse --json flag
MOVE SPACES TO WS-JSON-OUTPUT.
ACCEPT WS-ARG2 FROM ARGUMENT-VALUE.
IF WS-ARG2 = "--json"
MOVE "true" TO WS-JSON-OUTPUT
END-IF.
* Perform languages list
PERFORM LANGUAGES-LIST.
LANGUAGES-LIST.
* Languages list with 1-hour cache
IF WS-JSON-OUTPUT = "true"
* JSON output: extract language names as array with caching
STRING "CACHE_TTL=3600; "
"CACHE_FILE=\"$HOME/.unsandbox/languages.json\"; "
"if [ -f \"$CACHE_FILE\" ]; then "
"CACHE_TS=$(jq -r '.timestamp // 0' \"$CACHE_FILE\" 2>/dev/null); "
"CURRENT_TS=$(date +%s); "
"AGE=$((CURRENT_TS - CACHE_TS)); "
"if [ $AGE -lt $CACHE_TTL ]; then "
"jq -c '.languages' \"$CACHE_FILE\"; exit 0; fi; fi; "
"TS=$(date +%s); "
"SIG=$(echo -n \"$TS:GET:/languages:\" | "
"openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -X GET 'https://api.unsandbox.com/languages' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG); "
"LANGS=$(echo \"$RESP\" | jq -c '[.languages[].name]'); "
"mkdir -p \"$HOME/.unsandbox\"; "
"echo \"{\\\"languages\\\":$LANGS,\\\"timestamp\\\":$(date +%s)}\" "
"> \"$CACHE_FILE\"; "
"echo \"$LANGS\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
ELSE
* Plain output: one language per line with caching
STRING "CACHE_TTL=3600; "
"CACHE_FILE=\"$HOME/.unsandbox/languages.json\"; "
"if [ -f \"$CACHE_FILE\" ]; then "
"CACHE_TS=$(jq -r '.timestamp // 0' \"$CACHE_FILE\" 2>/dev/null); "
"CURRENT_TS=$(date +%s); "
"AGE=$((CURRENT_TS - CACHE_TS)); "
"if [ $AGE -lt $CACHE_TTL ]; then "
"jq -r '.languages[]' \"$CACHE_FILE\"; exit 0; fi; fi; "
"TS=$(date +%s); "
"SIG=$(echo -n \"$TS:GET:/languages:\" | "
"openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -X GET 'https://api.unsandbox.com/languages' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG); "
"LANGS=$(echo \"$RESP\" | jq -c '[.languages[].name]'); "
"mkdir -p \"$HOME/.unsandbox\"; "
"echo \"{\\\"languages\\\":$LANGS,\\\"timestamp\\\":$(date +%s)}\" "
"> \"$CACHE_FILE\"; "
"echo \"$RESP\" | jq -r '.languages[].name'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
CALL "SYSTEM" USING WS-CURL-CMD.
HANDLE-IMAGE.
* Get credentials
PERFORM GET-CREDENTIALS.
* Initialize image parameters
MOVE SPACES TO WS-ID.
MOVE SPACES TO WS-NAME.
MOVE SPACES TO WS-PORTS.
MOVE SPACES TO WS-IMAGE-SOURCE-TYPE.
MOVE SPACES TO WS-IMAGE-VISIBILITY.
* Parse image arguments
ACCEPT WS-ARG2 FROM ARGUMENT-VALUE.
IF WS-ARG2 = "-l" OR WS-ARG2 = "--list"
PERFORM IMAGE-LIST
ELSE IF WS-ARG2 = "--info"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM IMAGE-INFO
ELSE IF WS-ARG2 = "--delete"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM IMAGE-DELETE
ELSE IF WS-ARG2 = "--lock"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM IMAGE-LOCK
ELSE IF WS-ARG2 = "--unlock"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM IMAGE-UNLOCK
ELSE IF WS-ARG2 = "--publish"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM PARSE-IMAGE-PUBLISH-ARGS
PERFORM IMAGE-PUBLISH
ELSE IF WS-ARG2 = "--visibility"
ACCEPT WS-ID FROM ARGUMENT-VALUE
ACCEPT WS-IMAGE-VISIBILITY FROM ARGUMENT-VALUE
PERFORM IMAGE-VISIBILITY
ELSE IF WS-ARG2 = "--spawn"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM PARSE-IMAGE-SPAWN-ARGS
PERFORM IMAGE-SPAWN
ELSE IF WS-ARG2 = "--clone"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM PARSE-IMAGE-CLONE-ARGS
PERFORM IMAGE-CLONE
ELSE
DISPLAY "Error: Use --list, --info, --delete, "
"--lock, --unlock, --publish, --visibility, "
"--spawn, or --clone" UPON SYSERR
MOVE 1 TO RETURN-CODE
END-IF.
IMAGE-LIST.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:GET:/images:\" | "
"openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X GET 'https://api.unsandbox.com/images' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG | jq ."
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
IMAGE-INFO.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:GET:/images/"
FUNCTION TRIM(WS-ID)
":\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X GET 'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG | jq ."
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
IMAGE-DELETE.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:DELETE:/images/"
FUNCTION TRIM(WS-ID)
":\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -w '\\n%{http_code}' -X DELETE "
"'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG); "
"HTTP_CODE=$(echo \"$RESP\" | tail -n1); "
"BODY=$(echo \"$RESP\" | sed '$d'); "
"if [ \"$HTTP_CODE\" = \"428\" ]; then "
"CHALLENGE_ID=$(echo \"$BODY\" | jq -r '.challenge_id // empty'); "
"echo -e '\\x1b[33mConfirmation required. Check your email "
"for a one-time code.\\x1b[0m' >&2; "
"echo -n 'Enter OTP: ' >&2; read OTP; "
"if [ -z \"$OTP\" ]; then "
"echo -e '\\x1b[31mError: Operation cancelled\\x1b[0m' >&2; "
"exit 1; fi; "
"TS2=$(date +%s); "
"SIG2=$(echo -n \"$TS2:DELETE:/images/"
FUNCTION TRIM(WS-ID)
":\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP2=$(curl -s -w '\\n%{http_code}' -X DELETE "
"'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS2 "
"-H 'X-Signature: '$SIG2 "
"-H 'X-Sudo-OTP: '$OTP "
"-H 'X-Sudo-Challenge: '$CHALLENGE_ID); "
"HTTP_CODE2=$(echo \"$RESP2\" | tail -n1); "
"if [ \"$HTTP_CODE2\" = \"200\" ] || "
"[ \"$HTTP_CODE2\" = \"204\" ]; then "
"echo -e '\\x1b[32mImage deleted: "
FUNCTION TRIM(WS-ID) "\\x1b[0m'; "
"else echo \"$RESP2\" | sed '$d' | jq . 2>/dev/null || "
"echo \"$RESP2\" | sed '$d'; exit 1; fi; "
"elif [ \"$HTTP_CODE\" = \"200\" ] || "
"[ \"$HTTP_CODE\" = \"204\" ]; then "
"echo -e '\\x1b[32mImage deleted: "
FUNCTION TRIM(WS-ID) "\\x1b[0m'; "
"else echo \"$BODY\" | jq . 2>/dev/null || "
"echo \"$BODY\"; exit 1; fi"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
IMAGE-LOCK.
STRING "TS=$(date +%s); "
"BODY='{}'; "
"SIG=$(echo -n \"$TS:POST:/images/"
FUNCTION TRIM(WS-ID)
"/lock:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"/lock' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" >/dev/null && "
"echo -e '\x1b[32mImage locked: "
FUNCTION TRIM(WS-ID) "\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
IMAGE-UNLOCK.
STRING "TS=$(date +%s); "
"BODY='{}'; "
"SIG=$(echo -n \"$TS:POST:/images/"
FUNCTION TRIM(WS-ID)
"/unlock:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -w '\\n%{http_code}' -X POST "
"'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"/unlock' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\"); "
"HTTP_CODE=$(echo \"$RESP\" | tail -n1); "
"RESP_BODY=$(echo \"$RESP\" | sed '$d'); "
"if [ \"$HTTP_CODE\" = \"428\" ]; then "
"CHALLENGE_ID=$(echo \"$RESP_BODY\" | jq -r '.challenge_id // empty'); "
"echo -e '\\x1b[33mConfirmation required. Check your email "
"for a one-time code.\\x1b[0m' >&2; "
"echo -n 'Enter OTP: ' >&2; read OTP; "
"if [ -z \"$OTP\" ]; then "
"echo -e '\\x1b[31mError: Operation cancelled\\x1b[0m' >&2; "
"exit 1; fi; "
"TS2=$(date +%s); "
"SIG2=$(echo -n \"$TS2:POST:/images/"
FUNCTION TRIM(WS-ID)
"/unlock:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP2=$(curl -s -w '\\n%{http_code}' -X POST "
"'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"/unlock' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS2 "
"-H 'X-Signature: '$SIG2 "
"-H 'X-Sudo-OTP: '$OTP "
"-H 'X-Sudo-Challenge: '$CHALLENGE_ID "
"-d \"$BODY\"); "
"HTTP_CODE2=$(echo \"$RESP2\" | tail -n1); "
"if [ \"$HTTP_CODE2\" = \"200\" ] || "
"[ \"$HTTP_CODE2\" = \"204\" ]; then "
"echo -e '\\x1b[32mImage unlocked: "
FUNCTION TRIM(WS-ID) "\\x1b[0m'; "
"else echo \"$RESP2\" | sed '$d' | jq . 2>/dev/null || "
"echo \"$RESP2\" | sed '$d'; exit 1; fi; "
"elif [ \"$HTTP_CODE\" = \"200\" ] || "
"[ \"$HTTP_CODE\" = \"204\" ]; then "
"echo -e '\\x1b[32mImage unlocked: "
FUNCTION TRIM(WS-ID) "\\x1b[0m'; "
"else echo \"$RESP_BODY\" | jq . 2>/dev/null || "
"echo \"$RESP_BODY\"; exit 1; fi"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-IMAGE-PUBLISH-ARGS.
* Parse --source-type and --name for publish
MOVE SPACES TO WS-IMAGE-SOURCE-TYPE.
MOVE SPACES TO WS-NAME.
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "--source-type"
ACCEPT WS-IMAGE-SOURCE-TYPE FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--name"
ACCEPT WS-NAME FROM ARGUMENT-VALUE
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
IMAGE-PUBLISH.
* Validate source-type
IF WS-IMAGE-SOURCE-TYPE = SPACES
DISPLAY "Error: --publish requires --source-type "
"(service or snapshot)" UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
* Build publish request
STRING "TS=$(date +%s); "
"BODY='{\"source_type\":\""
FUNCTION TRIM(WS-IMAGE-SOURCE-TYPE)
"\",\"source_id\":\""
FUNCTION TRIM(WS-ID) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
IF WS-NAME NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"name\":\"" FUNCTION TRIM(WS-NAME) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
STRING FUNCTION TRIM(WS-CURL-CMD)
"}'; "
"SIG=$(echo -n \"$TS:POST:/images/publish:$BODY\" | "
"openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/images/publish' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" && "
"echo -e '\x1b[32mImage published\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
IMAGE-VISIBILITY.
STRING "TS=$(date +%s); "
"BODY='{\"visibility\":\""
FUNCTION TRIM(WS-IMAGE-VISIBILITY)
"\"}'; "
"SIG=$(echo -n \"$TS:POST:/images/"
FUNCTION TRIM(WS-ID)
"/visibility:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"/visibility' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" >/dev/null && "
"echo -e '\x1b[32mImage visibility set to: "
FUNCTION TRIM(WS-IMAGE-VISIBILITY) "\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-IMAGE-SPAWN-ARGS.
* Parse --name and --ports for spawn
MOVE SPACES TO WS-NAME.
MOVE SPACES TO WS-PORTS.
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "--name"
ACCEPT WS-NAME FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--ports"
ACCEPT WS-PORTS FROM ARGUMENT-VALUE
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
IMAGE-SPAWN.
* Build spawn request
STRING "TS=$(date +%s); "
"BODY='{"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
IF WS-NAME NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
"\"name\":\"" FUNCTION TRIM(WS-NAME) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
IF WS-PORTS NOT = SPACES
IF WS-NAME NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD) ","
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF
STRING FUNCTION TRIM(WS-CURL-CMD)
"\"ports\":[" FUNCTION TRIM(WS-PORTS) "]"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
STRING FUNCTION TRIM(WS-CURL-CMD)
"}'; "
"SIG=$(echo -n \"$TS:POST:/images/"
FUNCTION TRIM(WS-ID)
"/spawn:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"/spawn' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" && "
"echo -e '\x1b[32mService spawned from image\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-IMAGE-CLONE-ARGS.
* Parse --name for clone
MOVE SPACES TO WS-NAME.
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "--name"
ACCEPT WS-NAME FROM ARGUMENT-VALUE
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
IMAGE-CLONE.
* Build clone request
STRING "TS=$(date +%s); "
"BODY='{"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
IF WS-NAME NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
"\"name\":\"" FUNCTION TRIM(WS-NAME) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
STRING FUNCTION TRIM(WS-CURL-CMD)
"}'; "
"SIG=$(echo -n \"$TS:POST:/images/"
FUNCTION TRIM(WS-ID)
"/clone:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/images/"
FUNCTION TRIM(WS-ID)
"/clone' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" && "
"echo -e '\x1b[32mImage cloned\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
HANDLE-SNAPSHOT.
* Get credentials
PERFORM GET-CREDENTIALS.
* Get second argument (operation or --list)
ACCEPT WS-ARG2 FROM ARGUMENT-VALUE.
EVALUATE WS-ARG2
WHEN "--list"
WHEN "-l"
PERFORM SNAPSHOT-LIST
WHEN "--info"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SNAPSHOT-INFO
WHEN "--delete"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SNAPSHOT-DELETE
WHEN "--lock"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SNAPSHOT-LOCK
WHEN "--unlock"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SNAPSHOT-UNLOCK
WHEN "--restore"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SNAPSHOT-RESTORE
WHEN "--clone"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM PARSE-SNAPSHOT-CLONE-ARGS
PERFORM SNAPSHOT-CLONE
WHEN OTHER
DISPLAY "Usage: un snapshot [options]" UPON SYSERR
DISPLAY " --list, -l List snapshots" UPON SYSERR
DISPLAY " --info ID Get snapshot details"
UPON SYSERR
DISPLAY " --delete ID Delete snapshot"
UPON SYSERR
DISPLAY " --lock ID Lock snapshot" UPON SYSERR
DISPLAY " --unlock ID Unlock snapshot" UPON SYSERR
DISPLAY " --restore ID Restore snapshot"
UPON SYSERR
DISPLAY " --clone ID Clone snapshot" UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-EVALUATE.
SNAPSHOT-LIST.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:GET:/snapshots:\" | "
"openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X GET 'https://api.unsandbox.com/snapshots' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG | jq ."
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SNAPSHOT-INFO.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:GET:/snapshots/"
FUNCTION TRIM(WS-ID)
":\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X GET 'https://api.unsandbox.com/snapshots/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG | jq ."
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SNAPSHOT-DELETE.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:DELETE:/snapshots/"
FUNCTION TRIM(WS-ID)
":\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -w '\n%{http_code}' -X DELETE "
"'https://api.unsandbox.com/snapshots/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG); "
"HTTP_CODE=$(echo \"$RESP\" | tail -1); "
"BODY=$(echo \"$RESP\" | head -n -1); "
"if [ \"$HTTP_CODE\" = \"428\" ]; then "
"OTP=$(echo \"$BODY\" | jq -r '.otp // empty'); "
"if [ -n \"$OTP\" ]; then "
"TS2=$(date +%s); "
"SIG2=$(echo -n \"$TS2:DELETE:/snapshots/"
FUNCTION TRIM(WS-ID)
":\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X DELETE 'https://api.unsandbox.com/snapshots/"
FUNCTION TRIM(WS-ID)
"' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS2 "
"-H 'X-Signature: '$SIG2 "
"-H 'X-Sudo-OTP: '$OTP | jq .; "
"echo -e '\x1b[32mSnapshot deleted\x1b[0m'; fi; "
"else echo \"$BODY\" | jq .; fi"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SNAPSHOT-LOCK.
STRING "TS=$(date +%s); "
"SIG=$(echo -n \"$TS:POST:/snapshots/"
FUNCTION TRIM(WS-ID)
"/lock:\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/snapshots/"
FUNCTION TRIM(WS-ID)
"/lock' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG | jq . && "
"echo -e '\x1b[32mSnapshot locked\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SNAPSHOT-UNLOCK.
STRING "TS=$(date +%s); "
"BODY='{}'; "
"SIG=$(echo -n \"$TS:POST:/snapshots/"
FUNCTION TRIM(WS-ID)
"/unlock:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"RESP=$(curl -s -w '\n%{http_code}' -X POST "
"'https://api.unsandbox.com/snapshots/"
FUNCTION TRIM(WS-ID)
"/unlock' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\"); "
"HTTP_CODE=$(echo \"$RESP\" | tail -1); "
"BODY_RESP=$(echo \"$RESP\" | head -n -1); "
"if [ \"$HTTP_CODE\" = \"428\" ]; then "
"OTP=$(echo \"$BODY_RESP\" | jq -r '.otp // empty'); "
"if [ -n \"$OTP\" ]; then "
"TS2=$(date +%s); "
"SIG2=$(echo -n \"$TS2:POST:/snapshots/"
FUNCTION TRIM(WS-ID)
"/unlock:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/snapshots/"
FUNCTION TRIM(WS-ID)
"/unlock' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS2 "
"-H 'X-Signature: '$SIG2 "
"-H 'X-Sudo-OTP: '$OTP "
"-d \"$BODY\" | jq .; "
"echo -e '\x1b[32mSnapshot unlocked\x1b[0m'; fi; "
"else echo \"$BODY_RESP\" | jq .; fi"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
SNAPSHOT-RESTORE.
STRING "TS=$(date +%s); "
"BODY='{}'; "
"SIG=$(echo -n \"$TS:POST:/snapshots/"
FUNCTION TRIM(WS-ID)
"/restore:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/snapshots/"
FUNCTION TRIM(WS-ID)
"/restore' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" | jq . && "
"echo -e '\x1b[32mSnapshot restored\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-SNAPSHOT-CLONE-ARGS.
* Parse --type, --name, --ports, --shell
MOVE SPACES TO WS-TYPE.
MOVE SPACES TO WS-NAME.
MOVE SPACES TO WS-PORTS.
MOVE SPACES TO WS-SHELL.
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "--type"
ACCEPT WS-TYPE FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--name"
ACCEPT WS-NAME FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--ports"
ACCEPT WS-PORTS FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--shell"
ACCEPT WS-SHELL FROM ARGUMENT-VALUE
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
IF WS-TYPE = SPACES
DISPLAY "Error: --type required (session or service)"
UPON SYSERR
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
SNAPSHOT-CLONE.
* Build clone request
STRING "TS=$(date +%s); "
"BODY='{\"type\":\"" FUNCTION TRIM(WS-TYPE) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
IF WS-NAME NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"name\":\"" FUNCTION TRIM(WS-NAME) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
IF WS-PORTS NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"ports\":[" FUNCTION TRIM(WS-PORTS) "]"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
IF WS-SHELL NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",\"shell\":\"" FUNCTION TRIM(WS-SHELL) "\""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
STRING FUNCTION TRIM(WS-CURL-CMD)
"}'; "
"SIG=$(echo -n \"$TS:POST:/snapshots/"
FUNCTION TRIM(WS-ID)
"/clone:$BODY\" | openssl dgst -sha256 -hmac '"
FUNCTION TRIM(WS-SECRET-KEY)
"' | cut -d' ' -f2); "
"curl -s -X POST 'https://api.unsandbox.com/snapshots/"
FUNCTION TRIM(WS-ID)
"/clone' "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer "
FUNCTION TRIM(WS-PUBLIC-KEY)
"' "
"-H 'X-Timestamp: '$TS "
"-H 'X-Signature: '$SIG "
"-d \"$BODY\" | jq . && "
"echo -e '\x1b[32mSnapshot cloned\x1b[0m'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
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