diff --git a/build/agent_playbook_docs/source-lock.json b/build/agent_playbook_docs/source-lock.json index 544de1b5a7..a92215250f 100644 --- a/build/agent_playbook_docs/source-lock.json +++ b/build/agent_playbook_docs/source-lock.json @@ -12,6 +12,6 @@ "sdk_example_overlay_path": ".speakeasy/playbook/overlay-code-samples.yaml", "sdk_example_overlay_sha256": "d5e203a097e155fb26f2032a934febee24ebc247ce5b06319c963e8b7bcc8d51", "control_plane_contract_path": "out/smithy/public/playbook-cp/PlaybookControlPlaneService.openapi.json", - "control_plane_contract_sha256": "8e168dffbe2ec1b6f7b0ee03548725e3084116bbe4534899473c54408572604b", - "service_revision": "304467e3e3acf5ff9e6800e03055f0a7806bc14d" + "control_plane_contract_sha256": "b4e5ad6ff318dcd8e9f60bb75fb2d109058891d0e211e62f9781b24f221cf7f6", + "service_revision": "af81d8655a5c2ff48003cc96fb7bce42daf1935f" } diff --git a/content/operate/iris/agent-playbook/self-managed/_index.md b/content/operate/iris/agent-playbook/self-managed/_index.md index 623cc7770f..8c1ce583ff 100644 --- a/content/operate/iris/agent-playbook/self-managed/_index.md +++ b/content/operate/iris/agent-playbook/self-managed/_index.md @@ -11,7 +11,7 @@ weight: 40 hideListLinks: true --- -The `redis-agent-playbook` Helm chart deploys the Playbook control plane, data plane, Identity Service, and optional extraction worker. You provide Redis databases, an embedding provider, and a Playbook license. +The `redis-agent-playbook` Helm chart deploys the Playbook control plane, data plane, and optional extraction worker. Install Identity Service as a separate release. You provide Redis databases, an embedding provider, and a Playbook license. The chart version that matches these guides is not published yet. Set the chart version and image tags from the approved release manifest when it becomes available. Do not use the source chart's development version as a release version. diff --git a/content/operate/iris/agent-playbook/self-managed/authentication.md b/content/operate/iris/agent-playbook/self-managed/authentication.md index adb4d37ab7..442764263f 100644 --- a/content/operate/iris/agent-playbook/self-managed/authentication.md +++ b/content/operate/iris/agent-playbook/self-managed/authentication.md @@ -23,7 +23,7 @@ Keep both administrative tokens out of application configuration and model promp ## Read the bootstrap credentials -For [chart-generated credentials]({{< relref "/operate/iris/agent-playbook/self-managed/deploy" >}}), use an account that can read Secrets to load the administrative tokens: +For the two releases' [chart-generated administrative credentials]({{< relref "/operate/iris/agent-playbook/self-managed/deploy" >}}), use an account that can read Secrets to load the administrative tokens: ```bash export PLAYBOOK_ADMIN_TOKEN="$(kubectl -n "$PLAYBOOK_NAMESPACE" get secret \ @@ -32,45 +32,42 @@ export IDENTITY_CONTROL_TOKEN="$(kubectl -n "$PLAYBOOK_NAMESPACE" get secret \ playbook-identity-service-control-token -o jsonpath='{.data.token}' | base64 --decode)" ``` -The Secret names assume `fullnameOverride: playbook`. If you supply credentials externally, load them from your secret manager instead. +The Secret names assume the Playbook and Identity Service naming overrides from the deployment values. If you supply credentials externally, load them from your secret manager instead. Use these tokens to [create a playbook and application keys]({{< relref "/operate/iris/agent-playbook/self-managed/api-examples" >}}). See [playbook grants]({{< relref "/develop/ai/context-engine/agent-playbook/concepts#playbooks" >}}) for allowed actions. ## Supply service credentials -By default, the chart generates four service credentials. It retains their Secrets and reuses the values during live Helm upgrades. +Use four distinct credentials. The installation procedure supplies existing Secrets for shared internal and runtime credentials; the two charts generate their own administrative tokens unless you supply them. -For GitOps or offline chart rendering, supply existing Secrets to avoid generating new credentials on each render. Use a different value for each of the four credentials. - -| Credential | Helm Secret reference | Used by | +| Credential | Playbook values | Identity Service values | | --- | --- | --- | -| Control-plane admin token | `controlplane.adminToken.existingSecret` | Operators administering playbooks. | -| Control-plane internal token | `controlplane.internalToken.existingSecret` | Identity Service validating playbook grants. | -| Identity Service control token | `identityService.controlToken.existingSecret` | Operators administering application keys. | -| Identity Service runtime token | `identityService.runtimeToken.existingSecret` | Data plane validating application keys. | +| Control-plane admin token | `controlplane.adminToken.existingSecret` | Not shared. | +| Control-plane internal token | `controlplane.internalToken.existingSecret` | `productValidation.products.playbook.credential.existingSecret` | +| Identity Service control token | Not shared. | `control.existingSecret` | +| Data-plane runtime credential | `identityService.credential.existingSecret` | `runtime.serviceCredentials[]` entry named `playbook-dp`: `existingSecret` | -Each block supports `secretKey` (default `token`), `autoGenerate`, and `existingSecretChecksum`. Set `autoGenerate: false` to require an externally managed Secret. +Secret keys default to `token`. The shared values must match on both releases. The runtime credential allows `api-key-introspect` for `playbook`; extraction needs no additional lookup operation. Preserve independently configured service-subject integrations. -For example, provision four Secrets with a `token` key and add this overlay to `playbook-values.yaml`: +To supply existing administrative tokens too, add this to `playbook-values.yaml`: ```yaml controlplane: adminToken: existingSecret: playbook-controlplane-admin autoGenerate: false - internalToken: - existingSecret: playbook-controlplane-internal - autoGenerate: false +``` -identityService: - controlToken: - existingSecret: playbook-identity-control - autoGenerate: false - runtimeToken: - existingSecret: playbook-identity-runtime - autoGenerate: false +Add this to `identity-values.yaml`: + +```yaml +control: + existingSecret: playbook-identity-control + autoGenerate: false ``` +Provision those Secrets before installing. Load externally managed credentials from your secret manager instead of the chart-generated names. + ## Tenant and grant scope Set each grant's `tenant` to the deployment's `tenantId` and scope it to a playbook and allowed actions. The [key examples]({{< relref "/operate/iris/agent-playbook/self-managed/api-examples#create-application-keys" >}}) show the complete request. @@ -113,8 +110,8 @@ The control plane applies these role tiers: | Role | Control-plane access | | --- | --- | | `viewer` | List models and read playbooks. | -| `operator` | Viewer access plus create and update playbooks and manage extraction bindings. | -| `product_admin`, `suite_admin` | Operator access plus clear or delete playbooks. | +| `operator` | Viewer access plus create and update playbooks; inspect extraction bindings, update their FAQ policy, pause, or delete them. | +| `product_admin`, `suite_admin` | Operator access plus clear or delete playbooks; list extraction sources, create bindings, and enable or recover them. | On the data plane, explicitly mapped `product_admin` and `suite_admin` users receive full access within the configured tenant. Unmapped claim values grant no access, even when a value matches an Iris role name. Keep service identities out of human administrator groups. @@ -122,24 +119,21 @@ The issuer and JSON Web Key Set (JWKS) endpoints must use HTTPS unless you set t ## Configure OIDC access to API-key administration -Identity Service Control has a separate OIDC policy. Configure it when operators must manage application keys without the bootstrap control token: +Identity Service Control has a separate OIDC policy. Add this to `identity-values.yaml` when operators must manage application keys without the bootstrap control token: ```yaml -identityService: - controlAuth: - oidc: - enabled: true - issuer: https:/// - jwks_uri: https:/// - audience: - - - role_mappings: - : product_admin - roleBindings: - - claim_value: - scope: product - product: playbook - role: product_admin +controlAuth: + oidc: + enabled: true + issuer: https:/// + jwks_uri: https:/// + audience: + - + roleBindings: + - claim_value: + scope: product + product: playbook + role: product_admin ``` -The control token remains available as a deployment-administration credential. By default, `identityService.apiKeyTenant.singleTenantID` inherits `tenantId`. Identity Service fills an omitted grant tenant with that value and rejects grants for another tenant. +The control token remains available as a deployment-administration credential. Set `runtime.apiKeyTenant.singleTenantID` in the Identity Service values to Playbook's `tenantId`. Identity Service fills an omitted grant tenant with that value and rejects grants for another tenant. diff --git a/content/operate/iris/agent-playbook/self-managed/configuration.md b/content/operate/iris/agent-playbook/self-managed/configuration.md index 53c868bd0a..67c710e66a 100644 --- a/content/operate/iris/agent-playbook/self-managed/configuration.md +++ b/content/operate/iris/agent-playbook/self-managed/configuration.md @@ -35,7 +35,7 @@ metadata: - rediss://:@:6379 ``` -The [deploy procedure]({{< relref "/operate/iris/agent-playbook/self-managed/deploy#create-prerequisite-secrets" >}}) puts both files in one Secret under the chart's expected keys. +The [deploy procedure]({{< relref "/operate/iris/agent-playbook/self-managed/deploy#create-prerequisite-secrets" >}}) creates separate Secrets for Playbook storage and the standalone Identity Service metadata. ## Embedding configuration @@ -74,14 +74,17 @@ tenantId: controlplane: image: tag: + internalToken: + existingSecret: playbook-controlplane-internal dataplane: image: tag: + identityServiceBaseURL: https:// identityService: - image: - tag: + credential: + existingSecret: playbook-dp-credential storage: existingSecret: playbook-storage @@ -100,21 +103,52 @@ tests: enabled: true ``` -Use image tags from one approved release manifest. The chart generates the service credentials unless you set [existing credential Secrets]({{< relref "/operate/iris/agent-playbook/self-managed/authentication#supply-service-credentials" >}}). +Use image tags from the approved release manifest. The shared internal and runtime credentials must match the standalone Identity Service values. See [service credentials]({{< relref "/operate/iris/agent-playbook/self-managed/authentication#supply-service-credentials" >}}). + +Save `identity-values.yaml` for the separate Identity Service release, using the same tenant as Playbook: + +```yaml +fullnameOverride: playbook-identity-service +image: + tag: +metadata: + existingSecret: playbook-identity-metadata +runtime: + apiKeyTenant: + singleTenantID: + serviceCredentials: + - name: playbook-dp + subject: playbook-dp + allowedOperations: [api-key-introspect] + allowedProducts: [playbook] + existingSecret: playbook-dp-credential + secretKey: token +productValidation: + products: + playbook: + baseURL: https:// + credential: + existingSecret: playbook-controlplane-internal + secretKey: token +``` + +Provide certificate-verified routes to both configured TLS endpoints; the charts do not create TLS termination. Identity Service validates playbook grants through the control-plane endpoint. Configure it before installing, then wait for both releases and their routes before minting application keys. + +An existing Identity Service release can also serve Playbook. Merge the `playbook-dp` runtime credential and `playbook` validator into its existing lists and maps instead of replacing other products' configuration. Set `dataplane.identityServiceBaseURL` to that release's endpoint. Its single-tenant policy must match Playbook's `tenantId`. | Area | Values | Purpose | | --- | --- | --- | | Tenant | `tenantId` | Tenant shared by this deployment and its application grants. | -| Storage | `storage.existingSecret`, `storage.playbookKey`, `storage.identityServiceKey`, `storage.keyPrefix`, `storage.maxPlaybooksPerTenant` | Storage Secret, key namespace, and tenant inventory limit. The default maximum is 128 playbooks. | +| Storage | `storage.existingSecret`, `storage.playbookKey`, `storage.keyPrefix`, `storage.maxPlaybooksPerTenant` | Storage Secret, key namespace, and tenant inventory limit. The default maximum is 128 playbooks. | | Storage rollout | `storage.existingSecretChecksum` | Roll consuming pods after the storage Secret changes. | | License | `license.existingSecret`, `license.secretKey`, `license.existingSecretChecksum` | License Secret and rollout marker. The default key is `license`. | | Embeddings | `inference.models`, `inference.existingSecret`, `inference.secretKey`, `inference.existingSecretChecksum` | Public model identities and private configuration. The default Secret key is `inference.yaml`. | -| Images | `controlplane.image.*`, `dataplane.image.*`, `identityService.image.*`, `imagePullSecrets` | Pin or mirror the release images. | -| Capacity | `controlplane.replicaCount`, `dataplane.replicaCount`, `identityService.replicaCount`, each component's `resources` | Service replicas and Kubernetes resource requests and limits. | -| Services | `controlplane.service.*`, `dataplane.service.*`, `identityService.service.*` | Service type and port. | -| Service connections | `dataplane.identityServiceBaseURL`, `identityService.playbookControlPlaneBaseURL` | Override the chart's in-cluster service endpoints. | +| Images | `controlplane.image.*`, `dataplane.image.*`, `imagePullSecrets` | Pin or mirror the release images. | +| Capacity | `controlplane.replicaCount`, `dataplane.replicaCount`, each component's `resources` | Service replicas and Kubernetes resource requests and limits. | +| Services | `controlplane.service.*`, `dataplane.service.*`, standalone Identity Service `service.*` | Service type and port. | +| Service connections | `dataplane.identityServiceBaseURL`, standalone `productValidation.products.playbook.baseURL` | Connect the data plane to Identity Service and Identity Service to the control-plane validator. | | Private certificate authority | `tls.caCertSecret`, `tls.caCertSecretChecksum` | Trust additional certificate authorities and roll pods when they change. | -| Operator OpenID Connect (OIDC) | `auth.oidc.audience`, `dataplane.oidc`, `controlplane.oidc`, `identityService.controlAuth` | Validate operator identity provider tokens. | +| Operator OpenID Connect (OIDC) | `auth.oidc.audience`, `dataplane.oidc`, `controlplane.oidc`, standalone `controlAuth` | Validate operator identity provider tokens. | | Security | `serviceAccount`, `security.profile`, pod security contexts | Configure workload identity and the optional FIPS-oriented profile. | | Tracing | `tracing.enabled`, `tracing.otlpGRPCEndpoint`, `tracing.capture` | Send OpenTelemetry spans to a deployment-owned collector. | | Extraction | `extraction`, `worker` | Connect Redis Agent Memory and run optional background FAQ extraction. | @@ -131,7 +165,7 @@ kubectl -n '' create secret generic playbook-redis-ca \ --from-file=ca.crt=./ca.crt ``` -Add this to `playbook-values.yaml` before installing: +Add this to both `playbook-values.yaml` and `identity-values.yaml` before installing: ```yaml tls: @@ -142,7 +176,7 @@ The chart mounts the CA for all enabled workloads. Keep certificate verification ## Configure security and tracing -Set `security.profile: fips` only with images built for the Federal Information Processing Standards (FIPS) profile. Storage URLs must use `rediss://`. Set explicit HTTPS values for `dataplane.identityServiceBaseURL` and `identityService.playbookControlPlaneBaseURL`. The profile configures the processes but does not provide Transport Layer Security (TLS) termination or establish compliance by itself. +Set `security.profile: fips` only with images built for the Federal Information Processing Standards (FIPS) profile. Storage URLs must use `rediss://`. Set explicit HTTPS values for `dataplane.identityServiceBaseURL` and standalone `productValidation.products.playbook.baseURL`. The profile configures the processes but does not provide Transport Layer Security (TLS) termination or establish compliance by itself. To export traces, configure a trusted local OpenTelemetry Protocol (OTLP) collector: diff --git a/content/operate/iris/agent-playbook/self-managed/control-plane-api-reference/openapi-control-plane.json b/content/operate/iris/agent-playbook/self-managed/control-plane-api-reference/openapi-control-plane.json index b396c72e15..7950de5fcc 100644 --- a/content/operate/iris/agent-playbook/self-managed/control-plane-api-reference/openapi-control-plane.json +++ b/content/operate/iris/agent-playbook/self-managed/control-plane-api-reference/openapi-control-plane.json @@ -921,7 +921,7 @@ ] }, "post": { - "description": "Registers a client-supplied RAM store reference and FAQ policy. Returns preparing and enabled=false. The control plane asynchronously provisions extraction-worker RAM source read and Playbook destination read/propose grants; both accepted grants make the binding ready, refused grants make it blocked, and dependency outages remain pending for retry. The worker validates live RAM access when extraction runs. Poll GET until ready, then PATCH to enable.", + "description": "Validates caller management consent, exact same-account RAM source ownership, destination readiness and execution scope synchronously. Success returns ready and enabled=false. Every explicit enable revalidates with the current caller. Pause/delete require destination management only and make no source or grant-administration request. A foreign or missing source is refused without revealing its metadata.", "operationId": "CreateExtractionBinding", "requestBody": { "content": { @@ -1458,7 +1458,7 @@ }, "/v1/playbooks/{playbookId}/extraction/stores": { "get": { - "description": "Returns tenant-scoped deployment-configured store suggestions, which may be empty. A client may submit another syntactically valid storeId. No RAM call or approval check occurs.", + "description": "Returns tenant-scoped store suggestions, which may be empty. A suggestion does not approve a store. A client may submit another syntactically valid storeId; CreateExtractionBinding checks the caller's access to the store.", "operationId": "ListExtractionStores", "parameters": [ { @@ -1746,7 +1746,7 @@ }, "CreateExtractionBindingResponseContent": { "type": "object", - "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Ready means the configured extraction-worker grants were accepted; the worker validates live RAM access during execution. Without a configured RAM connection, preparation remains pending and cannot be enabled. This release has no extraction worker. Activation requires a separate PATCH after readiness.", + "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Creation synchronously validates caller consent and exact RAM source ownership, returning ready and disabled. Every explicit enable revalidates with the current caller. Consent is durable account-owned configuration.", "properties": { "bindingId": { "type": "string", @@ -1768,7 +1768,7 @@ }, "enabled": { "type": "boolean", - "description": "Activation intent. Creation is disabled; enabling requires ready status and a separate PATCH. Pausing clears intent and retains prepared source/destination grants." + "description": "Activation intent. Creation is disabled. A separate enable PATCH revalidates current source consent and ownership, and can recover a blocked binding. Pausing clears intent without a RAM request or source permission." }, "version": { "type": "integer", @@ -1778,7 +1778,7 @@ }, "blockedReason": { "type": "string", - "description": "Machine-readable source preparation or execution failure reason, such as source_grant_refused, source_not_found, or source_access_denied. Clients must tolerate unknown reasons." + "description": "Machine-readable runtime failure reason, such as source_not_found or source_access_denied. Clients must tolerate unknown reasons." }, "createdAt": { "type": "string", @@ -1919,7 +1919,7 @@ }, "DeleteExtractionBindingResponseContent": { "type": "object", - "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Ready means the configured extraction-worker grants were accepted; the worker validates live RAM access during execution. Without a configured RAM connection, preparation remains pending and cannot be enabled. This release has no extraction worker. Activation requires a separate PATCH after readiness.", + "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Creation synchronously validates caller consent and exact RAM source ownership, returning ready and disabled. Every explicit enable revalidates with the current caller. Consent is durable account-owned configuration.", "properties": { "bindingId": { "type": "string", @@ -1941,7 +1941,7 @@ }, "enabled": { "type": "boolean", - "description": "Activation intent. Creation is disabled; enabling requires ready status and a separate PATCH. Pausing clears intent and retains prepared source/destination grants." + "description": "Activation intent. Creation is disabled. A separate enable PATCH revalidates current source consent and ownership, and can recover a blocked binding. Pausing clears intent without a RAM request or source permission." }, "version": { "type": "integer", @@ -1951,7 +1951,7 @@ }, "blockedReason": { "type": "string", - "description": "Machine-readable source preparation or execution failure reason, such as source_grant_refused, source_not_found, or source_access_denied. Clients must tolerate unknown reasons." + "description": "Machine-readable runtime failure reason, such as source_not_found or source_access_denied. Clients must tolerate unknown reasons." }, "createdAt": { "type": "string", @@ -2048,7 +2048,7 @@ }, "ExtractionBinding": { "type": "object", - "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Ready means the configured extraction-worker grants were accepted; the worker validates live RAM access during execution. Without a configured RAM connection, preparation remains pending and cannot be enabled. This release has no extraction worker. Activation requires a separate PATCH after readiness.", + "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Creation synchronously validates caller consent and exact RAM source ownership, returning ready and disabled. Every explicit enable revalidates with the current caller. Consent is durable account-owned configuration.", "properties": { "bindingId": { "type": "string", @@ -2070,7 +2070,7 @@ }, "enabled": { "type": "boolean", - "description": "Activation intent. Creation is disabled; enabling requires ready status and a separate PATCH. Pausing clears intent and retains prepared source/destination grants." + "description": "Activation intent. Creation is disabled. A separate enable PATCH revalidates current source consent and ownership, and can recover a blocked binding. Pausing clears intent without a RAM request or source permission." }, "version": { "type": "integer", @@ -2080,7 +2080,7 @@ }, "blockedReason": { "type": "string", - "description": "Machine-readable source preparation or execution failure reason, such as source_grant_refused, source_not_found, or source_access_denied. Clients must tolerate unknown reasons." + "description": "Machine-readable runtime failure reason, such as source_not_found or source_access_denied. Clients must tolerate unknown reasons." }, "createdAt": { "type": "string", @@ -2107,7 +2107,7 @@ }, "ExtractionBindingSource": { "type": "object", - "description": "Typed RAM store reference. This release accepts only redis_agent_memory and a client-supplied storeId. The source is implicitly whole-store. When the deployment enables the RAM connection, server-side preparation verifies store access. Namespace selection and extraction workers arrive later; credentials are never accepted here. Unknown fields are rejected.", + "description": "Typed RAM store reference. Only redis_agent_memory with a client-supplied storeId is supported. The source is implicitly whole-store. The control plane verifies source consent and same-account/tenant RAM ownership on create and every explicit enable. Namespace selection is not supported. Credentials are never accepted here. Unknown fields are rejected.", "properties": { "storeId": { "type": "string", @@ -2127,7 +2127,6 @@ "ExtractionBindingStatus": { "type": "string", "enum": [ - "preparing", "ready", "blocked", "deleting" @@ -2347,7 +2346,7 @@ }, "GetExtractionBindingResponseContent": { "type": "object", - "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Ready means the configured extraction-worker grants were accepted; the worker validates live RAM access during execution. Without a configured RAM connection, preparation remains pending and cannot be enabled. This release has no extraction worker. Activation requires a separate PATCH after readiness.", + "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Creation synchronously validates caller consent and exact RAM source ownership, returning ready and disabled. Every explicit enable revalidates with the current caller. Consent is durable account-owned configuration.", "properties": { "bindingId": { "type": "string", @@ -2369,7 +2368,7 @@ }, "enabled": { "type": "boolean", - "description": "Activation intent. Creation is disabled; enabling requires ready status and a separate PATCH. Pausing clears intent and retains prepared source/destination grants." + "description": "Activation intent. Creation is disabled. A separate enable PATCH revalidates current source consent and ownership, and can recover a blocked binding. Pausing clears intent without a RAM request or source permission." }, "version": { "type": "integer", @@ -2379,7 +2378,7 @@ }, "blockedReason": { "type": "string", - "description": "Machine-readable source preparation or execution failure reason, such as source_grant_refused, source_not_found, or source_access_denied. Clients must tolerate unknown reasons." + "description": "Machine-readable runtime failure reason, such as source_not_found or source_access_denied. Clients must tolerate unknown reasons." }, "createdAt": { "type": "string", @@ -2863,7 +2862,7 @@ }, "UpdateExtractionBindingResponseContent": { "type": "object", - "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Ready means the configured extraction-worker grants were accepted; the worker validates live RAM access during execution. Without a configured RAM connection, preparation remains pending and cannot be enabled. This release has no extraction worker. Activation requires a separate PATCH after readiness.", + "description": "Durable tenant-owned metadata for one Playbook, one RAM store, and one FAQ policy. Creation synchronously validates caller consent and exact RAM source ownership, returning ready and disabled. Every explicit enable revalidates with the current caller. Consent is durable account-owned configuration.", "properties": { "bindingId": { "type": "string", @@ -2885,7 +2884,7 @@ }, "enabled": { "type": "boolean", - "description": "Activation intent. Creation is disabled; enabling requires ready status and a separate PATCH. Pausing clears intent and retains prepared source/destination grants." + "description": "Activation intent. Creation is disabled. A separate enable PATCH revalidates current source consent and ownership, and can recover a blocked binding. Pausing clears intent without a RAM request or source permission." }, "version": { "type": "integer", @@ -2895,7 +2894,7 @@ }, "blockedReason": { "type": "string", - "description": "Machine-readable source preparation or execution failure reason, such as source_grant_refused, source_not_found, or source_access_denied. Clients must tolerate unknown reasons." + "description": "Machine-readable runtime failure reason, such as source_not_found or source_access_denied. Clients must tolerate unknown reasons." }, "createdAt": { "type": "string", diff --git a/content/operate/iris/agent-playbook/self-managed/deploy.md b/content/operate/iris/agent-playbook/self-managed/deploy.md index a920089e32..7681c90b4c 100644 --- a/content/operate/iris/agent-playbook/self-managed/deploy.md +++ b/content/operate/iris/agent-playbook/self-managed/deploy.md @@ -11,7 +11,7 @@ weight: 40 hideListLinks: true --- -Install the control plane, data plane, and Identity Service with the `redis-agent-playbook` Helm chart. +Install Identity Service as a separate release, then install the control plane and data plane with the `redis-agent-playbook` Helm chart. Prepare the files in [Configuration]({{< relref "/operate/iris/agent-playbook/self-managed/configuration" >}}) and save your [license]({{< relref "/operate/iris/agent-playbook/self-managed/prerequisites#required-software-and-access" >}}) as `license`. @@ -32,12 +32,13 @@ kubectl create namespace "$PLAYBOOK_NAMESPACE" ## Create prerequisite Secrets -1. Create the storage Secret from the two configuration files: +1. Create separate storage configuration Secrets: ```bash kubectl -n "$PLAYBOOK_NAMESPACE" create secret generic playbook-storage \ - --from-file=playbook-storage.yaml=./playbook-storage.yaml \ - --from-file=identity-service-metadata.yaml=./identity-service-metadata.yaml + --from-file=playbook-storage.yaml=./playbook-storage.yaml + kubectl -n "$PLAYBOOK_NAMESPACE" create secret generic playbook-identity-metadata \ + --from-file=metadata.yaml=./identity-service-metadata.yaml ``` 2. Create the license Secret: @@ -54,6 +55,20 @@ kubectl create namespace "$PLAYBOOK_NAMESPACE" --from-file=inference.yaml=./inference.yaml ``` +4. Generate and provision the two shared service credentials: + + ```bash + umask 077 + openssl rand -hex 32 > playbook-controlplane-internal.token + openssl rand -hex 32 > playbook-dp.token + kubectl -n "$PLAYBOOK_NAMESPACE" create secret generic playbook-controlplane-internal \ + --from-file=token=./playbook-controlplane-internal.token + kubectl -n "$PLAYBOOK_NAMESPACE" create secret generic playbook-dp-credential \ + --from-file=token=./playbook-dp.token + ``` + + The control plane and Identity Service share the internal token. The data plane and Identity Service share the runtime credential. Keep their source files in your secret manager, separate from public values. + If your dependencies use a private certificate authority, also [create its CA Secret]({{< relref "/operate/iris/agent-playbook/self-managed/configuration#trust-a-private-ca" >}}). ## Install the chart @@ -73,12 +88,23 @@ If your dependencies use a private certificate authority, also [create its CA Se 3. Set the chart version from the approved release manifest: ```bash - export PLAYBOOK_CHART_VERSION='' + export PLAYBOOK_CHART_VERSION='' + export IDENTITY_CHART_VERSION='' ``` The matching chart is not published yet. Do not substitute the development version from the source chart. -4. Install the pinned chart with the prepared values: +4. Install the standalone Identity Service first: + + ```bash + helm install playbook-identity redis-ai/iris-identity-service \ + --version "$IDENTITY_CHART_VERSION" \ + --namespace "$PLAYBOOK_NAMESPACE" \ + -f identity-values.yaml \ + --wait --timeout 10m + ``` + +5. Install Playbook with its prepared values: ```bash helm install playbook redis-ai/redis-agent-playbook \ @@ -88,7 +114,7 @@ If your dependencies use a private certificate authority, also [create its CA Se --wait --timeout 10m ``` - This creates `playbook-controlplane`, `playbook-dataplane`, and `playbook-identity-service` because the values set `fullnameOverride: playbook`. + This creates `playbook-controlplane` and `playbook-dataplane` because the values set `fullnameOverride: playbook`. The separate Identity Service release creates `playbook-identity-service`. ## Verify the deployment @@ -97,6 +123,8 @@ If your dependencies use a private certificate authority, also [create its CA Se ```bash kubectl -n "$PLAYBOOK_NAMESPACE" get pods \ -l app.kubernetes.io/instance=playbook + kubectl -n "$PLAYBOOK_NAMESPACE" get pods \ + -l app.kubernetes.io/instance=playbook-identity ``` 2. Run the chart's checks: diff --git a/content/operate/iris/agent-playbook/self-managed/extraction.md b/content/operate/iris/agent-playbook/self-managed/extraction.md index 489a186f35..6b97742851 100644 --- a/content/operate/iris/agent-playbook/self-managed/extraction.md +++ b/content/operate/iris/agent-playbook/self-managed/extraction.md @@ -24,91 +24,71 @@ Prepare these values before enabling extraction: | Redis Agent Memory store ID | Source store for one extraction binding. The store tenant must match `tenantId`. | | ServiceAccount issuer and JSON Web Key Set (JWKS) URI | Validate projected control-plane and worker tokens. | | Redis Agent Memory data-plane URL | Let the worker read sessions. | -| Redis Agent Memory Identity Service URL | Let the Playbook control plane prepare source grants. | +| Redis Agent Memory control-plane URL | Discover stores and verify source ownership. | | Queue Redis URL | Store extraction jobs. Use persistence, `noeviction`, and capacity monitoring. Do not reuse Playbook metadata or content Redis. | | Generation provider and model | Extract and consolidate FAQ candidates. These credentials are separate from the embedding configuration. | -Use certificate-verified endpoints. Both data planes must reach the issuer's JWKS. Configure network policy for the control plane, worker, Redis, identity, and model-provider paths. +Use certificate-verified endpoints. The Memory control plane and both data planes must reach the issuer's JWKS. Configure network policy for the control plane, worker, Redis, identity, and model-provider paths. ## Configure Redis Agent Memory trust -Redis Agent Memory must trust the Playbook worker for store reads and the Playbook control plane for grant preparation. With release `playbook`, namespace ``, and `fullnameOverride: playbook`, the subjects are: +Redis Agent Memory trusts separate Playbook identities for source reads and ownership checks. With release `playbook`, namespace ``, and `fullnameOverride: playbook`, the subjects are: - `system:serviceaccount::playbook-worker` - `system:serviceaccount::playbook-controlplane` -Add these settings to the Redis Agent Memory values. Merge the list entries with its existing worker identities, grant policies, and runtime credentials: +Add these settings to the Redis Agent Memory values. Merge entries with existing identity lists, preserving Memory's own worker and application agent-key authentication: ```yaml memory: auth: - service_subjects: - enabled: true - issuer: https:// - jwks_uri: https:// - audience: - - api://memory-dp - subjects: - - system:serviceaccount::playbook-worker - -identityService: - subjectGrants: - allowed: - - product: memory - resourceType: mem-store - actions: - - read - controlAuth: - workloadIdentity: - enabled: true - issuer: https:// - jwks_uri: https:// - audience: - - api://memory-identity-control - subjects: - - subject: system:serviceaccount::playbook-controlplane - tenant: - roles: - - subject_grant_admin + method: agent_key + jwt_subjects: + - issuer: https:// + jwks_uri: https:// + audience: [api://memory-dp] + subjects: + - subject: "system:serviceaccount::playbook-worker" + authority: static + roles: [viewer] + resources: + "mem-store:*": + permissions: [read] +controlplane: + configData: + auth: + workload_identity: + enabled: true + issuer: https:// + jwks_uri: https:// + audience: [api://memory-cp] + subjects: + - subject: "system:serviceaccount::playbook-controlplane" + tenant: "" + roles: [viewer] ``` -The Redis Agent Memory data-plane runtime credential must also allow `subject-grant-lookup`. For example, preserve its current settings and include both runtime operations: +The control-plane viewer must use the same tenant as Playbook's `tenantId`. Do not give it `suite_admin`, which bypasses Memory's ownership checks. This viewer verifies ownership; it does not supply the operator's consent. Playbook extraction needs no Identity Service subject grants, lookup credential, or Control administration identity. Keep unrelated service-subject integrations configured separately. -```yaml -identityService: - runtime: - serviceCredentials: - - name: memory-dp - subject: memory-dp - secretKey: token - autoGenerate: true - allowedOperations: - - api-key-introspect - - subject-grant-lookup - allowedProducts: - - memory -``` +The `mem-store:*` wildcard gives this dedicated worker read access to all stores served by the Memory data plane. Binding configuration controls which sources it processes. Both Memory planes must trust the issuer's certificate authority and reach its JWKS endpoint. See the [Redis Agent Memory authentication guide]({{< relref "/operate/iris/agent-memory/self-managed/authentication" >}}) before changing an existing authentication configuration. ## Configure Playbooks extraction -Save `playbook-extraction-values.yaml`. Start with the worker disabled: +Save `playbook-extraction-values.yaml`. The worker starts without an active binding; creating a binding does not enable it: ```yaml extraction: enabled: true - sources: - - tenant_id: - store_id: connection: enabled: true issuer: https:// jwksURI: https:// - identityServiceBaseURL: https:// + ramControlPlaneBaseURL: https:// worker: - enabled: false + enabled: true replicaCount: 1 region: existingSecret: playbook-extraction-worker @@ -118,7 +98,7 @@ worker: extractConcurrency: 1 ``` -The chart adds its worker subject to the connection automatically. Use `extraction.connection.workerSubjects` only for additional worker identities. To discover stores from Redis Agent Memory instead of listing `extraction.sources`, configure `ramControlPlaneBaseURL` and its workload identity. Do not configure both discovery methods. +The chart projects separate tokens for the control plane and worker. Their audiences must match the Memory trust configuration. The control plane discovers stores live; no per-store deployment list is required. Keep the configured service endpoints fixed for the deployment. Save the private worker configuration as `worker.yaml`: @@ -168,7 +148,17 @@ helm upgrade playbook redis-ai/redis-agent-playbook \ ## Create and enable a binding -Create a disabled binding for the source store: +Use the installation admin token, or a same-tenant OpenID Connect (OIDC) user mapped to `product_admin` or `suite_admin`, to list sources, create a binding, or enable it. Operator and viewer roles cannot authorize those actions. + +Discover the current source stores for the destination playbook: + +```bash +curl --fail-with-body --silent --show-error \ + "$PLAYBOOK_CONTROL_URL/v1/playbooks/$PLAYBOOK_ID/extraction/stores" \ + -H "Authorization: Bearer $PLAYBOOK_ADMIN_TOKEN" | jq +``` + +Choose an owned store and create a disabled binding. The control plane verifies the exact source again; an earlier listing is not authorization: ```bash jq -n --arg store '' '{ @@ -190,17 +180,11 @@ curl --fail-with-body --silent --show-error \ export BINDING_ID="$(jq -er '.bindingId' binding.json)" ``` -Read the binding until `status` is `ready`. A ready binding confirms store ownership and prepared source and destination grants. It does not confirm worker or model connectivity. +Successful creation returns HTTP `202`, `status: ready`, and `enabled: false`. Readiness confirms caller consent, exact source ownership, destination readiness, and supported deployment scope. It does not confirm worker or model connectivity. -Set `worker.enabled: true` in `playbook-extraction-values.yaml`, apply the values again, and verify the worker: +Verify the worker before activation: ```bash -helm upgrade playbook redis-ai/redis-agent-playbook \ - --version "$PLAYBOOK_CHART_VERSION" \ - --namespace "$PLAYBOOK_NAMESPACE" \ - -f playbook-values.yaml \ - -f playbook-extraction-values.yaml \ - --wait --timeout 10m kubectl -n "$PLAYBOOK_NAMESPACE" rollout status deploy/playbook-worker ``` @@ -220,10 +204,14 @@ curl --fail-with-body --silent --show-error \ --data-binary @activation.json > binding.json ``` +Every explicit enable revalidates the current caller's consent and source ownership. Permission denial or a Memory outage leaves the binding unchanged. Re-enabling a valid blocked binding clears its blocked reason. An enabled binding belongs to the tenant; removing its creator's individual role does not automatically stop it. + The worker checks bindings every five minutes. Sessions must remain idle for 30 minutes by default. Set `job.idleWindowSeconds` from 60 to 86,400 when creating or updating a binding. Review generated proposals and evidence before publication. ## Pause or remove extraction -Pause a binding by sending `enabled: false` with its current `expectedVersion`. Pausing retains prepared grants. Delete the binding and wait for reads to return `404` before disabling `extraction.connection` or removing Redis Agent Memory trust. +An authorized destination manager can pause a binding with `enabled: false` and its current `expectedVersion`, or delete it. Neither operation requires source permissions or a successful Memory request. Both work when `extraction.enabled: false` or Memory is unavailable. Deletion removes scheduling intent, not source data, proposals, or FAQs. + +Pause and deletion fence subsequent extraction work. Requests already accepted by an external service can complete. They do not revoke the worker's static identity. To revoke its access, remove that worker's trust from both data planes and roll all replicas. -To stop new extraction configuration while retaining cleanup, set `extraction.enabled: false` and leave `extraction.connection.enabled: true`. Stop the worker only after pausing affected bindings. Scrape its `/metrics` endpoint on port `9203` and monitor queue age, permanent failures, exhausted attempts, lease loss, provider errors, and dispatch staleness. +Set `extraction.enabled: false` to block binding creation and activation. For an orderly shutdown, pause the bindings before stopping the worker. Scrape its `/metrics` endpoint on port `9203` and monitor queue age, permanent failures, exhausted attempts, lease loss, provider errors, and dispatch staleness. diff --git a/content/operate/iris/agent-playbook/self-managed/operations.md b/content/operate/iris/agent-playbook/self-managed/operations.md index 96bc911111..15953d8a4b 100644 --- a/content/operate/iris/agent-playbook/self-managed/operations.md +++ b/content/operate/iris/agent-playbook/self-managed/operations.md @@ -16,9 +16,9 @@ hideListLinks: true With the [local port-forwards]({{< relref "/operate/iris/agent-playbook/self-managed/deploy#connect-to-the-apis" >}}) running: ```bash -curl --fail-with-body --silent --show-error http://localhost:9200/v1/health/ready -curl --fail-with-body --silent --show-error http://localhost:9201/v1/health/ready -curl --fail-with-body --silent --show-error http://localhost:9202/v1/health/ready +curl --fail-with-body --silent --show-error http://localhost:9200/health/readiness +curl --fail-with-body --silent --show-error http://localhost:9201/health/readiness +curl --fail-with-body --silent --show-error http://localhost:9202/health/readiness ``` Inspect pods and the affected service's logs when readiness fails: @@ -64,7 +64,7 @@ curl --fail-with-body --silent --show-error \ -H "Authorization: Bearer $IDENTITY_CONTROL_TOKEN" ``` -Key validation is cached. The chart's `identityService.runtimeCache.hardTTLSeconds` defaults to 300 seconds. Allow for cached access decisions and verify that the old credential is denied before treating revocation as complete. +Key validation is cached. The standalone Identity Service's `runtime.cache.hardTtlSeconds` defaults to 300 seconds. Allow for cached access decisions and verify that the old credential is denied before treating revocation as complete. ## Clear playbook content @@ -108,7 +108,6 @@ Update an existing Secret from its source files. For example, update storage con ```bash kubectl -n "$PLAYBOOK_NAMESPACE" create secret generic playbook-storage \ --from-file=playbook-storage.yaml=./playbook-storage.yaml \ - --from-file=identity-service-metadata.yaml=./identity-service-metadata.yaml \ --dry-run=client -o yaml | kubectl apply -f - ``` @@ -130,9 +129,9 @@ kubectl -n "$PLAYBOOK_NAMESPACE" get secret playbook-storage -o json \ {{< /multitabs >}} -Copy it into `storage.existingSecretChecksum` in `playbook-values.yaml` and [upgrade the chart]({{< relref "/operate/iris/agent-playbook/self-managed/operations#upgrade-the-chart" >}}) to roll its consumers. The checksum triggers rollout; it does not validate integrity. Use the same pattern for license and inference Secrets. +Copy it into `storage.existingSecretChecksum` in `playbook-values.yaml` and [upgrade the chart]({{< relref "/operate/iris/agent-playbook/self-managed/operations#upgrade-the-chart" >}}) to roll its consumers. The checksum triggers rollout; it does not validate integrity. Use the same pattern for license and inference Secrets. Identity Service metadata uses its separate Secret and `metadata.existingSecretChecksum` in `identity-values.yaml`. -For service credentials, update the Secret and its `existingSecretChecksum` for affected services. For immutable Secrets, create a new name and update the reference. +For shared service credentials, update the Secret and its `existingSecretChecksum` on both releases where supported. Keep the runtime registration and product validator references aligned with Playbook. For immutable Secrets, create a new name and update the reference. ## Back up the deployment @@ -146,7 +145,20 @@ Coordinate metadata and content snapshots. A metadata-only restore cannot recove ## Upgrade the chart -Review the target release's configuration and compatibility requirements. Keep its packaged image versions together and recalculate checksums for changed Secrets. +Review the target release's configuration and compatibility requirements. Keep its packaged image versions together and recalculate checksums for changed Secrets. Retain every values file used by the installation on each upgrade. Choose the command that matches your extraction setup. + +Without extraction: + +```bash +export PLAYBOOK_CHART_VERSION='' +helm upgrade playbook redis-ai/redis-agent-playbook \ + --version "$PLAYBOOK_CHART_VERSION" \ + --namespace "$PLAYBOOK_NAMESPACE" \ + -f playbook-values.yaml \ + --wait --timeout 10m +``` + +With extraction configured, use this command instead, retaining the extraction overlay: ```bash export PLAYBOOK_CHART_VERSION='' @@ -154,21 +166,35 @@ helm upgrade playbook redis-ai/redis-agent-playbook \ --version "$PLAYBOOK_CHART_VERSION" \ --namespace "$PLAYBOOK_NAMESPACE" \ -f playbook-values.yaml \ + -f playbook-extraction-values.yaml \ --wait --timeout 10m ``` +Omitting that overlay resets the extraction and worker settings to their chart defaults. + Check all service rollouts: ```bash kubectl -n "$PLAYBOOK_NAMESPACE" rollout status deploy/playbook-controlplane kubectl -n "$PLAYBOOK_NAMESPACE" rollout status deploy/playbook-dataplane -kubectl -n "$PLAYBOOK_NAMESPACE" rollout status deploy/playbook-identity-service if kubectl -n "$PLAYBOOK_NAMESPACE" get deploy/playbook-worker >/dev/null 2>&1; then kubectl -n "$PLAYBOOK_NAMESPACE" rollout status deploy/playbook-worker fi helm test playbook --namespace "$PLAYBOOK_NAMESPACE" --logs ``` +Upgrade Identity Service separately with its approved version and values, then verify its rollout: + +```bash +export IDENTITY_CHART_VERSION='' +helm upgrade playbook-identity redis-ai/iris-identity-service \ + --version "$IDENTITY_CHART_VERSION" \ + --namespace "$PLAYBOOK_NAMESPACE" \ + -f identity-values.yaml \ + --wait --timeout 10m +kubectl -n "$PLAYBOOK_NAMESPACE" rollout status deploy/playbook-identity-service +``` + Reconnect [local port-forwards]({{< relref "/operate/iris/agent-playbook/self-managed/deploy#connect-to-the-apis" >}}), then repeat [application-access checks]({{< relref "/operate/iris/agent-playbook/self-managed/api-examples#verify-application-access" >}}). Review release compatibility before rollback; Helm does not restore Redis data. For extraction worker rollout, health checks, pause, and cleanup, see [Configure background FAQ extraction]({{< relref "/operate/iris/agent-playbook/self-managed/extraction" >}}). diff --git a/content/operate/iris/agent-playbook/self-managed/prerequisites.md b/content/operate/iris/agent-playbook/self-managed/prerequisites.md index 20879f7f59..5d85f27f49 100644 --- a/content/operate/iris/agent-playbook/self-managed/prerequisites.md +++ b/content/operate/iris/agent-playbook/self-managed/prerequisites.md @@ -17,7 +17,8 @@ hideListLinks: true | --- | --- | | Kubernetes cluster and `kubectl` | Run the services and create Secrets in the deployment namespace. | | [Helm 3](https://docs.helm.sh/docs/v3/intro/install/) | Install `redis-ai/redis-agent-playbook` from `https://helm.redis.io/ai`. Check `helm version --short`. | -| Bash, curl, and jq | Run the administrative API examples. Use a curl version that supports `--fail-with-body`. | +| Bash, curl, jq, and OpenSSL | Run the administrative API examples. Use a curl version that supports `--fail-with-body`. | +| Identity Service | Install the standalone `redis-ai/iris-identity-service` chart before Playbook, or register Playbook on an existing release. | | License | A Playbooks license or an Iris suite license that includes Playbooks. Contact your Redis representative. | | Embedding provider credentials | Configure a provider and model that the deployment can reach. | @@ -29,7 +30,7 @@ The chart consumes existing Redis deployments. It does not provision them. | Redis deployment | Requirements | Use | | --- | --- | --- | -| Metadata | Core Redis commands and Lua scripting. | Playbook configuration and Identity Service records. | +| Metadata | Core Redis commands and Lua scripting; JSON commands when extraction is enabled. | Playbook configuration and Identity Service records. | | Content | JSON and Search capabilities, including vector indexes. | Authored content, proposals, and retrieval. | Use physically separate deployments for metadata and content. Different database numbers or credentials on the same Redis endpoint do not satisfy this requirement. diff --git a/for-ais-only/agent-playbook-operator-docs-review.md b/for-ais-only/agent-playbook-operator-docs-review.md index a795976e3d..6b9e6f2703 100644 --- a/for-ais-only/agent-playbook-operator-docs-review.md +++ b/for-ais-only/agent-playbook-operator-docs-review.md @@ -1,8 +1,8 @@ # Redis Agent Playbooks operator documentation review -Ticket: [MOD-19679](https://redislabs.atlassian.net/browse/MOD-19679). Branch: `MOD-19679-playbook-operator-docs`, stacked on developer branch `MOD-19391-playbook-developer-docs` at `1009f9015`. The operator pull request targets the developer branch so its diff contains only operator scope and operator handoffs. +Tickets: [MOD-20121](https://redislabs.atlassian.net/browse/MOD-20121) and the original [MOD-19679](https://redislabs.atlassian.net/browse/MOD-19679) operator draft. The MOD-20121 follow-up is stacked on operator PR #4256 at `430c680a53e1a1060e26b8f46abb3ed6c7c6b753`; its current provenance, validation, and publication hold are recorded in the follow-up section. -## Reviewed inputs +## Prior reviewed inputs (October 17, 2026) - Iris `main`: `304467e3e3acf5ff9e6800e03055f0a7806bc14d` on October 17, 2026. - Public data-plane contract: identical between that Iris revision and the latest published SDKs. @@ -24,7 +24,7 @@ The chart that matches this source has not been released. The public guides ther - Locks the control-plane artifact hash and expected operation inventory in the Playbooks reference tests. - Replaces resolved TODO comments with links between developer and operator guidance. -## Local validation +## Prior local validation - The complete developer suite passes against the published alpha.3 packages: **126 passed**. - Operator reference and cross-link tests pass: **6 passed**. @@ -41,6 +41,59 @@ The generic `task lint` and `task test` commands are unavailable because this re - Add the approved chart version, image tags, platform matrix, and release-specific upgrade notes when the matching chart is published. - Production sizing, high availability, minimum Redis access-control-list permissions, and supported backup and restore guarantees still require qualification. The public pages retain actionable TODO comments for those items. -- The current generated control-plane contract contains stale extraction descriptions that say the release has no worker and that workers arrive later. Current Iris `main` includes the worker and its Helm deployment. The docs preserve the generated contract byte-for-byte; the Smithy descriptions should be corrected at the source and the reference regenerated. +- The earlier generated contract had stale extraction descriptions. The MOD-20121 follow-up corrects the Smithy source and refreshes the byte-identical generated reference; its candidate and release qualification are recorded next. -Both pull requests remain draft. Merge the developer pull request first, then retarget or rebase the operator pull request onto `main` and confirm its remaining diff. +The operator parent and MOD-20121 follow-up remain drafts. Merge in stack order only after the publication hold is satisfied, then retarget or rebase onto the intended documentation release branch and confirm the remaining diff. + +## MOD-20121 follow-up qualification + +This follow-up is isolated on `docs/MOD-20121-playbook-extraction-lifecycle`, stacked on operator PR [#4256](https://github.com/redis/docs/pull/4256) at `430c680a53e1a1060e26b8f46abb3ed6c7c6b753`. It does not modify that parent branch or PR. Backend [#2065](https://github.com/redislabsdev/iris/pull/2065) follows MOD-20115 and MOD-20116. Local qualification is complete for the production/API source at `af81d8655a5c2ff48003cc96fb7bce42daf1935f`; the final test/documentation-only child commit `2ed416db157ebd12c3f5081ca9eaaf26c8375f0d` still requires remote CI. Neither revision is a released-version claim. + +The extraction guide now uses static RAM data-plane read trust, a separate tenant-confined RAM control-plane viewer, live discovery and exact-source validation, ready/off create, explicit enable/recovery checks, and source-independent pause/delete. It retains the independent ordinary agent-key and RAM promotion-worker identities. The role table distinguishes operator stop/policy access from administrator consent to source discovery and activation. + +The same fresh-install walkthrough depended on obsolete bundled Identity Service values. The follow-up supplies separate Identity Service installation, metadata and shared credentials, ordinary product validation, and the current runtime cache and rotation paths. Existing product registrations and runtime operations must be merged rather than replaced. + +### Contract provenance + +The backend owner ran `task playbook:generate` after correcting the Smithy documentation. The public control-plane output was copied byte-for-byte from `out/smithy/public/playbook-cp/PlaybookControlPlaneService.openapi.json`, without editing the generated JSON. Its SHA256 is `b4e5ad6ff318dcd8e9f60bb75fb2d109058891d0e211e62f9781b24f221cf7f6`. The source lock records that hash and candidate revision together. The existing 13-operation inventory and hash validation remain enforced. + +Before changing the service provenance, the candidate data-plane contract was compared through the repository's `wire_contract` normalization. Its digest remains `991a428a09c362b4b45e1f90bf32c5d9980c6933d2f31d12b2dd730d5a6f34b3`, identical to the locked published SDK contract, with no added or removed operations. The data-plane reference and SDK pins therefore remain unchanged. + +### Publication hold + +The backend stack is unmerged and unreleased. These docs remain draft until the final backend checks pass, the stack is merged and released, and matching chart/image versions and endpoint requirements are verified. The source lock retains locally qualified `af81d8655`: the generated control-plane artifact remains byte-identical at the test/documentation-only child commit. Reverify this provenance against the eventual release before publication; a later test/docs commit alone does not require regenerating an unchanged contract. Coordinate parent [docs #4256](https://github.com/redis/docs/pull/4256), public RAM [#4330](https://github.com/redis/docs/pull/4330), and this [#4331](https://github.com/redis/docs/pull/4331). The contributor CLA condition remains pending; no agreement was signed or asserted. No production or documentation merge is authorized by this local validation. + +### Backend local qualification + +The backend owner's [qualification record](https://github.com/redislabsdev/iris/blob/2ed416db157ebd12c3f5081ca9eaaf26c8375f0d/docs/reviews/MOD-20121-cp-owned-extraction-bindings.md) records commands, fixture boundaries and image digests. At `af81d8655`, all 99 reported CI checks and Bugbot passed before local qualification. Final child CI is not claimed as complete here. + +- Packaged KinD `ci-auth`: all four cases passed in 873.105 seconds. Both projected probe tokens rotated and remained accepted, wrong audiences were rejected, strict worker-UID equality was retained, and RAM's own worker completed three promotion jobs. The content-free report has `complete: true`, exit 0 and no missing or unsuccessful scenarios. The 580.66-second rotation case does not inspect token files inside the distroless worker; per-request reload is covered separately by client tests. +- Authentication/fencing: 675 repeated boundary/transport executions and 18 real-HTTP method executions passed with the race detector. Real RAM/Playbook CPs and DPs, IdS, Redis and the shared worker cover cloud A/B plus late account C, source consent, foreign IDs, denied activation, stale versions and source-independent stop. External issuer, SM/AWS resolution and model responses are fixtures; this is not a deployed Neptune qualification. +- Shared-engine stress: four 4,096-producer admission bursts retain the exact 1,000/default and 64/custom per-queue bounds, drain and recover. Two runtimes complete 2,368 exact ledger/proposal-request pairs across 16 cloud accounts and 108 bindings, plus 520 pairs in four bindings under one on-prem tenant. The exact final stress source passed its runtime race rerun. Source/target HTTP peers and the model are substitutes; sampled runtime queue peaks and finite-workload progress establish neither fleet capacity nor sustained fairness. +- On-prem browser checks: reviewed cloud-ui [#3346](https://github.com/redislabsdev/cloud-ui/pull/3346) at `b38b4e18f47eec10ac13ce9bf8c9765178b08a57` and the installed `@redislabsdev/cloud-ui-irisultra@0.0.19` each passed live-source create/enable/pause/removal, stale-version recovery and late-source discovery against Helm-installed services. These are three source/destination cases in one configured tenant, not three on-prem accounts. +- App-qa console: the reviewed local MFE rendered and read-only/client-draft checks passed, but both serving backends report `044628e49096c2438ccc171b304933af98ff2cd9`. One authorized account and no eligible sources prevented current-candidate A/B/C or lifecycle mutations there. No extraction mutation or infrastructure provisioning was performed. + +### Coordinated UI release + +The installed `0.0.19` bundle preserves the tested happy path but does not contain #3346's blocked/off recovery and copy corrections. Release those changes through normal post-merge MFE publication, then explicitly update the actual Iris `package.json` and lockfile pin, rebuild/release the UI gateway, and run a packaged smoke without a local bundle override. There is no automatic cross-repository pin updater. Passing with the reviewed local bundle does not establish that a published UI image contains those fixes. + +### Follow-up local validation + +- Existing Playbooks suite: **138 passed** with the locked published SDKs; strict TypeScript compilation and `check_sdk_source.py` passed. +- All **14 YAML** and **46 Bash** fences under the operator section parse; all operator frontmatter is unchanged. +- Full Hugo **0.143.1** build passed: **19,928 pages**. No Playbook-page warnings. +- Complete documented Playbook and standalone Identity Service values, plus the current public RAM and standalone Identity Service values with the extraction overlays, pass Helm **3.20.2** strict lint and template rendering. No substitute base fixture was used. +- Rendered-config assertions confirm service and ServiceAccount names, all three projected audiences, static read/propose authority, the tenant-confined RAM CP viewer, ordinary introspection operations, shared credential mounts, and HTTPS product validators. +- Independent backend-owner review checked the complete diff. Its gate wording correction is applied: `extraction.enabled: false` blocks binding creation and activation; policy-only updates retain their existing behavior. +- `git diff --check` passed. The generated CP artifact is byte-identical to candidate output, and the DP reference and SDK pins remain unchanged. + +These checks validate the documentation artifacts and configuration wiring. The completed backend local qualification is separate evidence; production model quality, fleet capacity, deployed Neptune reachability and published chart/image support remain unqualified by these local runs. + +### Independent review corrections + +- D001: removed the unsupported global `product_admin` OIDC role mapping from the standalone Identity Service example. Product-scoped `roleBindings` remain. The full documented IdS values plus OIDC overlay render the scoped binding and configured tenant; the binary's `validateControlOperatorJWT` rejects the old mapping. Existing Go `TestControlConfigValidatesOperatorJWTBlocks` passed, including rejection of global `product_admin` and acceptance of scoped role bindings. +- D002: corrected all three operator health checks to `/health/readiness`, matching the actual rendered deployment readiness probes. +- D003: provided explicit upgrade commands for installations with and without extraction. The configured case retains `playbook-extraction-values.yaml`; supplying only the base values resets extraction settings. This matches [Helm 3.20.2 upgrade behavior](https://github.com/helm/helm/blob/v3.20.2/pkg/action/upgrade.go#L504). +- D004: metadata prerequisites now require JSON commands when extraction is enabled, matching the binding repository's `JSON.SET`/`JSON.GET` operations. + +These corrections affect documentation examples and prerequisites only. The existing SDK/reference suite remains applicable; the affected YAML/Bash and rendered documentation checks were repeated.