PlainID includes the AI Content Controls Service, a self-hosted service that categorizes prompts and masks sensitive data in the content that flows through agentic AI applications. Gateways, Authorizers, SDKs, and AI agents call the service to learn what a prompt is about and to mask sensitive entities before content reaches a model or a user. Categorization supports input guardrails, and masking supports response guardrails. The service analyzes and transforms content only. The PlainID Policy Decision Point (PDP) evaluates Policies and makes every authorization decision.
The service runs in a customer-managed Kubernetes cluster and installs with a Helm chart. It reads its category catalog from an Asset Type in a PlainID Environment, so administrators manage categories in the Platform and the service picks up changes automatically. Categorization uses a large language model (LLM) provider that the administrator configures. Masking runs locally in the same pod and does not need an LLM.
For more information on Input and Output Guardrail prompts, refer to our documentation on Input & Output Guardrails.
Capabilities
| Capability | Endpoint | Description |
|---|---|---|
| Prompt categorization | POST /v1/categorize |
Classifies a single prompt or a full conversation against the category catalog of an Asset Type. Callers can also send their own categories in the request, which the service adds to the catalog and labels by source. |
| Content masking | POST /v1/mask |
Detects sensitive entities in text and masks them. The configuration sets a default masking operator and can override it per entity type. |
| Category refresh | POST /v1/admin/categories/refresh |
Forces an immediate catalog refresh for one Asset Type (?asset_type_id=) or for every cached Asset Type. |
| Liveness | GET /healthz |
Reports liveness, the active configuration hash, and the status of each capability. |
| Readiness | GET /readyz |
Reports ready when the category catalog has loaded and masking is available or still within its grace window. |
| Metrics | GET /metrics |
Exposes metrics in Prometheus format. |
Prerequisites
Before deploying the service, make sure the following are in place:
- A Kubernetes cluster with Helm installed
- The PlainID Tenant ID and Environment ID
- PlainID API credentials: an API token, or a client ID and client secret
- Credentials for a supported LLM provider, such as Amazon Bedrock or Azure OpenAI
- An Asset Type in the Environment that holds the categories to classify against
Deploying the service
-
Create a Kubernetes secret that holds the PlainID credentials and the LLM provider credentials.
-
Install the chart, passing the Tenant ID, Environment ID, and secret name:
helm install ccs plainid-content-controls \ --set config.plainid.tenant_id=<tenant-id> \ --set config.plainid.env_id=<environment-id> \ --set existingSecret=<secret-name> -
Wait for the pod to report ready, then call
GET /healthzto confirm that each capability reports a healthy status.
The chart ships tenant_id and env_id empty on purpose. If either value is missing, the pod does not start, which prevents the service from reading categories from the wrong Environment.
Each pod runs three containers: the service and two Microsoft Presidio containers (analyzer and anonymizer) that perform masking.
Configuring the service
The service reads configuration from a YAML file (mounted as a ConfigMap) and from environment variables. When both set the same key, the environment variable takes precedence. In the Helm chart, every value under config.* becomes part of the ConfigMap.
Environment variables use the PLAINID_ prefix, with a double underscore for each level of nesting:
PLAINID_CONFIG_FILE=/etc/plainid/config.yaml
PLAINID_CLASSIFICATION__LLM__MODEL=<provider>/<model-id>
PLAINID_PLAINID__CLIENT_SECRET=<client-secret>
Set secrets only through environment variables, never in the configuration file. This includes the PlainID client secret or API token and the masking encryption key. Provider credentials such as AZURE_API_KEY or AWS credentials use the provider's standard environment variable names.
The service validates its configuration at startup and refuses to start if anything is invalid, for example a classification prompt template that does not reference the category list.
Category source
By default, the service loads categories from an Asset Type in the PlainID Environment and refreshes them in the background. For testing without a Platform connection, a static catalog can replace the Asset Type:
plainid:
categories:
source: static
static:
- {id: c1, name: HR, description: "Human resources, compensation, hiring"}
LLM provider
POST /v1/categorize is the only endpoint that needs an LLM provider. Masking, health, and metrics work do not require one.
When using Amazon Bedrock:
- Use the inference-profile model ID, which includes a region prefix such as
us. - Set
AWS_REGION_NAMEto the region the credential belongs to. A mismatch causes every call to fail authentication, regardless of the model. - The service automatically switches to prompt-instructed JSON output, because Bedrock does not accept structured-output requests.
Working with the service
- Correlate requests. Send an
X-Request-IDheader with each call. The service echoes it in the response and includes it in every log and audit record when the log format isjson. Provide this ID to PlainID Support when reporting an issue. - Pick up category changes immediately. Call
POST /v1/admin/categories/refreshafter changing categories in the Platform, rather than waiting for the next background refresh. - Monitor the service. Scrape
/metricswith Prometheus and watch the per-capability status in/healthz.
Notes
- Masking outage: If the Presidio containers restart or go down,
POST /v1/maskreturns 503 until they recover. The pod stays in service, and categorization keeps working. - Grace window: Readiness tolerates a masking outage for a configured grace window. After the window expires, the pod reports not ready.
- Input size: The configuration sets size limits per prompt and per conversation, and determines whether the service truncates or rejects input that exceeds them.
- No caller authentication. The service does not authenticate callers. Restrict access at the network layer, for example with Kubernetes network policies, so that only the gateways, Authorizers, and agents that use the service can reach it.
- Listener address. The container entrypoint controls the listening host and port. Setting
service.hostorservice.portin the configuration file has no effect. - Correlation IDs do not pass through to the Platform, the LLM provider, or Presidio.
- Masking uses Microsoft Presidio, an open-source data protection and de-identification SDK. For more information, see microsoft.github.io/presidio.