Skip to content

feat: scaffold the experimental filters crate and gateway image - #9

Merged
usize merged 6 commits into
praxis-proxy:mainfrom
usize:scaffold-experimental-filters
Aug 24, 2026
Merged

feat: scaffold the experimental filters crate and gateway image#9
usize merged 6 commits into
praxis-proxy:mainfrom
usize:scaffold-experimental-filters

Conversation

@usize

@usize usize commented Aug 24, 2026

Copy link
Copy Markdown
Member

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, which
is 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 via
    export_filters!. Ships one no-op placeholder filter so discovery is
    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 — builds and ships praxis-experimental-server.
  • examples/configs/minimal.yaml — smallest config that boots the server and
    answers health checks; used by the container smoke test.
  • deny.toml — allows the praxis-ai git source.
  • docs/identity-metadata.md — the filter_metadata identity contract that
    identity producers write and metering/budget consumers read.

Notes for review

Crate naming. Named praxis-experimental-* rather than switchyard-* 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.rs is copied before the dependency-cache build, because a
stubbed 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 --rm and expected the probe to run to completion; with a
long-running server it would hang until timeout. It now starts detached, polls
the HEALTHCHECK, curls the gateway, and always dumps logs. The admin listener
binds loopback (core requires this unless insecure_options.allow_public_admin
is set), so /healthy is checked from inside the container while the proxy
listener 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, and
cargo fmt --check are clean.

Discovery is proven end to end, not assumed — the generated
external_filters.rs contains
praxis_experimental_filters::register_filters(registry);.

The image config was smoke-tested locally: the server boots against
examples/configs/minimal.yaml, /healthy returns {"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

@github-actions

Copy link
Copy Markdown

Missing Signed-off-by trailer: 1c66b37, c00c8b5. Sign-off asserts you reviewed and understand your changes (see conventions). Amend with git commit --amend -s.

usize added 2 commits August 23, 2026 20:20
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>
@usize
usize force-pushed the scaffold-experimental-filters branch from c00c8b5 to 025baee Compare August 24, 2026 03:21
usize added 2 commits August 23, 2026 20:36
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 praxis-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread crates/praxis-experimental-filters/src/placeholder.rs
Comment thread crates/praxis-experimental-filters/src/placeholder.rs
@github-actions

Copy link
Copy Markdown

Non-conforming commit subjects (expected type(scope): summary, ≤72 chars, types: build/chore/ci/docs/feat/fix/perf/refactor/test):

  • 75f3bfc: style(filters): follow test conventions in the placeholder module

Amend with git commit --amend or rewrite with git rebase -i.

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>
@usize
usize force-pushed the scaffold-experimental-filters branch from 75f3bfc to d9ff85f Compare August 24, 2026 16:25
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>
@usize
usize merged commit 251b1bc into praxis-proxy:main Aug 24, 2026
17 checks passed
@usize
usize deleted the scaffold-experimental-filters branch August 24, 2026 16:53
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