diff --git a/swagger/docs.go b/swagger/docs.go index e5b0bcdd9d..da3aeb84f9 100644 --- a/swagger/docs.go +++ b/swagger/docs.go @@ -752,6 +752,448 @@ const docTemplate = `{ } } }, + "/api/addons/host-grants": { + "get": { + "description": "Administrators only (an administrator's session or API key). Returns the revision of the grants document, whether the deployment locks the UI store, the blanket approval switch (mode off, official or any; setBy ui or deployment), the deployment floor (the policy file and flags, read-only), the deny rules by source (file and flags, which cannot be edited here, and ui), and for each installed add-on the operations its verified manifest requests (required and optional) with a plain title, text and risk, whether each is allowed, not_allowed, blocked or unsupported, where the grant comes from (deployment, install, upgrade, edit or blanket), when and by whom, the operations granted that the manifest no longer requests, an identity mismatch flag and a pending upgrade that waits for approval. Read-only. Nothing in the answer is a credential. No MCP tool maps to this route.", + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Read the host access policy and what each add-on holds (administrators)", + "responses": { + "200": { + "description": "Host access state", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsView" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "Feature or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Addons or the owner are unavailable", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/blanket": { + "put": { + "description": "Administrators only, from their own browser session (X-LocalAI-Confirm equal to the operationId). Mode off, official or any. With it on, an install or upgrade grants the add-on the operations its verified manifest declares as required (also the optional ones with includeOptional) without the dialog step; it never grants an undeclared or unregistered operation and never overrides a deny rule. Mode official covers only the official library with its official signer. Mode any covers every library the administrator configured: ANY LIBRARY ENTRY THEN RECEIVES EXACTLY THE OPERATIONS ITS MANIFEST DECLARES, with no review, so a malicious or compromised entry gets what it asks for. Turning it on, or widening it, needs confirm equal to \"grant official add-ons what they declare\" (official) or \"grant any library add-on what it declares\" (any), else 400 confirmation_mismatch. applyToInstalled also grants the installed add-ons in the same revision. Turning it off keeps the grants it made, marked blanket; revokeOnOff revokes them all. When the deployment fixes the mode the answer is 403 locked_by_deployment.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Turn the blanket approval on, off or wider (browser session, typed confirmation)", + "parameters": [ + { + "description": "Blanket change", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsBlanketRequest" + } + } + ], + "responses": { + "200": { + "description": "The switch, the add-ons that were granted or skipped and the add-ons whose blanket grants were revoked", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsBlanketResult" + } + }, + "400": { + "description": "Malformed request, or confirmation_mismatch", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "not_human_surface, locked_by_deployment or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or operation_conflict", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/deny": { + "put": { + "description": "Administrators only. A deny rule beats every grant. deny maps an add-on ID, or \"*\" for all, to prefixes (\"egress.\"), exact operation names or the single token \"*\". The rules of the policy file and flags are not editable here and are unioned with these. With dryRun true the answer is the affected add-ons only (affects: the operations each would lose and whether it will stop because a required one is lost) and nothing changes; a dry run needs the administrator but not the browser session. Without dryRun the change needs the administrator's own browser session (X-LocalAI-Confirm equal to the operationId), the revision the caller read (409 revision_conflict otherwise), and applies to the next call with no restart.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Replace the deny rules set in the UI, or preview their effect (browser session)", + "parameters": [ + { + "description": "Deny rules", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsDenyRequest" + } + } + ], + "responses": { + "200": { + "description": "The rules and what they take", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsDenyResult" + } + }, + "400": { + "description": "Malformed request or invalid rule", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "not_human_surface, locked_by_deployment or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or operation_conflict", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/install": { + "post": { + "description": "Administrators only, from their own browser session (same-origin fetch, X-LocalAI-Confirm equal to the operationId). API keys, bearer tokens, MCP sessions and tool calls are refused with 403 not_human_surface. Carries the approval the dialog collected: the digest of the previewed package, the operations ticked, the revision of the grants document and the preview nonce. The owner stages the package again and grants only the operations that are approved AND declared by that verified manifest AND registered by the host AND not denied; a different package is 409 approval_stale, a changed document 409 revision_conflict, an expired or reused nonce 409 approval_stale. A required operation that is neither held nor approved is refused (approval_required) and one that a deny rule blocks is 403 blocked_by_policy; nothing is written then. For action upgrade, expectedDigest and candidateDigest pin the upgrade as in the plain upgrade. The plain dispatch install carries no approval. Repeating the operationId with the same request returns the recorded operation. After outcome_unknown keep the operationId and poll the operation status.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Install or upgrade an add-on with the host access the administrator approved (browser session)", + "parameters": [ + { + "description": "Install with approval", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsInstallRequest" + } + } + ], + "responses": { + "200": { + "description": "The lifecycle operation, with the host operations summary", + "schema": { + "$ref": "#/definitions/addons.OperationStatus" + } + }, + "400": { + "description": "Malformed request", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "Not a human surface (not_human_surface), locked_by_deployment, blocked_by_policy, approval_required, or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or approval_stale", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/preview": { + "post": { + "description": "Administrators only (an administrator's session or API key). The owner resolves and stages the release, reads the verified manifest of the staged package, computes the answer against the policy and discards the stage. Returns the digest of the staged package, the requested operations with plain text and risk, and for each one held (already allowed), grantable, blocked (a deny rule, which wins over every grant) or unsupported; canInstall false with blockedRequired when a required operation is blocked; whether the blanket approval would grant on install; the revision of the grants document; and a single-use nonce (five minutes, bound to the caller's credential, the add-on, the digest and the revision) that the install must present. Operations granted before an earlier uninstall carry hinted true so the dialog may tick them; a hint grants nothing. Changes no state.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Preview the host access an install or upgrade would ask for (administrators)", + "parameters": [ + { + "description": "Release to preview", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsPreviewRequest" + } + } + ], + "responses": { + "200": { + "description": "What the release asks for", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsPreviewView" + } + }, + "400": { + "description": "Malformed or unknown request fields", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "Feature or administration denied, or the release could not be verified", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Addons or the owner are unavailable", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/{addonId}": { + "put": { + "description": "Administrators only, from their own browser session (X-LocalAI-Confirm equal to the operationId). ops is the full set the add-on keeps: operations left out are revoked, new ones must be declared by the installed manifest, registered by the host and not denied (403 blocked_by_policy). Grants made by the policy file or flags are not editable here. keepBlanketAsOwn relabels the blanket grants as the administrator's own. revision is the revision the caller read; a stale one is 409 revision_conflict. Takes effect for the next call of the add-on with no restart.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Set the host operations an add-on keeps through the UI (browser session)", + "parameters": [ + { + "type": "string", + "description": "Add-on ID", + "name": "addonId", + "in": "path", + "required": true + }, + { + "description": "Edit", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsEditRequest" + } + } + ], + "responses": { + "200": { + "description": "The add-on entry and the new revision", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsAddonResult" + } + }, + "400": { + "description": "Malformed request", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "not_human_surface, locked_by_deployment, blocked_by_policy or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "404": { + "description": "The add-on is not installed", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or operation_conflict", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + }, + "delete": { + "description": "Administrators only, from their own browser session (X-LocalAI-Confirm equal to the operationId). Removes the UI and blanket grants of the add-on; grants of the policy file stay. Query: revision (the revision the caller read) and operationId. Takes effect for the next call with no restart; a running call finishes. An add-on whose required operation is revoked becomes unavailable.", + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Revoke every host operation granted to an add-on through the UI (browser session)", + "parameters": [ + { + "type": "string", + "description": "Add-on ID", + "name": "addonId", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Revision of the grants document the caller read", + "name": "revision", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Operation ID", + "name": "operationId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "The new revision", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsRevision" + } + }, + "400": { + "description": "Malformed request", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "not_human_surface, locked_by_deployment or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "404": { + "description": "The add-on is not installed", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or operation_conflict", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, "/api/addons/pins": { "get": { "description": "Returns the ordered add-on IDs the caller pinned to the sidebar. The route never pins anything by itself; Studio and Talk are pinned once, at upgrade, for the accounts that existed then. The list holds only IDs the caller may see right now: a hidden or removed add-on is left out and the route does not reveal whether it exists. Needs an authenticated caller and no administrator role.", @@ -14808,6 +15250,37 @@ const docTemplate = `{ } } }, + "addons.OperationStatus": { + "type": "object", + "properties": { + "addonID": { + "type": "string" + }, + "failureCode": { + "type": "string" + }, + "generation": { + "type": "integer", + "format": "int64" + }, + "hostOperations": { + "$ref": "#/definitions/hostops.RequestSummary" + }, + "operationID": { + "type": "string" + }, + "phase": { + "type": "string" + }, + "runtimeState": { + "type": "string" + }, + "stage": { + "description": "RuntimeState is an observation, never a durable execution receipt.", + "type": "string" + } + } + }, "addons.RecoveryBackupRequest": { "type": "object", "properties": { @@ -16844,6 +17317,521 @@ const docTemplate = `{ } } }, + "gateway.HostBlanketChange": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "ops": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gateway.HostBlanketPreview": { + "type": "object", + "properties": { + "applies": { + "type": "boolean" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostBlanketSkip": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "reason": { + "type": "string" + } + } + }, + "gateway.HostBlanketView": { + "type": "object", + "properties": { + "includeOptional": { + "type": "boolean" + }, + "mode": { + "type": "string" + }, + "revision": { + "type": "integer" + }, + "setBy": { + "description": "SetBy is ui, or deployment when a flag fixes the mode.", + "type": "string" + } + } + }, + "gateway.HostDenyAffect": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "loses": { + "type": "array", + "items": { + "type": "string" + } + }, + "willStop": { + "type": "boolean" + } + } + }, + "gateway.HostFloorAddon": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "prefixes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gateway.HostGrantsAddon": { + "type": "object", + "properties": { + "blanketGrants": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "identityMismatch": { + "type": "boolean" + }, + "noLongerRequested": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostOpView" + } + }, + "ops": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostOpView" + } + }, + "pendingUpgrade": { + "$ref": "#/definitions/gateway.PendingUpgrade" + }, + "requested": { + "$ref": "#/definitions/gateway.HostRequested" + }, + "state": { + "type": "string" + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "gateway.HostGrantsAddonResult": { + "type": "object", + "properties": { + "addon": { + "$ref": "#/definitions/gateway.HostGrantsAddon" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsApprove": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "ops": { + "type": "array", + "items": { + "type": "string" + } + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsBlanketRequest": { + "type": "object", + "properties": { + "applyToInstalled": { + "type": "boolean" + }, + "confirm": { + "description": "Confirm is the typed confirmation text, required to turn the switch\non or widen it.", + "type": "string" + }, + "includeOptional": { + "type": "boolean" + }, + "mode": { + "type": "string" + }, + "operationId": { + "type": "string" + }, + "revision": { + "type": "integer" + }, + "revokeOnOff": { + "description": "RevokeOnOff, with mode off, also revokes the grants the blanket made.", + "type": "boolean" + } + } + }, + "gateway.HostGrantsBlanketResult": { + "type": "object", + "properties": { + "blanket": { + "$ref": "#/definitions/gateway.HostBlanketView" + }, + "changed": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostBlanketChange" + } + }, + "revision": { + "type": "integer" + }, + "revoked": { + "type": "array", + "items": { + "type": "string" + } + }, + "skipped": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostBlanketSkip" + } + } + } + }, + "gateway.HostGrantsDenyRequest": { + "type": "object", + "properties": { + "deny": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "dryRun": { + "description": "DryRun answers what the rules would do and changes nothing.", + "type": "boolean" + }, + "operationId": { + "type": "string" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsDenyResult": { + "type": "object", + "properties": { + "affects": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostDenyAffect" + } + }, + "deny": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "dryRun": { + "type": "boolean" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsDenyView": { + "type": "object", + "properties": { + "file": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "ui": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "gateway.HostGrantsEditRequest": { + "type": "object", + "properties": { + "keepBlanketAsOwn": { + "description": "KeepBlanketAsOwn relabels the blanket grants of the add-on as the\nadministrator's own, so revoking the blanket grants leaves them.", + "type": "boolean" + }, + "operationId": { + "type": "string" + }, + "ops": { + "description": "Ops is the full set of operations the add-on keeps through the UI.", + "type": "array", + "items": { + "type": "string" + } + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsFloor": { + "type": "object", + "properties": { + "addons": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostFloorAddon" + } + }, + "source": { + "type": "string" + } + } + }, + "gateway.HostGrantsInstallRequest": { + "type": "object", + "properties": { + "action": { + "description": "Action is install or upgrade.", + "type": "string" + }, + "addonId": { + "type": "string" + }, + "approve": { + "$ref": "#/definitions/gateway.HostGrantsApprove" + }, + "candidateDigest": { + "type": "string" + }, + "expectedDigest": { + "description": "ExpectedDigest and CandidateDigest pin an upgrade; both or neither.", + "type": "string" + }, + "operationId": { + "type": "string" + }, + "sourceId": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "gateway.HostGrantsPreviewRequest": { + "type": "object", + "properties": { + "addonId": { + "type": "string" + }, + "sourceId": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "gateway.HostGrantsPreviewView": { + "type": "object", + "properties": { + "addonId": { + "type": "string" + }, + "blanket": { + "$ref": "#/definitions/gateway.HostBlanketPreview" + }, + "blockedRequired": { + "type": "array", + "items": { + "type": "string" + } + }, + "canInstall": { + "type": "boolean" + }, + "digest": { + "type": "string" + }, + "installed": { + "type": "boolean" + }, + "nonce": { + "type": "string" + }, + "nonceExpiresAt": { + "type": "string" + }, + "official": { + "type": "boolean" + }, + "ops": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostOpView" + } + }, + "requested": { + "$ref": "#/definitions/gateway.HostRequested" + }, + "revision": { + "type": "integer" + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "gateway.HostGrantsRevision": { + "type": "object", + "properties": { + "changed": { + "type": "boolean" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsView": { + "type": "object", + "properties": { + "addons": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostGrantsAddon" + } + }, + "blanket": { + "$ref": "#/definitions/gateway.HostBlanketView" + }, + "deny": { + "$ref": "#/definitions/gateway.HostGrantsDenyView" + }, + "floor": { + "$ref": "#/definitions/gateway.HostGrantsFloor" + }, + "locked": { + "type": "boolean" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostOpView": { + "type": "object", + "properties": { + "at": { + "type": "string" + }, + "by": { + "type": "string" + }, + "hinted": { + "description": "Hinted is true in a preview when the operation was granted before an\nearlier uninstall, so the dialog may tick it.", + "type": "boolean" + }, + "name": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "risk": { + "type": "string" + }, + "source": { + "type": "string" + }, + "status": { + "description": "Status is allowed, not_allowed, blocked or unsupported. In a preview it\nis held, grantable, blocked or unsupported.", + "type": "string" + }, + "text": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "gateway.HostRequested": { + "type": "object", + "properties": { + "optional": { + "type": "array", + "items": { + "type": "string" + } + }, + "required": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "gateway.OperationStatus": { "type": "object", "properties": { @@ -16883,6 +17871,20 @@ const docTemplate = `{ } } }, + "gateway.PendingUpgrade": { + "type": "object", + "properties": { + "delta": { + "type": "array", + "items": { + "type": "string" + } + }, + "version": { + "type": "string" + } + } + }, "gateway.Proposal": { "type": "object", "properties": { diff --git a/swagger/swagger.json b/swagger/swagger.json index cd41323e85..0faa004e3f 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -749,6 +749,448 @@ } } }, + "/api/addons/host-grants": { + "get": { + "description": "Administrators only (an administrator's session or API key). Returns the revision of the grants document, whether the deployment locks the UI store, the blanket approval switch (mode off, official or any; setBy ui or deployment), the deployment floor (the policy file and flags, read-only), the deny rules by source (file and flags, which cannot be edited here, and ui), and for each installed add-on the operations its verified manifest requests (required and optional) with a plain title, text and risk, whether each is allowed, not_allowed, blocked or unsupported, where the grant comes from (deployment, install, upgrade, edit or blanket), when and by whom, the operations granted that the manifest no longer requests, an identity mismatch flag and a pending upgrade that waits for approval. Read-only. Nothing in the answer is a credential. No MCP tool maps to this route.", + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Read the host access policy and what each add-on holds (administrators)", + "responses": { + "200": { + "description": "Host access state", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsView" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "Feature or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Addons or the owner are unavailable", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/blanket": { + "put": { + "description": "Administrators only, from their own browser session (X-LocalAI-Confirm equal to the operationId). Mode off, official or any. With it on, an install or upgrade grants the add-on the operations its verified manifest declares as required (also the optional ones with includeOptional) without the dialog step; it never grants an undeclared or unregistered operation and never overrides a deny rule. Mode official covers only the official library with its official signer. Mode any covers every library the administrator configured: ANY LIBRARY ENTRY THEN RECEIVES EXACTLY THE OPERATIONS ITS MANIFEST DECLARES, with no review, so a malicious or compromised entry gets what it asks for. Turning it on, or widening it, needs confirm equal to \"grant official add-ons what they declare\" (official) or \"grant any library add-on what it declares\" (any), else 400 confirmation_mismatch. applyToInstalled also grants the installed add-ons in the same revision. Turning it off keeps the grants it made, marked blanket; revokeOnOff revokes them all. When the deployment fixes the mode the answer is 403 locked_by_deployment.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Turn the blanket approval on, off or wider (browser session, typed confirmation)", + "parameters": [ + { + "description": "Blanket change", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsBlanketRequest" + } + } + ], + "responses": { + "200": { + "description": "The switch, the add-ons that were granted or skipped and the add-ons whose blanket grants were revoked", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsBlanketResult" + } + }, + "400": { + "description": "Malformed request, or confirmation_mismatch", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "not_human_surface, locked_by_deployment or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or operation_conflict", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/deny": { + "put": { + "description": "Administrators only. A deny rule beats every grant. deny maps an add-on ID, or \"*\" for all, to prefixes (\"egress.\"), exact operation names or the single token \"*\". The rules of the policy file and flags are not editable here and are unioned with these. With dryRun true the answer is the affected add-ons only (affects: the operations each would lose and whether it will stop because a required one is lost) and nothing changes; a dry run needs the administrator but not the browser session. Without dryRun the change needs the administrator's own browser session (X-LocalAI-Confirm equal to the operationId), the revision the caller read (409 revision_conflict otherwise), and applies to the next call with no restart.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Replace the deny rules set in the UI, or preview their effect (browser session)", + "parameters": [ + { + "description": "Deny rules", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsDenyRequest" + } + } + ], + "responses": { + "200": { + "description": "The rules and what they take", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsDenyResult" + } + }, + "400": { + "description": "Malformed request or invalid rule", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "not_human_surface, locked_by_deployment or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or operation_conflict", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/install": { + "post": { + "description": "Administrators only, from their own browser session (same-origin fetch, X-LocalAI-Confirm equal to the operationId). API keys, bearer tokens, MCP sessions and tool calls are refused with 403 not_human_surface. Carries the approval the dialog collected: the digest of the previewed package, the operations ticked, the revision of the grants document and the preview nonce. The owner stages the package again and grants only the operations that are approved AND declared by that verified manifest AND registered by the host AND not denied; a different package is 409 approval_stale, a changed document 409 revision_conflict, an expired or reused nonce 409 approval_stale. A required operation that is neither held nor approved is refused (approval_required) and one that a deny rule blocks is 403 blocked_by_policy; nothing is written then. For action upgrade, expectedDigest and candidateDigest pin the upgrade as in the plain upgrade. The plain dispatch install carries no approval. Repeating the operationId with the same request returns the recorded operation. After outcome_unknown keep the operationId and poll the operation status.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Install or upgrade an add-on with the host access the administrator approved (browser session)", + "parameters": [ + { + "description": "Install with approval", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsInstallRequest" + } + } + ], + "responses": { + "200": { + "description": "The lifecycle operation, with the host operations summary", + "schema": { + "$ref": "#/definitions/addons.OperationStatus" + } + }, + "400": { + "description": "Malformed request", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "Not a human surface (not_human_surface), locked_by_deployment, blocked_by_policy, approval_required, or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or approval_stale", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/preview": { + "post": { + "description": "Administrators only (an administrator's session or API key). The owner resolves and stages the release, reads the verified manifest of the staged package, computes the answer against the policy and discards the stage. Returns the digest of the staged package, the requested operations with plain text and risk, and for each one held (already allowed), grantable, blocked (a deny rule, which wins over every grant) or unsupported; canInstall false with blockedRequired when a required operation is blocked; whether the blanket approval would grant on install; the revision of the grants document; and a single-use nonce (five minutes, bound to the caller's credential, the add-on, the digest and the revision) that the install must present. Operations granted before an earlier uninstall carry hinted true so the dialog may tick them; a hint grants nothing. Changes no state.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Preview the host access an install or upgrade would ask for (administrators)", + "parameters": [ + { + "description": "Release to preview", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsPreviewRequest" + } + } + ], + "responses": { + "200": { + "description": "What the release asks for", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsPreviewView" + } + }, + "400": { + "description": "Malformed or unknown request fields", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "Feature or administration denied, or the release could not be verified", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Addons or the owner are unavailable", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, + "/api/addons/host-grants/{addonId}": { + "put": { + "description": "Administrators only, from their own browser session (X-LocalAI-Confirm equal to the operationId). ops is the full set the add-on keeps: operations left out are revoked, new ones must be declared by the installed manifest, registered by the host and not denied (403 blocked_by_policy). Grants made by the policy file or flags are not editable here. keepBlanketAsOwn relabels the blanket grants as the administrator's own. revision is the revision the caller read; a stale one is 409 revision_conflict. Takes effect for the next call of the add-on with no restart.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Set the host operations an add-on keeps through the UI (browser session)", + "parameters": [ + { + "type": "string", + "description": "Add-on ID", + "name": "addonId", + "in": "path", + "required": true + }, + { + "description": "Edit", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gateway.HostGrantsEditRequest" + } + } + ], + "responses": { + "200": { + "description": "The add-on entry and the new revision", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsAddonResult" + } + }, + "400": { + "description": "Malformed request", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "not_human_surface, locked_by_deployment, blocked_by_policy or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "404": { + "description": "The add-on is not installed", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or operation_conflict", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + }, + "delete": { + "description": "Administrators only, from their own browser session (X-LocalAI-Confirm equal to the operationId). Removes the UI and blanket grants of the add-on; grants of the policy file stay. Query: revision (the revision the caller read) and operationId. Takes effect for the next call with no restart; a running call finishes. An add-on whose required operation is revoked becomes unavailable.", + "produces": [ + "application/json" + ], + "tags": [ + "Addons" + ], + "summary": "Revoke every host operation granted to an add-on through the UI (browser session)", + "parameters": [ + { + "type": "string", + "description": "Add-on ID", + "name": "addonId", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Revision of the grants document the caller read", + "name": "revision", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Operation ID", + "name": "operationId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "The new revision", + "schema": { + "$ref": "#/definitions/gateway.HostGrantsRevision" + } + }, + "400": { + "description": "Malformed request", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "401": { + "description": "Named authentication required", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "403": { + "description": "not_human_surface, locked_by_deployment or administration denied", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "404": { + "description": "The add-on is not installed", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "409": { + "description": "revision_conflict or operation_conflict", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + }, + "503": { + "description": "Unavailable, or outcome_unknown (with operationId)", + "schema": { + "$ref": "#/definitions/addons.AddonErrorResponse" + } + } + } + } + }, "/api/addons/pins": { "get": { "description": "Returns the ordered add-on IDs the caller pinned to the sidebar. The route never pins anything by itself; Studio and Talk are pinned once, at upgrade, for the accounts that existed then. The list holds only IDs the caller may see right now: a hidden or removed add-on is left out and the route does not reveal whether it exists. Needs an authenticated caller and no administrator role.", @@ -14805,6 +15247,37 @@ } } }, + "addons.OperationStatus": { + "type": "object", + "properties": { + "addonID": { + "type": "string" + }, + "failureCode": { + "type": "string" + }, + "generation": { + "type": "integer", + "format": "int64" + }, + "hostOperations": { + "$ref": "#/definitions/hostops.RequestSummary" + }, + "operationID": { + "type": "string" + }, + "phase": { + "type": "string" + }, + "runtimeState": { + "type": "string" + }, + "stage": { + "description": "RuntimeState is an observation, never a durable execution receipt.", + "type": "string" + } + } + }, "addons.RecoveryBackupRequest": { "type": "object", "properties": { @@ -16841,6 +17314,521 @@ } } }, + "gateway.HostBlanketChange": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "ops": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gateway.HostBlanketPreview": { + "type": "object", + "properties": { + "applies": { + "type": "boolean" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostBlanketSkip": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "reason": { + "type": "string" + } + } + }, + "gateway.HostBlanketView": { + "type": "object", + "properties": { + "includeOptional": { + "type": "boolean" + }, + "mode": { + "type": "string" + }, + "revision": { + "type": "integer" + }, + "setBy": { + "description": "SetBy is ui, or deployment when a flag fixes the mode.", + "type": "string" + } + } + }, + "gateway.HostDenyAffect": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "loses": { + "type": "array", + "items": { + "type": "string" + } + }, + "willStop": { + "type": "boolean" + } + } + }, + "gateway.HostFloorAddon": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "prefixes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gateway.HostGrantsAddon": { + "type": "object", + "properties": { + "blanketGrants": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "identityMismatch": { + "type": "boolean" + }, + "noLongerRequested": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostOpView" + } + }, + "ops": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostOpView" + } + }, + "pendingUpgrade": { + "$ref": "#/definitions/gateway.PendingUpgrade" + }, + "requested": { + "$ref": "#/definitions/gateway.HostRequested" + }, + "state": { + "type": "string" + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "gateway.HostGrantsAddonResult": { + "type": "object", + "properties": { + "addon": { + "$ref": "#/definitions/gateway.HostGrantsAddon" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsApprove": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "ops": { + "type": "array", + "items": { + "type": "string" + } + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsBlanketRequest": { + "type": "object", + "properties": { + "applyToInstalled": { + "type": "boolean" + }, + "confirm": { + "description": "Confirm is the typed confirmation text, required to turn the switch\non or widen it.", + "type": "string" + }, + "includeOptional": { + "type": "boolean" + }, + "mode": { + "type": "string" + }, + "operationId": { + "type": "string" + }, + "revision": { + "type": "integer" + }, + "revokeOnOff": { + "description": "RevokeOnOff, with mode off, also revokes the grants the blanket made.", + "type": "boolean" + } + } + }, + "gateway.HostGrantsBlanketResult": { + "type": "object", + "properties": { + "blanket": { + "$ref": "#/definitions/gateway.HostBlanketView" + }, + "changed": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostBlanketChange" + } + }, + "revision": { + "type": "integer" + }, + "revoked": { + "type": "array", + "items": { + "type": "string" + } + }, + "skipped": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostBlanketSkip" + } + } + } + }, + "gateway.HostGrantsDenyRequest": { + "type": "object", + "properties": { + "deny": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "dryRun": { + "description": "DryRun answers what the rules would do and changes nothing.", + "type": "boolean" + }, + "operationId": { + "type": "string" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsDenyResult": { + "type": "object", + "properties": { + "affects": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostDenyAffect" + } + }, + "deny": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "dryRun": { + "type": "boolean" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsDenyView": { + "type": "object", + "properties": { + "file": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "ui": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "gateway.HostGrantsEditRequest": { + "type": "object", + "properties": { + "keepBlanketAsOwn": { + "description": "KeepBlanketAsOwn relabels the blanket grants of the add-on as the\nadministrator's own, so revoking the blanket grants leaves them.", + "type": "boolean" + }, + "operationId": { + "type": "string" + }, + "ops": { + "description": "Ops is the full set of operations the add-on keeps through the UI.", + "type": "array", + "items": { + "type": "string" + } + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsFloor": { + "type": "object", + "properties": { + "addons": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostFloorAddon" + } + }, + "source": { + "type": "string" + } + } + }, + "gateway.HostGrantsInstallRequest": { + "type": "object", + "properties": { + "action": { + "description": "Action is install or upgrade.", + "type": "string" + }, + "addonId": { + "type": "string" + }, + "approve": { + "$ref": "#/definitions/gateway.HostGrantsApprove" + }, + "candidateDigest": { + "type": "string" + }, + "expectedDigest": { + "description": "ExpectedDigest and CandidateDigest pin an upgrade; both or neither.", + "type": "string" + }, + "operationId": { + "type": "string" + }, + "sourceId": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "gateway.HostGrantsPreviewRequest": { + "type": "object", + "properties": { + "addonId": { + "type": "string" + }, + "sourceId": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "gateway.HostGrantsPreviewView": { + "type": "object", + "properties": { + "addonId": { + "type": "string" + }, + "blanket": { + "$ref": "#/definitions/gateway.HostBlanketPreview" + }, + "blockedRequired": { + "type": "array", + "items": { + "type": "string" + } + }, + "canInstall": { + "type": "boolean" + }, + "digest": { + "type": "string" + }, + "installed": { + "type": "boolean" + }, + "nonce": { + "type": "string" + }, + "nonceExpiresAt": { + "type": "string" + }, + "official": { + "type": "boolean" + }, + "ops": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostOpView" + } + }, + "requested": { + "$ref": "#/definitions/gateway.HostRequested" + }, + "revision": { + "type": "integer" + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "gateway.HostGrantsRevision": { + "type": "object", + "properties": { + "changed": { + "type": "boolean" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostGrantsView": { + "type": "object", + "properties": { + "addons": { + "type": "array", + "items": { + "$ref": "#/definitions/gateway.HostGrantsAddon" + } + }, + "blanket": { + "$ref": "#/definitions/gateway.HostBlanketView" + }, + "deny": { + "$ref": "#/definitions/gateway.HostGrantsDenyView" + }, + "floor": { + "$ref": "#/definitions/gateway.HostGrantsFloor" + }, + "locked": { + "type": "boolean" + }, + "revision": { + "type": "integer" + } + } + }, + "gateway.HostOpView": { + "type": "object", + "properties": { + "at": { + "type": "string" + }, + "by": { + "type": "string" + }, + "hinted": { + "description": "Hinted is true in a preview when the operation was granted before an\nearlier uninstall, so the dialog may tick it.", + "type": "boolean" + }, + "name": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "risk": { + "type": "string" + }, + "source": { + "type": "string" + }, + "status": { + "description": "Status is allowed, not_allowed, blocked or unsupported. In a preview it\nis held, grantable, blocked or unsupported.", + "type": "string" + }, + "text": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "gateway.HostRequested": { + "type": "object", + "properties": { + "optional": { + "type": "array", + "items": { + "type": "string" + } + }, + "required": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "gateway.OperationStatus": { "type": "object", "properties": { @@ -16880,6 +17868,20 @@ } } }, + "gateway.PendingUpgrade": { + "type": "object", + "properties": { + "delta": { + "type": "array", + "items": { + "type": "string" + } + }, + "version": { + "type": "string" + } + } + }, "gateway.Proposal": { "type": "object", "properties": { diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml index 3777ae1a36..764cba41c6 100644 --- a/swagger/swagger.yaml +++ b/swagger/swagger.yaml @@ -52,6 +52,27 @@ definitions: description: Digest is the digest of the proposal as it was displayed. type: string type: object + addons.OperationStatus: + properties: + addonID: + type: string + failureCode: + type: string + generation: + format: int64 + type: integer + hostOperations: + $ref: '#/definitions/hostops.RequestSummary' + operationID: + type: string + phase: + type: string + runtimeState: + type: string + stage: + description: RuntimeState is an observation, never a durable execution receipt. + type: string + type: object addons.RecoveryBackupRequest: properties: operationId: @@ -1665,6 +1686,355 @@ definitions: resourceId: type: string type: object + gateway.HostBlanketChange: + properties: + id: + type: string + ops: + items: + type: string + type: array + type: object + gateway.HostBlanketPreview: + properties: + applies: + type: boolean + revision: + type: integer + type: object + gateway.HostBlanketSkip: + properties: + id: + type: string + reason: + type: string + type: object + gateway.HostBlanketView: + properties: + includeOptional: + type: boolean + mode: + type: string + revision: + type: integer + setBy: + description: SetBy is ui, or deployment when a flag fixes the mode. + type: string + type: object + gateway.HostDenyAffect: + properties: + id: + type: string + loses: + items: + type: string + type: array + willStop: + type: boolean + type: object + gateway.HostFloorAddon: + properties: + id: + type: string + prefixes: + items: + type: string + type: array + type: object + gateway.HostGrantsAddon: + properties: + blanketGrants: + type: integer + id: + type: string + identityMismatch: + type: boolean + noLongerRequested: + items: + $ref: '#/definitions/gateway.HostOpView' + type: array + ops: + items: + $ref: '#/definitions/gateway.HostOpView' + type: array + pendingUpgrade: + $ref: '#/definitions/gateway.PendingUpgrade' + requested: + $ref: '#/definitions/gateway.HostRequested' + state: + type: string + title: + type: string + version: + type: string + type: object + gateway.HostGrantsAddonResult: + properties: + addon: + $ref: '#/definitions/gateway.HostGrantsAddon' + revision: + type: integer + type: object + gateway.HostGrantsApprove: + properties: + digest: + type: string + nonce: + type: string + ops: + items: + type: string + type: array + revision: + type: integer + type: object + gateway.HostGrantsBlanketRequest: + properties: + applyToInstalled: + type: boolean + confirm: + description: |- + Confirm is the typed confirmation text, required to turn the switch + on or widen it. + type: string + includeOptional: + type: boolean + mode: + type: string + operationId: + type: string + revision: + type: integer + revokeOnOff: + description: RevokeOnOff, with mode off, also revokes the grants the blanket + made. + type: boolean + type: object + gateway.HostGrantsBlanketResult: + properties: + blanket: + $ref: '#/definitions/gateway.HostBlanketView' + changed: + items: + $ref: '#/definitions/gateway.HostBlanketChange' + type: array + revision: + type: integer + revoked: + items: + type: string + type: array + skipped: + items: + $ref: '#/definitions/gateway.HostBlanketSkip' + type: array + type: object + gateway.HostGrantsDenyRequest: + properties: + deny: + additionalProperties: + items: + type: string + type: array + type: object + dryRun: + description: DryRun answers what the rules would do and changes nothing. + type: boolean + operationId: + type: string + revision: + type: integer + type: object + gateway.HostGrantsDenyResult: + properties: + affects: + items: + $ref: '#/definitions/gateway.HostDenyAffect' + type: array + deny: + additionalProperties: + items: + type: string + type: array + type: object + dryRun: + type: boolean + revision: + type: integer + type: object + gateway.HostGrantsDenyView: + properties: + file: + additionalProperties: + items: + type: string + type: array + type: object + ui: + additionalProperties: + items: + type: string + type: array + type: object + type: object + gateway.HostGrantsEditRequest: + properties: + keepBlanketAsOwn: + description: |- + KeepBlanketAsOwn relabels the blanket grants of the add-on as the + administrator's own, so revoking the blanket grants leaves them. + type: boolean + operationId: + type: string + ops: + description: Ops is the full set of operations the add-on keeps through the + UI. + items: + type: string + type: array + revision: + type: integer + type: object + gateway.HostGrantsFloor: + properties: + addons: + items: + $ref: '#/definitions/gateway.HostFloorAddon' + type: array + source: + type: string + type: object + gateway.HostGrantsInstallRequest: + properties: + action: + description: Action is install or upgrade. + type: string + addonId: + type: string + approve: + $ref: '#/definitions/gateway.HostGrantsApprove' + candidateDigest: + type: string + expectedDigest: + description: ExpectedDigest and CandidateDigest pin an upgrade; both or neither. + type: string + operationId: + type: string + sourceId: + type: string + version: + type: string + type: object + gateway.HostGrantsPreviewRequest: + properties: + addonId: + type: string + sourceId: + type: string + version: + type: string + type: object + gateway.HostGrantsPreviewView: + properties: + addonId: + type: string + blanket: + $ref: '#/definitions/gateway.HostBlanketPreview' + blockedRequired: + items: + type: string + type: array + canInstall: + type: boolean + digest: + type: string + installed: + type: boolean + nonce: + type: string + nonceExpiresAt: + type: string + official: + type: boolean + ops: + items: + $ref: '#/definitions/gateway.HostOpView' + type: array + requested: + $ref: '#/definitions/gateway.HostRequested' + revision: + type: integer + title: + type: string + version: + type: string + type: object + gateway.HostGrantsRevision: + properties: + changed: + type: boolean + revision: + type: integer + type: object + gateway.HostGrantsView: + properties: + addons: + items: + $ref: '#/definitions/gateway.HostGrantsAddon' + type: array + blanket: + $ref: '#/definitions/gateway.HostBlanketView' + deny: + $ref: '#/definitions/gateway.HostGrantsDenyView' + floor: + $ref: '#/definitions/gateway.HostGrantsFloor' + locked: + type: boolean + revision: + type: integer + type: object + gateway.HostOpView: + properties: + at: + type: string + by: + type: string + hinted: + description: |- + Hinted is true in a preview when the operation was granted before an + earlier uninstall, so the dialog may tick it. + type: boolean + name: + type: string + reason: + type: string + required: + type: boolean + risk: + type: string + source: + type: string + status: + description: |- + Status is allowed, not_allowed, blocked or unsupported. In a preview it + is held, grantable, blocked or unsupported. + type: string + text: + type: string + title: + type: string + type: object + gateway.HostRequested: + properties: + optional: + items: + type: string + type: array + required: + items: + type: string + type: array + type: object gateway.OperationStatus: properties: operationId: @@ -1700,6 +2070,15 @@ definitions: $ref: '#/definitions/gateway.Proposal' type: array type: object + gateway.PendingUpgrade: + properties: + delta: + items: + type: string + type: array + version: + type: string + type: object gateway.Proposal: properties: action: @@ -11423,6 +11802,376 @@ paths: summary: Submit a typed addon lifecycle operation or read an addon resource tags: - Addons + /api/addons/host-grants: + get: + description: Administrators only (an administrator's session or API key). Returns + the revision of the grants document, whether the deployment locks the UI store, + the blanket approval switch (mode off, official or any; setBy ui or deployment), + the deployment floor (the policy file and flags, read-only), the deny rules + by source (file and flags, which cannot be edited here, and ui), and for each + installed add-on the operations its verified manifest requests (required and + optional) with a plain title, text and risk, whether each is allowed, not_allowed, + blocked or unsupported, where the grant comes from (deployment, install, upgrade, + edit or blanket), when and by whom, the operations granted that the manifest + no longer requests, an identity mismatch flag and a pending upgrade that waits + for approval. Read-only. Nothing in the answer is a credential. No MCP tool + maps to this route. + produces: + - application/json + responses: + "200": + description: Host access state + schema: + $ref: '#/definitions/gateway.HostGrantsView' + "401": + description: Named authentication required + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "403": + description: Feature or administration denied + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "503": + description: Addons or the owner are unavailable + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + summary: Read the host access policy and what each add-on holds (administrators) + tags: + - Addons + /api/addons/host-grants/{addonId}: + delete: + description: 'Administrators only, from their own browser session (X-LocalAI-Confirm + equal to the operationId). Removes the UI and blanket grants of the add-on; + grants of the policy file stay. Query: revision (the revision the caller read) + and operationId. Takes effect for the next call with no restart; a running + call finishes. An add-on whose required operation is revoked becomes unavailable.' + parameters: + - description: Add-on ID + in: path + name: addonId + required: true + type: string + - description: Revision of the grants document the caller read + in: query + name: revision + required: true + type: integer + - description: Operation ID + in: query + name: operationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: The new revision + schema: + $ref: '#/definitions/gateway.HostGrantsRevision' + "400": + description: Malformed request + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "401": + description: Named authentication required + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "403": + description: not_human_surface, locked_by_deployment or administration denied + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "404": + description: The add-on is not installed + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "409": + description: revision_conflict or operation_conflict + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "503": + description: Unavailable, or outcome_unknown (with operationId) + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + summary: Revoke every host operation granted to an add-on through the UI (browser + session) + tags: + - Addons + put: + consumes: + - application/json + description: 'Administrators only, from their own browser session (X-LocalAI-Confirm + equal to the operationId). ops is the full set the add-on keeps: operations + left out are revoked, new ones must be declared by the installed manifest, + registered by the host and not denied (403 blocked_by_policy). Grants made + by the policy file or flags are not editable here. keepBlanketAsOwn relabels + the blanket grants as the administrator''s own. revision is the revision the + caller read; a stale one is 409 revision_conflict. Takes effect for the next + call of the add-on with no restart.' + parameters: + - description: Add-on ID + in: path + name: addonId + required: true + type: string + - description: Edit + in: body + name: request + required: true + schema: + $ref: '#/definitions/gateway.HostGrantsEditRequest' + produces: + - application/json + responses: + "200": + description: The add-on entry and the new revision + schema: + $ref: '#/definitions/gateway.HostGrantsAddonResult' + "400": + description: Malformed request + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "401": + description: Named authentication required + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "403": + description: not_human_surface, locked_by_deployment, blocked_by_policy + or administration denied + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "404": + description: The add-on is not installed + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "409": + description: revision_conflict or operation_conflict + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "503": + description: Unavailable, or outcome_unknown (with operationId) + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + summary: Set the host operations an add-on keeps through the UI (browser session) + tags: + - Addons + /api/addons/host-grants/blanket: + put: + consumes: + - application/json + description: 'Administrators only, from their own browser session (X-LocalAI-Confirm + equal to the operationId). Mode off, official or any. With it on, an install + or upgrade grants the add-on the operations its verified manifest declares + as required (also the optional ones with includeOptional) without the dialog + step; it never grants an undeclared or unregistered operation and never overrides + a deny rule. Mode official covers only the official library with its official + signer. Mode any covers every library the administrator configured: ANY LIBRARY + ENTRY THEN RECEIVES EXACTLY THE OPERATIONS ITS MANIFEST DECLARES, with no + review, so a malicious or compromised entry gets what it asks for. Turning + it on, or widening it, needs confirm equal to "grant official add-ons what + they declare" (official) or "grant any library add-on what it declares" (any), + else 400 confirmation_mismatch. applyToInstalled also grants the installed + add-ons in the same revision. Turning it off keeps the grants it made, marked + blanket; revokeOnOff revokes them all. When the deployment fixes the mode + the answer is 403 locked_by_deployment.' + parameters: + - description: Blanket change + in: body + name: request + required: true + schema: + $ref: '#/definitions/gateway.HostGrantsBlanketRequest' + produces: + - application/json + responses: + "200": + description: The switch, the add-ons that were granted or skipped and the + add-ons whose blanket grants were revoked + schema: + $ref: '#/definitions/gateway.HostGrantsBlanketResult' + "400": + description: Malformed request, or confirmation_mismatch + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "401": + description: Named authentication required + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "403": + description: not_human_surface, locked_by_deployment or administration denied + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "409": + description: revision_conflict or operation_conflict + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "503": + description: Unavailable, or outcome_unknown (with operationId) + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + summary: Turn the blanket approval on, off or wider (browser session, typed + confirmation) + tags: + - Addons + /api/addons/host-grants/deny: + put: + consumes: + - application/json + description: 'Administrators only. A deny rule beats every grant. deny maps + an add-on ID, or "*" for all, to prefixes ("egress."), exact operation names + or the single token "*". The rules of the policy file and flags are not editable + here and are unioned with these. With dryRun true the answer is the affected + add-ons only (affects: the operations each would lose and whether it will + stop because a required one is lost) and nothing changes; a dry run needs + the administrator but not the browser session. Without dryRun the change needs + the administrator''s own browser session (X-LocalAI-Confirm equal to the operationId), + the revision the caller read (409 revision_conflict otherwise), and applies + to the next call with no restart.' + parameters: + - description: Deny rules + in: body + name: request + required: true + schema: + $ref: '#/definitions/gateway.HostGrantsDenyRequest' + produces: + - application/json + responses: + "200": + description: The rules and what they take + schema: + $ref: '#/definitions/gateway.HostGrantsDenyResult' + "400": + description: Malformed request or invalid rule + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "401": + description: Named authentication required + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "403": + description: not_human_surface, locked_by_deployment or administration denied + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "409": + description: revision_conflict or operation_conflict + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "503": + description: Unavailable, or outcome_unknown (with operationId) + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + summary: Replace the deny rules set in the UI, or preview their effect (browser + session) + tags: + - Addons + /api/addons/host-grants/install: + post: + consumes: + - application/json + description: 'Administrators only, from their own browser session (same-origin + fetch, X-LocalAI-Confirm equal to the operationId). API keys, bearer tokens, + MCP sessions and tool calls are refused with 403 not_human_surface. Carries + the approval the dialog collected: the digest of the previewed package, the + operations ticked, the revision of the grants document and the preview nonce. + The owner stages the package again and grants only the operations that are + approved AND declared by that verified manifest AND registered by the host + AND not denied; a different package is 409 approval_stale, a changed document + 409 revision_conflict, an expired or reused nonce 409 approval_stale. A required + operation that is neither held nor approved is refused (approval_required) + and one that a deny rule blocks is 403 blocked_by_policy; nothing is written + then. For action upgrade, expectedDigest and candidateDigest pin the upgrade + as in the plain upgrade. The plain dispatch install carries no approval. Repeating + the operationId with the same request returns the recorded operation. After + outcome_unknown keep the operationId and poll the operation status.' + parameters: + - description: Install with approval + in: body + name: request + required: true + schema: + $ref: '#/definitions/gateway.HostGrantsInstallRequest' + produces: + - application/json + responses: + "200": + description: The lifecycle operation, with the host operations summary + schema: + $ref: '#/definitions/addons.OperationStatus' + "400": + description: Malformed request + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "401": + description: Named authentication required + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "403": + description: Not a human surface (not_human_surface), locked_by_deployment, + blocked_by_policy, approval_required, or administration denied + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "409": + description: revision_conflict or approval_stale + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "503": + description: Unavailable, or outcome_unknown (with operationId) + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + summary: Install or upgrade an add-on with the host access the administrator + approved (browser session) + tags: + - Addons + /api/addons/host-grants/preview: + post: + consumes: + - application/json + description: Administrators only (an administrator's session or API key). The + owner resolves and stages the release, reads the verified manifest of the + staged package, computes the answer against the policy and discards the stage. + Returns the digest of the staged package, the requested operations with plain + text and risk, and for each one held (already allowed), grantable, blocked + (a deny rule, which wins over every grant) or unsupported; canInstall false + with blockedRequired when a required operation is blocked; whether the blanket + approval would grant on install; the revision of the grants document; and + a single-use nonce (five minutes, bound to the caller's credential, the add-on, + the digest and the revision) that the install must present. Operations granted + before an earlier uninstall carry hinted true so the dialog may tick them; + a hint grants nothing. Changes no state. + parameters: + - description: Release to preview + in: body + name: request + required: true + schema: + $ref: '#/definitions/gateway.HostGrantsPreviewRequest' + produces: + - application/json + responses: + "200": + description: What the release asks for + schema: + $ref: '#/definitions/gateway.HostGrantsPreviewView' + "400": + description: Malformed or unknown request fields + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "401": + description: Named authentication required + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "403": + description: Feature or administration denied, or the release could not + be verified + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + "503": + description: Addons or the owner are unavailable + schema: + $ref: '#/definitions/addons.AddonErrorResponse' + summary: Preview the host access an install or upgrade would ask for (administrators) + tags: + - Addons /api/addons/pins: get: description: 'Returns the ordered add-on IDs the caller pinned to the sidebar.