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
| Posture | Meaning |
|---|---|
unmanaged | The system is known, but no Orqestra governance control is attached. |
observed | The system is inventoried and monitored through available evidence. |
gated | A pre-runtime or release checkpoint blocks unsafe use. |
routed | Orqestra 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=observedFetch 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-assignmentsets the assignment. Supplyaccountable_owner_refexplicitly, or omit it to default to the system’sowner_ref(the response’sowner_inference_sourcerecords which). Exactly one accountable owner is recorded; an unknown reviewer role is rejected with422.GET /v1/external-ai-systems/{system_id}/role-assignmentreturns the assignment plusrequired_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 withaccountable_owner_refandreviewer_rolefilters 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=0Each 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 5answer = 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:
- register the n8n workflow as an external AI system;
- certify an n8n external-runtime profile with the workflow digest, trigger posture, adapter artifact, and allowed mediated targets;
- bind that runtime profile to the Orqestra release;
- approve any required runtime permission grants for mediated tools, such as
the
mcp_clientplugin slug used by MCP calls; - start the run through
/v1/external-runtime/start; - call protected retrieval, model, external HTTP, and MCP capabilities through
/v1/external-runtime/capability-tokenand/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 graphThe 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=certifiedOperators 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_verifiedSupported 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 passedThe 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 contradictedExternal 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_gatedprofiles = await client.external_runtime_profiles.list(
engine="n8n",
conformance_level="admission_gated",
)orqestractl external-runtime-profile list \
--engine n8n --conformance-level admission_gatedExternal 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):
enrollcreates the enrollment inpausedmode, pinned to an approved, workspace-scoped qualification that binds the eligible accepted owner, and rewrites the profile version to the enrollment-ownedown1-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.pauseis proof/direct-authority-only and works even if the pinned qualification has expired — an operator can always stop new runs.activate(paused-only) flips tostrictand 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| Surface | Example |
|---|---|
| API | GET /v1/shadow-agents/ |
| Python SDK | client.shadow_agents.list() |
| CLI | orqestractl 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.