Skip to content

refactor(sre): configure the SRE agent at install; aep-api serves its handoff - #859

Merged
tharindulak merged 7 commits into
wso2:mainfrom
tharindulak:refactor/sre-install-time-config
Oct 4, 2026
Merged

tharindulak merged 7 commits into
wso2:mainfrom
tharindulak:refactor/sre-install-time-config

Conversation

@tharindulak

@tharindulak tharindulak commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Configures the OpenChoreo SRE agent entirely at install and serves its handoff from aep-api, as proposed in #846's review. The agent runs one Deployment per observability plane, with one model and one static MCP header. The per-org machinery around it (an SRE model connection table, a per-org token, a reconciler and a separate MCP server) served exactly one org anyway.

Following the review on this PR, one installation now serves every org on its plane. Each handoff call names its org, and aep-api verifies that claim against OpenChoreo's own alert record. There is no --org.

Decision recorded in ADR-0040; design note: services/aep-api/design/sre-handoff.md.

What changes

  • aectl writes the SRE agent's configuration at install. aectl sre install probes --llm-api-key-file/--llm-model/--llm-base-url (https only, no redirects) against <base URL>/models, then writes the model straight into the agent's Secret sre-agent-aep. A re-run without the flags keeps the model; without any model the agent waits at 0 replicas, as before.
  • One handoff key. aectl generates it once (32 random bytes, reused unless --rotate-handoff-token) into sre-agent-aep and aep-api's sre-handoff Secret. It passes the key's hash to the platform release, so a rotation rolls aep-api. The key authenticates the agent, not an org.
  • aep-api is the MCP server. POST /internal/v1/sre-handoff/mcp serves search_related_issues and create_issue in process, over the JSON-RPC plumbing now shared with the discovery MCP (platform/mcprpc, no MCP SDK). The tool descriptions and wire shapes are unchanged from aep-mcp-server.
  • Each call names its org, verified against the observer. Both tools take a required namespace, which the agent copies from the alert.
    • Why it is checked: that value reaches aep-api through a model that reads pod logs, so aep-api never takes it as given.
    • The check: before either tool reads or writes, aep-api asks OpenChoreo's observer (POST /api/v1alpha1/alerts/query, with aep-api's own service token) whether an alert fired in the last hour for that namespace and project, and, for a create, that component. It accepts the component as given or with the project prefix.
    • Failure: no alert is a 403 tool error; an observer that cannot answer is a 503. Either way nothing is read or filed.
    • Requirements: the handoff needs OBSERVER_URL and the service credential; aep-api and the chart refuse to enable it without them.
  • Removed:
    • SRE_HANDOFF_ORG and aectl sre install --org;
    • the REST handoff gate, the sreagent reconciler, the kubeobs client, the SRE model connection service and the SREAgent connection capability (contract regenerated, console fixtures updated);
    • aep-mcp-server, with its image (images/release workflows, skaffold, make dev-update, aectl platform update --mcp-server-image) and chart templates. The chart's aep-api-sre-handoff HTTPRoute matches exactly the handoff path.
  • Migration: phase24_drop_sre_model_connections drops org_sre_model_connections and the SRE org_secrets rows. It runs after main's phase23_agent_guardrail_applications; phase22 stays because databases have run it.
  • Removed on upgrade: aectl deletes the old push Role/RoleBinding and the sre-model-seed Secret.
  • Docs: ADR-0040 supersedes ADR-0038's two SRE amendments. The handoff skill and CONTEXT.md tell the agent to copy the namespace from the alert. The design note, runbook, security notes, install note and glossary are updated.

What the org check does and does not prevent

  • Prevents wrong-org filing: one agent sees every org's alerts, and with only a project name, a name clash could file one org's RCA into another org's repo. The namespace is now the org.
  • Prevents arbitrary orgs: the one key reaches only components that alerted in the last hour. A prompt-injected agent can at most act on another real incident within that window; binding each call to the alert's ID would narrow it to one alert.
  • Fails closed on WSO2 Cloud: its wc-… namespaces are not org handles, so the handoff resolves to no org there. It is not enabled on WSO2 Cloud today.

Testing

  • aep-api: go vet, the full suite, make deadcode-check, golangci-lint (0 issues) and make gen-api-check pass with Go 1.26.0. The phase22→phase23→phase24 migration tests pass against Postgres (testcontainers).
    • New tests cover the MCP tools: incident context bound, labels, actionStatuses, the 409 conflict, plan-issue annotation and the tool list.
    • New tests cover the org check: an unverified namespace reads and files nothing, an observer outage fails closed, the component-prefix match and the required arguments.
    • Also new: the observer alert client, the verifier and the handoff config.
  • aectl: go vet, all tests and golangci-lint pass. New tests cover model resolution, the probe (accept, reject without echoing the key, no redirects), key reuse and rotation, the agent Secret and scaling, and the removal of --org.
  • Console: typecheck and the settings/onboarding tests pass.
  • Chart: renders with the handoff on and off, and fails clearly if it is enabled without observer.baseURL.
  • CI: build/test/lint, images, the bal tool and the license check pass.
  • Live, on a fresh make dev-env from this branch (WITH_AGENT_MANAGER=0, seeded with a gpt-5.4 key), using a sample app whose service1 divides by zero on an empty catalog:
    1. Install: the key probe passed ("SRE model key accepted"), every ExternalSecret synced, the agent came up 1/1 on openai:gpt-5.4 with the ae extension, and aep-api logged SRE handoff enabled with no SRE_HANDOFF_ORG. From the agent's pod, the endpoint answered 200 with both tools requiring namespace, and 401 with a wrong key.
    2. Trigger: switching service2 to empty and calling GET /average-score (only those two requests) returned a 500 / by zero, and the alert fired.
    3. Handoff: the RCA ran, and the agent called search_related_issues and create_issue with namespace=default. aep-api's observer check accepted both, so aep-api's service token can read alerts. The issue was filed in that org's repo.
    4. Fix: the coding agent picked up the issue and merged a fix, which AE built and deployed. GET /average-score on the empty catalog then returned 200 {"average":0}, with no error log and no new alert.

Known, not caused by this PR

  • make deadcode-ts-check reports packages/web-search/src/aep-web.ts and mcp.ts, as on main.
  • promote-task-from-issue has no caller in this repo, and had none before; its comments now say so. Removing the operation is left for a follow-up.
  • CodeRabbit skips the review: 129 files exceed its limit of 100, mostly from removing aep-mcp-server.
  • Dev-env: OpenBao runs as bao server -dev, so a k3d node restart wipes it (and the CoreDNS host.k3d.internal entry). This hit testing twice on an overloaded VM; persistent OpenBao storage for dev is a separate fix.

🤖 Generated with Claude Code

tharindulak and others added 3 commits October 2, 2026 21:58
The agents' discovery MCP hand-writes MCP's Streamable HTTP in its
single-response form: JSON-RPC types, initialize/ping/tools/list
dispatch, notification 202s and the tool-result writers. Move that
protocol layer into platform/mcprpc so a second surface can serve its
own tools over it without an MCP SDK; mcpdiscovery keeps its tools and
behaves exactly as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… handoff

The OpenChoreo SRE agent runs one Deployment per plane with one model and
one static MCP header, so it can only ever serve one org. AE still kept
per-org machinery around it: an SRE model connection table, a per-org
handoff token in org_secrets, a reconciler pushing both into the agent's
Secret, and aep-mcp-server forwarding the agent's bearer to two REST
operations through a public-edge gate. Replace all of it with
install-time values and an in-process MCP surface.

aectl sre install:
- probes --llm-api-key-file/--llm-model/--llm-base-url (https only, no
  redirects) and writes the model straight into sre-agent-aep; a re-run
  without them keeps the model, and with no model the agent waits at 0
  replicas, as before;
- generates the handoff key once (32 random bytes, reused unless
  --rotate-handoff-token) into both sre-agent-aep and aep-api's
  sre-handoff Secret, and passes its hash to the platform release so a
  rotation rolls aep-api;
- removes the aep-api-sre-push Role/RoleBinding and the sre-model-seed
  Secret earlier versions installed.

aep-api:
- serves search_related_issues and create_issue at POST
  /internal/v1/sre-handoff/mcp (sourcecontrol/issues, over
  platform/mcprpc), calling the issue service in process with the
  handoff's incident context bound, behind a constant-time check of the
  install-time key (SRE_HANDOFF_ORG / SRE_HANDOFF_TOKEN);
- drops the REST handoff gate, the sreagent reconciler, the kubeobs
  client, the SRE model connection service and the SREAgent connection
  capability (contract regenerated, console fixtures updated);
- migration phase23 drops org_sre_model_connections and the SRE
  org_secrets rows; auto-RCA now follows SREHandoff.Enabled.

Removed: services/aep-mcp-server, its image (CI, release, skaffold, dev
Makefile, aectl --mcp-server-image) and chart templates; the platform
chart routes exactly the handoff path to aep-api instead. The handoff
skill moves next to the extension it is mounted with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR-0040 records the decision and supersedes ADR-0038's two SRE model
connection amendments. services/aep-api/design/sre-handoff.md replaces
sre-model-connection.md; the runbook, security notes, install design
note, glossary and READMEs describe the install-time model and handoff
key and aep-api's handoff surface.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 129 files, which is 29 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fca7ff27-6248-4c83-8641-4af6170f8b2e
📥 Commits

Reviewing files that changed from the base of the PR and between 5d60400 and f6a5be1.

⛔ Files ignored due to path filters (3)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • services/aep-api/internal/gen/models_gen.go is excluded by !**/gen/**
  • services/aep-api/internal/gen/server_gen.go is excluded by !**/gen/**
📒 Files selected for processing (129)
  • .github/workflows/images.yml
  • .github/workflows/release.yml
  • Makefile
  • README.md
  • apps/console/src/features/onboarding/components/OnboardingWizard.test.tsx
  • apps/console/src/features/onboarding/keyDisconnected.test.ts
  • apps/console/src/features/settings/aiSettings.test.ts
  • apps/console/src/features/settings/components/AiAgentsCard.test.tsx
  • apps/console/src/mocks/fixtures/settings.ts
  • apps/console/src/mocks/handlers/settings.ts
  • deployments/README.md
  • deployments/helm-charts/design/sre-agent-install.md
  • deployments/helm-charts/platform/templates/aep-api/deployment.yaml
  • deployments/helm-charts/platform/templates/aep-api/sre-handoff-route.yaml
  • deployments/helm-charts/platform/templates/aep-mcp-server/deployment.yaml
  • deployments/helm-charts/platform/templates/aep-mcp-server/httproute.yaml
  • deployments/helm-charts/platform/templates/aep-mcp-server/networkpolicy.yaml
  • deployments/helm-charts/platform/templates/aep-mcp-server/service.yaml
  • deployments/helm-charts/platform/values.yaml
  • deployments/scripts/setup-env-for-aectl.sh
  • deployments/scripts/setup-sre.sh
  • deployments/sre-agent-extensions/README.md
  • deployments/sre-agent-extensions/remediation/CONTEXT.md
  • deployments/sre-agent-extensions/remediation/skills/coding-agent-handoff/SKILL.md
  • docs/architecture.md
  • docs/decisions/ADR-0038-an-organization-has-one-model-connection.md
  • docs/decisions/ADR-0040-the-sre-agent-is-configured-at-install.md
  • docs/developer-guide/sre-handoff-runbook.md
  • docs/developer-guide/sre-handoff-security.md
  • docs/glossary.md
  • knip.jsonc
  • packages/contracts/api/v1/openapi.yaml
  • services/aep-api/.env.example
  • services/aep-api/README.md
  • services/aep-api/design/sre-handoff.md
  • services/aep-api/design/sre-model-connection.md
  • services/aep-api/internal/app/app.go
  • services/aep-api/internal/app/sre_agent.go
  • services/aep-api/internal/arch/domain_arch_test.go
  • services/aep-api/internal/clients/kubeauth/kubeauth.go
  • services/aep-api/internal/clients/kubeobs/client.go
  • services/aep-api/internal/clients/kubeobs/client_test.go
  • services/aep-api/internal/clients/observability/alerts.go
  • services/aep-api/internal/clients/observability/alerts_test.go
  • services/aep-api/internal/config/config.go
  • services/aep-api/internal/config/config_loader.go
  • services/aep-api/internal/config/config_test.go
  • services/aep-api/internal/delivery/task/handlers.go
  • services/aep-api/internal/dependencies/mcpdiscovery/mcp_server.go
  • services/aep-api/internal/dependencies/mcpdiscovery/mcp_server_test.go
  • services/aep-api/internal/dependencies/mcpdiscovery/mcp_tools.go
  • services/aep-api/internal/edge/app.go
  • services/aep-api/internal/edge/sre_handoff_gate.go
  • services/aep-api/internal/edge/sre_handoff_gate_test.go
  • services/aep-api/internal/edge/sre_handoff_surface_test.go
  • services/aep-api/internal/edge/surfaces.go
  • services/aep-api/internal/migrate/phase24_drop_sre_model_connections.go
  • services/aep-api/internal/migrate/phase24_drop_sre_model_connections_dbtest_test.go
  • services/aep-api/internal/migrate/run_all.go
  • services/aep-api/internal/migrate/step_order_test.go
  • services/aep-api/internal/organization/README.md
  • services/aep-api/internal/organization/agent_settings_service.go
  • services/aep-api/internal/organization/config_llm_component_test.go
  • services/aep-api/internal/organization/entity_org_sre_model_connection.go
  • services/aep-api/internal/organization/model_connection_service.go
  • services/aep-api/internal/organization/repository_agents_card.go
  • services/aep-api/internal/organization/repository_org_sre_model_connection.go
  • services/aep-api/internal/organization/sre_effective.go
  • services/aep-api/internal/organization/sre_effective_test.go
  • services/aep-api/internal/organization/sre_model_connection_dbtest_test.go
  • services/aep-api/internal/organization/sre_model_connection_service.go
  • services/aep-api/internal/organization/sre_model_connection_service_test.go
  • services/aep-api/internal/organization/sre_model_seed.go
  • services/aep-api/internal/organization/sre_model_seed_test.go
  • services/aep-api/internal/platform/auth/sre_handoff.go
  • services/aep-api/internal/platform/auth/sre_handoff_test.go
  • services/aep-api/internal/platform/mcprpc/mcprpc.go
  • services/aep-api/internal/platform/modelconn/modelconn.go
  • services/aep-api/internal/platform/modelconn/modelconn_test.go
  • services/aep-api/internal/platform/orgconfig/config_wire.go
  • services/aep-api/internal/projects/README.md
  • services/aep-api/internal/sourcecontrol/issue_component_test.go
  • services/aep-api/internal/sourcecontrol/issue_incident.go
  • services/aep-api/internal/sourcecontrol/issue_search.go
  • services/aep-api/internal/sourcecontrol/issue_sre_test.go
  • services/aep-api/internal/sourcecontrol/issues/doc.go
  • services/aep-api/internal/sourcecontrol/issues/handler.go
  • services/aep-api/internal/sourcecontrol/issues/handler_test.go
  • services/aep-api/internal/sourcecontrol/issues/sre_mcp.go
  • services/aep-api/internal/sourcecontrol/issues/sre_mcp_test.go
  • services/aep-api/internal/sourcecontrol/issues/sre_mcp_tools.go
  • services/aep-api/internal/sreagent/desired.go
  • services/aep-api/internal/sreagent/desired_test.go
  • services/aep-api/internal/sreagent/reconciler.go
  • services/aep-api/internal/sreagent/reconciler_test.go
  • services/aep-api/internal/sreagent/token.go
  • services/aep-api/internal/sreagent/token_test.go
  • services/aep-mcp-server/Dockerfile
  • services/aep-mcp-server/package.json
  • services/aep-mcp-server/skills/README.md
  • services/aep-mcp-server/src/aepClient.test.ts
  • services/aep-mcp-server/src/aepClient.ts
  • services/aep-mcp-server/src/env.test.ts
  • services/aep-mcp-server/src/env.ts
  • services/aep-mcp-server/src/handoffContext.test.ts
  • services/aep-mcp-server/src/handoffContext.ts
  • services/aep-mcp-server/src/main.ts
  • services/aep-mcp-server/src/platformIssues.test.ts
  • services/aep-mcp-server/src/platformIssues.ts
  • services/aep-mcp-server/src/server.test.ts
  • services/aep-mcp-server/src/server.ts
  • services/aep-mcp-server/tsconfig.json
  • skaffold.yaml
  • tools/aectl/cmd/platform.go
  • tools/aectl/cmd/sre.go
  • tools/aectl/cmd/sre_assets.go
  • tools/aectl/cmd/sre_assets_test.go
  • tools/aectl/cmd/sre_extensions.go
  • tools/aectl/cmd/sre_extensions_test.go
  • tools/aectl/cmd/sre_flags_test.go
  • tools/aectl/cmd/sre_mcp_url_test.go
  • tools/aectl/cmd/sre_model.go
  • tools/aectl/cmd/sre_model_test.go
  • tools/aectl/cmd/sre_postrender.go
  • tools/aectl/cmd/sre_secret_test.go
  • tools/aectl/cmd/sre_seed.go
  • tools/aectl/cmd/sre_seed_test.go
  • tools/aectl/cmd/update.go
  • tools/aectl/cmd/update_test.go

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

server_gen.go embeds the contract gzip-compressed, and compress/flate's
output differs between Go releases: the copy generated with Go 1.27.1
did not match what CI's Go 1.26.0 (go.work) produces, so gen-api-check
failed. Regenerated with GOTOOLCHAIN=go1.26.0; only the embedded spec's
bytes change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tharindulak
tharindulak marked this pull request as ready for review October 2, 2026 18:40
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kaje94

kaje94 commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Shall we update the SRE agent to send the org namespace with each mcp tool call?

Right now aep-api always uses SRE_HANDOFF_ORG, so incidents from other orgs go to the wrong org or fail. With the org namespace being sent in each tool call, we wouldn't need SRE_HANDOFF_ORG or --org xxx flag in aectl sre install .

tharindulak and others added 2 commits October 3, 2026 21:43
Conflicts:
- migrate: main shipped phase23_agent_guardrail_applications, so the SRE
  drop migration is renumbered phase24_drop_sre_model_connections and
  runs after it (design note and ADR-0040 follow).
- mcpdiscovery: main's new list_guardrail_policies tool moves onto the
  shared mcprpc helpers.
- gen: server_gen.go regenerated from the merged contract with Go 1.26.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rver

One SRE agent serves every org on its observability plane, but the
handoff filed every incident under the one org `--org` named: another
org's incident failed, or, on a project-name clash, landed in the wrong
org's repo with that org's RCA in it (review of wso2#859).

Both tools now take a required `namespace`, the alert's OpenChoreo
namespace, and `SRE_HANDOFF_ORG` / `aectl sre install --org` are gone.
That value reaches aep-api through a model that reads pod logs, so it is
a claim: before either tool reads or writes, aep-api asks the observer
(POST /api/v1alpha1/alerts/query, clients/observability.AlertQuerier,
with its own service token) whether an alert fired in the last hour for
that namespace and project, and, for create_issue, that component (as
given or with the project prefix). No alert is a 403 tool error, an
observer that cannot answer a 503; either way nothing is read or filed.
The handoff key now authenticates the agent, not an org, and aep-api
refuses to start the handoff without OBSERVER_URL and its service
credential.

The skill and CONTEXT.md tell the agent to copy the namespace from the
alert; ADR-0040, the design note, runbook and security notes record the
check and its bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tharindulak added a commit to wso2-tharindu-SF/sample-6 that referenced this pull request Oct 3, 2026
…ervice1 (#5) (#8)" (#9)

This reverts commit cff7021, restoring service1 to v1 so the
divide-by-zero on an empty catalog can be triggered again to test the
SRE handoff (wso2/labs-agentic-engineer#859).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@tharindulak
tharindulak merged commit 2254be3 into wso2:main Oct 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants