Search by

open-runtimes / sdk-for-php

loks0n

PHP SDK for the Open Runtimes orchestrator: jobs, deployments, and sandboxes.

Package info

github.com/open-runtimes/sdk-for-php

pkg:composer/open-runtimes/sdk-for-php

Statistics

Installs: 25 184

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-09-02 20:24 UTC

README

PHP SDK for the Open Runtimes orchestrator: jobs, deployments, and sandboxes.

Server: https://github.com/open-runtimes/orchestrator

Each service is its own client over one configured Utopia\Client:

$jobs        = new Jobs($http);
$deployments = new Deployments($http);
$sandboxes   = new Sandboxes($http);
use OpenRuntimes\Orchestrator\Enum\CallbackEvent;
use OpenRuntimes\Orchestrator\Jobs;
use OpenRuntimes\Orchestrator\Model\Artifact\DownloadArtifact;
use OpenRuntimes\Orchestrator\Model\Artifact\UploadArtifact;
use OpenRuntimes\Orchestrator\Model\Callback;
use Utopia\Client;
use Utopia\Client\Adapter\Curl\Client as CurlAdapter;

$http = new Client(new CurlAdapter())
    ->withBaseUri('http://localhost:8080')
    ->withBearerAuth('secret');

$jobs = new Jobs($http);

$response = $jobs->create(
    id: 'build-001',
    image: 'alpine:latest',
    command: 'sh -c "echo hello > /workspace/output.txt"',
    cpu: 0.5,
    memory: 512,
    timeoutSeconds: 300,
    artifacts: [
        new DownloadArtifact('source', 'https://example.com/source.tar.gz', 'code.tar.gz'),
        new UploadArtifact('result', 'output.txt', 'https://example.com/upload', depends: 'job'),
    ],
    callback: new Callback(
        url: 'https://app.example.com/orchestrator/events',
        events: [CallbackEvent::Log, CallbackEvent::Artifact, CallbackEvent::Exit],
        key: 'webhook-secret',
    ),
);

Jobs

$created = $jobs->create(id: 'build-001', image: 'alpine:latest', command: 'echo hello');
$status = $jobs->get('build-001');
$list = $jobs->list();
$jobs->delete('build-001');

Deployments

A deployment is a container serving HTTP behind the orchestrator's gateway, kept running, routable, and scaled — including down to zero. apply() is declarative: applying a changed spec for an existing id rolls out a new revision, and applying an identical one is a no-op.

use OpenRuntimes\Orchestrator\Deployments;
use OpenRuntimes\Orchestrator\Model\Autoscaling;
use OpenRuntimes\Orchestrator\Model\Probe;
use OpenRuntimes\Orchestrator\Model\Probes;

$deployments = new Deployments($http);

$web = $deployments->apply(
    id: 'web',
    image: 'ghcr.io/acme/web:v3',
    port: 8080,
    hosts: ['acme.com', 'www.acme.com'],
    autoscaling: new Autoscaling(minReplicas: 0, maxReplicas: 10, target: 100),
    probes: new Probes(readiness: new Probe(path: '/healthz', periodMillis: 500)),
    terminationGracePeriodSeconds: 60,
);

echo $web->url;               // primary host
echo $web->status->value;     // pending|ready|idle|degraded|failed|deleting

$deployments->get('web');
$deployments->list();
$deployments->delete('web');

Traffic

Every spec change mints an immutable revision, which makes canaries and rollbacks cheap. Pinning any split switches the deployment to manual mode; release() hands traffic back to auto.

use OpenRuntimes\Orchestrator\Model\TrafficTarget;

$revisions = $deployments->revisions('web');

// Canary: 90% stable, 10% new.
$deployments->setTraffic('web', [
    new TrafficTarget('web-00001', 90),
    new TrafficTarget('web-00002', 10),
]);

// Rollback is just a split.
$deployments->setTraffic('web', [new TrafficTarget('web-00001', 100)]);

// Back to auto: 100% on the latest revision, auto-cut re-armed.
$deployments->release('web');

Sandboxes

A sandbox is a live, isolated workspace you drive from the outside. Every create declares its complete pod shape. If the operator has matching warm capacity, the orchestrator claims it transparently; otherwise it creates the same pod directly.

use OpenRuntimes\Orchestrator\Enum\RuntimeClass;
use OpenRuntimes\Orchestrator\Sandboxes;

$sandboxes = new Sandboxes($http);

$sandbox = $sandboxes->create(
    image: 'python:3.12-slim',
    port: 3000,
    id: 'agent-run-42',
    ports: [5173],                 // extra ports, each at its own hostname
    timeoutSeconds: 0,             // no per-request bound, for long-lived sessions
    idleTimeoutSeconds: 900,
    artifacts: [
        new DownloadArtifact('code', 'https://acme.test/app.tar.gz', 'app.tar.gz'),
        new UnarchiveArtifact('unpack', 'app.tar.gz', '.', depends: 'code'),
    ],
    runtimeClass: RuntimeClass::Gvisor,
    terminationGracePeriodSeconds: 60,
);

$sandboxes->get('agent-run-42');
$sandboxes->list();
$sandboxes->delete('agent-run-42');   // invalidates the URL immediately

Running commands and moving files are not part of this API. They are an HTTP contract (POST /execute, GET|PUT|DELETE /files/{path}) served inside the sandbox, at the address in $sandbox->url — read secondary ports out of $sandbox->urls rather than building them.

Treat those URLs as secrets. Reaching one is sufficient to run commands in the sandbox, which is why the hostname carries an unguessable token instead of the id.

A read tells you what the sandbox is, not just where it is: $sandbox->image, $sandbox->cpu, and $sandbox->memory are the shape it is running in, recorded when its pod was created, so you never have to keep a record of what you asked for. They are null for a sandbox created before the orchestrator recorded it.

A sandbox that fails to materialize is not an error response: create() returns a status with SandboxState::Failed and an error, because the sandbox exists as a record you can read and delete.

Warm pools are operator-managed and have no public API or user-visible IDs in orchestrator 2.0. They can reduce startup latency for matching deployments and sandboxes without changing either SDK create surface or its returned status.

Errors

API responses with status >= 400 throw ApiException with statusCode, raw body, and decoded JSON when available.

use OpenRuntimes\Orchestrator\Exception\ApiException;

try {
    $jobs->get('missing');
} catch (ApiException $e) {
    echo $e->statusCode;
    echo $e->getMessage();
}

Callback Signatures

use OpenRuntimes\Orchestrator\Callback\Signature;

$valid = Signature::verifyEvent($rawBody, $headers['x-signature-256'] ?? '', $secret);

Development

composer install
composer test
composer analyze
composer format:check
composer refactor:check