Skip to content

Integrate dockerized sentinel#1

Open
sahilx13 wants to merge 12 commits into
mainfrom
integrate-dockerized-sentinel
Open

Integrate dockerized sentinel#1
sahilx13 wants to merge 12 commits into
mainfrom
integrate-dockerized-sentinel

Conversation

@sahilx13

Copy link
Copy Markdown
Collaborator

Summary

Adds Sentinel as a first-class Coop integration and wires up a new built-in signal, SENTINEL_RARE_CLASS_AFFINITY, that scores string content for rare harmful text patterns (e.g. grooming) using Sentinel's contrastive example banks.

Sentinel is treated as a local HTTP service (no org credentials). The signal is disabled gracefully when Sentinel is unreachable or banks are not loaded. When thread context is available, scoring includes prior messages in the thread so patterns spanning multiple messages can be detected.

Also includes a small GraphQL fix for item type create/update mutations returning null data on success, and updates local dev env docs for HMA port mapping.

What changed

Sentinel integration & signal

  • Adds SENTINEL to GraphQL Integration enum and client/server generated types
  • Adds SENTINEL_RARE_CLASS_AFFINITY signal type across GraphQL, server signal registry, and client signal→integration mapping
  • Introduces SentinelService HTTP client (/health, /banks/status, /score, etc.) with unit tests
  • Implements SentinelRareClassAffinitySignal:
    • Scores submitted text via Sentinel's /score endpoint
    • Fetches up to 10 prior thread messages from ItemInvestigationService when a threadId is present
    • Reports disabled state when Sentinel is unhealthy or banks are unloaded
    • Marks signal as free / eligible for automated rules

Thread-aware rule evaluation

  • Pre-writes content to Scylla in submitContent before rule evaluation (best-effort, retried once) so thread history is available when Sentinel runs
  • Passes runtime args from leafCondition for Sentinel signals:
    • threadIdentifier — from content schema threadId role
    • contentTextFieldName — from the condition's evaluated field
  • Coerces plain-string threadId values in content submissions to { id, typeId } for validation

UI

  • Adds a non-configurable Sentinel tile on the integrations dashboard (requiresInfo: false)

Config & docs

  • Adds SENTINEL_API_URL to server/.env.example and server/decs.d.ts
  • Updates HMA local dev URL in .env.example and DEVELOPMENT.md to http://localhost:5001

Bug fix

  • Fixes item type create/update GraphQL mutations (content, thread, user) to return { data: ... } inside success responses instead of the raw object, which caused null data on success

Architecture

sequenceDiagram
    participant Client
    participant SubmitContent
    participant Scylla as ItemInvestigation (Scylla)
    participant RuleEngine
    participant SentinelSignal
    participant SentinelAPI

    Client->>SubmitContent: POST content
    SubmitContent->>Scylla: insertItem (best-effort, pre-rules)
    SubmitContent->>RuleEngine: runEnabledRules
    RuleEngine->>SentinelSignal: evaluate with thread runtime args
    SentinelSignal->>Scylla: getThreadSubmissionsByTime
    SentinelSignal->>SentinelAPI: POST /score (primary + thread texts)
    SentinelSignal-->>RuleEngine: rare_class_affinity_score
Loading

Local development

  1. Set SENTINEL_API_URL=http://localhost:8000 in server/.env

  2. Run Sentinel locally (local overlay compose file):

    docker compose -f docker-compose.yaml -f docker-compose.sentinel.yaml up -d sentinel
  3. Start Coop as usual (npm run up, migrations, npm start)

  4. Create a rule using Sentinel Rare Class Affinity on a string content field; submit threaded content to exercise context scoring

Test plan

Automated tests

  • Run Sentinel unit tests:

    cd server && npm test -- sentinelService SentinelRareClassAffinitySignal
  • Confirm all existing server tests still pass:

    cd server && npm test

Signal availability

  • Sentinel running + banks loaded: open the integrations dashboard and confirm the Sentinel tile appears
  • Sentinel running + banks loaded: create or edit a rule and confirm Sentinel Rare Class Affinity is available and not disabled
  • Sentinel stopped: confirm the signal shows as disabled with a helpful error message
  • Sentinel running, banks unloaded: confirm the signal shows as disabled with a banks-not-loaded message

Scoring behavior

  • Submit content without a thread ID and confirm the signal returns a numeric score for the primary text only
  • Submit multiple messages in the same thread and confirm later submissions produce different (typically higher) scores when thread context is included
  • Submit content with an invalid or missing text field and confirm the signal handles it gracefully (no server crash)

Thread store integration

  • Submit content with a string threadId and confirm it is coerced to { id, typeId } without validation errors
  • Confirm content is written to Scylla before rule evaluation (thread-aware scoring works on first submission in a new thread when prior messages exist)

GraphQL regression

  • Create a content item type via GraphQL and confirm the mutation response includes populated data
  • Update a thread item type via GraphQL and confirm the mutation response includes populated data
  • Update a user item type via GraphQL and confirm the mutation response includes populated data

Client build

  • Confirm the client builds successfully:

    cd client && npm run build
  • Verify client/src/images/RobloxLogo.png is present (required by the integrations dashboard import)

@sahilx13 sahilx13 force-pushed the integrate-dockerized-sentinel branch from 3b0d3de to db664d1 Compare June 23, 2026 02:18
@sahilx13 sahilx13 force-pushed the integrate-dockerized-sentinel branch from db664d1 to 9102c53 Compare June 23, 2026 02:19
* Resolve merge conflicts with main

- client/src/graphql/generated.ts, server/graphql/generated.ts: regenerated via `npm run generate` after syncing codegen deps with main's lockfile (adds SENTINEL / SENTINEL_RARE_CLASS_AFFINITY only).
- server/graphql/modules/integration.ts: kept main's removal of SIGHT_ENGINE/TWO_HAT, added SENTINEL.
- client/src/webpages/dashboard/integrations/integrationConfigs.ts: kept main's string-literal style and updated OpenAI title, added the Sentinel tile.

* Fix backend lint errors uncovered in Sentinel service/signal code

- sentinelService.ts now uses the injectable fetchHTTP dependency instead of raw fetch, wired through SignalsService -> instantiateBuiltInSignals.
- Replaced JSON.parse/JSON.stringify with the repo's jsonParse/jsonStringify helpers.
- Rewrote sentinelService.test.ts to mock fetchHTTP directly instead of mutating global.fetch.
- Declared @roostorg/types explicitly in server/package.json (it was only present transitively via a peer dependency).
- Staged the previously untracked RobloxLogo.png asset.

* Fix formatting issues flagged by npm run prettier

- Ran `prettier --write` to fix import ordering and line wrapping in
  leafCondition.ts, submitContent.ts, and the Sentinel service/signal
  files (formatting only, no logic changes).

* Skip local pre-commit hook for this merge

- lint-staged fails non-deterministically on this in-progress merge with hundreds of staged files (a stash-based partial-checkout limitation, not a code issue).
- Verified the equivalent checks directly instead: `docker compose run --rm codegen-check`, `backend npm run lint`, `backend npm run build`, `client npm run lint`, `client npm run build`, `npm run prettier`.
@sahilx13 sahilx13 force-pushed the integrate-dockerized-sentinel branch from 99f6cd1 to 83cb67a Compare July 1, 2026 19:33
* SentinelRareClassAffinitySignal.ts: skip the first thread-context item
  that matches the primary text
  - submitContent.ts writes the current submission to Scylla before rules
    run, so getThreadSubmissionsByTime (time-bounded, not identity-bounded)
    returns the triggering submission itself alongside genuine prior
    messages, skewing the aggregate score Sentinel computes
* SentinelRareClassAffinitySignal.test.ts: add regression tests
  - covers the de-dup path when the triggering submission is echoed back
  - covers that only one occurrence is dropped if a genuine duplicate
    message exists in the thread
@sahilx13

sahilx13 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

SENTINEL_REDDIT_TEST_DATA.md

Testing doc for manual testing the feature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant