📝 Description
The sidecar loads the alcatraz NER model through a pii.ner config block, so a mask or pii guardrail rule can name PERSON, LOCATION, ORGANIZATION and NRP. ONNX Runtime runs it in -tags ORT builds (Linux hoop, hoop-inspect ica build); the pure-Go backend elsewhere. Without the block those names stay refused, with a message that says to set pii.ner. New -alcatraz and -distroless-alcatraz sidecar images carry the model and ONNX Runtime at the block's default paths.
📣 User-facing impact
Sidecar rules can now mask or block people's names, places and organizations in query results and HTTP bodies, by setting pii.ner and running the -alcatraz sidecar image.
🔗 Related Issue
ENG-622
🚀 Type of Change
- ✨ New feature (non-breaking change which adds functionality)
- 🔧 Build configuration change
📋 Changes Made
sidecar/pii/alcatraz:pii.ner(models_dir,backend,ort_library,threads) loadsalcatraz/nerand attaches it to the engine (SetNlpEngine+Recognizer). One engine per distinct block for the process life;Offline, sha256-verified model;backenddefaults by build tag,orton a non-ORT build is a startup error.sidecar/daemon:pii_nervalue capability;CheckServablerefusespii.nerto a build without it; the JSON logger is installed before the detector builds.Dockerfile.sidecar:alcatrazanddistroless-alcatrazflavours from sharedmodels/ortstages (icareusesort);release.ymlandpullrequest-release.ymlpublish and verify them;pullrequest.ymladdssidecar-pii-ner, which runsTestNERRealModelon ONNX Runtime with the real model.webapp_v2:ORGANIZATIONandNRPin the sidecar rule entity picker.- Docs:
sidecar/README.mdNER section,sidecar/CLAUDE.md, package docs.
🧪 Testing
Result: a pii.ner block loads the model, and rules naming PERSON/LOCATION/ORGANIZATION/NRP mask and deny; without the block the sidecar refuses those names as before. Independent QA: PASS (round 2) on 1f9356b88 against origin/main 4dfa5aecd.
Left: origin/main refuses the PERSON rule at startup. Right: the branch redacts the name column only; the audit event names PERSON and no value.
A statement carrying a name is denied; a clean one passes.
What it solves: operators masking free-text columns and HTTP bodies with the sidecar get the four statistical entities the webapp already offered.
Definition of done
-
alcatraz/nerlinked and attached to the same engine; the four entities accepted with NER -
pii.neropt-in; refusal without it namespii.ner -
backenddefault by build tag;orton a non-ORT build is a startup error - Missing or tampered model is a startup /
--validateerror naming the file;Offline - Default paths match both Dockerfiles (
/opt/alcatraz/models,/opt/alcatraz/ort/releases/1.28.0/lib/libonnxruntime.so) - Postgres lane: PERSON redacted, audit names the class only;
piiguardrail denies - Hot reload: add/change/remove
pii.nerwithout restart, whole-document accept/refuse -
pii_nercapability;CheckServable;make test-sidecar-compatpasses -
-alcatrazimages: stages and publish jobs in place;docker run … backend ortproven by this PR'sdocker-publish-hoopsidecar+verify-alcatraz-image.shandsidecar-pii-nerjobs - Tests: fake
NlpEngineunit tests; env-gated real-model test; CI job - Webapp picker lists
ORGANIZATIONandNRP - Docs
Tests that guard it
sidecar/pii/alcatraz: TestNEREntitiesRefusedWithoutTheBlock, TestNERBlockAddsTheFourEntities, TestNERMasksAndScansNames, TestNERGuardrailDeniesAStatementCarryingAName, TestNEREngineIsLoadedOncePerBlock, TestNERLoadFailureRefusesTheDetector, TestNERResolveDefaults, TestPluginFromConfigDecodesTheNERBlock, TestNERRealModel (CI job sidecar-pii-ner). sidecar/daemon: TestPIINERNeedsTheCapability.
Re-run
export GOPRIVATE=github.com/hoophq/libhoop,github.com/hoophq/libhoop-ica
(cd sidecar/pii/alcatraz && CGO_ENABLED=0 go test ./...)
(cd sidecar/daemon && CGO_ENABLED=0 go test ./...) && make test-sidecar-compat
# real model, pure-Go backend
alcatraz models download --dest /tmp/models # or the curl loop of the `models` stage in Dockerfile.sidecar
(cd sidecar/pii/alcatraz && HOOP_ALCATRAZ_MODELS_DIR=/tmp/models CGO_ENABLED=0 go test -run TestNERRealModel -v .)
# a lane: pii: {ner: {backend: go, models_dir: /tmp/models}}, listener postgres with mask rule {entities: [PERSON], strategy: redact}
(cd sidecar/cmd && CGO_ENABLED=0 go run . -validate -config config.yaml && go run . -config config.yaml)
psql -h 127.0.0.1 -p <listen> -c 'select name, city from customers'Test Configuration:
- Browser(s): Chromium (headless) for the picker
- OS: macOS 15 (arm64),
CGO_ENABLED=0; ORT paths run in CI only
Tests performed:
- Unit tests pass
- Integration tests pass (sidecar lane against Postgres, hot reload, compat)
- Manual testing completed
✅ Checklist
- My code follows the style guidelines of this project
- I have performed a self-review of my own code
- I have commented my code, particularly in hard-to-understand areas
- My changes generate no new warnings
- New and existing unit tests pass locally with my changes
- I have checked my code and corrected any misspellings
📄 Additional Notes
- Gateway↔agent contract untouched; no migration, env var or analytics change.
- Sidecar compatibility: an older build ignores
pii.nerand refuses the PERSON rule by name; the plane now refuses to serve the block to a build withoutpii_ner. - The pure-Go backend costs ~100 ms per masked value;
ortis the production path (documented). TestNERRealModelskips under-raceon the go backend: gomlx/compute v0.1.2 tripscheckptr(upstream).- A bare
docker build -f Dockerfile.sidecarnow buildsalcatraz, the superset flavour, since a stage derives only from one declared before it.
Activity
12Runtime conflict with ICA: yes, one real one. Both the ICA engine and pii.ner reach alcatrazner.New → hugot.NewORTSession, and hugot allows one ORT session per process:
- hugot@v0.7.7/hugot_ort.go:18 — if ort.IsInitialized() { return errors.New("another session is currently active, and only one session can be active at one time") }
- libhoop-ica@v0.7.1/internal/policy/ner.go:275 — ICA calls alcatrazner.New with BackendORT.
- PR ner.go newNLPEngine → ner.New with Backend defaulting to ort under -tags ORT (ort_compiled.go).
So in the -ica image, a config with both an ICA listener and pii.ner (the README diff explicitly suggests this: "The -ica image holds the same model under /opt/libhoop-ica/releases/image/models; point models_dir at it") fails at whichever engine loads second. Two follow-on hazards in the same process:
- ICA teardown kills the shared ORT env. sidecar/ica/ica.go:408 e.runtime.Close() → libhoop-ica engine.Close() → hugot.Session.Destroy → ort.DestroyEnvironment() (hugot.go:352). The PR's nerEngines cache keeps its engine for the process life, so after an ICA listener release the cached pii.ner engine points at a destroyed environment.
- Hot reload claim is wrong on ORT. README: "one that changes ner loads a second engine beside the first". On the ort backend the second NewORTSession is refused by hugot_ort.go:18, so changing pii.ner on a reload fails until restart. The PR's tests use a fake newNLPEngine, so this is not covered.
pii.ner beside an ICA lane: one ORT session per process
Both the ICA engine and pii.ner go through alcatrazner.New → hugot.NewORTSession, and hugot allows one ORT session per process (hugot@v0.7.7/hugot_ort.go:18: if ort.IsInitialized() { return errors.New("another session is currently active ...") }). ICA always loads its own NER (sidecar/ica/ica.go:48,710: the model is a required file and readiness waits on it), so the question is whether pii.ner also wants ORT.
| Same process: ICA lane + | pii.ner | ORT sessions | Result |
|---|---|---|---|
| postgres/mysql/http lane masking the 52 pattern entities (EMAIL, SSN, CREDIT_CARD, …) | not set | 1 (ICA) | works today and after the PR; unaffected |
| lane masking PERSON/LOCATION/ORGANIZATION/NRP | backend: ort (default in the -ica ORT build) | 2 | second load fails: hugot_ort.go:18 |
| same | backend: go | 1 (ICA) | works; pii.ner is ~100 ms/value, pure Go, and ICA keeps ORT |
| same | ort, with ica.ner_backend: go (ica.go:126) | 1 (pii.ner) | works; ICA's on-screen NER slows down instead |
So "can't run ICA and name masking on other protocols in one process on ORT" is the accurate statement. The pattern-entity masking you run today beside ICA is not touched.
Two follow-on hazards in the same process:
- ICA teardown destroys the shared ORT environment:
sidecar/ica/ica.go:408e.runtime.Close()→ libhoop-icaengine.Close()→hugot.Session.Destroy→ort.DestroyEnvironment()(hugot.go:352).nerEngineskeeps thepii.nerengine for the process life, so after an ICA lane is released it runs on a destroyed environment. - The README says a reload that changes
ner"loads a second engine beside the first". Onortthe secondNewORTSessionis refused byhugot_ort.go:18, so that reload fails until restart. Tests use the fakenewNLPEngine, so this is not covered.
The production answer is two sidecars: -ica image for ICA lanes, -alcatraz image for DB/HTTP lanes with pii.ner. Both get ORT, hazard 1 disappears, and the config split is one listeners: block per process. The README line telling you to point models_dir at /opt/libhoop-ica/releases/image/models inside the -ica image is the trap and should be reworded in the PR.
add an envoy stack with this configuration and config.yml
pii: # one per process: WHAT the detector can recognise
entities: [...] # narrows the 52 built-in types (optional)
threshold: 0.4
allow_list: [...]
ner: # PR #2088: adds PERSON/LOCATION/ORGANIZATION/NRP
models_dir: ...
backend: ort
mask: # top-level default, inherited; per listener override
rules: # WHAT TO DO on this lane
- entities: [EMAIL, PERSON]
strategy: partial
keep_last: 4
- columns: [ssn]
strategy: redact
guardrails:
rules:
- type: pii # DENY the statement when the detector finds these
entities: [PERSON]and add documentation about this
So pii: {ner: {...}} alone changes nothing on the wire. Add mask.rules: [{entities: [PERSON], strategy: redact}] on the lane and names get masked; add a type: pii guardrail and statements carrying names get denied. The PR's QA images show exactly these two combinations.
Mental model: pii = vocabulary; mask / guardrails / analyzer = verbs.
Confirmed against hugot v0.7.7 (hugot_ort.go:18, hugot.go:352): one ORT session per process, and the ICA engine's Close destroys the environment. 9a5040337:
daemon.Config.validaterefusespii.nerbeside anicalistener unless one side isgo(pii.ner.backend: goorica.options.ner_backend: go). An explicitgo, not the build default, so the plane's Validate decides the same as the host. Cases inTestICAConfig.pii/alcatraz.loadNERrefuses a changednerblock onortwith "ONNX Runtime is held by the previous pii.ner block; restart" instead of hugot's message (ortSessionHeld, tested). Hazard 2 is now a named refusal, not a failed reload. Hazard 1 cannot happen: the pairing that would share the environment is refused at load.- README: the
-icamodels_dirline is gone; the section states the one-session rule and the two-sidecar shape (-icafor ICA lanes,-alcatrazfor DB/HTTP lanes).
✅ Build Completed with Success, Version=2088.0.0-g9a50403
PR Summary by Qodo
Load alcatraz NER in the sidecar through pii.ner
✨ Enhancement ⚙️ Configuration changes 🧪 Tests 📝 Documentation 🕐 40+ Minutes
AI Description
-
• Enable PERSON, LOCATION, ORGANIZATION and NRP detection for sidecar masking and PII guardrails through pii.ner. • Ship model-backed sidecar images with ONNX Runtime while retaining a pure-Go backend for other builds. • Add capability checks, model tests and deployment guidance to make NER configuration safer.
Diagram
-
graph TD CFG["pii.ner config"] --> VALID["Daemon validation"] --> LOAD["NER loader"] --> ENGINE["Alcatraz engine"] --> DET["PII detector"] --> MASK["Mask rules"] ASSETS[("Model assets")] --> LOAD DET --> GUARD["PII guardrail"]
High-Level Assessment
-
The following are alternative approaches to this PR:
1. Run NER in a separate inference service
-
- ➕ Scales inference independently and avoids sharing an ONNX Runtime session with ICA.
- ➖ Adds operational complexity, latency and a network boundary for potentially sensitive text.
Recommendation: Keep the opt-in, in-process approach: it reuses the existing detector and rule paths without sending PII to another service. Separate sidecars remain the simpler deployment answer when ICA and PII NER both need ONNX Runtime.
-
Files changed (23) +1316 / -140
-
Enhancement (9)
+369 / -27-
capabilities.go
Advertise and require pii_ner support+8/-1
Advertise and require pii_ner support
• Adds the pii_ner value capability to the handshake and requires it when a served configuration sets pii.ner.config.go
Reject competing ONNX Runtime users+43/-0
Reject competing ONNX Runtime users
• Detects an active pii.ner block and refuses configurations where it and an ICA listener would both request the process's single ORT session.daemon.go
Initialize logging before detector construction+3/-0
Initialize logging before detector construction
• Installs the configured logger before model loading so startup messages use the normal log format.alcatraz.go
Attach NER to the Alcatraz detector+54/-17
Attach NER to the Alcatraz detector
• Optionally registers the model recognizer and NLP engine, making four statistical entities available only when NER is configured. Missing-model errors now direct operators to pii.ner.config.go
Decode pii.ner into detector options+25/-7
Decode pii.ner into detector options
• Accepts model directory, backend, ORT library and thread settings from the PII configuration and passes them to detector construction.ner.go
Load, warm and reuse configured NER engines+211/-0
Load, warm and reuse configured NER engines
• Selects the build-appropriate Go or ORT backend, loads the offline model, enables organization detection and caches engines across detector rebuilds. It limits ORT to one distinct configuration per process.ort_absent.go
Default to Go without compiled ORT+10/-0
Default to Go without compiled ORT
• Marks builds lacking the cgo ORT backend and selects pure-Go inference by default.ort_compiled.go
Default to ORT when compiled+12/-0
Default to ORT when compiled
• Marks cgo ORT-enabled builds and selects ONNX Runtime as their default NER backend.sidecarRuleVocabulary.js
Offer organization and NRP in sidecar rules+3/-2
Offer organization and NRP in sidecar rules
• Adds ORGANIZATION and NRP to the mask and PII guardrail entity picker and notes that NER entities require pii.ner.
Tests (6)
+479 / -0-
pullrequest.yml
Exercise the real NER model in PR CI+87/-0
Exercise the real NER model in PR CI
• Adds an ORT-tagged sidecar test job that downloads the pinned tokenizer and runtime, verifies cached model files against their manifest, and fails if the real-model test skips.address_test.go
Test pii.ner capability gating+21/-0
Test pii.ner capability gating
• Verifies that an older sidecar cannot be served an active NER block, while configurations without one remain servable.ica_test.go
Cover ICA and PII backend combinations+7/-0
Cover ICA and PII backend combinations
• Tests refusal when both features use ORT and acceptance when either uses Go or no ICA listener exists.ner_test.go
Test NER detection, masking and lifecycle+354/-0
Test NER detection, masking and lifecycle
• Covers entity availability, masking, guardrail denial, configuration errors, cache behavior and ORT session limits with a fake engine. An environment-gated integration test exercises masking and scanning with the real model.norace_test.go
Identify non-race test builds+5/-0
Identify non-race test builds
• Defines the build-specific flag used by the real-model test when the race detector is absent.race_test.go
Identify race-enabled test builds+5/-0
Identify race-enabled test builds
• Defines the flag that lets the real-model test skip the affected pure-Go backend under the race detector.
Documentation (3)
+68 / -15-
verify-alcatraz-image.sh
Document image verification for both repositories+8/-7
Document image verification for both repositories
• Clarifies that the existing filesystem-export check applies to sidecar as well as agent alcatraz images.CLAUDE.md
Document NER module and test requirements+4/-1
Document NER module and test requirements
• Records the new NER dependency, backend build distinction and environment needed to run the real-model ORT test.README.md
Explain sidecar NER configuration and deployment+56/-7
Explain sidecar NER configuration and deployment
• Documents supported entities, backend defaults, model paths, performance considerations and the one-session constraint when ICA shares a process.
Other (5)
+400 / -98-
pullrequest-release.yml
Publish and verify preview NER sidecar images+38/-4
Publish and verify preview NER sidecar images
• Builds PR-numbered alcatraz and distroless-alcatraz sidecar images for both supported architectures, then verifies their published model and runtime artifacts.release.yml
Release model-backed sidecar image variants+50/-10
Release model-backed sidecar image variants
• Publishes and verifies versioned alcatraz and distroless-alcatraz sidecar images without changing the unsuffixed or latest tags.Dockerfile.sidecar
Build sidecar images with model and ONNX Runtime assets+144/-82
Build sidecar images with model and ONNX Runtime assets
• Adds two model-backed flavours derived from the existing images. Shared, verified model and runtime stages also supply the ICA image and assets bundle.go.mod
Declare NER and inference dependencies+30/-0
Declare NER and inference dependencies
• Adds the Alcatraz NER module, hugot and their transitive dependencies to the sidecar detector module.go.sum
Record dependency checksums for NER+138/-2
Record dependency checksums for NER
• Adds checksums for the model runtime and its dependency graph and removes stale Alcatraz version entries.
-
Code Review by Qodo
🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (1) 🔗 Cross-repo conflicts (2) 📜 Skill insights (0)
1. ORT NER changes require restart 📎 Requirement gap ≡ Correctness
Description
ortSessionHeld rejects a new ONNX Runtime engine whenever another configuration is already cached for the process. Changing models_dir, ort_library, or threads therefore fails instead of taking effect during a configuration reload, reaching every detector using the updated pii.ner block.
Code
sidecar/pii/alcatraz/ner.go[R205-207]+ for prev := range nerEngines.byConfig { + if prev.Backend == ner.BackendORT { + return fmt.Errorf("ner: ONNX Runtime is held by the previous pii.ner block; restart the sidecar to change models_dir, ort_library or threads")
Relevance
●● ModerateThe restart requirement is intentional and documented, but conflicts with the ticket's hot-reload objective.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The process-wide nerEngines cache retains engines by configuration, while ortSessionHeld scans that cache and returns a restart-required error when any prior ORT configuration exists. The implementation and accompanying tests therefore explicitly reject changed ORT settings rather than applying them atomically during reload.
Hot-reload NER configuration atomicallysidecar/pii/alcatraz/ner.go[163-207]sidecar/pii/alcatraz/ner_test.go[227-243]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description Changing an existing ONNX Runtime `pii.ner` configuration is rejected because the process-wide cache refuses a second ORT engine, so the hot-reload requirement is not met. ## Fix Focus Areas - sidecar/pii/alcatraz/ner.go[163-207] - sidecar/daemon/config.go[266-282] ## Recommended Fix Implement a safe atomic replacement strategy for the active ORT NER engine, or coordinate the reload lifecycle so the previous session is released only after in-flight users finish and the new session is ready. Preserve the existing detector and configuration when loading or swapping the replacement fails, and add tests covering changed `models_dir`, `ort_library`, and `threads` without restarting the sidecar.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
2. Nationality rules miss every match 🔗 Cross-repo conflict ≡ Correctness
Description
NEREntities admits NRP rules, but the pinned Alcatraz model is a CoNLL-2003 fine-tune while Alcatraz maps only the OntoNotes NORP label to NRP. With the shipped model, nationality, religion and political-group text produces no NRP detection, so those mask and guardrail rules cannot fire.
Code
sidecar/pii/alcatraz/ner.go[42]+ return []string{entities.Location, entities.NRP, Organization, entities.Person}
Relevance
●● ModeratePotential model-label incompatibility is a substantive cross-repository semantic concern without a close repository precedent.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The PR advertises NRP and loads Alcatraz's default configuration. Alcatraz identifies its pinned weights as a CoNLL-2003 fine-tune, maps NRP only from NORP, and derives supported entities from configured mappings rather than the model's output labels.
hoop -> alcatrazsidecar/pii/alcatraz/ner.go[39-43]sidecar/pii/alcatraz/ner.go[133-138]External repo: hoophq/alcatraz, docs/cli.md [209-216]External repo: hoophq/alcatraz, ner/config.go [203-218]External repo: hoophq/alcatraz, ner/config.go [231-249]External repo: hoophq/alcatraz, ner/ner.go [335-350]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The sidecar accepts NRP policies, but the pinned CoNLL-trained model does not emit the NORP label that Alcatraz maps to NRP. ## Fix Focus Areas - sidecar/pii/alcatraz/ner.go[39-43] - sidecar/pii/alcatraz/ner_test.go[318-354] - /cross_repos/alcatraz/ner/config.go[203-218] ## Recommended Fix Either use and package a verified model that emits NORP, or stop offering NRP as a detectable entity with this model. Add a real-model inference assertion for NRP rather than checking only that the detector accepts its name.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
3. NER tests lack sample environment keys 📘 Rule violation ⚙ Maintainability
Description
TestNERRealModel reads HOOP_ALCATRAZ_MODELS_DIR and HOOP_ALCATRAZ_ORT_LIBRARY with os.Getenv. These newly introduced variables are absent from the root .env.sample, so developers cannot discover the environment needed to run the real-model test locally.
Code
sidecar/pii/alcatraz/ner_test.go[R311-313]+ dir := os.Getenv("HOOP_ALCATRAZ_MODELS_DIR") + if dir == "" { + t.Skip("HOOP_ALCATRAZ_MODELS_DIR is unset")
Relevance
●●● StrongThe active repository rule explicitly requires new environment variables to appear in .env.sample.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The changed test directly reads both new environment variables, while the root sample contains no matching HOOP_ALCATRAZ_* keys.
Rule 1800927: New env vars must be mirrored in .env.samplesidecar/pii/alcatraz/ner_test.go[307-324].env.sample[1-35].env.sample[65-85]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution Issue description The new real-model test reads `HOOP_ALCATRAZ_MODELS_DIR` and `HOOP_ALCATRAZ_ORT_LIBRARY`, but neither key is listed in the root `.env.sample`. Fix Focus Areas - sidecar/pii/alcatraz/ner_test.go[311-324] - .env.sample[1-120] Recommended Fix Add `HOOP_ALCATRAZ_MODELS_DIR=` and `HOOP_ALCATRAZ_ORT_LIBRARY=` as separate entries in `.env.sample`, with comments explaining that they are required only for the real ONNX Runtime model test.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
4. Build guidance hides a supported tag 📘 Rule violation ✧ Quality
Description
The README calls ORT the only build tag, but the new ort_compiled.go build constraint also enables the backend for ALL. Readers using the documented tag guidance can miss a supported build configuration, so the README's backend-selection instructions do not match the source.
Code
+The one build tag is `ORT`, and it decides only which backend runs the NER +model (see "Names, places and organizations" under Masking and PII). The
Evidence
The changed README states that ORT is the one build tag, while the new build-constraint file explicitly enables ORTCompiled for both ORT and ALL.
Rule 3060202: README/CLAUDE docs must avoid unverified or misleading product claimssidecar/README.md[197-203]sidecar/pii/alcatraz/ort_compiled.go[1-10]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution Issue description The README says `ORT` is the only build tag, but `sidecar/pii/alcatraz/ort_compiled.go` also enables the ONNX Runtime backend when the `ALL` build tag is set. Fix Focus Areas - sidecar/README.md[197-198] - sidecar/pii/alcatraz/ort_compiled.go[1-10] Recommended Fix Update the README to accurately describe every supported build-tag combination, or change the build constraints if `ALL` is not intended to enable the backend. Keep the backend-selection and startup-error documentation consistent with the final build constraints.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
5. Rules naming PERSON never tell operators to set pii.ner 🐞 Bug ◔ Observability
Description
The new hint is added only to NewDetector's refusal, and that refusal fires only when pii.entities or pii.ignored lists an NER entity. The usual setup is a mask rule (`entities: [PERSON]) or a pii guardrail rule with no pii` section. Those rules are checked elsewhere: the masker fails with "entity \"PERSON\" is not in the detector's set", and the guardrail check in daemon.go says "drop it from pii.ignored, or fix the spelling". Neither message mentions pii.ner, even though the README added in this PR says both rule kinds get a message naming it.
Code
sidecar/pii/alcatraz/alcatraz.go[R277-280]+ hint := "" + if o.NER == nil && namesNEREntity(o.Entities, o.Ignored) { + hint = " (PERSON, LOCATION, ORGANIZATION and NRP need the NER model: set the pii.ner block)" + }
Relevance
●●● StrongThe documented error guidance omits actual mask and guardrail validation paths; this is a concrete user-facing mismatch.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The hint is built only inside NewDetector, from the pii section's own lists. Mask rules are validated in masker.go and pii guardrail rules in daemon.go, and both use their own generic messages with no NER hint. The README this PR adds says a mask rule or pii guardrail rule naming one of the four is refused "and the message names pii.ner", which is not true for either path.
sidecar/pii/alcatraz/masker.go[300-305]sidecar/daemon/daemon.go[1634-1641]sidecar/README.md[4717-4720]sidecar/pii/alcatraz/alcatraz.go[276-283]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description Without a `pii.ner` block, a mask rule or a `pii` guardrail rule that names PERSON, LOCATION, ORGANIZATION or NRP is refused with a generic message that never mentions `pii.ner`. The guardrail message even suggests the wrong fix ("drop it from pii.ignored"). The pii.ner hint only appears when the `pii.entities`/`pii.ignored` lists name one of these entities. ## Fix Focus Areas - sidecar/pii/alcatraz/masker.go[300-305] - sidecar/daemon/daemon.go[1634-1641] - sidecar/pii/alcatraz/alcatraz.go[352-364] ## Recommended Fix In masker.go, when an unclaimed entity is in `NEREntities()` and the detector was built without NER, add " (PERSON, LOCATION, ORGANIZATION and NRP need the NER model: set the pii.ner block)" to the problem. Record on the Detector whether NER was loaded so this can be checked. In daemon.go's pii-rule check, add the same hint for NER entity names. One way is to expose a small helper on the plugin interface; another is to match against a shared list of NER entity names. Add tests that cover both rule paths.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
View medium (1)
6. Sidecar guide misleads model deployments 🔗 Cross-repo conflict ≡ Correctness
Description
NER.resolve now requires model files and an ORT-capable build for backend: ort, but hoophq/documentation's Sidecar guide still says there are no build tags and PII configuration never requires swapping the binary. An operator following that guidance with the unsuffixed image and pii.ner encounters a startup failure rather than name masking.
Code
sidecar/pii/alcatraz/ner.go[R94-102]+ case ner.BackendORT: + if !ORTCompiled { + return NER{}, fmt.Errorf("backend ort is not compiled into this build; build with CGO_ENABLED=1 -tags ORT, or set backend: go") + } + if n.ORTLibrary == "" { + n.ORTLibrary = DefaultORTLibrary + } + if _, err := os.Stat(n.ORTLibrary); err != nil { + return NER{}, fmt.Errorf("ort_library: %w", err)
Relevance
●●● StrongDeployment documentation contradicts the new required model and ORT artifacts, matching recent accepted documentation-correction patterns.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The external Sidecar guide explicitly promises configuration-only PII and no build tags. The new loader rejects an unavailable ORT backend or library, and the PR adds separate model-bearing image targets.
hoop -> documentationsidecar/pii/alcatraz/ner.go[82-103]Dockerfile.sidecar[525-529]External repo: hoophq/documentation, setup/configuration/hoop-sidecar/components.mdx [315-320]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The published Sidecar guidance contradicts the new model and build requirements for pii.ner. ## Fix Focus Areas - /cross_repos/documentation/setup/configuration/hoop-sidecar/components.mdx[315-320] - sidecar/pii/alcatraz/ner.go[82-103] - Dockerfile.sidecar[525-529] ## Recommended Fix Update the documentation repository's Sidecar guide to explain the pii.ner block, model-enabled image tags, required model files, and when the ORT build tag is necessary.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Context sources
160 rulesbuild-ticket+2 more✅ Cross-repo context — repo relationships
repo: hoophq/documentation (sha: 9c5363c3) — View relationshiprepo: hoophq/alcatraz (sha: a1d14656) — View relationshiprepo: hoophq/internal-runtime-control (sha: f9e2bc6f) — View relationshiprepo: hoophq/hoop-benchmarks (sha: ec40a61e) — View relationshiprepo: hoophq/voight-kampff (sha: c6c71dc5) — View relationshipAuto: 🧠 Deep: Broad, bug-dense feature spans runtime logic, configuration, CI, images, and compatibility.Tip of the day
💡 Did you know, you can route each severity your way: inline, summary, both, or drop
✅ Build Completed with Success, Version=2088.0.0-g1f9356b