Two images. One filter. Wherever you ship.
Every release publishes two distroless Linux imagesto GitHub Container Registry— one packaged as a GitHub Action, one as a standalone HTTP API. Both are built from the same Native AOT engine and tagged in lockstep.
Real pulls, straight fromGitHub Container Registry— the total downloads GitHub reports across both images.
Total downloads as reported by GitHub Container Registry.
Action container
ghcr.io/ievangelist/profanity-filter — the runner image used by the GitHub Action step. You usually don’t pull this directly; GitHub does it for you when a workflow says uses: IEvangelist/profanity-filter@main.
API container
ghcr.io/ievangelist/profanity-filter-api — the standalone HTTP API. Powers the Aspire integration, theProfanityFilter.Client NuGet, and any standalone deployment.
Action container
View on GHCRThe Action container packages the runner the workflow step uses. Powered by.NET 10 Native AOT, it cold-starts in milliseconds, has no .NET runtime to install, and is what makes a singleuses:line work end-to-end.
Pull
docker pull ghcr.io/ievangelist/profanity-filter:13.4.6.2API container
View on GHCRA standalone ASP.NET Core minimal-API surface over the same filter engine, plus a SignalR hub for live streaming. Same image theAspire integrationpulls.
Pull
docker pull ghcr.io/ievangelist/profanity-filter-api:13.4.6.2Run it standalone
The API listens on :8080inside the container. Map the port and you’re live.
# Run the API on http://localhost:8080
docker run --rm \
--name profanity-filter-api \
-p 8080:8080 \
ghcr.io/ievangelist/profanity-filter-api:13.4.6.2
# Optional — mount your own *.txt word lists, merged with the
# built-in 6,500+ word dictionary at startup:
docker run --rm \
--name profanity-filter-api \
-p 8080:8080 \
-v "$(pwd)/CustomData":/app/CustomData:ro \
ghcr.io/ievangelist/profanity-filter-api:13.4.6.2Or with Compose
# compose.yaml
services:
profanity-filter:
image: ghcr.io/ievangelist/profanity-filter-api:13.4.6.2
container_name: profanity-filter-api
ports:
- "8080:8080"
volumes:
- ./CustomData:/app/CustomData:ro
restart: unless-stoppedHTTP surface
All routes return JSON. The container also serves an interactiveScalarreference at /scalarand an OpenAPI document at /openapi/v1.json.
| Method | Path | Description |
|---|---|---|
| POST | /profanity/filter | Apply the filter to a body of text. Returns the filtered output, the matched words, and the per-step trace. |
| GET | /profanity/strategies | List the 14 replacement strategies (Emoji, RedactedRectangle, AngerEmoji, …). |
| GET | /profanity/targets | List the supported filter targets. |
| GET | /profanity/data | List the available word-list data set names (one per language plus any custom mounts). |
| GET | /profanity/data/{name} | Return the words in a single data set. |
| WS | /profanity/hub | SignalR hub — bi-directional streaming for live UIs (WebSockets / SSE / long-polling). |
Try it
curl -X POST http://localhost:8080/profanity/filter \
-H "content-type: application/json" \
-d '{
"text": "What the heck, dude?",
"strategy": "Emoji"
}'