Skip to Content
Task guidesTechnical: External AI Systems

External AI Systems

Technical reference. This page is for integrations, advanced configuration, or platform operations. For portal tasks, start with the user guide.

External AI systems are AI projects, agents, model deployments, workflows, tools, and applications that run outside Orqestra but still need to appear in the governance inventory. The registry records ownership, source platform, dependency hints, posture, and gaps so operators can see where the external estate is governed, partially governed, or still unmanaged.

This registry is an inventory and posture surface. Registering a system does not route traffic through Orqestra, enforce policy, or claim runtime control over the external platform. Runtime enforcement is added by later integration work that uses the inventory as its source of truth.

Posture Values

PostureMeaning
unmanagedThe system is known, but no Orqestra governance control is attached.
observedThe system is inventoried and monitored through available evidence.
gatedA pre-runtime or release checkpoint blocks unsafe use.
routedOrqestra controls launch or admission into the external runtime.

Use governance_gaps to record missing controls such as absent runtime hooks, unverified data entitlements, unclear ownership, or unapproved provider routes. These gaps are advisory facts on the registry row; they do not block execution by themselves.

routed does not mean full Orqestra runtime governance. It means controlled launch or admission. Strong runtime-governance claims require mediated sensitive capabilities or certified equivalent controls from a later protocol slice.

Registration Shape

Create rows through the control-plane API:

POST /v1/external-ai-systems/?workspace_id=<workspace-id>

The request must identify the source platform, external reference, display name, owner, workload type, and current posture:

{ "source_platform": "azure_ai_foundry", "external_ref": "azure:/subscriptions/sub-1/resourceGroups/rg-ai/projects/claims", "display_name": "Claims Assistant", "owner_ref": "team:claims-ai", "business_service": "Claims Operations", "workload_type": "agent", "risk_classification": "high", "governance_posture": "observed", "model_provider_dependencies": [ { "provider": "your-cloud-ai-provider", "route_id": "your-region" } ], "data_source_dependencies": [{ "kind": "azure_ai_search", "region": "westeurope" }], "tool_dependencies": [{ "kind": "function", "name": "claim_lookup" }], "governance_gaps": [{ "code": "no_runtime_hook", "severity": "medium" }], "metadata": { "resource_group": "rg-ai" } }

Supported source platforms cover the major cloud AI-hosting platforms, workflow orchestrators (databricks, langgraph, n8n, kestra), and custom for anything else — see the source_platform field’s enum in the OpenAPI schema for the exact, current set of identifiers. Supported workload types are project, agent, model_deployment, workflow, application, knowledge_resource, tool, and other.

Access And Scope

Rows are workspace-scoped. A caller needs external_ai.manage to register a system and external_ai.read to list or read one. Reads and writes are filtered by workspace_id, so a system registered in one workspace is not visible through another workspace’s registry view.

The operator’s External Systems view also shows linked runtime profiles and Hermes runtime inventory for the active workspace. Hermes telemetry is passive evidence: listing a runtime does not launch it or route its traffic through Orqestra. The detail view links profiles by their profile identity and links Hermes inventory by its runtime identity; a profile ID is not assumed to equal the external system ID.

List rows with optional filters:

GET /v1/external-ai-systems/?workspace_id=<workspace-id>&governance_posture=observed

Fetch a single row by ID:

GET /v1/external-ai-systems/<system-id>?workspace_id=<workspace-id>

Policy Attachment

Attach a policy to a registered external AI system through the control-plane API:

POST /v1/external-ai-systems/<system-id>/policy-attachments?workspace_id=<workspace-id>

The request names the policy reference and the enforcement mode Orqestra can honestly support for this external system:

{ "policy_ref": "policy-bundle:external-claims-governance@v1", "enforcement_mode": "routed", "evidence_requirements": [{ "kind": "release_evaluation", "required": true }], "review_cadence": "P30D" }

The response is the updated registry row. policy_attachments records the mode-specific evidence statement, including the control surface and runtime governance claim. For routed, Orqestra records control_surface: "controlled_launch" and runtime_governance_claim: "not_full_runtime_governance", and it adds the routed_not_full_runtime_governance gap when that gap is not already present.

Attaching policy requires external_ai.manage, is filtered by workspace_id, and writes a governance audit event.

Role Assignments (RACI)

Each external AI system can carry one accountable owner plus an optional list of reviewers over a closed RACI reviewer taxonomy (responsible, consulted, informed). These are advisory governance metadata — never platform auth roles — and assigning them never blocks draft, sandbox, release, or runtime work.

  • PUT /v1/external-ai-systems/{system_id}/role-assignment sets the assignment. Supply accountable_owner_ref explicitly, or omit it to default to the system’s owner_ref (the response’s owner_inference_source records which). Exactly one accountable owner is recorded; an unknown reviewer role is rejected with 422.
  • GET /v1/external-ai-systems/{system_id}/role-assignment returns the assignment plus required_role_gaps — the advisory required reviewer roles still missing. This list is empty by default (nothing is required for unregulated low-risk systems); frameworks and oversight policies populate it later.
  • GET /v1/external-ai-systems/role-assignments/ lists the workspace’s assignments with accountable_owner_ref and reviewer_role filters for owner/reviewer queues.

Reuses the external_ai.read / external_ai.manage permission pair; the PUT writes an external_ai_system.role_assignment_set governance audit event with the owner/reviewer before/after diff.

async with OrqestraClient(base_url=url, api_key=key, workspace_id=ws) as client: await client.external_ai_systems.set_role_assignment( system_id, accountable_owner_ref="user:alice", reviewer_roles=[{"role": "consulted", "reviewer_ref": "team:legal"}], ) assignment = await client.external_ai_systems.get_role_assignment(system_id)
orqestractl external-ai roles set <system-id> \ --owner user:alice \ --reviewer consulted:team:legal orqestractl external-ai roles show <system-id>

Owner Continuity

Owner continuity is the authoritative owner-identity lifecycle for an external AI system — distinct from, and never blended with, the advisory RACI role assignment above. RACI answers “who is accountable in the org chart”; owner continuity answers “which identity currently holds authority over this system, and can that be proven.” Only the server ever answers the second question.

GET /v1/external-ai-systems/<system-id>/ownership returns the current record:

{ "system_id": "...", "revision": 4, "status": "eligible", "reason": null, "accepted_actor_ref": "user:...", "accepted_generation": 2, "accepted_at": "2026-05-01T00:00:00Z", "authority_lost": false, "authority_epoch": 0, "supplier_ref": "orq-package:vendor-x", "creator_ref": null, "operator_ref": "team:claims-ai", "pending_proposal_id": null }

status is one of unverified, eligible, or ineligible. A client must never compute this value itself. Any client reading this API — including a future one — that receives a status it does not recognize must treat it as ineligible with a generic reason and stop there; guessing at a new status’s meaning is exactly the failure mode this field exists to prevent. The Portal’s ownership panel (below) implements this fail-closed rule at the UI layer, not just as a suggestion.

reason is populated whenever status is unverified or ineligible and names a closed code (for example ownership_mapping_unqualified, ownership_check_unavailable); supplier_ref, creator_ref, and operator_ref are opaque provenance refs, editable through the provenance route below.

Transfer Lifecycle

An ownership transfer is proposed, then accepted, rejected, or cancelled. Every write is idempotency-keyed and carries expected_revision as an optimistic-concurrency precondition against the ownership record’s current revision:

GET /v1/external-ai-systems/<system-id>/ownership/transfers/<proposal-id>?workspace_id=<workspace-id> POST /v1/external-ai-systems/<system-id>/ownership/transfers?workspace_id=<workspace-id> POST /v1/external-ai-systems/<system-id>/ownership/transfers/<proposal-id>/accept?workspace_id=<workspace-id> POST /v1/external-ai-systems/<system-id>/ownership/transfers/<proposal-id>/reject?workspace_id=<workspace-id> POST /v1/external-ai-systems/<system-id>/ownership/transfers/<proposal-id>/cancel?workspace_id=<workspace-id>

Propose names the target identity and pins the expected revision:

{ "target_scim_user_id": "...", "target_sub": "...", "expected_revision": 4 }

Accept additionally proves the accepting actor’s qualification:

{ "expected_revision": 5, "qualification_id": "...", "expected_qualification_digest": "..." }

Accepting or rejecting a transfer requires a fresh human sign-in — the server independently verifies the session’s auth_time is within the last 300 seconds. This is checked server-side only; no client sends an auth_time value, and no client-side timer or affordance can substitute for it. Reject and cancel require Idempotency-Key and a body {"expected_revision": <revision>}. The same key, proposal, revision, and operation replays the closed proposal. A missing key is 422. Clients read one proposal, including expires_at, with the GET above.

A 409 (ownership_revision_conflict, ownership_transfer_not_pending, ownership_idempotency_conflict) means the record moved since the caller last read it; refetch and act on the current state rather than retrying blind. A 403 (permission_denied, ownership_acceptor_mismatch, ownership_identity_ineligible, qualified_actor_ineligible) is a closed denial — the caller does not hold the authority the action requires, and no retry of the same request changes that.

Provenance And History

Edit the provenance refs under the same revision precondition:

PATCH /v1/external-ai-systems/<system-id>/ownership/provenance?workspace_id=<workspace-id>
{ "supplier_ref": "orq-package:vendor-x", "expected_revision": 5 }

Read the append-only ownership history, paged with limit/offset:

GET /v1/external-ai-systems/<system-id>/ownership/events?workspace_id=<workspace-id>&limit=10&offset=0

Each event carries event_id, revision, event_type, actor_ref, and created_at — opaque refs, digests, and timestamps only; never a token, a qualification payload, or a SCIM name or email.

Portal Owner-Continuity Panel

Operators use the same read/write surface through the Owner continuity panel on the external AI system detail page (OwnershipPanel, backed by use-external-ai-systems.ts hooks). The panel renders every server status value, every closed reason code, the pending transfer with its expires_at, the provenance refs, and the paged history — computing no eligibility of its own. Accept and reject controls are shown to every viewer with read access; a 403 from the server is what closes them, not a client-side permission check hiding the button. If the advisory RACI role assignment above is rejected with a 409 ownership_transfer_required or role_assignment_conflict, the RACI panel links directly to this panel to resolve the pending transfer first.

Causal Path Explainability

The causal-path read explains how one AI system reaches another and how confident that path is. It is advisory and read-only — it never enforces anything at runtime.

GET /v1/causal-paths?workspace_id=<workspace-id>&source_system_id=<src>&target_system_id=<tgt>

A caller needs external_ai.read (EXTERNAL_AI_READ). Optional max_hops (0..5) bounds the walk and edge_type narrows it to matching edges. The response is the deterministic shortest path (each hop carries its confidence weight), the hop_count, the confidence_decay (the product of the path’s weights) and its band, the weakest_link, and the companion answers (root_cause, downstream_impact, cycle_detected).

Both systems are re-resolved through the workspace-scoped repository, so a foreign or unknown system — or a pair that straddles two namespaces — returns a 404 and never blends another tenant’s edges into the answer.

Dependency, impact, and causal responses include coverage_status: "partial". Observed edges come from accepted evidence only, ambiguous system references remain uncovered, and source-restricted observations are filtered before graph traversal. This surface never claims a complete history or exposes hidden-edge counts. The portal, SDK, and CLI preserve this status.

weight_unknown semantics. Confidence decay is the product of the path’s edge weights, so a single missing weight makes the whole decay undefined. When any hop on the path has no weight the API sets confidence_decay to null, the band to "weight_unknown", and lists the offending hops in unweighted_edges. It never fabricates a decayed confidence from partial data; treat weight_unknown as “the path exists but its confidence is not yet known”, and add weights to the underlying dependency edges to resolve it.

The CLI and SDK expose the same read:

orqestractl ai-dependency-graph causal-path <source-system-id> <target-system-id> \ --workspace-id <workspace-id> --max-hops 5
answer = await client.ai_dependency_graph.get_causal_path( source_system_id, target_system_id, workspace_id=workspace_id, )

A portal Causal path panel (CausalPathPanel) and a useCausalPath hook expose the same read for operator surfaces that pin a source and target system.

Operational Use

Use the registry to answer three questions:

  • Which externally operated AI systems exist in this workspace?
  • Who owns each system and which business service does it support?
  • Which systems are unmanaged, observed, gated, or routed, and what gaps remain?

Operators can inspect the same posture inventory in the portal under Connect -> External systems -> Systems. The table shows each registered system’s owner, source platform, workload type, risk classification, current governance posture, provider dependency count, and recorded governance gap codes.

The Python SDK exposes the same control-plane workflows through client.external_ai_systems:

systems = await client.external_ai_systems.list(governance_posture="observed") registered = await client.external_ai_systems.register( source_platform="azure_ai_foundry", external_ref="azure:/subscriptions/sub-1/resourceGroups/rg-ai/projects/claims", display_name="Claims Assistant", owner_ref="team:claims-ai", workload_type="agent", risk_classification="high", governance_posture="observed", governance_gaps=[{"code": "no_runtime_hook", "severity": "medium"}], ) updated = await client.external_ai_systems.attach_policy( registered["system_id"], policy_ref="policy-bundle:external-claims-governance@v1", enforcement_mode="routed", evidence_requirements=[{"kind": "release_evaluation", "required": True}], )

orqestractl external-ai provides the corresponding CLI surface:

orqestractl external-ai list --governance-posture observed orqestractl external-ai register \ --source-platform azure_ai_foundry \ --external-ref azure:/subscriptions/sub-1/resourceGroups/rg-ai/projects/claims \ --display-name "Claims Assistant" \ --owner-ref team:claims-ai \ --workload-type agent \ --risk-classification high \ --governance-posture observed \ --governance-gap '{"code":"no_runtime_hook","severity":"medium"}' orqestractl external-ai attach-policy <system-id> \ --policy-ref policy-bundle:external-claims-governance@v1 \ --enforcement-mode routed \ --evidence-requirement '{"kind":"release_evaluation","required":true}'

Hermes Passive Runtimes

Hermes agents use a dedicated passive-runtime API instead of the generic external-ai registration command. The generic registry intentionally rejects source_platform: "hermes" so Hermes rows always get the paired external runtime profile, passive-governance metadata, and hermes_runtime.registered audit event.

Passive Hermes registration supports only inventory_only and passive_evidence. It records ownership, adapter connection reference, declared skills, declared toolsets, trust level, and memory policy posture. It does not mediate Hermes execution, approve tool calls, or certify the external runtime.

Hermes releases can also be launched through Orqestra after a release is bound to a registered Hermes runtime profile. That launch path receives Orqestra release binding, policy, budget, service-lane, egress, evidence, and output filtering controls at the boundary. It remains controlled launch, not full runtime governance: Hermes internal tools, secrets, retrieval, memory writes, and side effects are not trusted as Orqestra-mediated capabilities unless a later conformance slice explicitly certifies those controls.

Register a passive Hermes runtime through the Python SDK:

runtime = await client.hermes_runtimes.register( runtime_ref="hermes:claims-assistant", display_name="Claims Hermes", owner_ref="team:claims-ai", adapter_connection_ref="connection:hermes-claims", declared_version="1.4.2", declared_skills=[{"name": "summarize_claim", "version": "1.0"}], declared_toolsets=[{"name": "claims", "tools": ["claims.search"]}], governance_level="passive_evidence", trust_level="medium", ) telemetry = await client.hermes_runtimes.list_telemetry(runtime["runtime_id"])

Register and inspect the same surface through the CLI:

orqestractl hermes-runtime register \ --runtime-ref hermes:claims-assistant \ --display-name "Claims Hermes" \ --owner-ref team:claims-ai \ --adapter-connection-ref connection:hermes-claims \ --declared-version 1.4.2 \ --declared-skill '{"name":"summarize_claim","version":"1.0"}' \ --declared-toolset '{"name":"claims","tools":["claims.search"]}' \ --governance-level passive_evidence \ --trust-level medium orqestractl hermes-runtime list --governance-level passive_evidence orqestractl hermes-runtime telemetry <runtime-id>

Passive telemetry ingestion is replay-safe through the idempotency_key. Imported Hermes payload and summary content are treated as untrusted input; prompt-injection-shaped content is quarantined and surfaced through quarantined and redaction_flags in the response and portal telemetry view.

For launched runs, execution evidence includes a hermes_execution_normalization artifact. Operators can review it in the portal evidence detail view or through the SDK/CLI execution evidence surfaces. The artifact records runtime_kind: "hermes", governance_claim: "launched_through_orqestra", capability_mediation: "not_claimed", output-filter status, normalized trace counts, and caveats such as not_full_runtime_governance.

launch_evidence = await client.executions.list_hermes_launch_evidence("<execution-id>")
orqestractl execution hermes-evidence <execution-id>
await client.hermes_runtimes.ingest_telemetry( runtime["runtime_id"], event_ref="hermes:event/session-1", idempotency_key="session-1", event_type="session", occurred_at="2026-05-23T10:00:00Z", payload={"message": "observed"}, summary={"note": "read-only passive telemetry"}, )
orqestractl hermes-runtime ingest-telemetry <runtime-id> \ --event-ref hermes:event/session-1 \ --idempotency-key session-1 \ --event-type session \ --occurred-at 2026-05-23T10:00:00Z \ --payload-json '{"message":"observed"}' \ --summary-json '{"note":"read-only passive telemetry"}'

n8n Governed Runtimes

n8n workflows can be registered as external AI systems and, when the n8n runtime profile is certified and bound to a release, launched through the Orqestra external-runtime protocol. In that mode n8n remains the visible workflow runtime, but protected capability calls use the first-party Orqestra n8n adapter.

The supported governed path is:

  1. register the n8n workflow as an external AI system;
  2. certify an n8n external-runtime profile with the workflow digest, trigger posture, adapter artifact, and allowed mediated targets;
  3. bind that runtime profile to the Orqestra release;
  4. approve any required runtime permission grants for mediated tools, such as the mcp_client plugin slug used by MCP calls;
  5. start the run through /v1/external-runtime/start;
  6. call protected retrieval, model, external HTTP, and MCP capabilities through /v1/external-runtime/capability-token and /v1/external-runtime/capability-request.
orqestractl release bind-runtime <release-id> \ --runtime-profile <n8n-runtime-profile-id> \ --adapter-connection-ref <adapter-connection-ref> orqestractl release runtime-bindings <release-id> orqestractl execution evidence-graph <execution-id> --format graph

The governance claim is intentionally bounded. Orqestra governs release-bound start admission and the capabilities routed through the adapter. It does not claim control over arbitrary n8n nodes, n8n internals, or direct n8n side effects that bypass Orqestra mediation.

For the local Sample prospect demo, use the docs/proof-packs/n8n-governed-runtime-demo.md proof pack. It includes the 10-minute script, setup and reset commands, expected denials, and evidence capture map.

External Runtime Certification Posture

External runtime certification is separate from connector/plugin certification. Connector certification describes whether an installed tool integration may be granted to workloads. Runtime certification describes the posture of an external runtime profile and uses its own runtime_certification_* fields, API route, SDK client, CLI command, storage table, and audit action.

The list API returns posture summaries only. It does not expose signing-key anchors or raw evidence bundles:

GET /v1/external-runtime-certifications/?workspace_id=<workspace-id>&runtime_certification_state=certified

Operators holding EXTERNAL_AI_MANAGE can append a complete first-party n8n certification with an Idempotency-Key; the request body is the closed bundle contract and is normally supplied through the CLI JSON-file/stdin path. Complete operator readback is available by id or for the exact profile/runtime head:

POST /v1/external-runtime-certifications/?workspace_id=<workspace-id> Idempotency-Key: <stable-key> GET /v1/external-runtime-certifications/<certification-id>?workspace_id=<workspace-id> GET /v1/external-runtime-certifications/latest?workspace_id=<workspace-id>&profile_id=<profile-id>&runtime_id=<runtime-id>

Readback exposes non-secret identity, lock digests, target allow-sets, and profile-binding hashes; private keys, credential values, raw wrapper manifests, workflow content, and operator idempotency keys are never returned. A revoked, stale, malformed, or profile-drifted historical head is not replaced by an older row.

Filter by state, level, evidence completeness, and proof strength:

certifications = await client.external_runtime_certifications.list( runtime_certification_state="certified", runtime_certification_level="capability_mediated", evidence_completeness="complete", proof_strength="orqestra_verified", )
orqestractl external-runtime-certification list \ --state certified \ --level capability_mediated \ --evidence-completeness complete \ --proof-strength orqestra_verified

Supported certification states are uncertified, pending, certified, production_approved, restricted, deprecated, revoked, and stale. Supported levels are inventory_only, passive_evidence, admission_gated, routed_execution, capability_mediated, and certified_governed_runtime.

Incident response — revoking runtime certifications

Beyond the read surface above, operators holding EXTERNAL_AI_MANAGE can hard-revoke certifications: one at a time, or in bulk against every certification tied to a compromised runtime or signing key. Revocation is terminal and idempotent — an already-revoked certification is returned unchanged — and each transition emits a canonical v1.governance.external_runtime_certification_state_changed audit row, readable back through the state-history endpoint.

POST /v1/external-runtime-certifications/<certification-id>/revoke?workspace_id=<workspace-id> POST /v1/external-runtime-certifications/incidents/revoke-by-runtime?workspace_id=<workspace-id> POST /v1/external-runtime-certifications/incidents/revoke-by-signing-key?workspace_id=<workspace-id> GET /v1/external-runtime-certifications/<certification-id>/state-history?workspace_id=<workspace-id>
await client.external_runtime_certifications.revoke( certification_id, rationale="confirmed signing-key compromise" ) await client.external_runtime_certifications.revoke_by_runtime( runtime_id="runtime-1", rationale="compromised host" ) await client.external_runtime_certifications.revoke_by_signing_key( signing_key_ref="key-ref-1", rationale="key compromised" ) history = await client.external_runtime_certifications.state_history(certification_id)
orqestractl external-runtime-certification revoke <certification-id> \ --rationale "confirmed signing-key compromise" orqestractl external-runtime-certification revoke-by-runtime \ --runtime-id runtime-1 --rationale "compromised host" orqestractl external-runtime-certification revoke-by-signing-key \ --signing-key-ref key-ref-1 --rationale "key compromised" orqestractl external-runtime-certification state-history <certification-id>

External Runtime Run-Evidence Summary Posture

The run-summary half of the read surface — trace_state, bypass_check_status, evidence_completeness, evidence_seal_state, and governance_seal_status per execution’s latest reconciliation — is exposed separately from certification posture. The materialized governance_claim_label and gap_reasons are role-projected server-side: holding EXTERNAL_AI_MANAGE sees the exact gap detail, EXTERNAL_AI_READ alone sees the safe posture summary only.

summaries = await client.external_runtime_run_evidence_summaries.list( trace_state="sequence_complete", bypass_check_status="passed", evidence_completeness="complete", )
orqestractl external-runtime-run-evidence-summary list \ --trace-state sequence_complete --bypass-check-status passed

The response also carries the §13 sovereignty-closure posture and the per-capability reconciliation detail. sovereignty_closure_status is coarse posture and is always visible; sovereignty_gap and capability_evidence follow the same role projection as gap_reasons (None for an EXTERNAL_AI_READ-only caller). Filtering on sovereignty_closure_status is ungated, but filtering on gap_reason, capability_kind, capability_reconciliation_status, capability_evidence_completeness, or capability_proof_strength requires EXTERNAL_AI_MANAGE — a consumer-tier caller supplying any of them is denied (403), never silently narrowed, since row membership would otherwise leak the withheld detail one filter value at a time:

summaries = await client.external_runtime_run_evidence_summaries.list( sovereignty_closure_status="open_gap", capability_reconciliation_status="contradicted", # operator-only )
orqestractl external-runtime-run-evidence-summary list \ --sovereignty-closure-status open_gap \ --capability-reconciliation-status contradicted

External Runtime Profile Posture

Profile identity + posture (engine, protocol_version, conformance_level, mediated_capabilities, …) is exposed as its own read-only surface, separate from the certification and run-evidence postures it governs. Internal contract detail (workflow digest contract, capability gateways, commissioned contract versions, declared config, …) is never included, and a profile record carries no governance-claim label, so there is no role-based projection here.

GET /v1/external-runtime-profiles/?workspace_id=<workspace-id>&engine=n8n&conformance_level=admission_gated
profiles = await client.external_runtime_profiles.list( engine="n8n", conformance_level="admission_gated", )
orqestractl external-runtime-profile list \ --engine n8n --conformance-level admission_gated

External Runtime Profile Ownership Enrollment

An external runtime profile is under ownership-continuity admission control only when it is explicitly enrolled. Absent enrollment means legacy behavior: outside continuity coverage, no admission gate. Enrollment is an operator decision per workspace, never an automatic state.

The lifecycle is a closed operation set — enroll, requalify, pause, activate — each an idempotency-keyed, audited transition guarded by optimistic concurrency (expected_profile_version, expected_gate_revision):

  • enroll creates the enrollment in paused mode, pinned to an approved, workspace-scoped qualification that binds the eligible accepted owner, and rewrites the profile version to the enrollment-owned own1- digest. Only synchronous-request profiles (the only ingress that puts Orqestra in the run-start path) can be enrolled.
  • requalify (paused-only) pins a fresh approved qualification and supersedes the enrolled version; the prior version is retained.
  • pause is proof/direct-authority-only and works even if the pinned qualification has expired — an operator can always stop new runs.
  • activate (paused-only) flips to strict and requires the exact active-complete certification for the enrolled profile version and binding. No client can force strict success.

In strict mode every new-run admission re-derives the live ownership decision (identity, generation, qualification, direct authority) outside the decision cache, so a revocation, an identity change, or an authority loss denies the very next run even inside a cached allow window. Denials are closed, redacted reasons: ownership_unverified, ownership_ineligible, ownership_identity_changed, ownership_check_unavailable, ownership_profile_paused. The admission response carries the enrollment’s ownership_mode for enrolled profiles; the identity generation and the evidence trail live on the ownership API and the governance audit log.

GET /v1/external-runtime-profiles/<profile-id>/ownership-enrollment?workspace_id=<workspace-id> POST /v1/external-runtime-profiles/<profile-id>/ownership-enrollment?workspace_id=<workspace-id>
enrollment = await client.external_runtime_profiles.apply_ownership_enrollment( profile_id, operation="enroll", expected_profile_version="2026-05-20", expected_gate_revision=0, idempotency_key="enroll-attempt-1", qualification_id=qualification_id, expected_qualification_digest=qualification_digest, )
orqestractl external-runtime-profile ownership-enrollment apply <profile-id> \ --operation enroll --expected-profile-version 2026-05-20 \ --expected-gate-revision 0 \ --qualification-id <qualification-id> \ --expected-qualification-digest <digest>

The CLI prints the idempotency key to stderr before dispatch; reuse it to retry the identical command after a timeout. A reused key with a different payload is a terminal conflict, not a second mutation.

Azure AI Foundry Discovery

The first reference adapter discovers Azure AI Foundry posture from contract fixtures or an injected HTTP client. It lists projects through the Azure management API and then normalizes project-level deployments, model routes, agents, tools, knowledge resources, identities, and provider accounts into the same registry payload shape used by POST /v1/external-ai-systems/. Foundry identities are registered as workload_type: "other" with identity metadata so the adapter stays within the current registry contract.

Discovery results use governance_posture: "observed" and include explicit governance_gaps when Foundry exposes inventory but not an enforceable hook. Credential values are request-only inputs to the adapter; audit-safe result metadata records the configured credential reference, not the bearer token or secret value.

Portal, SDK, CLI, and scheduled operator workflows can build on this adapter and registry API without changing the registration shape.

Shadow Agent Detection

Shadow agent reconciliation surfaces agent identities observed in tracked fleets (executions, agent sessions) but not yet registered in the external AI or agent inventory — the same visibility gap this registry exists to close. Each row carries the observed identity, a display name, an owner reference where known, the last-seen timestamp, and the reason it is flagged as shadow (for example, observed without registration). It reuses EXTERNAL_AI_READ — the same permission gating the inventory reads above — rather than a new permission, since every role that can read the existing inventories already holds it.

orqestractl shadow-agent list
SurfaceExample
APIGET /v1/shadow-agents/
Python SDKclient.shadow_agents.list()
CLIorqestractl shadow-agent list

This is a detection read, not an enforcement gate: listing a shadow agent does not block, quarantine, or otherwise act on it. Registering the identity through POST /v1/external-ai-systems/ (see Registration Shape above) is the follow-up action that closes the inventory gap.