Skip to main content

Data Model


Overview

The SecurityV0 data model represents the execution/authority graph — a directed graph of 9 entity types connected by typed relationships. The graph answers two fundamental questions:

  1. What can this identity do? (authorization path)
  2. How does this workload reach external systems? (execution chain path)

It also tracks who is accountable for each identity (ownership) and how authority has changed over time (temporal drift).

Key distinction — identity vs workload: An identity (NHI — non-human identity) is a credential-bearing account that authenticates and acts in a system — e.g., an Azure Service Principal, an OAuth App registration, or a ServiceNow integration user. A workload is executable code that runs as an identity — e.g., a ServiceNow Business Rule that fires on incident creation, a Flow Designer workflow, or a scheduled data sync job. Workloads don't authenticate themselves; they delegate execution to an identity via RUNS_AS. This separation matters because one identity can be the execution context for dozens of workloads, and a single workload can chain through connections and credentials to reach a completely different identity in another system.

Terminology note: The entity type workload was previously named automation. The rename (see ADR-010) aligns with cloud-native vocabulary and resolves naming collisions between the entity type and the broader "Automation Definition" concept used in W1 product documentation. The platform accepts both "workload" and "automation" as entity_type / NormalizedNodeType values during the migration period. "automation" is deprecated and will be removed in a future version.

Entity types at a glance

The 9 entity types fall into three functional groups:

GroupTypesRole in the graph
Executionworkload, connection, credentialDefine how code runs, where it connects, and how it authenticates
Authorizationidentity, role, permission, resourceDefine who authenticates, what authority they hold, and what they can reach
Governanceowner, execution_evidenceTrack accountability and prove execution happened

Full graph structure

┌──────────────┐  BELONGS_TO   ┌──────────────┐  BELONGS_TO   ┌──────────────┐
│ Owner │◀──────────────│ Owner │◀──────────────│ Owner │
│(business_unit)│ │ (team) │ │ (human) │
└──────────────┘ └──────┬───────┘ └──────┬───────┘
│ │
│ OWNED_BY (secondary) │ OWNED_BY (primary)
│ │
└───────────────┬──────────────┘

┌─────────────────────────────┤
│ │
▼ ▼
┌────────────┐ ┌──────────┐
│ Workload │ │ Identity │
│(exec logic)│ │ (NHI) │
└──┬───┬─────┘ └────┬─────┘
│ │ │
CALLS │ │ INVOKES ┌───────┼──────────────────────────┐
│ │ │ │ │
▼ ▼ HAS_ROLE AUTH_TO AUTHENTICATES_TO
┌──────────────┐ │ │ │
│ Connection │ ▼ ▼ ▼
│(outbound cfg)│ ┌──────────┐ ┌──────────────┐
└──────┬───────┘ │ Role │ │ Identity │
│ └─────┬────┘ │(target system│
USES │ │ e.g. SN) │
│ GRANTS └──────┬───────┘
▼ │ │
┌────────────┐ ▼ HAS_ROLE
│ Credential │ ┌────────────┐ │
│(auth material)│ │ Permission │ ▼
└──────┬───────┘ └─────┬──────┘ ┌──────────┐
│ │ │ Role │
AUTHENTICATES_AS APPLIES_TO │(target │
│ │ │ system) │
▼ ▼ └─────┬────┘
┌──────────┐ ┌────────────┐ │
│ Identity │ │ Resource │◀── APPLIES_TO ◀── GRANTS ──┘
└──────────┘ └────────────┘

Core paths

Two paths define execution authority:

  1. Authorization path: Identity → Role → Permission → Resource — what an identity can do and where. Example: Service Principal sp-hr-onboarding → has role hr_admin → which grants DataWrite on hr_case table. For cross-system scenarios the path extends through AUTHENTICATES_TO: SP in Entra ID → authenticates as integration user in ServiceNow → holds roles → reaches resources.

  2. Execution chain path: Workload → Connection → Credential → Identity — how executable code flows from trigger through outbound integration to a destination identity. Example: Business Rule "Auto-close incidents" → calls Script Include RestApiUtils → invokes REST Message Microsoft Graph API → uses OAuth Profile graph-oauth → authenticates as Service Principal sp-graph-integration in Entra ID → which holds roles → that grant permissions → on Azure AD resources.

These two paths converge at the identity node. A workload's blast radius is determined by following the execution chain to the identity it authenticates as, then following that identity's authorization path to the resources it can reach.

Core model

The main spine — execution chain through authorization path, plus ownership:

Runtime + engineering view

Cycles, self-referential edges, execution evidence, and cross-system links:


Entity Types

Entity Types (Internal)

NormalizedNodeType vs Internal entity_type: Connectors emit human_identity as the NormalizedNodeType (per connector contract, 05-connectors.md). The platform normalizer maps this to internal entity_type: "owner". All other types are 1:1 between NormalizedNodeType and entity_type. The legacy autonomous_identity type is accepted for backward compatibility and remapped during ingestion: subtypes service_principal, oauth_app, machine_account, integration_useridentity; subtypes business_rule, script_include, flow_designer_flow, scheduled_job, event_script, transform_mapworkload; subtypes oauth_provider, oauth_profilecredential. The Entra-ServiceNow connector has been migrated to emit canonical types directly (Phase A2). New connectors should use the 9-type model. Legacy autonomous_identity acceptance will be removed in a future version.

TypePurposeExamplesNormalizedNodeTypeSubtypes
identityAuthenticates and acts in systemsService Principal, OAuth App, Machine AccountidentityIdentitySubtype
workloadDefines execution logicBusiness Rule, Script Include, Flow, Scheduled Jobworkload (deprecated: automation)WorkloadSubtype
connectionOutbound integration configurationREST Message, SOAP Message, HTTP ConnectionconnectionConnectionSubtype
credentialAuthentication materialOAuth Provider, OAuth Profile, API Key, CertificatecredentialCredentialSubtype
ownerHuman user or group who owns/creates entitiesEntra user, ServiceNow sys_userhuman_identity (normalizer maps)---
rolePermission groupingEntra role, ServiceNow rolerole---
permissionIndividual capabilityACL entry, Graph API scopepermission---
resourceData object being acted uponTable, API endpoint, Repositoryresource---
execution_evidenceProof of executionSign-in log, transaction logexecution_evidence---

Identity

What it is: An autonomous (non-human) identity that authenticates and acts in a system without a human being present at the keyboard. This is the primary subject of SecurityV0 analysis. Identities hold credentials and authenticate to systems. They are distinct from workloads (which define executable logic but do not themselves authenticate).

Real-world examples:

ExampleSource SystemWhy it matters
Azure Service Principal sp-payroll-sync that runs nightly Workday→ServiceNow payroll exportsEntra IDIf its owner leaves, payroll data still flows through an unaccountable pipe
OAuth App snow-integration-prod that reads/writes HR cases in ServiceNow via client credentialsEntra ID + ServiceNowNo human session — acts autonomously with whatever roles it has
GitHub App deploy-bot that triggers production deployments via org-level secretsGitHubHas write access to production infrastructure, may outlive the team that created it
Machine account svc-monitoring that scrapes CMDB data every 5 minutesServiceNowAccumulated 12 roles over 3 years, nobody remembers why
CI/CD OIDC identity that assumes an AWS IAM role for deploymentsGitHub Actions + AWSTransitive privilege chain: GitHub → AWS → S3/RDS access

Key properties:

  • identity_subtype — service_principal, oauth_app, machine_account, integration_user
  • execution_modeautonomous (no human trigger), operator_assisted (human initiates, automation executes independently), human_triggered (requires active human session), unknown. Replaces the older execution_type field. See Glossary for canonical definitions.
  • security_relevanceactive_external (executing with external egress), dormant_authority (has authority but no recent execution), internal_inventory (internal-only, no binding, no execution). Computed from egress + execution evidence + identity binding. See Glossary.
  • status — active, disabled, deleted
  • last_activity_at — when it last actually executed something (from logs)
  • source_system — which platform this identity lives in

Prior to v2, all automation artifacts were classified as identity. See ADR-006 for the reclassification rationale.


Workload (formerly Automation)

What it is: An entity that defines executable behavior in a source system. Workloads do NOT authenticate — they use RUNS_AS to delegate execution to an identity. They can call other workloads (CALLS), invoke outbound connections (INVOKES), and are triggered by resources or events (TRIGGERS_ON).

Migration note: This entity type was previously named automation. The entity_type value in the database is "workload". The platform ingestion normalizer accepts "automation" as a deprecated alias during the migration period and remaps it to "workload". See ADR-010.

Real-world examples:

ExampleSource SystemWhy it matters
Business Rule that fires on incident insert to notify on-call teamServiceNowExecutes automatically on every incident — if it RUNS_AS a privileged identity, that identity's authority is exercised without human intervention
Flow Designer automation for HR onboarding that creates cases and assigns tasksServiceNowComplex multi-step workflow that touches HR, IT, and security domains — blast radius spans multiple tables
Scheduled Job that syncs CMDB data nightly from external sourceServiceNowRuns on a schedule with no human trigger — if the run-as identity has drifted roles, the job inherits all of them
Script Include called by multiple Business Rules for REST API callsServiceNowShared code module — a single change affects all callers, multiplying blast radius

Key properties:

  • workload_subtype — business_rule, script_include, flow_designer_flow, scheduled_job, event_script, transform_map
  • execution_modeautonomous, operator_assisted, human_triggered, unknown
  • security_relevanceactive_external, dormant_authority, internal_inventory
  • status — active, disabled, deleted
  • last_activity_at — when it last actually executed (from logs)
  • source_system — which platform this workload lives in

Workload subtypes:

SubtypeWhat it representsExample
business_ruleServiceNow Business RuleScript that fires on incident insert to notify on-call
script_includeServiceNow Script IncludeReusable server-side code called by other workloads
flow_designer_flowServiceNow Flow Designer automationHR Onboarding workflow that creates cases and assigns tasks
scheduled_jobServiceNow Scheduled Job / cron-style executionNightly CMDB sync that runs as system
event_scriptServiceNow Event ScriptScript triggered by a platform event
transform_mapServiceNow Transform MapData transformation during import set processing

Connection

What it is: An outbound integration endpoint configuration. Connections define WHERE a workload sends data — the target URL, protocol, and authentication method. Connections use credentials (USES) to authenticate to external systems.

Real-world examples:

ExampleSource SystemWhy it matters
REST Message for Microsoft Graph API callsServiceNowDefines the outbound HTTP endpoint — if misconfigured, data leaks to wrong target
SOAP Message for legacy ERP integrationServiceNowLegacy protocol with weaker security defaults — often overlooked in audits
HTTP Connection profile for monitoring webhookServiceNowOutbound data flow — credentials embedded in connection config

Key properties:

  • connection_subtype — rest_message, rest_method, soap_message, http_connection
  • target_url — where the connection sends data
  • auth_method — how the connection authenticates (oauth2, basic, api_key, certificate)
  • status — active, disabled, deleted

Credential (updated)

What it is: Authentication material that an identity or connection uses to prove itself to a target system — an OAuth client secret, certificate, personal access token, API key, OAuth profile, or federation trust. Credentials have lifecycles (creation, expiry, rotation) and their state affects security posture.

Credentials in SecurityV0 carry cross-system context: they record which system issued the credential, which system it authenticates to, and what grant type is used. This enables tracking execution authority that flows across system boundaries.

With the entity reclassification, OAuth Providers and OAuth Profiles are now credential type entities (previously modeled as identity subtypes). They represent authentication material, not authenticating entities.

Key properties:

  • credential_subtype — oauth_provider, oauth_profile, api_key, certificate, client_secret
  • credential_type — oauth_client_secret, certificate, pat, api_key, oidc_token, federation_trust, role_assumption
  • status — active, expired, revoked, rotated
  • expires_at — when the credential becomes invalid
  • last_used_at — when it was last used for authentication
  • created_at — age of the credential

For full cross-system properties and real-world examples, see the Credential detail section below.


Owner

What it is: An entity accountable for one or more autonomous identities. Owners don't execute — they authorize. SecurityV0 tracks owners to determine ownership state (is someone or some team still accountable for this identity?).

Owners are polymorphic — discriminated by owner_type:

Owner TypeWhat it representsDecay signal
humanAn individual person (employee, contractor)Account disabled/deleted/departed
teamA named group responsible for a set of integrationsTeam disbanded or emptied
business_unitA department or organizational divisionBU restructured or dissolved
organizationA top-level org (for cross-org scenarios)Org removed

Real-world examples:

ExampleOwner TypeStatusImplication
Jane Smith, Senior DevOps Engineer who created sp-payroll-sync 18 months agohumandepartedThe SP is now orphaned — no individual is accountable
IT-Automation-Team, a 4-person group that manages all ServiceNow integrationsteamactiveTeam exists and is accountable, even if the original creator left
Bob Chen, IT Admin who approved 5 ServiceNow integration roles for the OAuth apphumanactiveHe's still here, but did he know about the 3 additional roles added after his approval?
Data-Platform-BU, the business unit that owns all data pipeline automationsbusiness_unitactiveBU-level accountability — less specific than a team, but still a valid escalation path
Alex Kim, Contractor whose AD account was disabled when contract endedhumandisabledAccount disabled but never deleted — SP still references this owner
Integration-Ops-Team, disbanded during a reorg 3 months agoteamdisbandedAll integrations this team owned are now orphaned at the team level

Common properties (all owner types):

  • owner_type — human, team, business_unit, organization
  • display_name — human-readable name
  • status — active, disabled, deleted, departed, disbanded, restructured
  • source_system — where this owner entity is defined (Entra ID, ServiceNow, etc.)
  • dissolved_at — when the owner entity ceased to be valid

Type-specific properties:

For owner_type: "human":

  • email — for ownership attribution
  • org_unit — for context (does this person still work in the relevant team?)
  • job_title — role context
  • disabled_at / deleted_at / departed_at — when ownership effectively decayed

For owner_type: "team":

  • team_id — source system identifier (e.g., Entra group ID, ServiceNow assignment group sys_id)
  • member_count — current active members (at last sync)
  • lead_email — team lead for escalation
  • disbanded_at — when team was dissolved

For owner_type: "business_unit":

  • bu_id — source system identifier
  • head_email — BU head for escalation
  • parent_bu_id — for hierarchy (nullable)
  • restructured_at — when BU was merged or dissolved

For owner_type: "organization":

  • org_id — source system identifier
  • primary_contact — escalation path

Role

What it is: A named grouping of permissions in a source system. Roles are the mechanism by which identities receive capabilities. One identity can have many roles; one role can grant many permissions.

Real-world examples:

ExampleSource SystemWhat it grants
itil role in ServiceNowServiceNowIncident/problem/change management access
hr_admin role in ServiceNowServiceNowFull CRUD on HR tables (cases, employee records)
Application.ReadWrite.All in Entra IDEntra IDCan read and modify any application registration
org-admin in GitHubGitHubFull administrative control over all repositories
AmazonS3FullAccess IAM policy attached to a roleAWSRead/write/delete any S3 bucket in the account

Key properties:

  • role_name — human-readable identifier from the source system
  • role_type — application, directory, cloud_iam, custom
  • is_privileged — whether this is an elevated/admin-level role
  • source_system — which platform defines this role

Why roles matter for SecurityV0: Roles are where scope drift happens. An identity starts with one role, and over months accumulates more. Each role addition is legitimate in isolation, but the aggregate creates unexpected access.


Permission

What it is: A single, normalized capability — the ability to perform one action on one scope. Permissions are the atomic unit of access in SecurityV0. Every source system's permission model gets normalized to SecurityV0's 7 standard actions.

Normalized actions:

ActionMeaningExamples from source systems
createMake new records/resourcesServiceNow: insert on table; GitHub: create repo; AWS: CreateBucket
readView/query existing dataServiceNow: read on table; Entra: User.Read.All; AWS: GetObject
updateModify existing recordsServiceNow: write on table; GitHub: push to branch; AWS: PutObject
deleteRemove records/resourcesServiceNow: delete on table; GitHub: delete repo; AWS: DeleteBucket
executeRun/trigger actionsGitHub: trigger workflow; AWS: InvokeFunction; ServiceNow: run script
adminSystem-level configurationEntra: manage app registrations; ServiceNow: modify ACLs
delegateGrant access to othersEntra: add role assignments; AWS: iam:PassRole; GitHub: manage teams

Real-world examples:

PermissionNormalizedWhat it means in practice
ServiceNow ACL: incident.writeupdate on scope incidentCan modify incident tickets
Entra: Mail.Sendexecute on scope mailCan send email as any user in the org
AWS: s3:GetObject on arn:aws:s3:::payroll-data/*read on scope s3/payroll-dataCan read every file in the payroll bucket
GitHub: contents:write on repo infra-deployupdate on scope repo/infra-deployCan push code to infrastructure repo

Key properties:

  • normalized_action — one of the 7 standard actions
  • scope — what the permission applies to (table name, resource ARN, repo name)
  • source_system — where this permission is defined
  • permission_name — original name in the source system

Resource

What it is: Something that an identity can act upon — a table, API, repository, secret store, cloud resource, or workflow. Resources are classified by business domain and sensitivity to help assess blast radius.

Real-world examples:

ResourceTypeBusiness DomainSensitivityWhy it matters
ServiceNow hr_case tabletablehrconfidentialContains employee grievances, termination records
ServiceNow incident tabletableit_opsinternalIT tickets — lower sensitivity but high volume
ServiceNow customer_contact tabletablecustomerconfidentialCustomer PII — regulated data
GitHub repo infrastructure-as-coderepositoryengineeringrestrictedContains cloud provisioning; write = deploy
AWS S3 bucket payroll-exports-prodcloud_storagefinancerestrictedSalary data for entire company
ServiceNow sys_propertiestablesecurityrestrictedSystem configuration — admin-level impact

Business domain classification:

DomainContainsExample tables/resources
hrEmployee records, cases, PIIhr_case, hr_profile, sn_hr_core_case
financeFinancial data, payroll, billingcost_center, payroll_export, billing_account
customerCustomer PII, contracts, casescustomer_contact, csm_case, contract
it_opsIncidents, changes, CMDBincident, change_request, cmdb_ci
securitySecurity config, audit, access controlsys_security_acl, sys_audit, sn_sec_*
engineeringCode, deployments, infrastructurerepositories, workflows, cloud resources

Key properties:

  • resource_type — table, module, api_endpoint, repository, secret, workflow, cloud_storage
  • business_domain — hr, finance, customer, it_ops, security, engineering, unknown
  • sensitivity — public, internal, confidential, restricted
  • contains_pii — boolean flag for regulated data

Credential (detail)

What it is: Authentication material that an identity or connection uses to prove itself to a target system — an OAuth client secret, certificate, personal access token, API key, OAuth profile, or federation trust. Credentials have lifecycles (creation, expiry, rotation) and their state affects security posture.

Credentials in SecurityV0 carry cross-system context: they record which system issued the credential, which system it authenticates to, and what grant type is used. This enables tracking execution authority that flows across system boundaries.

With the entity reclassification (v2), OAuth Providers and OAuth Profiles are now credential type entities. They represent authentication material — not authenticating entities. Connections USES credentials; credentials AUTHENTICATES_AS identities.

Real-world examples:

CredentialTypeIssuing → TargetRisk signal
OAuth 2.0 client secret for sp-payroll-syncoauth_client_secretEntra ID → ServiceNowOwner departed but secret still valid and authenticating to ServiceNow every 15 minutes
OAuth Profile for Graph API integrationoauth_profileServiceNow → Entra IDStores client_id and token endpoint — the bridge between connection and identity
X.509 certificate for mutual TLS to ServiceNowcertificateCorporate CA → ServiceNowExpired but identity still active — is it using a different auth method?
GitHub PAT created by a former employee, scoped to org:adminpatGitHub → GitHubToken outlived the person's employment — still active?
OIDC federation trust: GitHub Actions → AWSfederation_trustGitHub → AWSAny workflow in the repo can assume an AWS role with S3 admin access
API key for monitoring integration, last rotated 2 years agoapi_keyServiceNow → ServiceNowLong-lived credentials with no rotation indicate operational neglect
AWS IAM role assumed by an Entra-federated identityrole_assumptionEntra ID → AWSCross-cloud privilege chain: Entra SP → AWS role → production resources

Key properties:

  • credential_subtype — oauth_provider, oauth_profile, api_key, certificate, client_secret
  • credential_type — oauth_client_secret, certificate, pat, api_key, oidc_token, federation_trust, role_assumption
  • status — active, expired, revoked, rotated
  • expires_at — when the credential becomes invalid
  • last_used_at — when it was last used for authentication
  • created_at — age of the credential

Cross-system properties:

  • issuing_system — which system issued/manages this credential (e.g., "entra_id", "github", "okta")
  • issuing_entity_id — the specific entity in the issuing system (e.g., app registration ID)
  • target_system — which system this credential authenticates TO (e.g., "servicenow", "aws")
  • target_endpoint — specific endpoint URI (e.g., "https://instance.service-now.com")
  • grant_type — protocol/grant used (e.g., "client_credentials", "oidc_federation", "saml_assertion", "role_assumption")
  • scopes_granted — what scopes/permissions the credential itself carries (e.g., ["Mail.Read", "User.Read.All"])

Execution Evidence

What it is: An immutable, first-class record proving that an autonomous identity actually executed an action in a target system. Execution evidence distinguishes "this identity can do X" (authority, from the role/permission graph) from "this identity did do X" (execution, from source system logs). Without execution evidence, findings about active risk are claims without proof.

Real-world examples:

EvidenceSource TableWhat It Proves
ServiceNow transaction log entry showing sn-integration-user called POST /api/now/table/incident at 14:00syslog_transactionThe identity actively executes against the incident table
Flow Designer context record for flow HR Onboarding Workflow that ran at 09:00sys_flow_contextThe automation is actively executing, not just configured
Azure sign-in log entry for sp-hr-onboarding authenticating via client_credentialssignInsThe service principal actively authenticates
ECC queue entry showing MID Server task processed for discovery scanecc_queueThe MID Server identity executed a discovery action

Key properties:

  • source_table — where the evidence was fetched from (e.g., syslog_transaction, signIns)
  • source_record_id — ID in the source system (for verification and audit trail)
  • source_timestamp — when the execution actually occurred
  • evidence_type — api_call, flow_execution, scheduled_job, sign_in
  • action — what was done (e.g., "POST /api/now/table/incident")
  • target_resource — what was acted upon (e.g., "incident")
  • outcome — success, failure, unknown
  • payload_hash — SHA256 of the source record content (integrity verification without storing raw data)

Relationship to other entities: Execution evidence records are linked to identities via entity_id. They provide the factual basis for EXECUTES_ON relationships and for findings that claim active execution (dormant authority detection relies on the absence of execution evidence).


Entity Classification Decision Tree

When a new entity is discovered by a connector, use this decision tree to determine the correct entity type:

Key distinctions:

  • Identity vs Workload: If it authenticates (has credentials, appears in sign-in logs), it is an identity. If it defines executable logic but delegates authentication via RUNS_AS, it is a workload.
  • Connection vs Resource: If it defines an outbound integration endpoint (URL, protocol, auth config), it is a connection. If it is data that gets read/written, it is a resource.
  • Credential vs Identity: If it stores/manages authentication material (tokens, keys, certificates, OAuth profiles), it is a credential. If it uses that material to authenticate, it is an identity.

Subtype Definitions

export type IdentitySubtype =
| "service_principal" | "oauth_app" | "machine_account"
| "integration_user";

export type WorkloadSubtype =
| "business_rule" | "script_include" | "flow_designer_flow"
| "scheduled_job" | "event_script" | "transform_map";

/** @deprecated Use WorkloadSubtype */
export type AutomationSubtype = WorkloadSubtype;

export type ConnectionSubtype =
| "rest_message" | "rest_method" | "soap_message" | "http_connection";

export type CredentialSubtype =
| "oauth_provider" | "oauth_profile" | "api_key"
| "certificate" | "client_secret";

These subtypes are used in the identity_subtype, workload_subtype, connection_subtype, and credential_subtype properties respectively. They allow fine-grained classification within each entity type while keeping the top-level type system clean.


Finding

What it is: A deterministic detection — a rule-based trigger that fired because a specific condition is true in the graph. Findings are not predictions or risk scores. They are facts: "this condition exists."

Finding types:

TypeConditionPlain-language explanation
orphaned_ownershipALL owners (primary + secondary) have decayed status"Nobody is accountable for what this identity does — not a person, not a team, not a business unit"
ownership_degradedPrimary owner decayed, but secondary/inherited owner still active"Direct accountability lost — only inherited team/BU ownership remains"
dormant_authorityIdentity has elevated permissions but no recent activity"This identity can do powerful things but hasn't done anything in months — why does it still have access?"
scope_driftRoles/permissions expanded over time without re-approval"This identity started with 2 roles and now has 7 — each was approved individually but nobody approved the aggregate"
privilege_justification_gapElevated permissions with no evidence of need"This identity has admin access but only ever reads data — why the elevated privilege?"
unproven_executionWorkload can execute autonomously but no execution evidence can be deterministically linked"This workload is configured to run autonomously, but we cannot prove it actually executes — evidence is absent or linkage fails"
unknown_identity_bindingWorkload has no deterministic RUNS_AS relationship, or the identity is not uniquely identifiable"This workload has no deterministic identity binding — we cannot establish which identity it executes as"
reachable_sensitive_domainIdentity/workload can reach a resource classified as confidential or restricted"This identity can reach confidential HR data through its role assignments — a sensitive data domain is reachable"
llm_egressWorkload has LLM egress classification (sends data to an AI/LLM endpoint)"This workload sends data to an LLM gateway — autonomous AI-connected execution with external egress"
external_egressWorkload has external egress classification (sends data outside the organization)"This workload sends data to an external endpoint — autonomous execution with outbound data flow"
ownership_ambiguousEntity has only group/team owners; no individual has ever been assigned"This workload is owned only by 'Platform-Team' — no specific person is accountable for its behavior"
ownership_unknownInsufficient deterministic metadata to determine ownership state"Ownership edges exist but the target entities lack sufficient identifying information to validate accountability"

W1 finding types: The types unproven_execution, unknown_identity_binding, reachable_sensitive_domain, llm_egress, external_egress, ownership_ambiguous, and ownership_unknown are W1-scope findings focused on autonomous execution exposure. The existing types (orphaned_ownership, ownership_degraded, dormant_authority, scope_drift, privilege_justification_gap) continue to operate across all wedges. W1 UX views filter to the W1-scope subset.

Note on ownership_ambiguous: This is a distinct finding type from ownership_degraded, not an extension. ownership_degraded = an individual owner was once assigned but decayed. ownership_ambiguous = the entity has only group/team owners and has never had an individual owner assigned (no specific person is accountable). See W1 logic.md section 5 and the ownership finding rules below.

Real-world examples:

Finding: Ownership Degraded

Service principal sp-hr-onboarding (Entra ID) lost its primary owner. Sarah Chen (sarah.chen@contoso.com) departed on 2025-07-15. The secondary owner (IT-Automation-Team) is still active with 4 members. The SP continues executing every 15 minutes, authenticating to ServiceNow Production via OAuth client credentials.

Remediation: Assign a new primary owner from IT-Automation-Team (suggested: team lead). New owner reviews current permissions vs. business need.

Finding: Orphaned Ownership

Service principal sp-hr-onboarding (Entra ID) has no active owner at any level. Primary owner Sarah Chen departed 2025-07-15. Secondary owner IT-Automation-Team was disbanded 2026-01-15 during a reorg. The SP continues executing daily, accessing 4 ServiceNow tables across HR and IT domains through the sn-integration-user account. Last execution: 2026-01-22T14:00:00Z.

Remediation: Assign new owner from IT-Operations-BU (parent business unit); review whether hr_agent_workspace and personalize roles are still needed; rotate client secret.

Key properties:

  • finding_type — which trigger fired
  • status — active, acknowledged, remediated, false_positive
  • deterministic_explanation — plain-language, no interpretation needed
  • entity_id — the identity this finding is about
  • affected_resources — resources in the blast radius
  • evidence_completeness — declares what evidence was available vs unavailable for this finding (see below)

Evidence Completeness

Every finding MUST declare what evidence was available and what was unavailable. This is core to the "deterministic, no inference" constraint — a finding that honestly declares its evidence gaps is more credible than one that silently omits them.

EvidenceAvailability values:

ValueMeaning
availableEvidence source was accessible and data was retrieved
unavailable_not_enabledSource exists but feature is not enabled (e.g., sys_audit_role requires glide.role_management.v2.audit_roles)
unavailable_no_accessSource exists but connector lacks permissions to read it
unavailable_not_applicableEvidence category does not apply to this source system
partialSome data available but incomplete (e.g., retention window expired, only recent records retrieved)

Evidence categories:

CategoryWhat it coversExample source
current_rolesCurrent role assignmentssys_user_has_role
role_historyHistorical role changessys_audit_role
execution_evidenceProof of actual executionsyslog_transaction, sys_flow_context
ownership_recordsOwner assignment evidenceEntra owners, group membership
approval_recordsChange approval evidenceCHG tickets, Jira issues
credential_stateCredential lifecycle evidenceoauth_entity, app registrations

Each category carries a notes field with a human-readable explanation when evidence is not fully available.

Impact on finding text: When evidence is unavailable, the finding's deterministic_explanation MUST state this explicitly:

  • "Role change history unavailable (sys_audit_role not enabled); current role state from sys_user_has_role shows 4 roles assigned."
  • NOT: "4 roles were added without approval" (implies approval check was possible when it may not have been)

Example:

{
"evidence_completeness": {
"current_roles": "available",
"role_history": "unavailable_not_enabled",
"execution_evidence": "available",
"ownership_records": "available",
"approval_records": "unavailable_no_access",
"credential_state": "available",
"notes": {
"role_history": "sys_audit_role not enabled in target instance (glide.role_management.v2.audit_roles = false)",
"approval_records": "ServiceNow change_request table not accessible with current connector permissions"
}
}
}

Source Metadata Policy (P0 — Pre-Live Enforcement)

Entity documents store allowlisted source system metadata rather than full API responses. This prevents persisting secrets (tokens in headers), regulated data (PII in user records), or large payloads. This is a data exposure control, not a scale optimization — it must be enforced before the first live connector ingest.

Policy:

  1. The field source_metadata replaces the previous raw_data / raw_api_response field on all entity documents
  2. Each connector defines an allowlist of fields to persist per entity type
  3. Fields NOT on the allowlist are excluded before storage — connectors MUST NOT be able to persist entity data without passing through the allowlist filter
  4. A source_metadata_hash (SHA256 of the full original response) is stored for integrity verification — proves the original data hasn't changed without storing it

Allowlist examples:

ConnectorEntity TypeAllowed Fields
Entra IDservice_principalappId, displayName, appOwnerOrganizationId, servicePrincipalType, signInAudience, createdDateTime, accountEnabled
Entra IDcredentialkeyId, type, startDateTime, endDateTime, displayName
ServiceNowsys_useruser_name, active, locked_out, last_login_time, sys_created_on, sys_updated_on
ServiceNowoauth_entityclient_id, name, active, user (reference), type

Excluded by default: Authorization headers, tokens, password hashes, full request/response bodies, PII fields not needed for evidence.


Evidence Pack

What it is: A sealed, immutable artifact that packages all evidence supporting a finding. Evidence packs are the primary delivery mechanism — they are what gets shared with security teams, auditors, and operators.

Structure:

SectionContents
Identity SummaryName, type, source system, creation date, current status
Cross-System AuthAuthentication chain: issuing system → credential → target system → target identity
Authority SnapshotExact roles, permissions, reachable resources at detection time (across all systems in the auth chain)
Ownership TimelineAll owners (primary/secondary/inherited), their types (human/team/BU), when assigned, what happened to each
Temporal ContextAge of identity, last activity, drift events over time
Blast RadiusGraph of all reachable resources with domain/sensitivity classification (spanning system boundaries)
Deterministic ExplanationPlain-language statement of why this finding exists
RemediationConcrete actions: reassign owner, revoke role, rotate credential
Integrity MarkerSHA256 hash + timestamp — proves the pack hasn't been tampered with

Key properties:

  • integrity_hash — SHA256 of content (immutability guarantee)
  • sealed_at — when the pack was finalized
  • schema_version — for forward compatibility
  • content — the full evidence JSON

Relationships

Core Relationships

RelationshipFrom → ToMeaningKey Properties
OWNED_BYany → owner"This owner is accountable for this entity"since, until, status, ownership_level
CREATED_BYany → owner"This entity was created by this human"created_at, creation_context
BELONGS_TOowner → owner"This owner is part of a parent owner"since, until, role_in_parent, status
HAS_ROLEidentity → role"This identity holds this role"granted_at, granted_by, revoked_at
GRANTSrole → permission"This role provides this capability"source evidence
APPLIES_TOpermission → resource"This permission acts on this resource"---
AUTHENTICATES_TOidentity → identity"This identity authenticates to a target-system identity"via_credential_id, auth_protocol, target_system, trust_chain_position, evidence_references
RUNS_ASworkload → identity | owner"This workload executes as this identity or human user"run_as_type
TRIGGERS_ONworkload → resource"This workload is triggered by this resource or event"trigger_type, schedule
EXECUTES_ONworkload → resource"This workload reads/writes this resource"last_execution, execution_count_30d
CALLSworkload → workload"This workload invokes this code"call_type (direct, include, delegate)
INVOKESworkload → connection"This code uses this outbound connection"invocation_type
USESconnection → credential"This connection uses this auth material"auth_method
AUTHENTICATES_AScredential → identity"This credential represents this identity"credential_type, binding_method

OWNED_BY details

Any entity can have multiple owners with different accountability levels:

ownership_levelMeaning
primaryThe directly accountable entity — gets the 2 AM phone call
secondaryShared responsibility (e.g., a team that co-manages this integration)
inheritedDerived from hierarchy (team → BU) — fallback escalation path

Ownership finding rules:

  • orphaned_ownership — triggers when ALL ownership levels have decayed (no active owner at any level).
  • ownership_degraded — triggers when the primary (individual) owner has decayed but a secondary owner (team/BU) is still active.
  • ownership_ambiguous — triggers when the entity has ONLY group/team owners and has never had an individual owner assigned. Per the W1 Exposure Definition, groups-only ownership is inherently ambiguous because no specific individual is accountable. This is distinct from degraded (which implies an individual was once assigned and lost).

BELONGS_TO details

Captures ownership hierarchy. Enables inherited accountability:

  • Human BELONGS_TO Team
  • Team BELONGS_TO Business Unit
  • Business Unit BELONGS_TO Organization
PropertyTypeDescription
sincedatetimeWhen membership started
untildatetimeWhen membership ended (null = current)
role_in_parentstring"lead", "member", "admin"
statusenumactive, removed, transferred

AUTHENTICATES_TO details

Makes cross-system execution paths explicitly traversable. This is how SecurityV0 connects an Entra service principal to its ServiceNow integration user (or a GitHub Actions identity to its AWS IAM role).

PropertyTypeDescription
via_credential_idstringWhich credential enables this authentication
auth_protocolenumoauth2, saml, oidc, api_key, certificate, role_assumption
target_systemstringWhich platform the target identity lives in
trust_chain_positionnumberPosition in multi-hop chain (0 = direct, 1 = first hop, etc.)
established_atdatetimeWhen this auth binding was created
last_used_atdatetimeLast successful authentication via this binding
evidence_referencesobjectCross-system linkage proof (see below)

evidence_references stores the deterministic matching proof for cross-system identity linkage. This is what makes AUTHENTICATES_TO an evidence-grade claim rather than an assertion. Client_id matching alone is insufficient — in multi-tenant or multi-instance setups, the issuing tenant and target instance must be captured to prevent ambiguous links.

FieldRequiredDescription
issuing_system_idYesThe identifier in the issuing system (e.g., Entra SP appId / client_id)
issuing_tenant_idYesTenant/directory context in the issuing system (e.g., Entra tenant ID 72f988bf-...). Disambiguates multi-tenant deployments.
target_system_idYesThe identifier in the target system (e.g., ServiceNow oauth_entity.client_id)
target_instance_idYesInstance/environment context in the target system (e.g., https://corp.service-now.com or ServiceNow instance sys_id). Disambiguates multi-instance deployments.
target_record_sys_idNoSpecific record ID in target system for direct verification (e.g., oauth_entity.sys_id = abc123)
matching_fieldYesWhich field was used for deterministic matching (e.g., "client_id")
matching_valueYesThe actual value that matched (e.g., "a1b2c3d4-...")
target_user_bindingNoIf applicable, how the OAuth identity maps to a user (e.g., "oauth_entity.user -> sys_user.user_name")

Example (Entra SP → ServiceNow):

{
"issuing_system_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"issuing_tenant_id": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"target_system_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"target_instance_id": "https://corp.service-now.com",
"target_record_sys_id": "oauth-entity-sys-id-xyz",
"matching_field": "client_id",
"matching_value": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"target_user_binding": "oauth_entity.user -> sys_user.user_name = sn-integration-user"
}

Edge Migration Mapping

During the migration window, the platform ingestion normalizer accepts legacy edge types and remaps them to new types based on source/target entity types.

Legacy EdgeLegacy UsageNew EdgeWhen
EXECUTES_ONautomation → REST messageINVOKESWhen target is connection type
EXECUTES_ONautomation → table/resourceEXECUTES_ON (kept)When target is resource type
AUTHENTICATES_VIAconnection → credential (e.g., REST → OAuth profile)USESAlways (connection → credential)
AUTHENTICATES_VIAidentity → credential (legacy usage)(removed)Identity-to-credential path now goes through AUTHENTICATES_AS (credential → identity, reverse direction)
(none)---CALLSNew: automation → automation (BR → SI)
(none)---AUTHENTICATES_ASNew: credential → identity

Note on similar-sounding relationships: AUTHENTICATES_TO is a cross-system hop between two identities (e.g., Entra SP authenticates to ServiceNow Integration User). AUTHENTICATES_AS is an intra-chain binding from a credential to the identity it represents (e.g., OAuth Profile represents Service Principal). These serve different purposes and appear at different points in the execution chain.

Workload and Execution Chain Relationships

RelationshipFrom → ToMeaningKey Properties
RUNS_ASworkload → identity | owner"This workload executes as this identity or human user"run_as_type (configured, inherited, system)
TRIGGERS_ONworkload → resource"This workload is triggered by this resource or event"trigger_type (schedule, event, manual, api_call), schedule
EXECUTES_ONworkload → resource"This workload reads/writes this resource"last_execution, execution_count_30d
CALLSworkload → workload"This workload invokes this code (e.g., BR calls SI)"call_type (direct, include, delegate)
INVOKESworkload → connection"This code uses this outbound connection"invocation_type
USESconnection → credential"This connection uses this auth material"auth_method
AUTHENTICATES_AScredential → identity"This credential represents this identity"credential_type, binding_method
CREATED_BYany → owner"This entity was created by this human" (distinct from OWNED_BY)created_at, creation_context

RUNS_AS enables execution path traversal through workloads. The path materializer follows RUNS_AS edges to "borrow" the target identity's execution paths:

Workload (workload) -[RUNS_AS]-> SP (identity) -[HAS_ROLE]-> Role -[GRANTS]-> Permission -[APPLIES_TO]-> Resource

Critical design decision: RUNS_AS does not consume the auth chain depth budget. It is identity binding (which identity does this workload run as), not auth delegation. The target identity starts with a fresh depth=0 for its own AUTHENTICATES_TO traversal. This means a workload can follow RUNS_AS → SP → AUTHENTICATES_TO → OAuth without being blocked by depth limits. The visited set prevents cycles.

CALLS enables tracing execution flow through code dependencies. When a Business Rule calls a Script Include, the CALLS edge captures this relationship. This is critical for blast radius analysis — a single Script Include change can affect all calling workloads.

INVOKES → USES → AUTHENTICATES_AS forms the execution chain from automation logic through outbound connection to authenticating identity. This three-hop chain replaces the previous pattern of modeling REST messages and OAuth profiles as identity subtypes.

CREATED_BY is distinct from OWNED_BY. Creation is a historical fact (who created this thing); ownership is an ongoing accountability relationship. sys_created_by maps to CREATED_BY, not OWNED_BY. Ownership should be explicit and may be reassigned.

For ServiceNow specifically, maintainer hints such as sys_updated_by, update sets, or app scope are attribution signals only; they do not create authoritative OWNED_BY relationships by themselves.

Derived Relationships

RelationshipFrom → ToMeaningHow Computed
EXECUTION_PATHidentity → resource"This identity can reach this resource"Single-system: HAS_ROLE → GRANTS → APPLIES_TO. Cross-system: AUTHENTICATES_TO → HAS_ROLE → GRANTS → APPLIES_TO. Automation: RUNS_AS → (target identity paths). Execution chain: CALLS → INVOKES → USES → AUTHENTICATES_AS → (identity paths)
CONCERNSFinding → entity"This finding is about this entity"Set when finding is created. Can target any entity type — identity, automation, credential, etc.
AFFECTSFinding → resource"This finding affects this resource"Computed from blast radius at finding time

Relationship Lifecycle Example

Timeline for identity "sp-hr-onboarding" (Entra SP → ServiceNow):

2025-03-10: OWNED_BY → Sarah Chen (ownership_level: primary, status: active)
2025-03-10: OWNED_BY → IT-Automation-Team (ownership_level: secondary, status: active)
2025-03-10: AUTHENTICATES_TO → sn-integration-user (auth_protocol: oauth2, target_system: servicenow)
2025-03-10: (sn-integration-user) HAS_ROLE → sn_incident_write
2025-03-10: (sn-integration-user) HAS_ROLE → sn_task_assign
2025-04-22: (sn-integration-user) HAS_ROLE → sn_incident_close (approved: JIRA-IT-4521)
2025-07-15: OWNED_BY → Sarah Chen (status: decayed, until: 2025-07-15)
↑ Sarah's account disabled — primary ownership decays
→ Finding: ownership_degraded (secondary owner IT-Automation-Team still active)
2025-09-03: (sn-integration-user) HAS_ROLE → itil, hr_agent_workspace, personalize, task_editor
↑ Roles replaced during platform migration — no approval record
→ Finding: scope_drift
2026-01-15: IT-Automation-Team disbanded during reorg (status: disbanded)
↑ Secondary owner also decayed
→ Finding: orphaned_ownership (no active owner at any level)

Execution Chains

An execution chain is an ordered set of entities from a trigger event to a destination resource, capturing the full provenance of how autonomous execution flows through a system. Execution chains are the primary unit of analysis for understanding blast radius and accountability.

W1 note: W1 (Exposure wedge) does NOT depend on the execution_chains collection. W1 computes Authority Paths and Exposures from entity relationships and execution_paths[] directly (see W1 Derived Concepts). The execution_chains collection continues to serve operational tracking, temporal analysis, and future wedges (drift detection, chain fingerprinting).

Chain Composition

An execution chain connects entities in this order:

Resource (trigger) → Automation → [Automation]* → Connection → Credential → Identity → Role → Permission → Resource (target)

Not all chains include every entity type. A simple chain might be:

Automation (scheduled job) → Identity (runs-as SP) → Role → Permission → Resource

A complex cross-system chain includes all entity types:

incident table → Business Rule → Script Include → REST Message → OAuth Profile → Service Principal → Role → Permission → Graph API

Chain Identity

Each execution chain is anchored to its entry-point automation (e.g., a Business Rule's sys_id). The chain identity survives entity rotation — if a credential is rotated or a role is reassigned, the chain persists as long as the entry-point automation exists. If the entry-point automation is deleted, the chain is marked as no longer seen (last_seen_at stops updating). This enables temporal tracking of how a chain's composition changes over time. See ADR-008 for the full chain identity design.

Chain Composition Fingerprint

Each chain carries a composition fingerprint: SHA256 of the sorted entity_id:role pairs in the chain. This fingerprint changes when the structural composition of the chain changes (entity added/removed, role changed) but NOT when properties change (e.g., last_activity_at). This enables efficient detection of structural drift.

Chain Assembly

Execution chains are assembled platform-side via BFS from entry points (automations with TRIGGERS_ON edges or scheduled execution). The connector provides the individual entities and relationships; the platform assembles them into chains during ingestion.

Chain Lifecycle

  1. Created on first sync when the entry point and at least one downstream entity exist
  2. Versioned on each subsequent sync where the composition fingerprint changes
  3. Marked inactive when the entry point is deleted or disabled
  4. Preserved in history for temporal comparison and drift detection

For the MongoDB schema of the execution_chains collection, see 03-database.md.

Execution Flow Provenance Diagram

The following diagram shows a complete execution chain from trigger through automation, connection, credential, and identity to the target resource authorization path:

This diagram illustrates the key insight of the 9-type entity model: each entity in the chain has a distinct purpose (logic, transport, authentication, authorization), and the relationships between them are typed to reflect that purpose. The chain is fully traversable — from a trigger event on the incident table, you can walk the graph to determine exactly which Graph API permissions are exercised and through which intermediaries.


Execution Paths

The execution path is the core concept in SecurityV0. It answers: "Given this identity, what can it actually do, and through which chain of authority?"

Simple Path (2 hops)

Identity: sp-payroll-sync
→ Role: hr_admin
→ Permission: hr_case.write (normalized: update, scope: hr_case)
→ Resource: hr_case table (domain: hr, sensitivity: confidential)

Reading this: The service principal sp-payroll-sync holds the hr_admin role, which grants update access to the hr_case table — a confidential HR resource.

Cross-System Path (via AUTHENTICATES_TO)

Identity: sp-hr-onboarding (Entra SP, source: entra_id)
→ AUTHENTICATES_TO → Identity: sn-integration-user (source: servicenow)
via_credential: oauth_client_secret, auth_protocol: oauth2, trust_chain_position: 0
→ Role: hr_agent_workspace (ServiceNow)
→ Permission: hr_case.write (normalized: update, scope: hr_case)
→ Resource: hr_case table (domain: hr, sensitivity: confidential)

Reading this: The Entra service principal sp-hr-onboarding authenticates to ServiceNow via OAuth client credentials, operating as the ServiceNow integration user sn-integration-user. That integration user holds the hr_agent_workspace role, which grants write access to the confidential hr_case table.

Transitive Path (multi-hop, cross-system)

Identity: github-actions-deploy (GitHub, source: github)
→ AUTHENTICATES_TO → Identity: aws-deploy-role (AWS IAM Role, source: aws)
via_credential: oidc_federation_trust, auth_protocol: oidc, trust_chain_position: 0
→ Role: S3FullAccess (AWS)
→ Permission: s3:* (normalized: admin, scope: s3)
→ Resource: s3://production-database-backups (sensitivity: restricted)

Reading this: The GitHub Actions identity authenticates to AWS via OIDC federation, assuming the aws-deploy-role IAM role. That role has S3 full access to production database backups. This is a transitive privilege chain — compromising the GitHub workflow grants access to production database backups.


W1 Derived Concepts

W1 (Exposure wedge) introduces three derived concepts that are computed from the existing entity graph. None of these are new MongoDB collections or persisted entities. They are views, projections, and aggregations over existing data.

Exposure

What it is: A derived assessment unit representing one Authority Path from a workload to a data domain, enriched with its associated findings, execution activity, ownership state, and evidence completeness.

Key properties:

  • One exposure = one authority path = one (workload, identity, destination, data_domain) tuple
  • A single workload can produce multiple exposures if it reaches multiple data domains through different paths
  • Exposures carry associated findings (e.g., llm_egress, ownership_unknown), execution evidence state, and evidence completeness
  • The EXP-NNN display ID is deterministic: hash(tenant_id, workload_id, identity_id, destination, data_domain)

What an Exposure is NOT:

  • NOT an execution chain. One ExecutionChainDoc is a BFS tree that can span multiple branches and resources. An Exposure is a single linear path through that tree.
  • NOT a persisted entity. Exposures are computed from entity relationships at query time (or materialized during sync for performance).
  • NOT a finding. A finding is an issue-level alert. An Exposure is the workload-level authority summary to which findings are attached.

Computation: Exposures are derived from:

  1. Workload entities (entity_type: "workload")
  2. Their RUNS_AS identity targets
  3. The identity's execution_paths[] array (already materialized on entity documents)
  4. Associated findings from the evaluator

W1 scope explicitly does NOT require execution_chains persistence (logic.md section 9). Exposures are computed from entity relationships directly, not from the execution_chains collection.

Authority Path

What it is: A 4-node linear projection representing the structural path from a workload to a data domain:

Workload → Identity → Destination → Data Domain

Mapping to existing data:

Authority Path NodeData Source
Workload (entry point)The workload entity itself
Identity (service principal)RUNS_AS relationship target
Destination (API gateway, system)Derived from INVOKES → connection target, or execution_paths[].source_system
Data Domainexecution_paths[].business_domain + execution_paths[].sensitivity

Authority Paths are NOT a new collection. They are a projection computed from entity relationships and the already-materialized execution_paths[] array. The existing chain-builder.ts BFS already collects exactly this data — the Authority Path is a simplified 4-node summary.

Risk Cluster

What it is: A compound-condition grouping of exposures that share the same combination of risk attributes. Risk Clusters are computed ephemerally via MongoDB aggregation — they are NOT persisted.

Compound condition dimensions (4D):

  1. Data domain sensitivity — sensitive (confidential/restricted) vs non-sensitive (internal/public)
  2. Egress classification — llm, external, internal, unknown
  3. Execution status — active (execution_count_30d > 0) vs dormant (execution_count_30d = 0)
  4. Ownership status — valid, invalid, ambiguous, unknown

Example cluster labels:

  • "Sensitive + LLM + Active + Invalid Owner" (highest risk)
  • "External + Active + Valid Owner"
  • "Internal + Dormant + Unknown Owner"

Relationship to existing RG1-RG5: The connector currently assigns RG1-RG5 based on a 2D matrix (egress × data_domains sensitivity). W1 Risk Clusters extend this to 4 dimensions by adding execution status and ownership status. RG1-RG5 remains available as a connector-computed property; Risk Clusters are platform-computed aggregations.

Key constraint: Grouping does not replace canonical findings and does not introduce new risk semantics (logic.md section 7). Risk Clusters are a triage grouping, not a finding type.

W1 Posture Summary

What it is: An aggregate view of the tenant's autonomous execution landscape. Four stat cards showing identity-level and workload-level counts.

Metrics and their counting units:

MetricUnitHow Computed
Execution IdentitiesDistinct identitiesCount distinct identities that are RUNS_AS targets of autonomous workloads with execution_count_30d > 0. Deduped — multiple workloads sharing one identity count as 1.
Dormant Authority IdentitiesDistinct identitiesCount distinct identities that are RUNS_AS targets of autonomous workloads with execution_count_30d = 0. Deduped.
Operator-Assisted WorkloadsWorkloadsCount workload entities where execution_mode = operator_assisted.
Human-Triggered WorkloadsWorkloadsCount workload entities where execution_mode = human_triggered.

Identity-first counting for cards 1-2 is critical. If 12 workloads all RUNS_AS the same Service Principal, that's 1 execution identity, not 12. The overcount risk was identified during plan review — posture cards must count the unit specified above, not raw entity counts.


Scenario: A ServiceNow integration (Entra service principal) with layered ownership and cross-system execution.

BEFORE (ownership active, narrow scope):

┌──────────────────┐ OWNED_BY (primary) ┌───────────────────┐
│ sp-hr-onboarding │─────────────────────▶│ Sarah Chen │
│ (Entra SP) │ │ (human, active) │
│ │ OWNED_BY (secondary) └───────────────────┘
│ │─────────────────────▶┌───────────────────┐
│ │ │IT-Automation-Team │
└────────┬─────────┘ │(team, active) │
│ └────────┬──────────┘
│ │ BELONGS_TO
AUTHENTICATES_TO ▼
(oauth2, pos: 0) ┌───────────────────┐
│ │IT-Operations-BU │
▼ │(business_unit) │
┌──────────────────┐ └───────────────────┘
│sn-integration-usr│
│(ServiceNow) │
└────────┬─────────┘

HAS_ROLE

┌────▼────────────┐
│ sn_incident_write│── GRANTS ──▶ incident.create ── APPLIES_TO ──▶ incident table
└─────────────────┘
┌─────────────────┐
│ sn_task_assign │── GRANTS ──▶ task.create ────── APPLIES_TO ──▶ task table
└─────────────────┘

AFTER (primary ownership decayed, scope drifted, then team disbanded):

┌──────────────────┐ OWNED_BY (primary, DECAYED) ┌───────────────────┐
│ sp-hr-onboarding │────────────────────────────────▶│ Sarah Chen │
│ (Entra SP) │ │ (human, departed) │
│ │ OWNED_BY (secondary, DECAYED) └───────────────────┘
│ │────────────────────────────────▶┌───────────────────┐
│ │ │IT-Automation-Team │
└────────┬─────────┘ │(team, disbanded) │
│ └───────────────────┘
AUTHENTICATES_TO
(oauth2, pos: 0)


┌──────────────────┐
│sn-integration-usr│
│(ServiceNow) │
└────────┬─────────┘

HAS_ROLE (scope drifted — roles replaced + expanded without approval)

┌────▼────┐
│ itil │──── GRANTS ──▶ incident.* ────── APPLIES_TO ──▶ incident table
└─────────┘
┌─────────────────────┐
│ hr_agent_workspace │── GRANTS ──▶ hr_case.* ──── APPLIES_TO ──▶ hr_case (⚠️ confidential)
└─────────────────────┘
┌─────────────────┐
│ personalize │── GRANTS ──▶ sys_user.write ── APPLIES_TO ──▶ sys_user (⚠️ confidential)
└─────────────────┘
┌─────────────────┐
│ task_editor │── GRANTS ──▶ task.* ────────── APPLIES_TO ──▶ all task tables
└─────────────────┘

Findings:
1. orphaned_ownership — no active owner at any level
(primary: Sarah departed; secondary: team disbanded)
2. scope_drift — 4 broad roles replaced 2 narrow ones without approval
3. blast_radius — HR cases, user profiles, all task types reachable
from an unowned, unmonitored identity executing every 15 minutes

OAA Mapping Reference

SecurityV0's internal data model is a superset of the Veza Open Authorization API (OAA) format. The following table shows how internal entity types map to OAA export entities:

SecurityV0 TypeOAA Export EntityMapping Fidelity
identitylocal_userHIGH
workloadresource (type: workload)HIGH
connectionresource (type: connection)HIGH
credentialcustom property on local_userMEDIUM
ownerlocal_user (type: human)HIGH
rolelocal_roleHIGH
permissioncustom_permissionHIGH
resourceresourceHIGH

OAA is an export format, not the internal data model. SecurityV0's model is a superset of OAA — it captures execution chains, temporal drift, and evidence provenance that OAA does not represent. See ADR-009 for the projection design.


Property Reference

For full MongoDB document schemas, indexes, and query patterns, see 03-database.md.

For the normalized schema vocabulary (how source system fields map to these entities), see 05-connectors.md. The connector interface uses 9 NormalizedNodeType values: identity, workload (deprecated: automation), connection, credential, human_identity, role, permission, resource, execution_evidence. The platform normalizer maps human_identity to internal entity_type: "owner" and maps the deprecated automation to workload — see the Entity Types table above for the full mapping.