The RHOAI 3.6 transition target is not for Praxis to call IPP. It is for Praxis
to replace IPP as Envoy's ExternalProcessor service while preserving the
MaaS body-based routing (BBR) behavior used by RHOAI 3.5. The gaps and
implementation phases in this document are the work required for RHOAI 3.6.
The planned release paths are:
| Release | Intended request architecture |
|---|---|
| RHOAI 3.6 | Client -> Envoy -> Praxis ExternalProcessor -> provider |
| RHOAI 3.7 expected continuation | Same Envoy and Praxis integration, subject to its own release validation |
| RHOAI 3.8 candidate | Client -> Praxis -> provider |
The 3.6 row is product shorthand. Under standard Envoy ext_proc
semantics, Envoy remains the HTTP data plane. It exchanges request and response
events with Praxis over bidirectional gRPC, applies the mutations returned by
Praxis, and then forwards the HTTP request to the selected provider:
client
|
v
Envoy <---- ext_proc gRPC ----> Praxis ExternalProcessor
|
| applies routing and payload mutations returned by Praxis
v
provider
Praxis therefore needs two distinct layers:
- An Envoy-compatible
ExternalProcessorgRPC server. - The relevant IPP and MaaS BBR processing behavior behind that server.
praxis-proxy/ai PR #334
implements the opposite protocol role: Praxis is an ext_proc client that
calls an external processor. Its protocol types and tests may be reusable, but
the PR does not provide the server or BBR implementation required for the
RHOAI 3.6 transition.
The work should be designed so the model resolution, provider selection,
translation, and credential logic is independent of gRPC. The RHOAI 3.6
ExternalProcessor server can call that logic through an Envoy adapter, and a
future direct Praxis data path can call the same logic without Envoy.
These are the major gaps that must be closed before Praxis can claim parity with the deployed IPP/BBR path.
| Priority | Gap | Required outcome |
|---|---|---|
| P0 | ExternalProcessor server |
Praxis exposes Envoy's bidirectional Process gRPC service with production listener, TLS, health, readiness, shutdown, and resource controls. |
| P0 | Full processing lifecycle | Praxis correctly handles request and response headers, bodies, and trailers; full-duplex streaming; mode overrides; cancellation; protocol errors; and immediate responses. |
| P0 | BBR request processing | Praxis extracts the requested model from the body, resolves the MaaS model and provider, and returns the route, host, path, model, and header mutations Envoy needs. |
| P0 | Pre-auth and post-auth profiles | Praxis supports the two ordered MaaS processing stages without moving authorization or changing their fail-open/fail-closed behavior. |
| P0 | MaaS header boundary | Consumer-supplied MaaS and provider-authentication headers are captured or removed so callers cannot choose an unauthorized provider or pass credentials through unchanged. |
| P0 | API translation | Praxis provides the request and response transformations required by the supported OpenAI, Anthropic, Vertex, Bedrock, and other pinned release fixtures. |
| P0 | Provider credential generation | Praxis performs the release-required API key and cloud authentication flows and fails closed when a referenced credential is unavailable. |
| P0 | Kubernetes state integration | Model, provider, credential-reference, and route state reaches Praxis with defined ownership, readiness, update, deletion, and failure semantics. |
| P0 | Product packaging | The shipped Praxis image contains and enables the server, includes supported configuration, has complete RBAC and manifests, and is used by the MaaS deployment. |
| P0 | Release parity suite | The RHOAI 3.5 IPP/BBR conformance fixtures pass through Envoy with Praxis substituted for IPP. |
| P1 | Streaming correctness and limits | Streaming request/response transformations preserve SSE semantics and enforce body, time, concurrency, and memory bounds. |
| P1 | Observability | Protocol, plugin, provider-selection, mutation, error, latency, and credential-generation results are measurable without exposing payloads or secrets. |
| P1 | Live updates | Provider, model, translation, and credential state can change without stale routing or process restarts, with last-known-good behavior where appropriate. |
| P1 | Broader upstream plugin parity | Additional upstream IPP scoring, profile, cost, session, and metering plugins are implemented only when the pinned product contract requires them. |
This table isolates the upstream IPP framework work from the MaaS-specific BBR plugins and Kubernetes controllers described later.
| IPP capability | What IPP provides | Gap in Praxis |
|---|---|---|
| Envoy service role | Implements Envoy's ExternalProcessor.Process bidirectional gRPC server |
PR #334 implements an ext_proc client; Praxis does not provide the production server Envoy needs. |
| Stream lifecycle | Tracks the ordered request and response header, body, and trailer events for one HTTP exchange | Implement a server-side state machine with validation, cancellation, cleanup, and bounded per-request state. |
| Body processing | Collects and parses inference JSON before running request processors | Add bounded body assembly and parsing, including malformed, incomplete, oversized, and streamed input behavior. |
| Processing pipeline | Runs ordered pre-processors, a selected profile, and post-processors with shared cycle state | Add a reusable processing pipeline and typed per-request context independent of Envoy and Pingora. |
| Profile selection | Selects configured processing behavior for a request | Add explicit, validated profiles, beginning with the deployed MaaS pre-auth and post-auth profiles. |
| Plugin contracts | Defines request and response processing extension points | Add only the stable extension interfaces needed by the pinned product profiles; source-level plugin API compatibility is not required. |
| Mutations | Returns request and response header/body mutations and requests route-cache clearing | Generate valid Envoy mutations with deterministic conflict, ordering, removal, and route-recalculation rules. |
| Immediate rejection | Returns an ImmediateResponse when parsing, policy, or processing fails |
Map internal failures to stable HTTP responses without leaking request content, credentials, or internal state. |
| Processing-mode override | Can change which later request or response events Envoy sends | Implement and validate the mode overrides required by the deployed Envoy configuration. |
| Response processing | Processes buffered JSON and streamed response data, including SSE-oriented paths | Add bounded response processing and preserve streaming semantics across arbitrary chunk boundaries. |
| Shared request state | Supplies cycle state to cooperating processors | Add typed state with clear ownership, mutation rules, and lifecycle; avoid an unstructured cross-plugin map where possible. |
| Configuration and data layer | Loads processor profiles and supplies changing model or routing data to plugins | Add validated configuration and atomic state snapshots with readiness and last-known-good reporting. |
| Operational server behavior | Runs as a separately deployable payload-processing service | Add listener configuration, TLS, gRPC health, readiness, metrics, graceful shutdown, concurrency limits, manifests, and image packaging. |
| Protocol conformance | Exercises Envoy protocol behavior through IPP's server implementation | Convert the useful PR #334 protocol fixtures to server-side tests and add real Envoy interoperability coverage. |
The deployed MaaS configuration places two IPP stages around authorization. Praxis must preserve this division.
sequenceDiagram
participant Client
participant Envoy
participant Auth as MaaS Authorization
participant Praxis as Praxis ExternalProcessor
participant Provider
Client->>Envoy: Inference request
Envoy->>Praxis: Pre-auth ext_proc<br/>headers and request body
Praxis-->>Envoy: Resolved model identity<br/>and bounded mutations
Envoy->>Auth: Authenticate and authorize
Auth-->>Envoy: Authorization result
Envoy->>Praxis: Post-auth ext_proc<br/>headers and request body
Praxis-->>Envoy: Provider, path, payload,<br/>and credential mutations
Envoy->>Provider: Mutated provider request
Provider-->>Envoy: Provider response
Envoy->>Praxis: Response ext_proc
Praxis-->>Envoy: Translated response
Envoy-->>Client: Client-facing response
Envoy owns the ordering around authorization. Praxis does not need to recreate
Envoy's entire filter chain for RHOAI 3.6, but it must implement each
ExternalProcessor exchange correctly and expose distinct, explicit pre-auth
and post-auth processing profiles.
The inspected MaaS deployment configures:
body-field-to-headermodel-provider-resolver- request headers enabled
- request body in
FULL_DUPLEX_STREAMEDmode - request trailers enabled
- response body disabled
failure_mode_allow: trueallow_mode_override: true
The parity test must prove that a BBR request's model identity is available to the existing MaaS authorization layer in the same form and at the same point in the request lifecycle.
The inspected MaaS deployment configures:
maas-headers-guardstream-usage-enforcermodel-provider-resolverapi-translationapikey-injection- response
api-translation - request and response headers enabled
- request and response bodies in
FULL_DUPLEX_STREAMEDmode - request and response trailers enabled
failure_mode_allow: falseallow_mode_override: true
The parity test must prove that authorization has already succeeded before provider credentials are selected or injected, and that processing failures do not silently forward an unprocessed request.
Body-based routing presents a unified inference endpoint. Instead of choosing a different URL for each model, the client places the desired model in the request body:
{
"model": "publishers/example/models/model-a",
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}The BBR processing path then:
- Parses the supported inference request without losing streaming behavior.
- Resolves the client-facing model identity.
- Makes that identity available to MaaS authorization.
- Resolves an eligible internal or external provider.
- Rewrites the target model, authority, path, and provider-specific headers.
- Translates the API shape when the provider does not use the client-facing protocol.
- Generates or injects the provider credential after authorization.
- Translates the response back to the client-facing protocol when needed.
Replacing only the IPP gRPC protocol is insufficient. Praxis must also replace the deployed BBR behavior that currently runs on top of IPP.
PR #334 adds an optional llmd-ext-proc integration under
integrations/llmd/ext-proc. It implements an HttpFilter that:
- opens a
tonicchannel to an external processor; - sends Envoy
ProcessingRequestmessages; - receives and applies
ProcessingResponsemutations inside Praxis; and - uses mock
ExternalProcessorservers for protocol tests.
This is useful for a different topology:
client -> Praxis ext_proc client -> external processor -> Praxis -> provider
It does not implement the required topology:
client -> Envoy ext_proc client -> Praxis ExternalProcessor server
Additional product limitations observed in the PR source:
- The server crate's default features do not enable
llmd-ext-proc. - The production
Containerfiledoes not build with that feature. - The integration tests exercise deterministic mock processors rather than the deployed MaaS payload-processing implementation.
- The filter is documented as an llm-d compatibility integration, not as a replacement MaaS BBR service.
The following pieces are candidates for reuse:
- vendored/generated Envoy
ext_procprotobuf types; - protocol validation knowledge captured by the extensive client tests;
- header, body, trailer, immediate-response, mode-override, timeout, and cancellation fixtures;
- common mutation conversion helpers, if their ownership does not couple them to the client state machine; and
- build tooling for the Envoy protobuf API.
The following should not be treated as the server implementation:
- the
tonic::transport::Channelclient; - the
HttpFilterlifecycle; - endpoint configuration for calling an external processor; and
- client-side buffering and mutation application.
PR #334 should either remain a separately scoped client integration or be split so only genuinely shared protocol code is reused by the new server. It should not be described as IPP/BBR parity.
| Capability | IPP/Envoy behavior | Praxis status | Required work |
|---|---|---|---|
ExternalProcessor.Process server |
Bidirectional gRPC stream per request | Missing | Implement a production ExternalProcessor service. |
| Request headers | Delivered and mutated | Client-side support only | Accept, validate, process, and return header responses. |
| Request body | Full-duplex streamed in MaaS config | Client-side support only | Assemble or incrementally process with explicit limits and end-of-stream handling. |
| Request trailers | Enabled | Protocol fixtures exist | Implement server behavior and parity fixtures. |
| Response headers | Enabled post-auth | Client-side support only | Run response plugins and emit mutations. |
| Response body | Full-duplex streamed post-auth | Client-side support only | Support JSON and SSE-safe translation with bounded state. |
| Response trailers | Enabled post-auth | Protocol fixtures exist | Implement server behavior and parity fixtures. |
| Mode override | Allowed by MaaS Envoy config | Client recognizes protocol | Return valid overrides and test invalid/unsupported combinations. |
| Immediate response | Used to reject processing failures | Client recognizes protocol | Map policy, parsing, routing, and credential errors to stable client responses. |
| Route-cache clearing | Needed when authority/path/route metadata changes | Not a server capability | Return the proper mutation response and prove Envoy re-evaluates its route. |
| Cancellation | Stream may end early | Client tests exist | Release per-request state and work promptly. |
| Timeouts | MaaS config allows long processing windows | Client has callout timeouts | Define server deadlines and ensure Envoy failure policy remains authoritative. |
| Health/readiness | Processor must be discoverable and ready | Missing server | Add gRPC health plus meaningful dependency readiness. |
| Transport security | Cluster-local service contract | Missing server | Define mTLS or service-mesh protection and authorization policy. |
| Behavior | Current implementation | Praxis gap |
|---|---|---|
| Body model extraction | IPP body-field-to-header |
Implement equivalent extraction and exact output header contract. |
| Model/provider resolution | ODH model-provider-resolver |
Consume model/provider state, resolve aliases, select eligible providers, and emit authority/path/model mutations. |
| Weighted providers | Resolver treats weights as eligibility/selection input; zero disables a provider | Implement and test deterministic eligibility and weighted selection semantics. |
| MaaS header guard | Removes or captures x-maas-*, authorization, and x-api-key input |
Implement before provider credential generation and test spoof rejection. |
| Streaming usage normalization | Ensures supported streaming requests include usage information | Implement the same body mutation without breaking arbitrary JSON fields. |
| Request API translation | ODH translators mutate path, headers, and request JSON | Implement all transformations in the pinned release matrix. |
| Response API translation | ODH translators convert provider responses to the client-facing API | Implement JSON and SSE response transformations. |
| Static API keys | Secret-backed provider authentication | Implement credential lookup and injection with fail-closed behavior. |
| AWS authentication | SigV4 generator | Implement and validate canonical signing across supported services and streaming bodies. |
| GCP authentication | OAuth2 generator exists in source | Confirm it is enabled in the pinned release before making it a release blocker; implement if required. |
| Provider Secret updates | Kubernetes Secret informer/store | Define watched state, rotation, deletion, readiness, and last-known-good semantics without logging values. |
| Unsupported model/path | Rejected by resolver or excluded route | Match stable status and error behavior. |
| Malformed body | Rejected by processor | Enforce bounded parsing and stable error behavior. |
The current ODH payload-processing service does more than transform a request. It also watches or participates in reconciling product state.
| Integration | Current source behavior | Decision required |
|---|---|---|
ExternalModel state |
Watched by model/provider resolver code | Decide whether Praxis watches the CRD directly or consumes a rendered snapshot from a controller. |
ExternalProvider state |
Watched and reconciled | Assign authoritative ownership and deletion/update behavior. |
| Kubernetes Secrets | Watched for provider credentials | Prefer a bounded credential-reference interface; if Praxis watches Secrets, scope RBAC and namespaces narrowly. |
| ConfigMaps | Used by the BBR framework/configuration | Define immutable schema, validation, hot reload, and last-known-good behavior. |
Services and HTTPRoute resources |
Controllers participate in network resource reconciliation | Keep this in the MaaS controller unless a clear product requirement assigns it to Praxis. |
| Istio resources | Source includes ServiceEntry and DestinationRule management |
Preserve in the platform/controller layer unless Praxis explicitly owns it. |
| Bypass routes | Ext-proc is disabled for model, subscription, API-key, and MaaS API routes | Preserve the route exclusions in Envoy configuration and add rendered-config tests. |
Parity does not require moving every controller into the Praxis process. It requires preserving the complete externally observable contract and assigning each responsibility to a supported component. The issue should identify the owner of each CRD, Secret, and networking-resource behavior.
Avoid implementing the product logic directly inside the gRPC service. Use three layers:
An ext_proc_server component should:
- implement Envoy's generated
ExternalProcessortrait; - validate the event sequence;
- maintain bounded state for one request;
- convert protocol messages into internal processing events;
- convert internal mutations and rejections back into Envoy responses;
- enforce concurrency, body-size, event-count, and deadline limits; and
- expose health, readiness, metrics, and graceful shutdown.
An IPP-compatibility or MaaS-processing core should own:
- typed request context and cycle state;
- named, ordered processing profiles;
- model identity extraction and normalization;
- provider resolution and selection;
- request and response translation;
- credential-generation interfaces;
- mutation conflict rules; and
- typed errors with stable external mappings.
This core should have no dependency on Envoy gRPC streams or Pingora request
sessions. That allows 3.8's direct Praxis path to call it without emulating
ext_proc.
Narrow interfaces should provide:
- model catalog snapshots;
- provider catalog and eligibility;
- credential references and provider-authentication material;
- translation configuration; and
- revision/readiness metadata.
The Kubernetes implementation may watch resources directly or receive rendered state from MaaS controllers. Either choice needs atomic snapshots, bounded retention, deletion handling, provenance, and explicit last-known-good status.
Full source-level reproduction of IPP's plugin framework is not automatically required. The safest first milestone is:
- implement the deployed pre-auth and post-auth profiles;
- expose stable internal plugin traits only where multiple implementations actually require them;
- add additional upstream filter/score/pick profiles only when a pinned RHOAI release or supported user story requires them.
This avoids claiming parity with every upstream experimental plugin while still meeting the product's full deployed behavior.
- Pin the exact RHOAI 3.5 payload-processing image digest.
- Capture its rendered Envoy filters, processing profiles, RBAC, CRDs, and configuration.
- Export request/response fixtures for each supported provider and API.
- List every enabled IPP and ODH plugin.
- Record fail-open/fail-closed behavior and bypass routes.
- Decide whether "full parity" means the shipped 3.5 product or all features in the current upstream repositories.
Exit criterion: one reviewed conformance manifest defines the target without mutable image tags or branch-dependent assumptions.
- Create a separately testable Praxis AI crate or binary component for the
RHOAI 3.6
ExternalProcessorserver. - Reuse the Envoy protobuf package where appropriate.
- Add listener, TLS, gRPC health, readiness, shutdown, and resource limits.
- Implement the request event state machine.
- Port relevant protocol fixtures from PR #334 from the server's point of view.
Exit criterion: Envoy can exercise headers, bodies, trailers, mode overrides, immediate responses, cancellation, and error cases against Praxis.
- Extract and validate the body model field.
- Resolve aliases and canonical identities required by MaaS authorization.
- Emit the exact headers and mutations consumed by the existing auth layer.
- Preserve fail-open behavior only where the current Envoy configuration intentionally permits it.
Exit criterion: existing MaaS authorization accepts and rejects the same BBR requests with Praxis substituted for the pre-auth IPP service.
- Port the MaaS header guard.
- Implement provider resolution and weighted eligibility.
- Implement path, authority, target-model, and header mutations.
- Implement static-key and release-required cloud authentication.
- Fail closed on missing, stale, deleted, or unauthorized credential references.
Exit criterion: provider selection and credential isolation match the pinned IPP behavior, including rotation and negative tests.
- Port every request and response translator in the pinned release.
- Implement stream-usage normalization.
- Preserve tool calls, multimodal content, JSON mode, system prompts, and multi-turn messages.
- Validate JSON and SSE streaming across arbitrary chunk boundaries.
- Add body-size and processing-time limits.
Exit criterion: the complete provider fixture matrix passes for buffered and streaming responses.
- Assign ownership for
ExternalModel,ExternalProvider, Secret, route, and mesh resources. - Implement or connect the required state providers.
- Add revisioned, atomic hot reload and explicit readiness.
- Verify deletion, rollback, stale state, invalid state, and restart behavior.
- Package RBAC and network policy with least privilege.
Exit criterion: a clean cluster installation converges from CRDs and Secrets to a ready processor without manual patches.
- Enable the server in the production image and manifests.
- Add upgrade and rollback coverage from IPP to Praxis.
- Run scale, churn, restart, and fault-injection suites.
- Expose supported metrics and diagnostics.
- Add a native Praxis adapter over the same processing core.
Exit criterion: RHOAI 3.6 can replace IPP behind Envoy, while the core logic can
support the candidate 3.8 Client -> Praxis -> provider path without a rewrite.
- request headers only;
- request body split at every meaningful JSON boundary;
- request trailers;
- response headers only;
- JSON response body;
- SSE response chunks split across event and UTF-8 boundaries;
- response trailers;
- mode override before and after body events;
- immediate response at each processing stage;
- client cancellation and Envoy timeout;
- duplicate, out-of-order, unsolicited, and missing events;
- oversized bodies and excessive event counts;
- processor restart and graceful shutdown; and
- concurrent streams under configured limits.
- unified endpoint selects the correct internal model;
- canonical and alias model identities;
- nonexistent, disabled, and ambiguous models;
- provider weighting and weight-zero exclusion;
- provider deletion and recovery;
- authorization sees the pre-auth resolved identity;
- consumer-supplied
authorization,x-api-key, andx-maas-*values cannot cross the provider boundary; - missing or deleted provider Secret fails closed;
- credential rotation takes effect without exposing the credential;
- bypass routes do not invoke payload processing;
- OpenAI-compatible provider;
- Anthropic request/response translation;
- Vertex Anthropic translation and authentication;
- AWS request translation and SigV4;
- any additional provider in the pinned release;
- tool calling;
- multimodal input;
- JSON mode;
- system prompts;
- multi-turn conversations;
- streaming usage;
- malformed JSON, empty messages, and unsupported paths; and
- provider errors translated without hiding meaningful status.
- clean installation from released manifests;
- exact image digest and feature presence;
- readiness remains false until required state is accepted;
- configuration and Secret updates;
- invalid update retains and reports last-known-good state;
- restart during active streams;
- bounded memory under large bodies and high concurrency;
- RBAC denial behavior;
- network isolation between Envoy, Praxis, and unrelated workloads;
- metrics and logs contain correlation data but no credentials or request payloads by default; and
- upgrade from the RHOAI 3.5 IPP deployment with rollback.
These decisions must be resolved before implementation is split into PRs:
- What exact RHOAI 3.5 image digest and rendered configuration define parity?
- Is Praxis one service with named pre/post profiles, or two deployments using the same image?
- Which MaaS controllers remain responsible for
ExternalModel,ExternalProvider, route, and service-mesh resources? - Does Praxis read Kubernetes Secrets directly, or use a narrower credential-provider interface?
- Which provider and translation combinations are supported release requirements rather than source-only capabilities?
- Is GCP OAuth2 enabled in the parity baseline?
- Are external metering and NeMo guard plugins in the parity baseline?
- Does PR #334 remain as a supported ext-proc client use case, or should its shared protocol code be split from the client?
- What compatibility guarantee is required between the Envoy version shipped
in RHOAI and Praxis's vendored
ext_procprotobuf API? - For 3.8, which component assumes the authorization and policy position that Envoy owns in RHOAI 3.6?
IPP/BBR parity is complete only when:
- Praxis runs as Envoy's production
ExternalProcessorservice; - the pinned pre-auth and post-auth profiles are represented exactly;
- the full pinned 3.5 plugin and provider matrix passes;
- authorization ordering and failure policy are unchanged;
- BBR model resolution, provider routing, translation, and credential isolation pass positive and negative tests;
- streaming behavior is correct and resource-bounded;
- Kubernetes state ownership and update semantics are documented and tested;
- the released image enables the feature by default for the supported deployment;
- clean install, upgrade, rollback, restart, and scale tests pass; and
- the reusable processing core is not coupled to Envoy, preserving the direct Praxis architecture planned for a later release.
This analysis used locally pinned source rather than mutable documentation:
| Source | Revision |
|---|---|
llm-d/llm-d-inference-payload-processor current audit |
4bdb5de697c8cb1972d23860af39642830b954b9 |
| IPP dependency pinned by ODH payload processing | a8bbe6a6a75a9435cbf8d0703613cf5ac9875f90 |
opendatahub-io/models-as-a-service |
006206d1f3cdc27bf575923278f3490504ea11a2 |
opendatahub-io/ai-gateway-payload-processing |
7889f6a5cd261e69db95cb29617537c3ad8ebe0e |
| Praxis AI PR #334 audit branch | e75a9b03a0ca3f1003a0b9629a564fd92d9b2662 |
The ODH Go module pins IPP pseudo-version
v0.1.0-rc.4.0.20260721200012-a8bbe6a6a75a. The inspected MaaS deployment
manifest references a mutable odh-stable payload-processing image. Final
release signoff therefore still requires the exact RHOAI 3.5 image digest and
rendered configuration; repository HEAD alone cannot prove product parity.
Primary repository references: