The shortest path is Phileas, the redaction library. It runs in your process, needs no service, and has no machine learning dependencies. It ships for Python, Java, and .NET.
Java
Add the dependency. The log4j-bom import is required: Phileas 4.1.0 resolves
log4j-api and log4j-core at different versions, which fails at startup with a
NoSuchMethodError unless you align them.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>2.26.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>ai.philterd</groupId>
<artifactId>phileas</artifactId>
<version>4.1.0</version>
</dependency>
</dependencies>
Redact something. Policies are written in PhiSQL and loaded with Policy.fromPhiSQL:
import ai.philterd.phileas.PhileasConfiguration;
import ai.philterd.phileas.policy.Policy;
import ai.philterd.phileas.model.filtering.TextFilterResult;
import ai.philterd.phileas.services.context.DefaultContextService;
import ai.philterd.phileas.services.disambiguation.vector.InMemoryVectorService;
import ai.philterd.phileas.services.filters.filtering.PlainTextFilterService;
import java.util.Properties;
public class Example {
public static void main(String[] args) throws Exception {
final PhileasConfiguration configuration = new PhileasConfiguration(new Properties());
final PlainTextFilterService filterService = new PlainTextFilterService(
configuration,
new DefaultContextService(),
new InMemoryVectorService(),
null); // optional HttpClient for PhEye; null uses the default
final Policy policy = Policy.fromPhiSQL("""
REDACT EMAIL_ADDRESS WITH REDACT;
REDACT ZIP_CODE WITH REDACT;
""");
final TextFilterResult result = filterService.filter(
policy, "context", "Email test@example.com, zip 90210.");
System.out.println(result.getFilteredText());
}
}
Output:
Email {{{REDACTED-email-address}}}, zip {{{REDACTED-zip-code}}}.
Requires Java 21 or later.
Python
Install:
pip install phileas-redact
Redact something:
from phileas.policy.policy import Policy
from phileas.services.filter_service import FilterService
policy = Policy.from_dict({
"name": "my-policy",
"identifiers": {
"emailAddress": {"emailAddressFilterStrategies": [
{"strategy": "REDACT", "redactionFormat": "{{{REDACTED-%t}}}"}]},
"ssn": {"ssnFilterStrategies": [
{"strategy": "REDACT", "redactionFormat": "{{{REDACTED-%t}}}"}]},
},
})
result = FilterService().filter(
policy=policy,
context="my-context",
document_id="doc-001",
text="Contact john@example.com or call about SSN 123-45-6789.",
)
print(result.filtered_text)
for span in result.spans:
print(f"[{span.filter_type}] -> {span.replacement} "
f"at {span.character_start}:{span.character_end}")
Output:
Contact {{{REDACTED-email-address}}} or call about SSN {{{REDACTED-ssn}}}.
[email-address] -> {{{REDACTED-email-address}}} at 8:24
[ssn] -> {{{REDACTED-ssn}}} at 43:54
.NET
Install. The package is Philterd.Phileas, not Phileas:
dotnet add package Philterd.Phileas
Redact something:
using Phileas.Policy;
using Phileas.Policy.Filters;
using Phileas.Services;
var policy = new Policy
{
Name = "basic-policy",
Identifiers = new Identifiers
{
Ssn = new Ssn(),
EmailAddress = new EmailAddress(),
PhoneNumber = new PhoneNumber()
}
};
var result = new FilterService().Filter(
policy,
context: "session-1",
piece: 0,
input: "SSN: 123-45-6789 Email: alice@example.com Phone: 555-867-5309"
);
Console.WriteLine(result.FilteredText);
Output:
SSN: {{{REDACTED-ssn}}} Email: {{{REDACTED-email-address}}} Phone: {{{REDACTED-phone-number}}}
Identifiers default to redaction, so no strategy is specified above.
What just happened
A policy declares which identifier types to look for and what to do with each one. The three ports express it differently, PhiSQL, a dict, and typed objects, but the underlying model is the same.
The result carries both the rewritten text and details of every span that changed, with character offsets. Those spans are what you log, review, or feed into a human review step.
The context groups documents that should be treated as related. It matters when you use strategies that keep replacements consistent, so the same input value maps to the same replacement everywhere it appears within that context.
REDACT is one of several strategies. Others mask, hash, shift dates, or encrypt in a
reversible way. Each port supports a different subset, so check its documentation for
the full list.
Where to go next
- Decide whether you want a library, a service, or an LLM proxy: choose a tool.
- Read the reference documentation for the project you land on.