feat: scaffold the experimental filters crate and gateway image - #9
Conversation
|
Missing |
Turn this repo from an empty workspace into a home for experimental Praxis AI filters, and make it produce a runnable gateway image. - `praxis-experimental-filters`: carries the `[package.metadata.praxis-filters]` marker and registers via `praxis_filter::export_filters!`. Ships one no-op placeholder filter so discovery and registration are provable before real filters land. - `praxis-experimental-server`: thin bin whose `build.rs` runs praxis-ai's discovery (`praxis-ai-build-support`) and composes the stock praxis-ai server with this workspace's filters. praxis-ai is pinned to an exact rev because its crates are `publish = false`. - `Containerfile`: build and ship `praxis-experimental-server` rather than only the probe binary, following praxis-ai's cache-stub layout. The server's real `build.rs` is copied before the dependency-cache build; a stub would compile but emit no registration code, silently producing a server with none of this workspace's filters. - `deny.toml`: allow the praxis-ai git source. - `docs/identity-metadata.md`: the `filter_metadata` identity contract that identity producers write and metering/budget consumers read. Track A of the Standalone AI Gateway MVP epic (praxis-proxy/ai#758). Split out of praxis-proxy#4 so the image build does not depend on `switchyard_route`, which is being handed off separately. Assisted by Opus 5 Signed-off-by: usize <mofoster@redhat.com>
The container workflow ran `docker run --rm` and expected the process to
exit, which was correct for the probe binary but hangs forever now that the
entrypoint is a long-running server.
Start the container detached with a minimal config, poll the Containerfile's
HEALTHCHECK until it reports healthy, and confirm the gateway answers on
:8080. Logs are dumped unconditionally so a failure is diagnosable.
`examples/configs/minimal.yaml` is the smallest config that boots the server
and answers health checks — no providers, no auth, no token accounting. It
exists to prove the image runs; the quickstart config lands with the MVP
packaging work.
The admin listener binds loopback (core requires this unless
`insecure_options.allow_public_admin` is set), so /healthy is reachable only
from inside the container, which is where HEALTHCHECK runs. The proxy
listener binds 0.0.0.0 so the published port is reachable from the runner.
Verified locally: server boots against this config, /healthy returns
{"status":"ok"}, / returns the expected body, and unknown paths 404.
Assisted by Opus 5
Signed-off-by: usize <mofoster@redhat.com>
c00c8b5 to
025baee
Compare
Three CI gates failed on the first push: - **audit**: three advisories in the Pingora fork's tree, reached through praxis-ai and not under our control. Ignored with the same rationale praxis-proxy/praxis and praxis-proxy/ai already use for the first two; `lru` is newer and not yet in their lists. - **coverage**: 83% lines against a 90% floor. The placeholder's `from_config` was never exercised, only its registration. Added tests that build it from both a populated and an empty config mapping, bringing the workspace to 95% lines / 97% regions. - **meta-lint**: `taplo fmt --check` on the workspace `members` array and the new `deny.toml` ignore list. `HttpFilterContext` has no public constructor and praxis-filter's `test_utils` is `pub(crate)`, so an external filter crate cannot unit-test `on_request` directly. The placeholder's `on_request` is therefore left to integration coverage rather than faked here. Assisted by Opus 5 Signed-off-by: usize <mofoster@redhat.com>
meta-lint enforces MD060 table-column-style "aligned": every row's pipes must line up with the header separator. The separator row was narrower than the header, so the closing pipes drifted. Content unchanged; `git diff -w` is empty. This restores the alignment fix from experimental#4 that the scaffold split did not carry over. Verified: markdownlint-cli2@0.23.2 over all tracked *.md reports 0 issues. Assisted by Opus 5 Signed-off-by: usize <mofoster@redhat.com>
praxis-bot
left a comment
There was a problem hiding this comment.
PR Review: scaffold the experimental filters crate and gateway image
This PR converts the experimental repo from an empty workspace into a functional gateway by adding a filter crate with build-time discovery, a thin server binary composing praxis-ai with this workspace's filters, a proper Containerfile, CI smoke tests, and an identity metadata contract document.
Overall Assessment
Solid scaffold work. The build-time filter discovery mechanism is well-designed, the Containerfile correctly handles the build.rs ordering subtlety (real build script before dependency cache to avoid silent zero-registration), and the CI smoke test is a significant improvement over the previous docker run --rm approach. The identity metadata contract is a useful forward-looking document.
Two convention violations found in the test module.
Findings
| Severity | File | Finding |
|---|---|---|
| Medium | placeholder.rs |
Doc comments on test functions |
| Medium | placeholder.rs |
Missing separator comment before test module |
|
Non-conforming commit subjects (expected
Amend with |
Two convention violations from review: - Test functions carried `///` doc comments. Per docs/conventions.md the function name is the documentation, so the comments are removed; the explanatory text was already duplicated in the assertion messages, which is where the conventions say it belongs. - The test module had no preceding separator. Added the full-width (77-dash) `Tests` separator, matching crates/experimental-probe. No behavior change; the same four tests pass and coverage is unchanged at 95% lines / 97% regions. Assisted by Opus 5 Signed-off-by: usize <mofoster@redhat.com>
75f3bfc to
d9ff85f
Compare
The doc proposed a cross-repo `identity.*` filter_metadata namespace, with
normative rules ("exactly one producer per pipeline", "a later producer MUST
NOT overwrite") and named producers and consumers owned by other repos
(ai#698, ai#130, ai#577). Its own header said "Status: proposed... needs
maintainer sign-off before consumers build on them".
That is a proposal, not scaffolding, and it now has a home: the
Discussion -> Proposal -> Experimental -> Standard process in
praxis-proxy/enhancements. Merging it here as documentation would skip the
sign-off it asks for, and would risk `api_key_auth` being written against a
namespace that ai#698 later contradicts.
Nothing in the scaffold reads it, so removing it is inert. It will be
re-filed as a proposal, informed by a working `api_key_auth` prototype —
the experimental-phase guide notes the How section is stronger that way.
Narrows the scope of praxis-proxy#1, which had listed the contract as a deliverable.
Assisted by Opus 5
Signed-off-by: usize <mofoster@redhat.com>
Turns this repo from an empty workspace into a home for experimental Praxis AI
filters, and makes it produce a runnable gateway image.
Split out of #4 so the image build does not depend on
switchyard_route, whichis being handed off separately. #4 keeps only the filter, its guide, and its
demo, and rebases cleanly on top of this.
What's here
praxis-experimental-filters— carries the[package.metadata.praxis-filters]marker and registers viaexport_filters!. Ships one no-op placeholder filter so discovery isprovable before real filters land.
praxis-experimental-server— thin bin whosebuild.rsruns praxis-ai'sdiscovery (
praxis-ai-build-support) and composes the stock praxis-ai serverwith this workspace's filters. praxis-ai is pinned to an exact rev because its
crates are
publish = false.Containerfile— builds and shipspraxis-experimental-server.examples/configs/minimal.yaml— smallest config that boots the server andanswers health checks; used by the container smoke test.
deny.toml— allows the praxis-ai git source.docs/identity-metadata.md— thefilter_metadataidentity contract thatidentity producers write and metering/budget consumers read.
Notes for review
Crate naming. Named
praxis-experimental-*rather thanswitchyard-*as on#4, matching the names #1 specified. The scaffold shouldn't be named after a
feature that may be dropped.
The Containerfile was not actually building the gateway. On #4 the new crate
manifests were added to the stub stage but the build still only produced
experimental-probe— green CI, container with no gateway in it. Fixed here.The real
build.rsis copied before the dependency-cache build, because astubbed build script compiles fine and silently emits zero filter
registrations.
Container CI was waiting for a process that never exits. The workflow ran
docker run --rmand expected the probe to run to completion; with along-running server it would hang until timeout. It now starts detached, polls
the
HEALTHCHECK, curls the gateway, and always dumps logs. The admin listenerbinds loopback (core requires this unless
insecure_options.allow_public_adminis set), so
/healthyis checked from inside the container while the proxylistener binds
0.0.0.0.MSRV. The 1.96.1 bump from #4 is reverted; it existed only for Switchyard's
pin.
Verification
cargo clippy --workspace --all-targets,cargo test --workspace, andcargo fmt --checkare clean.Discovery is proven end to end, not assumed — the generated
external_filters.rscontainspraxis_experimental_filters::register_filters(registry);.The image config was smoke-tested locally: the server boots against
examples/configs/minimal.yaml,/healthyreturns{"status":"ok"},/returns the expected body, and unknown paths 404.
Process note
#1 was closed by the proposal migration to praxis-proxy/enhancements, not
because it was done. This change is the experimental repo's own plumbing —
build-time filter registry, workspace configuration, and CI — which the
enhancements guide lists under the exempt path rather than the
prototype-in-experimental path. Filing it directly here; happy to write it up as
an exempt proposal instead if maintainers prefer.
Refs #1, #4, praxis-proxy/ai#758