Capabilities

Operations

Signed and retried webhooks, a dual audit ledger with a tamper-evident hash chain, time-boxed impersonation with consent, break-glass with mandatory rotation and drill health checks, and the admin console.

Identity systems earn their keep on the bad days: the incident, the audit, the locked-out CEO. Sentinel’s operational surface is built for those days — with the paper trail attached.

Webhooks

Admin-managed outbound webhooks (MapSentinelWebhookAdmin, default /sentinel-admin/webhooks) subscribe endpoints to event kinds with wildcard patterns (login.*, breakglass.*) — realm-wide, or scoped to a single organization so a tenant can receive only their own events.

Deliveries are signed and survivable:

  • Signature: X-Sentinel-Signature: t=<unix>,v1=<hex hmac-sha256(secret, "t.body")> with a constant-time verify helper and a 5-minute default tolerance, plus X-Sentinel-Event and an X-Sentinel-Delivery idempotency key. Secrets are whsec_…, rotatable per endpoint.
  • Retries: a lightweight outbox inside Sentinel.Core (no Relay dependency) with exponential backoff — 1m, 5m, 30m, 2h, 12h — then dead-letter with admin visibility. Ten consecutive failures disable the endpoint and emit webhook.endpoint_disabled rather than hammering a dead listener forever.
  • Testing: POST /{endpointId}/test fires a signed test event; GET /{endpointId}/deliveries shows the delivery log.

The in-process ISentinelEventSink port remains for hosts that bridge events into their own alerting or brokers. The event vocabulary is wide — login.*, mfa.*, passkey.*, token.refresh_reuse_detected, risk.*, abuse.*, oidc.*, saml.*, federation.*, workload.*, breakglass.*, impersonation.*, user.*, webhook.* — and every kind is a stable string your SIEM can key on.

Audit: two ledgers, one of them chained

Sentinel keeps two deliberately different records:

  1. security_events — user-visible events (logins, new devices, MFA failures, erasures) that power profile “activity” pages and webhooks.
  2. admin_audit — every admin mutation with before/after JSON snapshots, actor and actor kind (User, ServiceAccount, ApiKey, System, BreakGlass), target, org, and a tamper-evident hash chain: each entry’s hash covers its content plus the previous entry’s hash, per-realm, from a genesis of zeros. VerifyChainAsync (and the admin audit browser) detects any retroactive edit.

The chain coexists with retention: payload digests are stored separately from payloads, so retention redaction can null a payload while the chain still verifies. Tampering and forgetting are different operations, and the ledger can tell them apart.

Impersonation

Support needs to see what the user sees; auditors need to see that support did it. Impersonation (MapSentinelImpersonation, under /sentinel-admin/impersonation) is:

  • Time-boxed — 30 minutes by default, the token’s exp capped to the window.
  • One active per actor — no parallel disguises.
  • Reasoned — the reason field is mandatory and lands in both ledgers.
  • Optionally consented — consent mode (global or per-realm) sends the target a single-purpose consent token (impconsent+sentinel); the session starts only on POST /consent/approve.
  • Visible in the token — the access token carries an act claim with the actor’s id and the impersonation id; SentinelPrincipal.ImpersonatorId / .ImpersonationId expose it server-side, and GET /profile/me surfaces the banner contract so SDKs can render “you are being impersonated / you are impersonating”.

Sessions are fenced by sentinel:org:impersonate / sentinel:global:impersonate — evaluated per-target-resource like every other admin operation.

Break-glass

The 2 a.m. account everyone has and nobody tests. Sentinel’s version is complete — including the part where you rehearse it:

  • Capped grants: break-glass users are marked by attribute, and a decorating ISubjectDataSource intersects their snapshot with a fixed pattern cap at build time — the emergency account cannot quietly accumulate power.
  • Alarm on use: every login emits breakglass.login through the event sink and webhooks. Break-glass that doesn’t page is just a backdoor.
  • Mandatory rotation after use — surfaced as breakglass.rotation_required until done.
  • Drill health check: SentinelBreakGlassHealthCheck degrades the host’s health when break-glass hasn’t been exercised within the configured window (90 days by default). An untested escape hatch fails your readiness probe, on purpose. GET /sentinel-admin/break-glass/status and the drill marker endpoint drive the routine.

The admin console

Sentinel.Admin ships the React console as embedded static assets — app.MapSentinelAdminUi() serves it (default /sentinel-admin/ui) with no Node tooling at consume time. Dashboard, orgs, users, roles and grants, the authz inspector, the audit browser with chain verification, webhooks, break-glass status, impersonation and privacy actions — all against the same fenced admin API, authenticated through Sentinel itself. The static routes are unauthenticated by design; every byte of data comes from the authorized API.

Observability

Per-package health checks (database, signing-key ring, break-glass drill) tag into liveness (/healthz) and readiness (/readyz) in the Sentinel Server, and are registerable in any embedding host. The Server wires standard OpenTelemetry ASP.NET Core + HttpClient instrumentation with an OTLP exporter (SENTINEL_OTLP); a packaged AddSentinelInstrumentation() with Sentinel-specific meters is on the roadmap this wave. SENTINEL_DEBUG=1 boot diagnostics follow the Relay convention.

Learn by building

The tutorials for this area, in order — each with a runnable sample.