Local Development
The OPA Entity Checker Processor has a self-contained dev setup that lets you run and test it in isolation — no full Backstage app, no database, and no catalog ingestion cycle required.
Prerequisites
- Node 22 or 24
- A running OPA server at
http://localhost:8181with theentity_checkerpolicy loaded — rundocker-compose up -dfrom the repo root - Dependencies installed:
yarn install --immutable
Starting the plugin
yarn workspace @parsifal-m/backstage-plugin-opa-entity-checker-processor start
The backend starts at http://localhost:7007.
How it works
The dev backend directly instantiates EntityCheckerClientImpl (the OPA HTTP client) and CatalogOPAEntityValidator (the processor), then wires them into a single POST /process endpoint. Send any entity, get back the same entity with the open-policy-agent/entity-checker-validation-status annotation added. No catalog, no database, no waiting.
The root app-config.yaml in this repo already has the required config:
openPolicyAgent:
baseUrl: 'http://localhost:8181'
entityCheckerProcessor:
enabled: true
policyEntryPoint: 'entity_checker/violation'
Testing the endpoint
POST /api/opa-entity-checker-processor-dev/process
Send an entity body. The response is the same entity with the validation annotation added.
Example: entity that passes all checks
curl -X POST http://localhost:7007/api/opa-entity-checker-processor-dev/process \
-H 'Content-Type: application/json' \
-d '{
"apiVersion": "backstage.io/v1alpha1",
"kind": "Component",
"metadata": {
"name": "good-component",
"namespace": "default",
"tags": ["java", "service"]
},
"spec": {
"type": "service",
"lifecycle": "production",
"owner": "user:default/mock",
"system": "examples"
}
}'
Expected response — annotation is pass:
{
"apiVersion": "backstage.io/v1alpha1",
"kind": "Component",
"metadata": {
"name": "good-component",
"namespace": "default",
"tags": ["java", "service"],
"annotations": {
"open-policy-agent/entity-checker-validation-status": "pass"
}
},
"spec": {
"type": "service",
"lifecycle": "production",
"owner": "user:default/mock",
"system": "examples"
}
}
Example: entity with violations
Remove metadata.tags, set an invalid spec.lifecycle, and omit spec.system to trigger violations in the entity_checker policy:
curl -X POST http://localhost:7007/api/opa-entity-checker-processor-dev/process \
-H 'Content-Type: application/json' \
-d '{
"apiVersion": "backstage.io/v1alpha1",
"kind": "Component",
"metadata": {
"name": "bad-component",
"namespace": "default"
},
"spec": {
"type": "custom-type",
"lifecycle": "legacy",
"owner": "user:default/mock"
}
}'
Expected response — annotation is error:
{
"apiVersion": "backstage.io/v1alpha1",
"kind": "Component",
"metadata": {
"name": "bad-component",
"namespace": "default",
"annotations": {
"open-policy-agent/entity-checker-validation-status": "error"
}
},
"spec": {
"type": "custom-type",
"lifecycle": "legacy",
"owner": "user:default/mock"
}
}
Modifying the policy
The entity_checker Rego file lives at policies/entity_checker/entity_checker.rego. OPA runs with --watch in docker-compose, so policy changes reload without restarting OPA or the dev backend. Send another request immediately after saving the policy file to see the new result.
No auth header required
The /process route is explicitly marked as unauthenticated (addAuthPolicy({ allow: 'unauthenticated' })), so no Authorization header is needed in Bruno or curl.