un-inception/clients/c
russell@unturf.com 15f37c8acc chore: bump version to 4.2.29
Test QR code functional tests with updated container image.
2026-01-28 15:32:42 -05:00
..
examples fix: Rename C SDK files to un.c/un.h to match naming convention 2026-01-15 16:50:03 -05:00
src chore: bump version to 4.2.29 2026-01-28 15:32:42 -05:00
tests fix(c): Use static CLI functions instead of library API 2026-01-17 10:54:07 -05:00
IMPLEMENTATION.md fix: Rename C SDK files to un.c/un.h to match naming convention 2026-01-15 16:50:03 -05:00
Makefile fix(c): Wrap library API functions in ifdef UNSANDBOX_LIBRARY 2026-01-17 09:12:25 -05:00
README.md fix: Rename C SDK files to un.c/un.h to match naming convention 2026-01-15 16:50:03 -05:00

un.com C SDK

A complete, production-ready C SDK for the un.com code execution API.

Features

  • Synchronous execution (unsandbox_execute) - Get results immediately
  • Asynchronous execution (unsandbox_execute_async + unsandbox_wait_job) - Fire and forget with polling
  • Job management - Query, cancel, and list jobs
  • Language detection - Automatically detect language from file extensions
  • Language support - 50+ languages including Python, JavaScript, Go, Rust, etc.
  • Credential management - 4-tier priority system (args > env > ~/.unsandbox/ > ./accounts.csv)
  • HMAC-SHA256 authentication - Built-in OpenSSL integration
  • HTTP client - libcurl-based requests with proper error handling
  • Memory safe - Proper allocation and deallocation patterns
  • No heavy dependencies - Only requires libcurl and OpenSSL (standard on Linux)

Installation

Dependencies

sudo apt install build-essential libcurl4-openssl-dev libssl-dev

Building

# Build library and examples
cd clients/c
make

# Just build the library (src/un.c and src/unsandbox.h)
make lib

# Build examples (hello_world, fibonacci)
make examples

# Run tests
make test

# Clean
make clean

Quick Start

As a Library

Include the header and link with libcurl and libssl:

#include "unsandbox.h"

int main(void) {
    // Execute code synchronously
    unsandbox_result_t *result = unsandbox_execute(
        "python",
        "print('Hello, World!')",
        NULL,  // uses env vars or ~/.unsandbox/accounts.csv
        NULL
    );

    if (result && result->success) {
        printf("Output: %s\n", result->stdout);
        unsandbox_free_result(result);
    } else {
        printf("Error: %s\n", unsandbox_last_error());
        return 1;
    }

    return 0;
}

Compile:

gcc -o myapp myapp.c src/un.c -Isrc -lcurl -lssl -lcrypto

Examples

Hello World (Synchronous)

#include "unsandbox.h"

int main(void) {
    // Execute immediately, wait for result
    unsandbox_result_t *result = unsandbox_execute(
        "python",
        "print('Hello')",
        NULL, NULL
    );

    if (result) {
        printf("%s", result->stdout);
        unsandbox_free_result(result);
    }
    return 0;
}

Run the example:

cd examples
gcc -o hello_world hello_world.c ../src/un.c -I../src -lcurl -lssl -lcrypto
./hello_world

Fibonacci (Asynchronous)

#include "unsandbox.h"

int main(void) {
    // Submit job
    char *job_id = unsandbox_execute_async("python",
        "def fib(n): return n if n<2 else fib(n-1)+fib(n-2)\n"
        "print(fib(10))",
        NULL, NULL);

    if (job_id) {
        printf("Job: %s\n", job_id);

        // Wait for completion (with exponential backoff polling)
        unsandbox_result_t *result = unsandbox_wait_job(job_id, NULL, NULL);
        if (result) {
            printf("Result: %s\n", result->stdout);
            unsandbox_free_result(result);
        }
        free(job_id);
    }
    return 0;
}

Run the example:

cd examples
./fibonacci

API Reference

Core Functions

unsandbox_execute(language, code, public_key, secret_key)

Execute code synchronously and return result immediately.

unsandbox_result_t *result = unsandbox_execute("python", "print(42)", NULL, NULL);

if (result->success) {
    printf("stdout: %s\n", result->stdout);
    printf("stderr: %s\n", result->stderr);
    printf("exit_code: %d\n", result->exit_code);
    unsandbox_free_result(result);
}

Parameters:

  • language (const char *) - Language identifier (e.g., "python", "javascript")
  • code (const char *) - Code to execute
  • public_key (const char *) - API public key (NULL to use env/config)
  • secret_key (const char *) - API secret key (NULL to use env/config)

Returns: unsandbox_result_t * - Result struct or NULL on error

Result struct:

typedef struct {
    char *stdout;           // Program output
    char *stderr;           // Error output
    int exit_code;          // Process exit code
    char *language;         // Language used
    double execution_time;  // Time in seconds
    int success;            // 1 if successful, 0 if error
    char *error_message;    // Error description (if any)
} unsandbox_result_t;

unsandbox_execute_async(language, code, public_key, secret_key)

Submit code for async execution, returns immediately with job ID.

char *job_id = unsandbox_execute_async("python", "print(42)", NULL, NULL);
if (job_id) {
    printf("Job ID: %s\n", job_id);
    free(job_id);
}

Returns: char * - Job ID string (must be freed) or NULL on error

unsandbox_wait_job(job_id, public_key, secret_key)

Wait for async job completion using exponential backoff polling.

unsandbox_result_t *result = unsandbox_wait_job(job_id, NULL, NULL);
if (result) {
    // Process result
    unsandbox_free_result(result);
}

Returns: unsandbox_result_t * - Result when job completes

unsandbox_get_job(job_id, public_key, secret_key)

Get current job status without waiting.

unsandbox_job_t *job = unsandbox_get_job(job_id, NULL, NULL);
if (job) {
    printf("Status: %s\n", job->status);  // "pending", "running", "completed"
    unsandbox_free_job(job);
}

unsandbox_cancel_job(job_id, public_key, secret_key)

Cancel a running job.

int result = unsandbox_cancel_job(job_id, NULL, NULL);
if (result == 0) {
    printf("Job cancelled\n");
}

unsandbox_list_jobs(public_key, secret_key)

List all active jobs for the user.

unsandbox_job_list_t *jobs = unsandbox_list_jobs(NULL, NULL);
if (jobs) {
    for (size_t i = 0; i < jobs->count; i++) {
        printf("%s: %s\n", jobs->jobs[i].id, jobs->jobs[i].status);
    }
    unsandbox_free_job_list(jobs);
}

Language Functions

unsandbox_detect_language(filename)

Detect language from file extension.

const char *lang = unsandbox_detect_language("script.py");  // Returns "python"

Supported extensions:

  • Python: .py
  • JavaScript: .js, TypeScript: .ts
  • Go: .go, Rust: .rs, C: .c, C++: .cpp
  • Ruby: .rb, PHP: .php, Bash: .sh
  • And 40+ more languages...

unsandbox_get_languages(public_key, secret_key)

Get list of all supported languages from the API.

unsandbox_languages_t *langs = unsandbox_get_languages(NULL, NULL);
if (langs) {
    for (size_t i = 0; i < langs->count; i++) {
        printf("%s\n", langs->languages[i]);
    }
    unsandbox_free_languages(langs);
}

Credential Management

4-Tier Priority System

Credentials are resolved in this order:

  1. Function arguments - unsandbox_execute("python", code, "key", "secret")
  2. Environment variables - UNSANDBOX_PUBLIC_KEY, UNSANDBOX_SECRET_KEY
  3. Home directory - ~/.unsandbox/accounts.csv (line 0, or UNSANDBOX_ACCOUNT=N)
  4. Current directory - ./accounts.csv (same format)

accounts.csv Format

public_key_1,secret_key_1
public_key_2,secret_key_2
public_key_3,secret_key_3

Usage Examples

# Option 1: Environment variables
export UNSANDBOX_PUBLIC_KEY="unsb-pk-..."
export UNSANDBOX_SECRET_KEY="unsb-sk-..."
./myapp

# Option 2: Config file
mkdir -p ~/.unsandbox
echo "unsb-pk-...,unsb-sk-..." > ~/.unsandbox/accounts.csv
chmod 600 ~/.unsandbox/accounts.csv
./myapp

# Option 3: Multiple accounts (select with UNSANDBOX_ACCOUNT)
echo "unsb-pk-1,unsb-sk-1" > ~/.unsandbox/accounts.csv
echo "unsb-pk-2,unsb-sk-2" >> ~/.unsandbox/accounts.csv
UNSANDBOX_ACCOUNT=1 ./myapp  # Uses account 2 (0-indexed)

# Option 4: Inline (least secure)
./myapp --public-key=unsb-pk-... --secret-key=unsb-sk-...

Manual Credential Resolution

char *public_key = NULL, *secret_key = NULL;

if (unsandbox_resolve_credentials(&public_key, &secret_key, NULL, NULL) == 0) {
    // Use public_key and secret_key
    unsandbox_result_t *result = unsandbox_execute("python", code, 
                                                   public_key, secret_key);
    // Clean up
    free(public_key);
    free(secret_key);
    unsandbox_free_result(result);
}

Memory Management

Always clean up allocated memory:

void unsandbox_free_result(unsandbox_result_t *result);
void unsandbox_free_job(unsandbox_job_t *job);
void unsandbox_free_job_list(unsandbox_job_list_t *jobs);
void unsandbox_free_languages(unsandbox_languages_t *langs);
void unsandbox_free_quota(unsandbox_quota_t *quota);

Utility Functions

unsandbox_last_error()

Get the last error message from a failed operation.

unsandbox_result_t *result = unsandbox_execute("python", "code", NULL, NULL);
if (!result) {
    printf("Error: %s\n", unsandbox_last_error());
}

unsandbox_health_check()

Check if the API is available.

int status = unsandbox_health_check();
// Returns: 1 = available, 0 = unavailable, -1 = error
if (status == 1) {
    printf("API is up\n");
}

unsandbox_version()

Get SDK version.

printf("SDK version: %s\n", unsandbox_version());

Supported Languages (50+)

Interpreted: Python, JavaScript, TypeScript, Ruby, PHP, Bash, Perl, Lua, R, Clojure, CommonLisp, Elixir, Erlang, Groovy, Idris2, Julia, Nim, Raku, Scheme, Tcl, Dart, Deno, Crystal, Kotlin

Compiled: C, C++, Go, Rust, Java, C#, F#, Haskell, OCaml, Cobol, D, Fortran, Odin, Pascal, V, Zig, Objective-C

Other: Prolog, Forth, WASM (C/C++/Rust/Zig/Go via Emscripten)

Error Handling

All functions return NULL or negative values on error. Check unsandbox_last_error() for details:

unsandbox_result_t *result = unsandbox_execute("python", "code", NULL, NULL);
if (!result) {
    fprintf(stderr, "Error: %s\n", unsandbox_last_error());
    return 1;
}

// Check execution errors
if (result->exit_code != 0) {
    fprintf(stderr, "Execution failed (exit %d): %s\n", 
            result->exit_code, result->stderr);
}

unsandbox_free_result(result);

Authentication

Requests are authenticated using HMAC-SHA256:

Authorization: Bearer <public_key>
X-Timestamp: <unix_seconds>
X-Signature: HMAC-SHA256(secret_key, "timestamp:METHOD:path:body")

The SDK handles this automatically.

Testing

# Run all tests
make test

# Test specific functionality
make test-library

# Run examples
./examples/hello_world
./examples/fibonacci

Performance

  • Synchronous execution: 50-200ms (Python/Bash) to 5-30s (JVM languages)
  • Asynchronous execution: ~50ms allocation + background execution
  • Polling backoff: 300ms → 450ms → 700ms → 900ms → ... → 2000ms (capped)

Limitations

  • Max request body: 1MB per execution
  • Max concurrent jobs per API key: Based on subscription tier
  • Timeout: 30 seconds per request
  • JSON parsing is simplified (use a real JSON library for complex responses)

Troubleshooting

"No credentials found"

Set credentials via environment, config file, or function arguments:

export UNSANDBOX_PUBLIC_KEY="your-key"
export UNSANDBOX_SECRET_KEY="your-secret"
./myapp

"HTTP 401 Unauthorized"

Check your credentials are valid:

curl -H "Authorization: Bearer $UNSANDBOX_PUBLIC_KEY" https://api.un.com/health

Compilation errors

Ensure dependencies are installed:

sudo apt install libcurl4-openssl-dev libssl-dev

Check include paths:

gcc -I/usr/include -c src/un.c

License

PUBLIC DOMAIN - NO LICENSE, NO WARRANTY

Use freely for any purpose.

Examples Directory

  • hello_world.c - Basic synchronous execution
  • fibonacci.c - Asynchronous execution with polling

Compile examples:

cd examples
make -C .. examples  # Or:
gcc -o hello_world hello_world.c ../src/un.c -I../src -lcurl -lssl -lcrypto
./hello_world

See Also

  • Python SDK: ../python/sync/src/un.py
  • Go SDK: ../go/sync/src/un.go
  • API Documentation: https://api.un.com/