One identity truth governs both logical AND physical access, and everything integrates through open standards — not bespoke connectors. HRIS is the source of who-is-who; every downstream system (Duo, AD/Entra, apps, and the door) is granted through the same provisioning standard and re-evaluated through the same signaling standard. Add a system by speaking the standard, not by writing a one-off integration.
Identity integration splits cleanly into a slow, stateful provisioning plane and a fast, event-driven signaling plane. Governance lives on the first; continuous, real-time reaction lives on the second. Every hook into the fabric uses one or both.
The governed, slow path — establishes who has what. HRIS → IGA reconcile → ITSM change gate → grant/revoke on Duo, AD/Entra, and (new) the PACS. Inbound SCIM to IGA-Lite is already ITSM-gated; outbound writes are the Admin-API / directory legs.
The real-time path — broadcasts what just changed. One transmitter, many receivers; each re-evaluates continuously. CAEP carries session/security events, RISC carries account lifecycle. This is the "everyone integrates the same way" layer.
flowchart LR
HRIS[HRIS-Lite\nsource of record] --> IGA[IGA-Lite\ngovernance / JML]
REQ[Self-service\naccess request]:::req --> IGA
CMDB[CMDB-Lite\nCI / device trust] -.enriches.-> IGA
IGA -->|F3 · RFC| ITSM{ITSM-Lite\nchange gate}
ITSM -->|Duo push · SG-CAB-Approvers RBAC\nserialized · SoD| CAB([CAB approver]):::cab
CAB -->|approve / deny · F4| ITSM
ITSM -->|SCIM / Admin API| DUO[Duo]
ITSM -->|SCIM / LDAPS| AD[AD / Entra]
ITSM -->|SCIM| PACS[PACS-Lite\nthe door]
subgraph SIG[Signaling plane — SSF]
TX[VIGIL SSF\ntransmitter]
end
IGA -->|lifecycle → RISC| TX
IGA -->|grant applied → assurance-level-change| TX
CMDB -->|posture → CAEP| TX
TX -->|CAEP / RISC| DUO
TX -->|CAEP / RISC| CII[CII / Oort]
TX -->|CAEP / RISC| PACS
TX -->|CAEP / RISC| APPS[Apps / RPs]
PACS -.badge-in = presence.-> TX
classDef req fill:#160f22,stroke:#3b3170,color:#e6edf3;
classDef cab fill:#201a10,stroke:#a98f2a,color:#f0f6fc;
The ITSM gate doesn't auto-approve grants — it requires a human CAB decision, authenticated by a Duo push. The push goes only to members of the SG-CAB-Approvers AD security group — the RBAC source of truth, resolved from AD and stamped into every RFC — so approver identity is governed, not hardcoded.
| Mechanism | What it does | Why |
|---|---|---|
| Duo-push approval | a change in awaiting_approval sends an async Duo push; the approver's allow/deny on-device is the decision (F4) | phishing-resistant, on-device CAB approval |
| SG-* RBAC | iga resolves SG-CAB-Approvers members → Duo usernames → RFC approvers[]; itsm pushes only to them | who-may-approve is an AD group, governed like any entitlement |
| Serialized pushes | at most one push outstanding per approver; the next sends only after the current resolves | a multi-grant persona never fires simultaneous pushes (Duo anti-fraud) |
| SoD routing | a privileged group grant routes as normal + SOD-PRIV, not the auto-approvable standard | segregation of duties on privileged access |
| Grant → SSF | on an applied grant, iga fans a CAEP assurance-level-change to CII + PACS | a posture increase re-evaluates downstream in real time |
Requests arrive two ways — birthright (persona-derived) and self-service (the /request intake) — and both flow through the same gate: RFC → Duo-push CAB → approve → apply → assurance-level-change. Every decision lands on the tamper-evident audit spine and the Grafana Change Approval Board row.
You already built the hard part. The VIGIL SSF/CAEP stack (transmitter :8001 · receiver :8002 · bridge :8003, SSF 1.0 / CAEP 1.0) is live on its own tunnel and verified interop with caep.dev and with CII/Oort (a real session-revoked push returned HTTP 202). The SCIM→RISC leg already maps lifecycle changes to RISC events. So SSF isn't something to add — it's the spine that turns the fabric from a set of point integrations into a broadcast bus.
| Layer | Standard | Carries | Fabric hook | Status |
|---|---|---|---|---|
| Provisioning | SCIM 2.0 | account/badge create·update·deactivate·delete | inbound → IGA (ITSM-gated); outbound → Duo/AD/PACS | live (inbound gated) |
| Session signals | SSF · CAEP | session-revoked · credential-change · assurance-level-change · device-compliance-change | VIGIL transmitter → receivers (Duo, CII, PACS, apps) | verified (caep.dev + CII) |
| Lifecycle signals | SSF · RISC | account-disabled · account-purged · account-enabled · identifier-changed/recycled | IGA lifecycle → SCIM→RISC leg → transmitter | built (poll-diff reconciler) |
| Reader ↔ controller | OSDP + Secure Channel | encrypted physical read (replaces clear-text Wiegand) | PACS-Lite reader edge (P4 realism) | roadmap |
| Mobile credential | Aliro 1.0 | NFC/UWB wallet badge (CSA, Feb 2026) | PACS-Lite credential issuance | roadmap |
| Badge enrollment | RFIDeas Universal Enroll SDK | read card CSN / facility+ID (125 kHz prox · 13.56 MHz iCLASS/MiFare · mag-stripe) | PACS-Lite enrollment reader (pcProx / WAVE ID) → bind badge to identity | personal IP |
| Assurance scoring | CIAF (VIGIL) | continuous assurance score from the signal stream → adaptive decisions | the bridge's CIAF seam (Voxiren IP, deferred) | seam only |
Lane: the SSF/CAEP stack is Personal/R&D (vigil-ssf-poc) — standards plumbing only. The patentable CIAF scoring core is Voxiren IP and stays deferred until IP assignment; it plugs into the one ciaf_seam boundary. Nothing here builds the scoring engine.
Physical access is the last identity domain the fabric doesn't govern. Modern PACS (Verkada, Avigilon Alta, Brivo) now ship native SCIM + webhooks, so the door governs exactly like a Duo group — through the same two planes, three concrete hooks:
flowchart TB
subgraph FABRIC[Lab Identity Governance Fabric]
IGA2[IGA-Lite] --> ITSM2{ITSM gate}
TX2[SSF transmitter]
end
ITSM2 -->|1 · SCIM target\nbadge granted on hire,\nrevoked on leaver| PACS2[PACS-Lite]
TX2 -->|2 · SSF receiver\nrevoke on session-revoked /\naccount-disabled — instantly| PACS2
PACS2 -->|3 · SSF transmitter\nbadge-in = presence signal| TX2
TX2 -.assurance / impossible-travel\nbadge-in vs SSO login.-> RISK[CIAF assurance\nVoxiren · deferred]
Before a badge can be governed it has to be bound to an identity. PACS-Lite enrolls the physical credential with the RFIDeas Universal Enroll SDK — an enrollment reader (pcProx / WAVE ID) reads the card's CSN (or iCLASS facility + ID) and PACS-Lite ties it to the worker, so the same JML that provisions the account also provisions the door.
Lane: PACS-Lite functionality, design, and integration/interoperability are personal IP (Voxiren) — the RFIDeas enrollment leg and the badge-vs-SSO impossible-travel signal are part of that IP; the CIAF assurance core stays deferred until IP assignment.
| Phase | Build | Proves |
|---|---|---|
| P0 | mock badge-event feed → Splunk HEC → Grafana; PACS-Lite as a SCIM SP + SSF receiver stub | the door on the two planes, end-to-end, in software |
| P1 | ~$15 ESP32 / ESP-RFID reader → Node-RED → HEC | real hardware badge events |
| P2 | IGA-Lite reconciles the badge; ITSM gates it; SSF revoke wired | one leaver → logical + physical access gone |
| P3 | validate SCIM-from-Entra against Brivo dev sandbox / Verkada trial | the pattern holds against a real vendor API |
| P4 | OSDP + Aliro realism | modern physical-layer standards |
Open standards cover the new systems; the lab's legacy AD is covered by Duo's on-prem protection surfaces, and Cisco Identity Intelligence ingests AD directly.
| Surface | Protects / ingests | How |
|---|---|---|
| Duo · Windows Logon | Interactive console + RDP logon | Duo Authentication for Windows Logon (credential provider) |
| Duo · Legacy AD Protection | Legacy on-prem apps that auth via AD (Kerberos / NTLM) | Sub-auth filter DLL on the DCs → Duo MFA (Push / phone) · fail-open configurable |
| Duo · Command-Line Protection | Remote non-interactive: PS-Remoting · SSH · SFTP | MFA on command-line logons (user must pre-exist in Duo) |
| CII · AD integration | AD users / groups / attributes · stale + guest · password hygiene | Lightweight PowerShell → SCIM to Cisco Identity Intelligence (no DC agent) |
Windows Logon + Legacy AD + Command-Line Protection close the MFA gap across every AD logon path — interactive, legacy-app, and remote shell — while the CII AD ingest folds on-prem AD into the same identity-intelligence plane (Oort · Astrix · BHE) as cloud and SaaS.
Governance decides who may hold a credential; a vault holds the secret itself and brokers every checkout. The lab runs 1Password as its PAM/vault tier — the same split CyberArk draws between PVWA (people) and CCP / AAM (machines):
People retrieve elevated and break-glass credentials from a scoped 1Password vault — the CyberArk PVWA equivalent. Checkout is Duo-gated, per-vault access follows the tier model, and use is audited. The break-glass secret lives here dormant, not in a config file.
Services and CI fetch secrets programmatically from self-hosted 1Password Connect (the API the consoles already health-check) and Service Accounts — no secret in the image, none in git. This is the CyberArk CCP / AAM / Conjur equivalent: the machine authenticates, the vault releases only the secret it's entitled to.
| CyberArk workflow | 1Password equivalent | Fabric use |
|---|---|---|
| PVWA — human credential checkout | 1Password app + op CLI | elevated / break-glass retrieval · Duo-gated · audited |
| CPM — rotation / managed accounts | 1Password item rotation | service-account secrets rotated in the vault, not hand-edited |
| CCP / AAM / Conjur — app-to-app | 1Password Connect + Service Accounts | consoles & CI fetch op:// refs at runtime — no secret in image/git |
This closes the loop with the ownership model: a service account's identity is governed in the NHI registry while its secret is vaulted in 1Password — human-checkout for break-glass, machine-fetch via Connect for the account itself. Personal/CastleOps lane; secrets never land in the repo.