Quickstart
1. Create a key in Developer → API keys (the demo sandbox key works out of the box). 2. Call preflight right before the risky step. 3. Execute only what Orbis authorized, then report the outcome.
import { OrbisClient } from "@orbis/sdk";
const orbis = new OrbisClient({ apiKey: process.env.ORBIS_API_KEY!, baseUrl: "http://localhost:4310" });
const decision = await orbis.preflight({ actor, action, resources, destination, intent });
if (decision.isAllowed()) await execute();
else if (decision.requiresApproval()) {
const final = await decision.waitForResolution({ timeoutMs: 600_000 });
if (final.approved) await execute(final.approvedParameters);
}
await orbis.reportOutcome(decision.actionId, "succeeded");from orbis_relay import OrbisClient
orbis = OrbisClient(api_key=os.environ["ORBIS_API_KEY"], base_url="http://localhost:4310")
d = orbis.preflight(actor=..., action=..., resources=[...], destination=..., intent=...)
if d.is_allowed: execute()
elif d.requires_approval:
final = d.wait_for_resolution(timeout=600)
if final.approved: execute(final.approved_parameters)Action envelope
Describe the action, not its payload. Orbis never needs raw documents or secrets — send classifications, counts, amounts and a redacted summary. The tenant is derived from your API key, never from the body.
{
"request_id": "req_01J…", // idempotency key
"actor": { "type": "agent", "id": "procurement-agent", "owner": "finance" },
"action": { "type": "external_send", "tool": "llm.summarize", "arguments_summary": "4 vendor contracts" },
"resources": [{ "type": "document", "classification": "confidential", "count": 4 }],
"destination": { "type": "external_model", "value": "quickscribe-ai.app" },
"intent": { "reason": "Respond to vendor RFP", "source": "agent" },
"business_context": { "deal_id": "D-82" },
"signals": { "incident_active": false },
"evidence": [{ "label": "Data class", "value": "Confidential — pricing terms", "source": "DLP" }],
"blast_radius": ["4 contracts leave the trust boundary"],
"ttl_seconds": 600
}Decision states
received → evaluating → allow | warn | deny | approval_required approval_required → pending → approved | approved_modified | rejected | expired | cancelled final authorization → outcome: succeeded | failed | not_executed | unknown
approved_modified means a human edited parameters or chose a safe alternative. Always execute with approved_parameters — e.g. the redirected destination.
Policy model
Policies are versioned rule sets. Published versions are immutable; drafts are simulated against real history and published with a second admin. Every enabled policy is evaluated; the most severe matching effect wins: allow < allow_log < warn < require_confirmation < require_approval < require_quorum < deny < freeze. A frozen actor is always denied.
{
"id": "ext_model_confidential",
"when": { "all": [
{ "field": "destination.type", "op": "eq", "value": "external_model" },
{ "field": "resource.classification_rank", "op": "gte", "value": 2 },
{ "field": "destination.approved", "op": "neq", "value": true } ] },
"effect": "require_approval", "route": "ai-governance", "step_up": "biometric",
"safe_alternatives": [{ "id": "redirect_internal_model", "type": "redirect",
"apply": { "destination": "northstar-private-llm" } }]
}The risk score (0–100) prioritizes and routes. It never overrides a hard rule, and core enforcement never calls a model.
Approval lifecycle
Approvals route to a group (resource owner, role, on-call). The server verifies assignment, tenant, state, expiry and required step-up before any response mutates state. SLA escalation fires at 50% of TTL; quorum rules require N distinct approvers; any rejection rejects. Push notifications carry only a title and a deep link.
Receipts
Every final decision yields a receipt: canonical JSON (sorted keys) signed with Ed25519, plus its SHA-256. Reporting an outcome issues revision 2, which commits to revision 1's hash. Verify with POST /v1/receipts/:id/verify, offline with the JWK at /v1/receipts/public-key, or on the verifier page.
Webhooks
Orbis-Signature: t=1791234567,v1=5f2b… // HMAC-SHA256(secret, t + "." + rawBody)
import { verifyWebhookSignature } from "@orbis/sdk";
if (!(await verifyWebhookSignature(secret, req.headers["orbis-signature"], rawBody))) return 401;Reject if the timestamp is more than 5 minutes old, compare in constant time, and dedupe on Idempotency-Key. Callback URLs must be public https endpoints — private, loopback and link-local addresses are rejected.
Agent adapter
import { createAgentAdapter } from "@orbis/sdk";
const adapter = createAgentAdapter(orbis, { type: "agent", id: "procurement-agent" });
const summarize = adapter.wrap({
name: "llm.summarize",
classify: (a) => ({ action: { type: "external_send" }, resources: [{ type: "document", classification: a.classification, count: a.docs.length }],
destination: { type: "external_model", value: a.provider } }),
applyApproved: (a, p) => ({ ...a, provider: String(p.destination ?? a.provider) }),
run: (a) => llm.summarize(a),
});
// Denials throw ToolBlockedError — the agent can explain and re-plan.Endpoint intelligence
An optional macOS endpoint runtime (apps/macos-endpoint, Swift + Core ML) scores each action locally from privacy-preserving metadata. Destinations are hashed on the device and content never leaves it. The gateway also scores every action itself. The edge risk model is advisory: deterministic policy decides first, and the model can only raise an outcome (allow → warn → approval), never lower one. It is ignored when unavailable, stale, incompatible or killed, and that rule is recorded on the decision and the receipt.
POST /v1/decisions/preflight
{ …envelope,
"endpoint_signal": { "endpoint_id": "ep_procurement-agent_host",
"signal": { "model_version": "1.0.0", "feature_schema": "edge-features/1", "class": "high_risk",
"risk": 0.99, "abstain": false, "runtime": "coreml-fp32", "reasons": [ … ] } } }
→ "ml": { "source": "local+cloud", "model": "orbis-edge-risk@1.0.0", "class": "high_risk",
"explanation": "High risk because sensitive data is going to an unverified destination…",
"fusion": { "rule": "deterministic_approval_wins", "changed": false } }Endpoints fetch an Ed25519-signed manifest (GET /v1/endpoint/models/manifest). It is bound to a tenant, feature schema, minimum app version, expiry and a monotonic sequence. The endpoint verifies the artifact sha256, compiles and smoke-tests the model, and activates it atomically. Model mutations (/v1/ml/models/:v/promote, /rollback, /v1/ml/settings) need an admin role, passkey step-up and a passing release-gate report. Analyst labels from POST /v1/ml/review/:prediction_id are exported with GET /v1/ml/feedback/export for offline retraining; they never change the live model. See Console → Intelligence for the registry, evaluation, fleet, drift and review queue.
Errors
{ "error": { "code": "step_up_required", "message": "…", "remediation": "Complete Face ID and resubmit." } }
401 unauthenticated · invalid_key · key_revoked 403 not_assigned · forbidden · device_not_trusted
404 not_found 409 already_resolved · idempotency_conflict 410 expired
413 payload_too_large 422 validation_error · field_not_editable · out_of_range 428 step_up_requiredAPI reference
Machine-readable contract: /api/v1/openapi (OpenAPI 3.1).
| POST | /v1/decisions/preflight | Evaluate a proposed action before it executes |
| POST | /v1/approvals | Create a human approval explicitly |
| GET | /v1/approvals | Approver inbox / console queue |
| GET | /v1/approvals/{id} | Read status and safe display context |
| POST | /v1/approvals/{id}/respond | Authorized response; server verifies assignment, state, expiry and step-up |
| POST | /v1/approvals/{id}/cancel | Caller withdraws a pending approval |
| GET | /v1/actions | Search actions and their decisions/outcomes |
| POST | /v1/actions/{id}/outcome | Caller reports execution outcome (idempotent) |
| GET | /v1/receipts/{id} | Fetch the final signed decision receipt |
| POST | /v1/receipts/{id}/verify | Verify receipt integrity (stored or supplied copy) |
| GET | /v1/receipts/public-key | Ed25519 JWK set for offline verification |
| GET | /v1/actors | Actor registry with freeze state |
| POST | /v1/actors/{id}/freeze | Freeze an actor — gateway denies all new requests |
| DELETE | /v1/actors/{id}/freeze | Unfreeze with permission and audit reason |
| GET | /v1/policies | List policies and versions |
| POST | /v1/policies/simulate | Run a candidate policy against historical actions |
| PUT | /v1/policies/{id}/draft | Save draft (published versions are immutable) |
| POST | /v1/policies/{id}/publish | Publish draft with second approver |
| POST | /v1/webhooks/test | Validate a callback endpoint and preview the signed request |
| GET | /v1/audit/events | Search the tenant audit trail |
| GET | /v1/audit/verify | Recompute the audit hash chain |
| POST | /v1/protect/analyze | Explicit user-submitted URL/text/QR/screenshot-text risk analysis |
| POST | /v1/auth/device | Orbis iOS sign-in + device registration (demo SSO) |
| GET | /v1/me | Current user, device and permissions |
| GET | /v1/stream | Server-Sent Events for live updates |
| GET | /v1/endpoint/models/manifest | Ed25519-signed model manifest for this endpoint (production or canary by bucket). Header X-Orbis-Endpoint. |
| GET | /v1/endpoint/models/signing-key | Public Ed25519 key (JWK) for model manifests |
| GET | /v1/endpoint/models/{name}/{version}/artifact | Redirect to the zipped .mlpackage (integrity from the signed manifest sha256) |
| POST | /v1/endpoint/models/{name}/{version}/activation-result | Report activation success/failure (signature, hash, compile, smoke test) |
| POST | /v1/endpoint/health | Endpoint health snapshot (latency, memory, failures); may trigger automatic canary rollback |
| POST | /v1/endpoint/events/batch | Privacy-filtered telemetry batch (summary fields only) |
| GET | /v1/ml/models | Model registry: lifecycle, release gates, parity, benchmarks, lineage |
| GET | /v1/ml/models/{id} | One model version |
| GET | /v1/ml/models/{id}/evaluation | Machine-readable evaluation report |
| POST | /v1/ml/models/{id}/promote | Promote to shadow / canary / production (admin + step-up + passing gates) |
| POST | /v1/ml/models/{id}/rollback | Roll back to the previous production model (step-up) |
| POST | /v1/ml/settings | Fusion mode (off/advisory/escalate) and model kill switch (step-up) |
| GET | /v1/ml/drift | Feature/prediction PSI per 7-day window, OOD rate, behavioural drift, retrain trigger |
| GET | /v1/ml/quality | Prediction mix, abstention, policy/human disagreement, confirmed FP/FN |
| GET | /v1/ml/endpoints | Fleet: active vs assigned model, health, activations |
| GET | /v1/ml/review | Active-review queue |
| POST | /v1/ml/review/{id} | Analyst label for a prediction (enters the next dataset version, never the live model) |
| GET | /v1/ml/feedback/export | JSONL of reviewed labels with canonical events (input to orbis_ml retrain) |