medOS ultra

RUDS - Rogue User Detection

Platform-wide behavioral threat detection: unified action-event hypertable, rules engine, two-tier scorer, LLM second-opinion, SOC dashboard.

21 min read diagramsUpdated 2026-05-25docs/architecture/rogue-user-detection-system.md

Status: Phases 1–3 landed + AI integration · Phases 4–8 pending Branch: claude/rogue-user-detection-K9rRd Scope: Platform-wide (all 16 services + web) behavioral threat detection, prevention, and alerting. Audience: Engineering, security/compliance officers, hospital admins.

Landed (commits on this branch)

Phase 1 — foundation

  • infrastructure/medbase/migrations/20260522b_ruds_foundation.sql — 5 tables + RLS + tier trigger
  • packages/security-kit/src/ruds/{types,evaluate}.ts + jest spec — pure rule evaluator
  • services/gateway/src/ruds/rudsEmitter.ts + ApiGatewayService.ts hook — buffered event emission

Phase 2 — scoring + prevention

  • infrastructure/medbase/migrations/20260522c_ruds_seed_rules.sql — 17 baseline rules + ack subject type + 8 cron jobs
  • packages/security-kit/src/ruds/rulesCache.ts — TTL-cached rule snapshot
  • packages/security-kit/src/middleware/moleculerRudsGuard.ts — per-service lockout middleware factory
  • services/gateway/src/ruds/rudsScorer.ts — inline scorer + lockout cache + in-memory window ring
  • services/gateway/src/ApiGatewayService.ts — lockout precheck in authorize(); scoreEvent call in onAfterCall()

Phase 3 — alerting

  • packages/security-kit/src/ruds/dispatch.ts — applies EvaluationResults: writes user_risk_scores delta + security_alerts + AcknowledgementRequest with severity-appropriate channels/deadline/escalation

AI integration

  • infrastructure/medbase/functions/ruds-ai-analyst/index.ts — Claude Haiku/Sonnet second-pass with prompt caching, anonymization (SHA-256 truncated hashes), strict guardrails (cap=84, confidence≥0.7, no block/lockout from AI, severity hard-clipped to ≤high)

Phase 4 — baselines + batch sweeper

  • infrastructure/medbase/functions/ruds-baseline-recompute/index.ts — nightly p10/p90 of usual hours, top-3 geos, hashed devices, p95 read/export rates, peer-group inference
  • infrastructure/medbase/functions/ruds-batch-scorer/index.ts — 4 modes: impossible-travel, rbac-audit, orphan-tokens, peer-drift

Phase 6 — admin UI

  • /admin/security/detection-rules — list every rule, toggle enabled, view predicate JSON, recent-alerts panel (auto-refresh 30s)
  • /admin/security/dashboardSOC dashboard with severity counters (open critical/high/medium/low), recent-alerts table, elevated-users sidebar (above-green tier), top-firing-rules-24h. Realtime via useRudsRealtime.
  • /admin/security/users?userId=<uuid>per-user drill-down: current risk + tier badge, lockout status with manual clear + manual 30-min lockout actions, behavioral baseline panel (usual hours/geos/devices/read+export rates), alerts for this user, last 100 events with matched-rules badges. Auto-refreshes via realtime.
  • /admin/security/livestreaming live tail: realtime feed of user_action_events, filter by all / risky (≥30) / rule-matches-only, pause/resume, click any user id to open drill-down.
  • web/src/hooks/useRudsRealtime.ts — singleton/refcount realtime hook mirroring usePolicyGatesRealtime (debounced invalidation across 7 query keys spanning the 3 surfaces).
  • Routes registered in web/src/routes/AdminRoutes.tsx. /security/ruds and /security/ruds/live paths from the original spec are deferred to a future security-area top-level shell; today they live under /admin/security/* since the admin route prefix already enforces the role gate.

Phase 7 — cross-region seed packs

  • infrastructure/market-packs/medos-japan/seed-ruds-rules.sql — exclude nurse_night from off-hours, narrow new-country to JP/SG/US/GB, APPI-tighter 2.5× read ratio
  • infrastructure/market-packs/medos-philippines/seed-ruds-rules.sql — disable new-country (multi-island normal spread), narrow off-hours window, tighter bill-void threshold (12 vs 20)

Phase 2.5 — per-service middleware (full platform rollout)

  • packages/platform-api-schema/src/common/middlewares/ruds-guard.middleware.ts — self-bootstrapping wrapper around security-kit.makeMoleculerRudsGuard. Reads SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY from env on first call. Toggle off with RUDS_GUARD_ENABLED=false.
  • Mounted in all 15 Moleculer services (both dev + prod broker configs): medication, financial, clinical, auth, administration, diagnostic, foundation, global-sequence, eform, filestore, messaging, interoperability, patient-relationship, public-api, llm.
  • gateway uses its own inline scorer (already protected via authorize() precheck) — no Moleculer middleware needed there.
  • migration is a data-loader service, no end-user actions to guard.

Phase 6 follow-up — JSON editor + dry-run tester

  • /admin/security/detection-rules now has an Edit button per row opening a tabbed dialog: Edit (JSON for predicate/action/scope + description + score_weight, syntax-validated) and Test (sample event JSON + Run-test button calling the ruds-test-rule edge fn).
  • infrastructure/medbase/functions/ruds-test-rule/index.ts — self-contained Deno port of the security-kit evaluator. Same predicate DSL; the jest spec on the Node side keeps the canonical behavior pinned.

Phase 8 — compliance hardening

  • infrastructure/medbase/migrations/20260522d_ruds_compliance.sql:
    • Immutability triggers on user_action_events and security_alerts. UAE rows are append-only after 5-min back-fill window; SA create-time fields (rule, severity, evidence, etc.) are frozen forever, only status/resolution/false_positive may change. DELETE is blocked outside the retention sweep, which sets app.ruds_retention_sweep=true session var to pass the trigger.
    • user_action_events_archive (weekly aggregate, 7y retention) + security_alerts_archive (full alert copy).
    • ruds_archive_sweep(days) function — rolls non-flagged events older than N days into weekly aggregates, copies resolved alerts into the alerts archive, deletes originals.
    • ruds_hard_delete_after_7y() — drops everything older than 7y across all 4 tables.
    • Two new cron jobs (disabled): ruds-archive-sweep (Sun 04:00), ruds-hard-delete-7y (monthly 2nd, 05:00).
    • Evidence export RPCs (SECURITY DEFINER, role-gated):
      • ruds_export_user_evidence(user_id, from, to) returns a single JSONB packet (baseline + risk + detail events + aggregate events + live alerts + archive alerts) for HIPAA / NHSO / APPI audit responses.
      • ruds_export_tenant_summary(tenant_id, from, to) returns aggregate counts only (no PHI) for periodic compliance reports. Event enrichment + bot/API detection (Phase 9)
  • services/gateway/src/ruds/eventEnricher.ts — per-request metadata enrichment (no I/O, in-memory counters). Produces: resourceType, actionCategory, userRoleCategory, localHour, authKind, isExport, hourlyReadCount, requestsPerMinuteFromIp, userAgentCategory, distinctUsersFromIp, sequential404s, missingAcceptLang, withinChangeWindow.
  • infrastructure/medbase/migrations/20260522e_ruds_bot_api_detection.sql — 8 bot/API-abuse rules:
    • ruds.bot.high_velocity_ip_v1 — >300 req/min from one IP → block 15m
    • ruds.bot.credential_stuffing_v1 — 20+ distinct userIds from same IP in 10m → block 60m + alert CRITICAL
    • ruds.bot.scanner_ua_v1 — nikto/sqlmap/nuclei/burp etc. UA → block immediately + alert CRITICAL
    • ruds.bot.bot_ua_authed_v1 — python-requests/curl on authed endpoints → alert MEDIUM
    • ruds.bot.sequential_404_v1 — 10+ consecutive 404s from one IP → block 30m
    • ruds.bot.no_accept_lang_velocity_v1 — no Accept-Language + >60 req/min → alert MEDIUM
    • ruds.bot.unauthed_flood_v1 — anonymous IP >100 req/min → block 30m
    • ruds.bot.service_token_off_hours_v1 — service token active outside Mon–Fri 09:00–17:00 → alert MEDIUM

Operator steps to deploy

  • Manual migration apply — per CLAUDE.md, supabase db push is blocked; apply 20260522b through 20260522e via SQL editor or psql -f
  • Edge fn deploysupabase functions deploy ruds-ai-analyst ruds-baseline-recompute ruds-batch-scorer ruds-test-rule
  • Cron enable — cron jobs ship enabled=FALSE; admin enables in /super-admin/cron-jobs after the edge fns are deployed and ANTHROPIC_API_KEY is set
  • Legal review — APPI/GDPR/HIPAA sign-off before activation in production (spec §14 open question)

1. Problem

medOS-ultra has fragments, not a system:

Fragment Location Limitation
Kaigo login lockout web/src/services/kaigo/kaigoSessionGuards.service.ts Client-only (localStorage), Japan-only, no backend correlation
Auth audit log services/auth/src/api/aaa/modules/auditLog/ Capture only — no detection runs on top
Public-API audit log services/public-api/.../modules/auditlog/ Same — separate silo, no cross-correlation
LLM call audit services/llm/src/api/llm/modules/auditLog/ Same — separate silo
Patient action audit packages/platform-api-schema/.../patientAuditLog/ Per-record only, not per-user behavior
FHIR subscription rate limit 100/min per subscription Resource protection, not behavior
FHIR DetectedIssue services/public-api/.../detectedIssue.controller.ts Clinical (drug interactions), not user behavior
Z-score outlier detection packages/health-data-platform/.../health-intelligence.ts Patient vitals, not user behavior

What’s missing: a single layer that ingests every user action across every service, scores it against configurable rules + an LLM analyst, blocks the obviously-malicious ones inline, alerts the suspicious ones to a security officer, and periodically re-baselines every user.

This document specifies that layer.


2. Threat model

What we want to catch:

Category Example Default response
Credential abuse 50 failed logins in 10 min; impossible-travel (Bangkok → Manila in 5 min); login from a new country Block + alert
Mass exfiltration Single user opens 500 patient charts in 1 hour; bulk-export to CSV at 2 AM Alert + require step-up auth; block if score > 90
Snooping / VIP access Non-care-team user reads a VIP/celebrity/colleague chart Alert (always)
Privilege escalation RBAC grant added outside change-window; self-grant; new admin role assigned Block + alert
Off-hours access Cleaner / cashier / clerical role active 02:00–05:00 local Alert
Concurrent sessions Same user account active from 2+ IPs simultaneously Alert; block second session if score > 70
Workflow abuse Cashier voids 20+ paid bills in a shift; pharmacist dispenses controlled substances without an order Block + alert
API abuse Service token used outside its declared scope; FHIR write API called from an IP not in allowlist Block
Data tampering Backdated note edits; retroactive diagnosis changes after billing closed Alert (always — HIPAA / NHSO audit risk)
LLM abuse Same user generates 200+ LLM completions in an hour; prompt-injection patterns in input Rate-limit + alert

Out of scope (handled elsewhere): clinical decision support (cds_rules), drug interactions (DetectedIssue), patient vitals anomalies (health-data-platform), workflow gates (policy_gates).


3. Architecture overview

   ┌─────────────────────────────────────────────────────────────────────────┐
   │  All services (gateway, auth, clinical, financial, medication, ...)     │
   │  emit a UserActionEvent on every authenticated request                  │
   └────────────────────────┬────────────────────────────────────────────────┘
                            │ NATS: ruds.events.*
                            ▼
   ┌─────────────────────────────────────────────────────────────────────────┐
   │  ruds-collector (NestJS service, new)                                   │
   │  • normalizes the 4 audit-log silos + live NATS feed                    │
   │  • writes to user_action_events (Supabase, hypertable)                  │
   │  • forwards to inline scorer                                            │
   └────────────────────────┬────────────────────────────────────────────────┘
                            │
            ┌───────────────┴────────────────┐
            ▼                                ▼
   ┌─────────────────────┐         ┌─────────────────────────────┐
   │  INLINE scorer      │         │  BATCH scorer (cron)        │
   │  (sync, < 50 ms)    │         │  • baseline recompute       │
   │  • deterministic    │         │  • AI sweep over 24h window │
   │    detection_rules  │         │  • peer-group comparison    │
   │  • cache hot rules  │         │  • drift / new-user check   │
   └─────────┬───────────┘         └──────────────┬──────────────┘
             │                                    │
             │           ┌────────────────────────┘
             ▼           ▼
   ┌─────────────────────────────────────────────────────────────────────────┐
   │  user_risk_scores  (current score per user)                             │
   │  security_alerts   (one row per detection hit)                          │
   │  user_baselines    (learned per-user behavior fingerprint)              │
   └─────────────────────────┬───────────────────────────────────────────────┘
                             │
            ┌────────────────┼────────────────────┐
            ▼                ▼                    ▼
   ┌──────────────┐  ┌────────────────┐  ┌──────────────────────┐
   │ PREVENTION   │  │ ALERTING       │  │ DASHBOARD            │
   │ • gateway    │  │ AcknowledgementRequest │ /security/ruds  │
   │   middleware │  │   to security officer  │ widget rail     │
   │ • per-service│  │ • multi-channel        │ live tail       │
   │   guard      │  │   (APP/EMAIL/SMS/PUSH) │                 │
   └──────────────┘  └────────────────┘  └──────────────────────┘

Key design choices:

Choice Rationale
Rules as JSON in detection_rules (mirrors policy_gates, cds_rules) Admin-editable, realtime-propagated, no redeploy
Two-tier scorer (inline + batch) Inline catches the obvious in < 50 ms; batch catches the subtle patterns (impossible travel needs prior login, bulk-export needs an hour of context)
Per-user baselines, not a global rule A radiologist reading 200 charts/hour is normal; a cashier doing it isn’t
AI is a second opinion, not the primary gate LLMs are non-deterministic. Deterministic rules block; AI only raises alerts. Never the other way.
Reuse AcknowledgementRequest for alerting Multi-channel delivery, escalation, RRULE reminders already shipped (docs/architecture/acknowledgement-system.md)
Reuse cron_jobs registry for batch sweeps Visible, auditable, toggleable from admin UI — no “rogue cron” (docs/architecture/cron-jobs-registry.md)
Append-only event log (TimescaleDB hypertable) Forensic trail; required for HIPAA / NHSO / ISO 27001 audits. 7-year retention.
Fail-open at the prevention layer If the scorer is down, requests pass. RUDS must never break clinical care. Alerts fire on scorer-down too.

4. Data model

4.1 user_action_events (Supabase, TimescaleDB hypertable)

The unified event stream. Replaces / supplements the 4 audit-log silos.

Column Type Notes
event_id uuid PK
occurred_at timestamptz hypertable partition key
user_id uuid references app_users
session_id uuid from JWT sid claim
service text gateway, clinical, financial, …
action text LOGIN, READ_PATIENT, EXPORT_CSV, RBAC_GRANT, …
resource_type text Patient, Encounter, Invoice, …
resource_id text nullable
tenant_id uuid hospital / market pack
ip inet
geo jsonb {country, region, city, lat, lng} (from MaxMind on the gateway)
user_agent text
http_status int nullable for non-HTTP actions
elapsed_ms int
metadata jsonb action-specific payload
risk_score int 0–100, set inline by scorer
matched_rules text[] rule ids that fired

Retention: 7 years for compliance; downsample to weekly aggregates after 90 days for non-flagged events.

4.2 detection_rules (mirrors policy_gates)

Column Notes
rule_id text PK, e.g. ruds.login.brute_force_v1
name, description
category credential | exfiltration | snooping | escalation | off_hours | concurrent | workflow | api | tampering | llm_abuse
tier inline (sync, hot path) or batch (cron sweep)
predicate_json event-matching expression — see §5
window sliding window for aggregation, e.g. 10m, 1h, 24h
threshold_json { count: 50, distinct: 'ip' } etc.
action_json { block: true, alert: { severity: 'HIGH', recipient: 'role:security_officer' }, requireStepUp: false }
score_weight 0–100, added to user risk score on match
scope_json role / tenant / market-pack scoping (same shape as policy_gates)
enabled, active, managed_by

4.3 user_risk_scores

One row per user. Updated by inline scorer (delta) and batch scorer (full recompute).

Column Notes
user_id PK
score 0–100, capped
tier green (0–29), yellow (30–59), orange (60–84), red (85–100)
last_event_at, last_alert_at
recent_rules text[] — last 10 matched rules
step_up_required_until timestamptz — gateway forces re-auth + MFA
lockout_until timestamptz — set when tier=red and rule has block: true

4.4 user_baselines

The learned-normal fingerprint per user. Populated by nightly batch job.

Column Notes
user_id PK
usual_hours jsonb — {mon: [8, 18], tue: [8, 18], ...}
usual_geos jsonb — [{country: 'TH', region: 'Bangkok', lastSeen: ...}]
usual_devices jsonb — UA fingerprints + frequency
usual_read_rate numeric — patients/hour, p95
peer_group text — role + department (e.g. nurse:ward_a)
last_recomputed_at timestamptz

4.5 security_alerts

One row per detection hit that triggered an alert.

Column Notes
alert_id uuid PK
created_at
user_id subject
rule_id detection_rules.rule_id
severity low | medium | high | critical
event_window jsonb — start, end, sample event ids
summary text — AI-generated narrative (≤ 200 chars)
evidence_json jsonb — feeding events, counts, geo, etc.
ack_request_id uuid — FK to AcknowledgementRequest
status OPEN | ACKNOWLEDGED | DISMISSED | ESCALATED | RESOLVED
resolution_notes text
false_positive bool — feeds back into AI tuning

5. Predicate DSL (rule expressions)

The same shape as policy_gates.predicate_json, extended for time-window aggregation.

{
  "all": [
    { "event.action": "LOGIN_FAILED" },
    { "_aggregate": {
        "window": "10m",
        "groupBy": ["user_id"],
        "count": { "gte": 10 }
      }
    }
  ]
}

Supported operators: all, any, not, eq, neq, in, gte, lte, between, regex, geoDistanceKm, peerCompare (score vs user_baselines.usual_*), _aggregate (windowed count/distinct/sum).

A pure evaluateDetection(rule, eventStream, baseline) → {matched, evidence, score} function lives in packages/security-kit/src/ruds/evaluate.ts. Testable without Supabase; reused by inline scorer and batch sweeper.


6. AI / LLM layer

Deterministic rules always run first. The AI layer is a second pass over the batch window.

nightly cron ──▶ ruds-ai-analyst (Supabase edge fn, Deno)
                      │
                      ├─ pulls last 24h of events per user
                      ├─ pulls user_baselines fingerprint
                      ├─ calls Claude with structured prompt + JSON schema response
                      │     (model: claude-haiku-4-5-20251001 for cost; escalate
                      │      to claude-sonnet-4-6 for users already at tier ≥ orange)
                      ├─ schema: { risk_delta, narrative, suggested_action,
                      │             novel_pattern, confidence }
                      └─ writes back to user_risk_scores + security_alerts
                            (only when confidence ≥ 0.7)

Strict rules for the AI layer:

  1. AI cannot block. Only deterministic rules with action_json.block = true can block requests. AI output flows through alerts.
  2. AI cannot raise score above 84 (orange tier max). Pushing a user into red (85+) requires a deterministic rule hit.
  3. Confidence < 0.7 → discard. No alert raised.
  4. Every AI alert carries a novel_pattern boolean. When true, an admin can promote it to a new detection_rules row (with a “from AI suggestion” provenance tag), reviewed before activation.
  5. Prompt caching is mandatory — the baseline fingerprint + rule catalog go into the cached prefix; only the 24h event sample is in the dynamic suffix. (Pattern: claude-api skill.)
  6. No PHI in prompts. Events are anonymized server-side: patient ids hashed, free-text note bodies dropped. Only metadata + counts + timestamps.
  7. Audit log — every LLM call is written to services/llm/.../auditLog with the prompt hash, response, cost, latency.

7. Prevention layer

7.1 Gateway middleware (inline)

Every request hits services/gateway/.../middleware/ruds-guard.ts before the route handler:

on request:
  1. fetch user_risk_scores for current user_id (cached 30s)
  2. if lockout_until > now → 423 Locked, alert security officer
  3. if step_up_required_until > now → 401 + WWW-Authenticate: step-up
  4. emit UserActionEvent to NATS (fire-and-forget)
  5. inline scorer runs deterministic hot rules against this event
  6. if any matched rule has action_json.block = true → reject
  7. otherwise pass through; risk_score delta written async

Performance budget: p95 < 50 ms added latency. If the score cache misses and Supabase is slow, fail-open (request passes, alert fires on scorer-down).

7.2 Per-service guards (defense in depth)

The gateway can be bypassed (internal NATS calls between services). Each service mounts a thin Moleculer middleware that checks user_risk_scores.lockout_until on every ctx.meta.user.id. Same fail-open behavior.

7.3 Step-up auth

When step_up_required_until is set, the next request returns 401 with WWW-Authenticate: step-up realm="ruds". Frontend handles by showing the existing MFA / re-auth modal. On success, the gateway clears the flag and emits a STEP_UP_PASSED event.


8. Alerting layer

Every security_alerts row creates an AcknowledgementRequest:

Severity Recipient Channels Escalation
low role:security_officer APP none (just inbox)
medium role:security_officer APP, EMAIL after 60m → security manager
high role:security_officer + role:ciso APP, EMAIL, PUSH after 15m → CISO
critical role:ciso + role:hospital_admin APP, EMAIL, SMS, PUSH after 5m → on-call + page

The acknowledgement’s subject points back at security_alerts/{alert_id}. Acknowledging marks the alert ACKNOWLEDGED; declining marks it DISMISSED (and false_positive = true, feeding AI tuning).

Alerts are also surfaced via:

  • WidgetRail widget SecurityAlertsWidget (icon dock + flyout, see docs/architecture/widget-rail-surface-system.md) — visible to security-officer role only.
  • Live tail at /security/ruds/live for the SOC view.
  • Dashboard at /security/ruds — heat map by user/department, score trends, top firing rules, false-positive rate per rule.

9. Periodic checks (cron jobs)

Registered in cron_jobs so they’re visible and toggleable at /super-admin/cron-jobs:

Job Schedule Purpose
ruds-baseline-recompute 0 2 * * * (02:00 UTC daily) Recompute every active user’s user_baselines from last 30 days
ruds-ai-sweep 15 2 * * * (02:15 UTC daily) AI second-pass over last 24h per user
ruds-decay-scores 0 * * * * (hourly) Decay risk scores: −2 per quiet hour, floor at 0
ruds-peer-drift-check 30 3 * * 0 (weekly Sun 03:30) Compare each user to peer group median; alert on outliers
ruds-orphan-token-scan 0 4 * * * (daily) Find service tokens unused 30+ days; auto-disable
ruds-rbac-change-audit 0 */6 * * * Sweep RBAC grants in last 6h; flag those outside change-window
ruds-impossible-travel-sweep */10 * * * * 10-min sweep for geo-impossible login pairs (belt-and-suspenders alongside inline rule)
ruds-retention-prune 0 5 1 * * (monthly) Drop event detail older than 90 days for non-flagged users; keep aggregates

10. Baseline rule library (seed)

Ships as a migration; admin can edit / disable any of them.

Rule id Category Tier Default action Notes
ruds.login.brute_force_v1 credential inline block 30m, alert HIGH ≥10 LOGIN_FAILED in 10m
ruds.login.impossible_travel_v1 credential inline block, alert CRITICAL two successful logins > 500km apart in < geo_speed_kmh window
ruds.login.new_country_v1 credential inline step-up, alert MEDIUM first login from a country not in baseline
ruds.session.concurrent_v1 concurrent inline alert HIGH, block 2nd if score ≥ 70 same user_id, 2+ active session_ids, different IPs
ruds.read.bulk_patient_v1 exfiltration batch alert HIGH, step-up if > 200 reads > 3× user’s p95 in 1h
ruds.export.bulk_csv_v1 exfiltration inline block, alert CRITICAL any CSV export > 100 rows requires data_export role
ruds.read.vip_chart_v1 snooping inline alert HIGH (always) non-care-team user reads a chart flagged vip=true
ruds.read.colleague_chart_v1 snooping inline alert MEDIUM user reads a chart whose patient_id matches a staff record
ruds.access.off_hours_v1 off_hours inline alert MEDIUM role in clerical_roles active outside 02:00–05:00 local
ruds.rbac.self_grant_v1 escalation inline block, alert CRITICAL RBAC_GRANT where granted_to == granted_by
ruds.rbac.outside_window_v1 escalation batch alert HIGH RBAC change outside Mon–Fri 09:00–17:00
ruds.workflow.bill_void_storm_v1 workflow batch alert HIGH cashier voids ≥ 20 bills/shift
ruds.workflow.controlled_no_order_v1 workflow inline block, alert CRITICAL controlled-substance dispense without matching MedicationRequest
ruds.note.backdated_edit_v1 tampering inline alert HIGH (always) note effectiveAt updated > 24h after first save
ruds.api.token_scope_breach_v1 api inline block, alert HIGH service-token call to route outside declared scopes
ruds.llm.prompt_storm_v1 llm_abuse batch rate-limit, alert MEDIUM > 200 LLM completions / user / hour
ruds.llm.injection_pattern_v1 llm_abuse inline alert MEDIUM prompt matches known injection regex set

11. Admin UI

/admin/security/detection-rules — list/edit/toggle rules (same shape as /admin/policy-gates). /admin/security/users/:userId — per-user view: risk score, baseline, recent events, alerts, manual lockout/clear. /security/ruds — SOC dashboard (officer view). /security/ruds/live — live tail of events + alerts. /super-admin/ruds-config — global config: AI model selection, retention windows, default channels, escalation chains.

All four pages role-gated to security_officer / ciso / super_admin.


12. Cross-region considerations

Same pattern as cross-region-policy-gates-deployment.md:

  • The migration ships an empty detection_rules table; the 17-rule seed is per region.
  • Off-hours bounds, geo-distance thresholds, and peer-group definitions vary:
    • Japan: shift system + Tokuyou night staff means off-hours rule must exclude nurse_night role.
    • Philippines: multi-shift + barangay outreach means geo-distance baseline is wider.
    • Thailand: default seed.
  • Locale: alert messages bilingual (local + English), enforced by the same i18n pipeline as the rest of the platform (CLAUDE.md rule 7).
  • Currency-sensitive workflow rules (bill voids, refunds) use the region’s currency threshold.

13. Implementation phases

Phase Scope Effort Dependency
0. Spec sign-off This doc reviewed by engineering + security + compliance 1 wk
1. Event collector ruds-collector service, user_action_events hypertable, gateway middleware emitting events (no scoring yet) 2 wks
2. Inline scorer + 6 rules detection_rules table, evaluate engine, ship rules 1–6 (login + concurrent + bulk export) with prevention 2 wks phase 1
3. Alerting wire-up security_alerts table, AcknowledgementRequest integration, SOC dashboard v1, widget rail 2 wks phase 2
4. Baselines + batch sweeper user_baselines table, nightly cron, peer-group comparison, rules 7–17 2 wks phase 3
5. AI analyst ruds-ai-analyst edge fn, prompt-caching, novel-pattern promotion flow 2 wks phase 4
6. Admin UI v2 Rule editor, per-user drill-down, false-positive feedback loop 1 wk phase 5
7. Cross-region seeds Per-market-pack rule packs (JP/PH/TH), locale-aware messages 1 wk phase 6
8. Compliance hardening 7-year retention policies, immutability constraints, NHSO/HIPAA evidence exports 2 wks phase 7

Total: ~15 weeks single-track; ~10 weeks with two engineers.


14. Open questions

  1. TimescaleDB vs vanilla Postgres partitioning — TimescaleDB extension status on Supabase? If unavailable, fall back to monthly-partitioned tables.
  2. Event volume budget — at ~5 hospitals × ~2k users × ~200 events/user/day, we’re at ~2M events/day. Confirm Supabase write throughput + the NATS topic doesn’t drown other consumers.
  3. AI cost ceiling — Haiku for routine sweeps + Sonnet for tier ≥ orange. Set a per-tenant monthly cost cap; alert security officer when 80% consumed.
  4. Service-account events — do internal NATS calls between services emit RUDS events? Spec assumes yes for compromise detection, but the volume needs review.
  5. Federation with hospital SIEM — many hospitals already run Splunk / Sentinel. Should RUDS push alerts via syslog / CEF or stay closed-loop?
  6. EU / PDPA / HIPAA legal review — passive behavior monitoring of clinical staff is regulated. Need legal sign-off per region before phase 1 ships, especially in Japan (APPI) and EU (GDPR Art. 22 auto-decision restrictions).

15. Non-goals

  • Not a replacement for network IDS / WAF — RUDS sees application events, not raw packets.
  • Not a DLP product — we detect access patterns, not file-content classification.
  • Not a clinical-decision engine — cds_rules owns that surface.
  • Not a workflow gate — policy_gates owns that surface.
Ask Anything