Entity Type Classification — Product Owner Analysis
Role: Product Owner Date: 2026-02-13 (Round 5) Core Question: What entity type should Business Rules, Script Includes, REST Messages, OAuth Profiles, Flow Designer Flows, and Scheduled Jobs actually be?
Executive Summary
The current data model classifies every automation artifact as entity_type: "identity" with different identitySubtype values. This is semantically wrong and creates a product-level UX problem that no amount of label-fixing can solve.
In Round 2, all 5 roles agreed to "keep entity_type: identity and fix labels in UI only." I now reverse that position. The Round 2 decision was a tactical shortcut — it avoided schema disruption during a sprint-focused analysis. But the founder's challenge is correct: a Script Include does not authenticate. A Business Rule does not appear as an actor in audit logs. These are code artifacts, not identities. Calling them identities — even with subtype labels — creates a fundamental category error that compounds as the product scales.
My recommendation: Introduce a proper entity type taxonomy with distinct types for Identity, Automation, and Configuration. This is not a cosmetic change. It restructures how the CISO thinks about the platform, how the UI organizes information, and how the API answers questions. There are zero active clients. The cost of doing this later (with live data, live customers, live integrations) is orders of magnitude higher than doing it now.
Table of Contents
- Why I Am Reversing My Round 2 Position
- The CISO Mental Model Test
- User Experience Impact — Artifact by Artifact
- The Listing Problem
- Entity Type Decision Tree
- Proposed Entity Types for the Product
- The Five-Type Taxonomy
- Mapping Artifacts to the Taxonomy
- Impact on Execution Chain Display
- Impact on NormalizedNodeType Enum
- Impact on API and Query Patterns
- Impact on Findings and Evidence Packs
- Impact on OAA Export
- Impact on Connectors
- Why "Fix the Labels" Is Insufficient
- Counter-Arguments and Responses
- The Demo Walkthrough — Before and After
- Implementation Principles
- Open Questions for Other Roles
- Recommendation
1. Why I Am Reversing My Round 2 Position
In Round 2, the team unanimously agreed: "Keep entity_type: identity — fix labels in UI only." I endorsed that decision. I was wrong, and I need to explain why.
What I Got Right in Round 2
The tactical analysis was correct:
- Changing entity_type mid-sprint would have broken queries, filters, and the path materializer
- The label fix was the fastest path to a better demo
- The
identitySubtypesystem does provide semantic richness within the identity bucket
What I Got Wrong in Round 2
I optimized for sprint velocity over product correctness. The three things I missed:
1. Label fixing is a patch on a broken mental model. When the CISO sees "Business Rule (identity)" with the word "identity" replaced by "Business Rule," they still see it on the Identities page. The page title says "Identities." The URL says /identities. The API endpoint says /entities?entity_type=identity. The subtype label is a cosmetic fix on a structural lie.
2. The Identities count is permanently polluted. The dashboard says "92 identities." The CISO hears "92 things that can authenticate." The real number of authenticating entities is approximately 15. There is no label fix that makes "92 identities" mean the right thing. You must either redefine what an identity is (breaking the English word) or move non-identity artifacts out of the identity bucket (fixing the model).
3. The problem scales catastrophically. ServiceNow is one platform. When we add GitHub Actions, AWS Lambda, Azure Functions, Terraform modules, Kubernetes CronJobs — each has its own automation artifacts. If every automation is an "identity," the identity count goes from 92 to 500+ while the actual identity count stays under 50. The ratio of signal to noise in the "Identities" list gets worse with every new connector.
What Changed Between Round 2 and Round 5
Three things happened:
-
Round 3 introduced execution_chains. The team unanimously agreed that automation chains are NOT entities in the traditional sense — they are platform-computed composites. This established the precedent that not everything needs to be an "identity" to participate in the graph.
-
Round 4 introduced the OAA analysis. The OAA Specialist confirmed that automation artifacts are neither identities nor applications in OAA terms — they are resources within a source system. The Architect stated: "Automation artifacts are simultaneously identities AND resources." This duality means forcing them into one bucket (identity) is always wrong.
-
The founder directly challenged the model. The founder said: "A Script Include doesn't authenticate, doesn't have credentials, doesn't appear as an actor in audit logs. It's code, not an identity." This is the product owner's north star — the founder is articulating the customer's mental model. When the founder says it's wrong, and the founder's reasoning is sound, the product owner must listen.
I was wrong in Round 2. I am fixing that now.
2. The CISO Mental Model Test
Before proposing entity types, I need to understand how a CISO actually thinks about these artifacts. The entity type system must match their mental model, not our implementation convenience.
The "What Is This Thing?" Test
I am going to describe each artifact type in plain English and ask: what category does the CISO expect to find it in?
Service Principal (Entra ID). "This is a cloud identity that authenticates to APIs using client credentials. It has an appId, a client secret, and it shows up in Azure sign-in logs as the actor."
CISO expectation: Identity. This is the thing that logs in. This is what I want to count, monitor, and govern.
OAuth App Registration (Entra ID). "This is the configuration that defines an application in Azure AD. It specifies which permissions the app requests, which APIs it can call, and what redirect URIs it uses."
CISO expectation: Identity (or very close to it). This is the application definition that gets a client_id. It is the source of the authentication capability. Many CISOs think of the app registration and the service principal as "the same thing" — one is the definition, the other is the instance.
ServiceNow Integration User (sys_user). "This is a user account in ServiceNow that the service principal authenticates as. It has roles, it appears in transaction logs, and it owns records."
CISO expectation: Identity. This is an actor in the target system. It authenticates, it has sessions, it has audit trails.
Business Rule (ServiceNow). "This is a server-side script that fires when a record is inserted, updated, or deleted on a specific table. It runs automatically — no human clicks anything. It can read and write records, call Script Includes, and modify the current transaction."
CISO expectation: Automation. This is code that executes. It is not a user. It does not log in. It does not have credentials. It is a trigger-activated program. The CISO expects to find it under "Automations" or "Automation Rules," not under "Identities."
Script Include (ServiceNow). "This is a reusable JavaScript library that other server-side code can call. It does not execute on its own — it must be invoked by a Business Rule, Flow, or other automation. It defines functions that manipulate data."
CISO expectation: Code / Library / Configuration. This is not an automation (it does not trigger independently). It is not an identity (it does not authenticate). It is a code artifact — a building block. The CISO expects to find it under "Components" or "Code Libraries" or "Configuration," not under "Identities."
REST Message (ServiceNow). "This is a configuration that defines how ServiceNow calls an external API. It specifies the endpoint URL, HTTP method, authentication profile, and request/response format."
CISO expectation: Configuration / Integration Config. This is not an actor. It is not automation that triggers on its own. It is a configured outbound integration definition — a template for API calls. The CISO expects to find it under "Integration Configuration" or "Outbound Connections."
OAuth Profile (ServiceNow). "This is the configuration that stores the OAuth client credentials used by REST Messages to authenticate to external APIs. It holds the token URL, client_id, client_secret."
CISO expectation: Credential / Authentication Config. This is where the secret material lives. The CISO expects to find it under "Credentials" or "Authentication Profiles." It is the closest thing to a credential entity in the ServiceNow side.
Flow Designer Flow (ServiceNow). "This is a no-code/low-code automation workflow built in ServiceNow's Flow Designer. It has a trigger (record-based, schedule-based, or application-based), a sequence of actions, and it runs as a configured identity."
CISO expectation: Automation. Like a Business Rule, this is an automation that triggers and executes. It is not a user. It does not log in. It runs as a user (via the "Run As" setting), but it IS not that user. The CISO expects to find it under "Automations" or "Workflows."
Scheduled Job (ServiceNow). "This is a cron-style job that runs on a schedule. It executes a script or calls a function at defined intervals."
CISO expectation: Automation. This is a scheduled task. The CISO expects to find it under "Scheduled Tasks" or "Automations."
Summary: The CISO's Mental Categories
| Artifact | CISO Category | Why |
|---|---|---|
| Service Principal | Identity | Authenticates, has credentials, appears in logs |
| OAuth App Registration | Identity | Defines the application identity, gets client_id |
| ServiceNow Integration User | Identity | Has sessions, roles, audit trails |
| Business Rule | Automation | Trigger-activated code, does not authenticate |
| Script Include | Code / Configuration | Library code, does not trigger independently |
| REST Message | Integration Configuration | Outbound call template, does not execute alone |
| OAuth Profile | Credential / Auth Config | Stores secret material for authentication |
| Flow Designer Flow | Automation | Trigger-activated workflow, does not authenticate |
| Scheduled Job | Automation | Schedule-activated task, does not authenticate |
Three distinct categories emerge: Identity, Automation, Configuration.
The CISO does not think of Business Rules as identities. The CISO does not think of Script Includes as identities. Forcing them into the "identity" bucket violates their mental model every time they look at the screen.
3. User Experience Impact — Artifact by Artifact
3.1 Business Rules
Current UX: Listed on the Identities page. Badge says "Business Rule" (after Round 2 label fix). Entity type in the API: identity. Identity subtype: business_rule.
Problem: A CISO searching for "how many identities do I have?" includes Business Rules in the count. A CISO filtering the Identities list sees Business Rules mixed with Service Principals. A compliance report that lists "all identities" includes Business Rules.
Expected UX: Business Rules appear on an "Automations" page. They are never counted in the identity total. They are linked to identities via RUNS_AS edges (showing which identity they execute as), but they themselves are not identities. When the CISO asks "show me all automations that run as this SP," Business Rules appear. When the CISO asks "show me all identities," Business Rules do not appear.
Where the CISO expects to find it:
- Automations page: YES
- Identities page: NO
- Identity count: EXCLUDED
- Execution chain: YES (as the trigger/entry point)
- Finding: YES (if the identity it runs as is orphaned, or if it touches sensitive data)
3.2 Script Includes
Current UX: Listed on the Identities page. Badge says "Script Include." Entity type in the API: identity. Identity subtype: (not currently emitted by connector, but conceptually would be something like script_include).
Problem: A Script Include is a JavaScript library. It does not trigger on its own. It does not authenticate. It has no credentials. It has no "Run As" identity. It is invoked by other code (Business Rules, Flows). Listing it as an identity is the most egregious category error in the current model.
Expected UX: Script Includes appear as components within an execution chain visualization, not as standalone entities on any primary page. They are visible in the chain detail view when the CISO drills into an automation's execution flow. They are NOT listed on the Identities page. They are NOT listed on the Automations page (because they don't trigger independently). They appear on a "Components" or "Code Artifacts" secondary page, or only within the context of the chain that calls them.
Where the CISO expects to find it:
- Automations page: NO (it does not trigger independently)
- Identities page: NO
- Identity count: EXCLUDED
- Execution chain detail: YES (as an intermediate code component)
- Components / Configuration inventory: YES
- Finding: Indirect only (via the chain that calls it)
3.3 REST Messages
Current UX: Listed on the Identities page. Badge says "REST Message." Entity type in the API: identity.
Problem: A REST Message is an outbound API call configuration. It is a template — it defines how ServiceNow will call an external API, but it does not execute on its own. It is invoked by automations (Business Rules, Flows) that call RESTMessageV2(). It is configuration, not an actor.
Expected UX: REST Messages appear as outbound integration components within execution chain visualizations. They are visible when the CISO asks "what external systems does this automation call?" They are NOT on the Identities page. They could appear on a "Connections" or "Integrations" page that lists all outbound integration configurations.
Where the CISO expects to find it:
- Automations page: NO
- Identities page: NO
- Identity count: EXCLUDED
- Execution chain detail: YES (as the outbound call target)
- Integrations / Connections page: YES
- Finding: Indirect (e.g., "this REST message targets a deprecated API endpoint")
3.4 OAuth Profiles
Current UX: Listed on the Identities page. Badge says "OAuth Client." Entity type in the API: identity.
Problem: An OAuth Profile stores client credentials. It is authentication infrastructure — the thing that holds the client_id and client_secret used by REST Messages to authenticate to external APIs. It is closer to a Credential entity than an Identity entity. It does not execute, trigger, or act autonomously. It enables authentication.
Expected UX: OAuth Profiles appear in the Credentials section of the UI. They are linked to the REST Messages that use them and to the Service Principals they authenticate as. When the CISO asks "what credentials does this integration use?", the OAuth Profile appears. When they ask "show me all identities," it does not.
Where the CISO expects to find it:
- Automations page: NO
- Identities page: NO
- Identity count: EXCLUDED
- Credentials page: YES
- Execution chain detail: YES (as the authentication mechanism)
- Finding: YES (expired credential, unrotated secret)
3.5 Flow Designer Flows
Current UX: Listed on the Identities page. Badge says "Flow Designer Flow." Entity type in the API: identity. Identity subtype: flow_designer_flow.
Problem: A Flow Designer Flow is an automation workflow. It has a trigger (record-based, schedule-based, application-based). It executes actions. It runs as a configured identity. But it IS NOT that identity — it delegates its execution to that identity via RUNS_AS. The flow itself does not authenticate, does not have credentials, and does not appear in sign-in logs.
Expected UX: Flow Designer Flows appear on the Automations page alongside Business Rules and Scheduled Jobs. They are the primary automation type that CISOs want to inventory and govern. When the CISO asks "how many automations do I have?", flows are counted. When they ask "how many identities do I have?", flows are not.
Where the CISO expects to find it:
- Automations page: YES (primary listing)
- Identities page: NO
- Identity count: EXCLUDED
- Automation count: INCLUDED
- Execution chain: YES (as the automation engine)
- Finding: YES (dormant authority, orphaned ownership)
3.6 Scheduled Jobs
Current UX: Listed on the Identities page. Badge says "Scheduled Job." Entity type in the API: identity. Identity subtype: scheduled_job.
Problem: Identical to Flow Designer Flows. A Scheduled Job is a cron-style automation that runs on a schedule. It executes as a configured identity (often "System"). It does not authenticate itself.
Expected UX: Same as Flow Designer Flows — listed on the Automations page, excluded from the identity count.
Where the CISO expects to find it:
- Automations page: YES
- Identities page: NO
- Identity count: EXCLUDED
- Automation count: INCLUDED
- Execution chain: YES (as the scheduled trigger)
- Finding: YES (scheduled job running as system with excessive permissions)
4. The Listing Problem
4.1 The Identity Count Problem
Scenario: CISO opens SecurityV0 dashboard. Sees: "92 Identities Discovered."
CISO thinks: "We have 92 non-human identities that can authenticate to our systems. That's a lot. Let me see which ones are high-risk."
Reality: Of those 92, approximately 15 are actual identities (Service Principals, OAuth Apps, integration users). The remaining ~77 are Flow Designer Flows, Business Rules, Script Includes, REST Messages, and OAuth Profiles.
The damage:
- The CISO's first impression of the product is wrong. They think they have 92 identities. They have 15.
- When they filter for "active identities with elevated permissions," the results include Business Rules that happen to have
is_privileged: truebecause the identity they RUN AS has elevated permissions. The Business Rule itself has no permissions — it borrows them. - When they export the identity list for a compliance report, the auditor receives 92 "identities" and asks: "Why is 'Incident - Close' listed as an identity? That's a workflow."
- When they compare identity counts across platforms (Entra: 12, ServiceNow: 80), the ServiceNow number is inflated by automation artifacts. This makes ServiceNow look like it has a worse NHI posture than it actually does.
4.2 The "Show Me Resources" Problem
Scenario: CISO asks "What resources does the AzureGraphRouter automation touch?"
Current answer: The platform returns resources (tables, API endpoints) via the execution path. But if Script Includes are "identities" and the incident table is a "resource," the CISO sees:
Identities: AzureGraphRouter (Script Include)
Auto-route identity tickets (Business Rule)
sn-ticket-router (Service Principal)
Azure Graph OAuth Client (OAuth Profile)
Resources: incident (table)
graph.microsoft.com (API endpoint)
The CISO looks at this and asks: "Why is the Script Include in the same category as the Service Principal? One is code, the other is a cloud identity. Why is the OAuth Client in the identity list? That's a credential configuration."
What they expect:
Identities: sn-ticket-router (Service Principal)
Automations: AzureGraphRouter (Script Include)
Auto-route identity tickets (Business Rule)
Credentials: Azure Graph OAuth Client (OAuth Profile)
Resources: incident (table)
graph.microsoft.com (API endpoint)
This makes immediate sense. Each category contains things that belong together. The CISO can answer their question ("what resources does this touch?") without mentally filtering out non-resources from a mixed list.
4.3 The Dashboard Metric Problem
Current dashboard metrics (hypothetical):
Identities: 92
Active: 69
Disabled: 23
Orphaned: 12
Dormant Authority: 9
Active External: 1
Problem: "92 Identities" includes 77 automation artifacts. "12 Orphaned" might include 8 orphaned automations and 4 orphaned actual identities. The CISO cannot tell at a glance.
Expected dashboard metrics:
Identities: 15
Active: 12
Disabled: 3
Orphaned: 4
Automations: 65
Active: 45
Disabled: 20
Orphaned: 8
Dormant: 9
External Egress: 1
Now the CISO can answer:
- "How many actual service accounts do I have?" -> 15
- "How many are orphaned?" -> 4 (identities), 8 (automations) = 12 total
- "How many automations reach external systems?" -> 1
- "What percentage of my identities are orphaned?" -> 4/15 = 27% (not 12/92 = 13%)
That 27% is the real number. 13% understates the problem because the denominator is inflated.
4.4 The Compliance Export Problem
When SecurityV0 exports data for a SOC2 audit or SCIM feed:
Current: "92 identity entities" includes Script Includes. The auditor or downstream system receives Script Includes labeled as identities.
Problem: SOC2 CC6.1 asks about access controls for identities (accounts, service principals, API keys). It does not ask about "how many JavaScript libraries does ServiceNow have." Sending automation artifacts as identities pollutes the compliance record.
Expected: The export separates identities from automations. The auditor receives 15 identity entities (with their full execution chains and blast radius) and, separately, 65 automation artifacts (with their triggers, run-as identities, and data domains).
5. Entity Type Decision Tree
This decision tree is based on what the user EXPECTS, not what is convenient for implementation.
START: You have discovered an artifact in a source system.
|
|-- Q1: Can this thing authenticate to another system?
| (Does it have credentials? Does it appear in sign-in logs?
| Does it have a client_id, API key, certificate, or session?)
|
|-- YES --> IDENTITY
| |
| |-- Examples: Service Principal, OAuth App Registration,
| | ServiceNow Integration User, GitHub App, AWS IAM Role,
| | Machine Account, PAT holder
| |
| |-- Key test: If you searched the authentication/sign-in logs
| | of any system, would you find this artifact as the ACTOR?
| | If yes: Identity.
|
|-- NO --> Q2: Does this thing execute autonomously?
| (Does it have a trigger? Does it run on a schedule?
| Does it fire when a record changes? Can it run without
| a human clicking a button?)
|
| |-- YES --> AUTOMATION
| | |
| | |-- Examples: Business Rule (fires on record change),
| | | Flow Designer Flow (trigger-based),
| | | Scheduled Job (cron-based),
| | | AWS Lambda (event-triggered),
| | | GitHub Action (workflow_dispatch, push, schedule)
| | |
| | |-- Key test: If left alone with no human interaction,
| | | could this artifact execute and produce side effects?
| | | If yes: Automation.
| | |
| | |-- Note: An Automation RUNS AS an Identity.
| | | The automation is the trigger/code.
| | | The identity is the execution context.
| | | They are linked by RUNS_AS, not by being the same entity.
| |
| |-- NO --> Q3: Does this thing hold or manage credentials?
| | (Does it store a client_secret, API key, certificate,
| | or token? Is it the thing you rotate?)
| |
| | |-- YES --> CREDENTIAL
| | | |
| | | |-- Examples: OAuth Profile (stores client credentials),
| | | | X.509 Certificate, PAT, API Key,
| | | | AWS IAM Access Key, Federation Trust Config
| | | |
| | | |-- Key test: If compromised, does the attacker
| | | | gain the ability to authenticate as someone?
| | | | If yes: Credential.
| |
| | |-- NO --> Q4: Is this thing code or configuration
| | | that other artifacts reference?
| | | (Is it a library? A template? A connection
| | | definition? An integration endpoint?)
| | |
| | | |-- YES --> CONFIGURATION
| | | | |
| | | | |-- Examples: Script Include (library),
| | | | | REST Message (outbound call template),
| | | | | MID Server config, LDAP config,
| | | | | API endpoint definition
| | | | |
| | | | |-- Key test: Does this artifact only DO
| | | | | something when another artifact invokes it?
| | | | | If yes: Configuration.
| | |
| | | |-- NO --> Q5: Is this thing data that gets
| | | | read, written, or modified?
| | | |
| | | | |-- YES --> RESOURCE
| | | | | |
| | | | | |-- Examples: Database table,
| | | | | | API endpoint (as a target),
| | | | | | S3 bucket, Git repository,
| | | | | | ServiceNow table, Kubernetes secret
| | | | |
| | | | |-- NO --> UNCLASSIFIED
| | | | | (Flag for human review)
Decision Tree Summary Table
| Question | Yes Answer | No Answer |
|---|---|---|
| Can it authenticate? | Identity | Continue |
| Does it execute autonomously? | Automation | Continue |
| Does it hold credentials? | Credential | Continue |
| Is it code/config referenced by others? | Configuration | Continue |
| Is it data that gets read/written? | Resource | Unclassified |
Applying the Decision Tree to Each Artifact
| Artifact | Q1 (Auth?) | Q2 (Execute?) | Q3 (Credential?) | Q4 (Config?) | Q5 (Data?) | Result |
|---|---|---|---|---|---|---|
| Service Principal | YES | -- | -- | -- | -- | Identity |
| Integration User | YES | -- | -- | -- | -- | Identity |
| Business Rule | NO | YES | -- | -- | -- | Automation |
| Flow Designer Flow | NO | YES | -- | -- | -- | Automation |
| Scheduled Job | NO | YES | -- | -- | -- | Automation |
| Script Include | NO | NO | NO | YES | -- | Configuration |
| REST Message | NO | NO | NO | YES | -- | Configuration |
| OAuth Profile | NO | NO | YES | -- | -- | Credential |
| incident table | NO | NO | NO | NO | YES | Resource |
Every artifact lands in exactly one category. The decision tree is deterministic — no judgment calls, no ambiguity.
6. Proposed Entity Types for the Product
How Many Types?
The existing model has 7 NormalizedNodeTypes: autonomous_identity, human_identity, role, permission, resource, credential, execution_evidence.
The question is not "how many types total" but "how many types should the USER see?" The user does not care about role, permission, or execution_evidence as top-level navigational concepts — those are supporting entities in the execution path.
User-visible categories (navigational):
- Identities — Things that can authenticate
- Automations — Things that can execute
- Credentials — Things that enable authentication
- Resources — Things that get acted upon
Supporting categories (visible in detail views, not in primary navigation):
- Configuration — Code and templates referenced by automations
- Roles — Permission groupings
- Permissions — Atomic capabilities
- Execution Evidence — Proof of actual execution
Why Not Merge Automations into a Sub-View of Identities?
Because the questions are different:
| CISO Question | Answer Location |
|---|---|
| "How many non-human identities do I have?" | Identities page |
| "Which identities are orphaned?" | Identities page, filtered |
| "How many automations run in my environment?" | Automations page |
| "Which automations have external egress?" | Automations page, filtered |
| "What identity does this automation run as?" | Automation detail -> RUNS_AS link |
| "What automations use this identity?" | Identity detail -> reverse RUNS_AS |
These are different pages answering different questions about different entity types.
Why Not Just Add More NormalizedNodeType Values?
The current design has autonomous_identity covering both "Service Principal" and "Business Rule." Adding automation as a new NormalizedNodeType is exactly what I am proposing. The question is whether we also need configuration or whether that folds into resource.
My argument: configuration is distinct from resource. A Script Include is not a resource in the same way that the incident table is a resource. The incident table is data. The Script Include is code. The CISO does not think of code and data as the same kind of thing. But configuration does not need to be a top-level navigational concept — it can be a subtype of resource or its own type visible only in chain detail views.
7. The Five-Type Taxonomy
After running the decision tree and the CISO mental model test, I propose five entity types that the product should present to users:
Type 1: Identity
Definition: An entity that can authenticate to a system and appear as an actor in audit logs.
Key characteristic: It has credentials (or can receive them). It has sessions. It appears in sign-in logs. If you asked "who did this?", the answer is an Identity.
Subtypes (identitySubtype):
service_principal— Entra ID SPoauth_app— Entra ID App Registrationintegration_user— ServiceNow sys_user (non-human)machine_account— Generic machine identitygithub_app— GitHub Applicationiam_role— AWS IAM Role (when used as an identity)system_execution— ServiceNow "System" pseudo-identity
Navigational: Primary. Gets its own page ("Identities").
Count meaning: "How many things can authenticate in my environment."
Type 2: Automation
Definition: An entity that can execute autonomously (without human intervention) in response to a trigger or schedule.
Key characteristic: It has a trigger mechanism (record change, schedule, event, API call). It produces side effects (creates/updates/deletes records, calls APIs). It RUNS AS an Identity but IS NOT that identity.
Subtypes (automationSubtype):
business_rule— ServiceNow BRflow_designer_flow— ServiceNow Flowscheduled_job— ServiceNow Scheduled Job / crongithub_action— GitHub Actions workflow (future)lambda_function— AWS Lambda (future)azure_function— Azure Function (future)
Navigational: Primary. Gets its own page ("Automations").
Count meaning: "How many automation artifacts are configured in my environment."
Type 3: Configuration
Definition: A code artifact or integration template that does not execute on its own but is referenced by automations and identities.
Key characteristic: It only does something when another entity invokes it. It is a building block, not an actor.
Subtypes (configSubtype):
script_include— ServiceNow Script Include (JS library)rest_message— ServiceNow REST Message (outbound call template)mid_server_config— MID Server configurationldap_config— LDAP integration configurationapi_definition— API endpoint definition
Navigational: Secondary. Does NOT get its own primary page. Visible in:
- Execution chain detail views
- "Components" tab on Identity or Automation detail pages
- Full inventory list (with "Show all components" toggle)
Count meaning: "How many code/config artifacts exist." This is inventory data, not security data.
Type 4: Resource (existing)
Definition: Something that an identity or automation can act upon — a table, API endpoint, repository, secret store, cloud resource.
No change from current model. Resources stay as they are. The resource NormalizedNodeType is correct.
Type 5: Credential (existing)
Definition: Authentication material that enables an identity to prove itself — an OAuth client secret, certificate, PAT, API key.
One addition: ServiceNow OAuth Profiles should be classified as Credential entities, not Identity entities. The OAuth Profile stores the client_id and client_secret — it is authentication material.
Note on OAuth Profile ambiguity: In the current connector, the OAuth Profile is emitted as an autonomous_identity because it has a client_id that matches the Entra SP's appId. This is the AUTHENTICATES_TO matching logic. But the OAuth Profile itself is not an identity — it is the credential configuration that enables the identity linkage. The matching should remain (it is how we build AUTHENTICATES_TO edges), but the entity type should be credential, not identity.
8. Mapping Artifacts to the Taxonomy
Complete Mapping Table
| Source System Artifact | Current entity_type | Current identitySubtype | Proposed entity_type | Proposed subtype | Rationale |
|---|---|---|---|---|---|
| Entra Service Principal | autonomous_identity | service_principal | identity | service_principal | Authenticates. Has credentials. Appears in sign-in logs. |
| Entra OAuth App Registration | autonomous_identity | oauth_app | identity | oauth_app | Defines the application identity. Gets client_id. |
| ServiceNow Integration User | autonomous_identity | machine_account | identity | integration_user | Has sessions, roles, transaction logs. Authenticates. |
| ServiceNow "System" | autonomous_identity | system_execution | identity | system_execution | Privileged NHI for automations with no user context. |
| ServiceNow Business Rule | autonomous_identity | business_rule | automation | business_rule | Trigger-activated code. Does not authenticate. |
| ServiceNow Flow Designer Flow | autonomous_identity | flow_designer_flow | automation | flow_designer_flow | Trigger-activated workflow. Does not authenticate. |
| ServiceNow Scheduled Job | autonomous_identity | scheduled_job | automation | scheduled_job | Schedule-activated task. Does not authenticate. |
| ServiceNow Script Include | autonomous_identity | (not emitted) | configuration | script_include | Library code. No trigger. No authentication. |
| ServiceNow REST Message | autonomous_identity | (not emitted) | configuration | rest_message | Outbound call template. No trigger. No authentication. |
| ServiceNow OAuth Profile | autonomous_identity | (not emitted) | credential | oauth_profile | Stores client credentials. Is authentication material. |
| Owner (human) | human_identity | -- | owner (existing) | human/team/bu | No change. |
| Role | role | -- | role (existing) | -- | No change. |
| Permission | permission | -- | permission (existing) | -- | No change. |
| Resource (table, API) | resource | -- | resource (existing) | -- | No change. |
| Credential (Entra) | credential | -- | credential (existing) | -- | No change. |
| Execution Evidence | execution_evidence | -- | execution_evidence (existing) | -- | No change. |
What Changes
| Type | Before | After | Net |
|---|---|---|---|
| autonomous_identity | ~92 entities | ~15 entities | -77 |
| identity (new name) | -- | ~15 entities | +15 |
| automation (new) | 0 | ~65 entities | +65 |
| configuration (new) | 0 | ~8 entities | +8 |
| credential | existing count | existing + ~4 OAuth Profiles | +4 |
| resource | existing count | no change | 0 |
The identity list shrinks from 92 to 15. The "noise" moves to where it belongs: Automations (65), Configuration (8), Credentials (4).
9. Impact on Execution Chain Display
Current Execution Chain Visualization
The current display shows a chain of entities that are ALL typed as "identity":
Identity → Identity → Identity → Identity → Identity
(BR) (SI) (REST) (OAuth) (SP)
The only way to tell them apart is the identitySubtype label. The visual language is monotone — every node looks the same because every node is the same type.
Proposed Execution Chain Visualization
With proper entity types, the chain becomes visually stratified:
Automation → Configuration → Configuration → Credential → Identity
(BR) (SI) (REST) (OAuth) (SP)
Each entity type gets a distinct visual treatment:
| Entity Type | Icon/Shape | Color | Visual Meaning |
|---|---|---|---|
| Identity | Shield | Blue | "This is who" |
| Automation | Lightning bolt | Orange | "This is what triggers" |
| Configuration | Gear | Gray | "This is how" |
| Credential | Key | Yellow | "This authenticates" |
| Resource | Database/Globe | Green | "This is where" |
The AzureGraphRouter Chain — Before and After
Before (all identities, color-coded by subtype label):
[identity: BR] → [identity: SI] → [identity: REST] → [identity: OAuth] → [identity: SP]
Auto-route AzureGraph graph.microsoft Azure Graph sn-ticket-
tickets Router .com OAuth Client router
Every node is the same shape, same base type. The labels differentiate them, but the visual hierarchy is flat.
After (typed entities, visually distinct):
[automation: BR] → [config: SI] → [config: REST] → [credential: OAuth] → [identity: SP]
Auto-route AzureGraph graph.microsoft Azure Graph sn-ticket-
tickets Router .com OAuth Client router
↓ ↓ ↓
[resource: [resource: [resource:
incident] graph.microsoft.com] assignment_group]
The CISO can now read the chain left-to-right and immediately understand the structure:
- An automation (Business Rule) fires on the incident table
- It calls code (Script Include) that processes the data
- The code uses an integration (REST Message) to call an external API
- The integration authenticates via a credential (OAuth Profile)
- The credential belongs to an identity (Service Principal) in the target system
- The identity has roles/permissions that reach resources in both systems
This is the execution story told through entity types, not through labels on a flat identity list.
The Flow Diagram Summary (L1 View)
The simplified summary cards also benefit from entity typing:
Before:
TRIGGER: incident table → AUTOMATION: AzureGraphRouter (identity) → AUTH: OAuth (identity) → DESTINATION: graph.microsoft.com
After:
TRIGGER: incident table → AUTOMATION: AzureGraphRouter (automation) → AUTH: OAuth (credential) → IDENTITY: sn-ticket-router (identity) → DESTINATION: graph.microsoft.com
The L1 summary now naturally separates the automation from the identity, and the credential from both.
Cross-System Path Display
The full cross-system execution path also becomes clearer:
Before:
Identity (Entra SP) → AUTHENTICATES_TO → Identity (SN User) → HAS_ROLE → Role → GRANTS → Permission → APPLIES_TO → Resource
But also:
Identity (BR) → CALLS → Identity (SI) → EXECUTES_ON → Identity (REST) → AUTHENTICATES_VIA → Identity (OAuth) → ... → Identity (SP)
Everything is "Identity → Identity → Identity." The edge types carry all the semantic information; the node types carry none.
After:
Automation (BR) → CALLS → Configuration (SI) → USES → Configuration (REST) → AUTHENTICATES_VIA → Credential (OAuth) → BINDS_TO → Identity (SP) → AUTHENTICATES_TO → Identity (SN User) → HAS_ROLE → Role → GRANTS → Permission → APPLIES_TO → Resource
Now the node types AND the edge types both carry semantic information. The chain reads as a sentence: "An automation calls code that uses a REST template, authenticating via a credential that binds to an identity, which authenticates to a target identity, which has roles that grant permissions on resources."
10. Impact on NormalizedNodeType Enum
Current Enum
export type NormalizedNodeType =
| "autonomous_identity"
| "human_identity"
| "role"
| "permission"
| "resource"
| "credential"
| "execution_evidence";
Proposed Enum
export type NormalizedNodeType =
| "identity" // Replaces "autonomous_identity" — things that authenticate
| "automation" // NEW — things that execute autonomously (BR, Flow, Job)
| "configuration" // NEW — code/templates invoked by automations (SI, REST Message)
| "owner" // Replaces "human_identity" — things that are accountable
| "role" // Unchanged
| "permission" // Unchanged
| "resource" // Unchanged
| "credential" // Unchanged (now also includes SN OAuth Profiles)
| "execution_evidence" // Unchanged
Changes
| Old Value | New Value | Migration |
|---|---|---|
autonomous_identity | identity OR automation OR configuration | Based on decision tree — Q1/Q2/Q4 |
human_identity | owner | 1:1 rename — owners are accountable entities, not identities in the NHI sense |
Why Rename autonomous_identity to identity?
Because the word "autonomous" is redundant when non-autonomous entities (automations, configurations) are no longer in this type. An identity IS autonomous by definition in SecurityV0's model — it authenticates and acts without human intervention. The qualifier is unnecessary.
Why Rename human_identity to owner?
Because human_identity is confusing. Humans are not the subject of SecurityV0's analysis — they are the accountability layer. The data model already calls them "Owners." The NormalizedNodeType should match. This also prevents the question "why are humans listed as identities?" from auditors who expect "identity" to mean "service account."
11. Impact on API and Query Patterns
Current API Usage
GET /api/v1/entities?entity_type=autonomous_identity
→ Returns 92 entities (SPs + BRs + Flows + SIs + REST + OAuth)
GET /api/v1/entities?entity_type=autonomous_identity&identitySubtype=service_principal
→ Returns ~5 SPs (correct but requires knowing the subtype)
Proposed API Usage
GET /api/v1/entities?entity_type=identity
→ Returns ~15 entities (SPs, Integration Users, Machine Accounts)
GET /api/v1/entities?entity_type=automation
→ Returns ~65 entities (BRs, Flows, Jobs)
GET /api/v1/entities?entity_type=configuration
→ Returns ~8 entities (SIs, REST Messages)
GET /api/v1/entities?entity_type=credential
→ Returns existing credentials + ~4 OAuth Profiles
GET /api/v1/entities?entity_type=identity,automation
→ Returns ~80 entities (identities + automations — for comprehensive views)
Dashboard Summary API
GET /api/v1/dashboard/summary
{
"identities": {
"total": 15,
"active": 12,
"disabled": 3,
"orphaned": 4,
"withActiveFindings": 6
},
"automations": {
"total": 65,
"active": 45,
"disabled": 20,
"activeExternal": 1,
"dormantAuthority": 9,
"internalInventory": 55
},
"configurations": {
"total": 8
},
"credentials": {
"total": 22,
"expired": 3,
"neverRotated": 7
},
"resources": {
"total": 48,
"sensitive": 12
}
}
The CISO can now answer their questions from the dashboard without drilling into filtered lists.
Finding Association
Findings currently reference entity_id and the finding is always about an "identity." With the new types:
- Orphaned ownership findings can be about an Identity OR an Automation
- Dormant authority findings are about Automations (they have authority via RUNS_AS but no execution)
- Scope drift findings are about Identities (their roles expanded)
- Credential findings (expired, unrotated) are about Credentials
This is more precise. The CISO does not need to decode what kind of entity the finding is about — the entity type tells them.
12. Impact on Findings and Evidence Packs
Current Finding Text
Finding: Orphaned Ownership
Identity
AzureGraphRouter(ServiceNow, identity_subtype: script_include) has no active owner...
The CISO reads this and thinks: "Why is a Script Include called an 'Identity'? Script Includes don't own anything. They don't authenticate. This finding is about a piece of code, not an identity."
Proposed Finding Text
Finding: Orphaned Automation Chain
Automation
Auto-route identity tickets via Entra(ServiceNow, type: business_rule) triggers execution chainAzureGraphRouterwith no active owner. The chain runs as Identitysn-ticket-router(Service Principal) which authenticates to Microsoft Graph API via orphaned credentialAzure Graph OAuth Client. Last execution: never (dormant authority). Blast radius: identity_platform domain.
Now the finding correctly attributes the concern:
- The automation is the trigger (Business Rule)
- The identity is what it runs as (SP)
- The credential is how it authenticates (OAuth Profile)
- The chain is the composite (AzureGraphRouter execution chain)
Each entity is called what it actually is.
Evidence Pack Structure Impact
Evidence packs currently have an "Identity Summary" section. This should generalize to "Entity Summary" or, better, become context-aware:
- For identity-centric findings: "Identity Summary" (shows SP details)
- For automation-centric findings: "Automation Summary" (shows BR/Flow details, trigger, run-as identity)
- For credential-centric findings: "Credential Summary" (shows expiry, rotation, binding)
The evidence pack structure otherwise does not change — it already captures the full chain regardless of entity types.
13. Impact on OAA Export
Round 4 concluded that OAA export is a projection layer. The entity type changes affect the projection mapping:
| SecurityV0 Type | OAA Mapping |
|---|---|
| Identity | local_user within the source system's OAA Application |
| Automation | resource within the source system's OAA Application (sub_type: "automation") |
| Configuration | resource within the source system's OAA Application (sub_type: "code_artifact") |
| Credential | Custom properties on local_user (OAA has no credential entity) |
| Resource | resource within the source system's OAA Application |
This is EXACTLY what the Round 4 OAA Specialist recommended — automations as resources, not identities. The entity type change in SecurityV0 makes the OAA export semantically correct by default, rather than requiring a type-check-and-remap at export time.
14. Impact on Connectors
What Changes in the Connector
The connector currently emits all automation artifacts as nodeType: "autonomous_identity" with different identitySubtype values. The change:
- Business Rules:
nodeType: "autonomous_identity"becomesnodeType: "automation", propertyautomationSubtype: "business_rule" - Flow Designer Flows:
nodeType: "autonomous_identity"becomesnodeType: "automation", propertyautomationSubtype: "flow_designer_flow" - Scheduled Jobs:
nodeType: "autonomous_identity"becomesnodeType: "automation", propertyautomationSubtype: "scheduled_job" - Script Includes:
nodeType: "autonomous_identity"becomesnodeType: "configuration", propertyconfigSubtype: "script_include" - REST Messages:
nodeType: "autonomous_identity"becomesnodeType: "configuration", propertyconfigSubtype: "rest_message" - OAuth Profiles:
nodeType: "autonomous_identity"becomesnodeType: "credential", propertycredentialSubtype: "oauth_profile" - Service Principals:
nodeType: "autonomous_identity"staysnodeType: "identity"(renamed), propertyidentitySubtype: "service_principal" - Integration Users:
nodeType: "autonomous_identity"staysnodeType: "identity"(renamed), propertyidentitySubtype: "integration_user"
Connector Effort Estimate
The changes are in transformer.py, specifically in the functions that create NormalizedNode objects. The mapping is deterministic — the artifact's source table determines its type:
| Source Table | Old nodeType | New nodeType |
|---|---|---|
sys_script (Business Rule) | autonomous_identity | automation |
sys_script_include | autonomous_identity | configuration |
sys_rest_message | autonomous_identity | configuration |
oauth_entity | autonomous_identity | credential |
sys_flow (Flow Designer) | autonomous_identity | automation |
sysauto (Scheduled Job) | autonomous_identity | automation |
servicePrincipals (Entra) | autonomous_identity | identity |
sys_user (SN integration) | autonomous_identity | identity |
This is a straightforward mapping change. The connector already knows the source table — it just needs to emit the correct nodeType.
Estimated effort: 4-6 hours including test updates. The connector has 247 tests; the nodeType changes will affect test assertions but not test logic.
15. Why "Fix the Labels" Is Insufficient
Round 2 recommended "keep entity_type: identity, fix labels in UI." Here is why that approach fails at the product level:
Problem 1: The API Cannot Lie
When a customer's SOC tooling calls GET /api/v1/entities?entity_type=autonomous_identity, it receives Business Rules mixed with Service Principals. The customer must then filter by identitySubtype to separate them. But the customer's tooling was written to consume "identities" — it does not expect to filter out code artifacts from an identity list.
Fixing labels in the UI does not fix the API. The API is the product's contract with automation tooling, SIEM integrations, and compliance exports.
Problem 2: The Count Is Still Wrong
Even with label fixes, the dashboard metric "92 Identities" is visible before the user opens any filtered view. The first number they see is wrong. Label fixing changes what the items look like on the list page, but it does not change the count on the dashboard or the number in the compliance export.
Problem 3: Filtering Is User Burden
Requiring the CISO to filter identitySubtype NOT IN (business_rule, flow_designer_flow, scheduled_job, script_include, rest_message) every time they want to see actual identities is unacceptable UX. The default view should show identities on the Identities page — not identities mixed with everything else.
Problem 4: New Connectors Compound the Problem
When we add GitHub Actions, AWS Lambda, Terraform State, Kubernetes CronJobs:
- GitHub: ~20 workflow definitions as "identities" + ~5 actual GitHub Apps as identities
- AWS: ~30 Lambda functions as "identities" + ~10 IAM roles as identities
- Each new connector inflates the identity count with non-identity automation artifacts
The identity count becomes meaningless. Label fixing is a per-connector patch that must be applied every time. Proper entity typing is a structural fix that works for all future connectors automatically.
Problem 5: The Mental Model Cannot Be Patched
When the CISO sees a page titled "Identities" and it contains Business Rules, no amount of labeling makes that feel right. The page title creates an expectation. The entity types must match the page they are on. If we have an "Identities" page, only identities should be on it. If we have an "Automations" page, only automations should be on it.
This is not a label problem. It is a classification problem.
16. Counter-Arguments and Responses
Counter: "Automations participate in the identity graph — they ARE identity-like"
Response: Participation in the identity graph does not make something an identity. Resources also participate in the identity graph (via APPLIES_TO and EXECUTES_ON). Roles participate in the identity graph (via HAS_ROLE and GRANTS). Credentials participate (via AUTHENTICATES_VIA). None of these are classified as "identity." They each have their own type.
Automations should be the same — they participate in the graph via RUNS_AS, TRIGGERS_ON, and CALLS edges. They are connected to identities. They are not identities.
Counter: "Adding new entity types breaks existing queries"
Response: There are zero active clients. Every query that references autonomous_identity is in our own codebase. The migration is a search-and-replace in the platform code plus a re-emission from the connector. This is the cheapest possible time to make this change.
Counter: "The Round 2 consensus was unanimous — we should respect it"
Response: Round 2's question was "how should we display execution flows?" — not "what entity type should automations be?" The Round 2 consensus was about display strategy within the constraint of the existing model. Round 5's question is about whether that model is correct. New information (Round 3's execution_chains, Round 4's OAA analysis, the founder's challenge) changes the calculus.
I am the same Product Owner who agreed in Round 2. I am also the same Product Owner who reversed my Round 3 dissent in Round 4 based on new analysis. Changing one's position based on evidence is not inconsistency — it is intellectual honesty.
Counter: "Automations execute with authority — that makes them identities"
Response: Automations execute with BORROWED authority. A Business Rule runs as "System" or as "User who initiates." It borrows the execution context of an actual identity. The authority belongs to the identity it runs as, not to the Business Rule itself.
Analogy: A cron job on a Linux server runs as a user (e.g., root, www-data). The cron job is not a user — it is a scheduled task that executes in the context of a user. Nobody would list cron jobs on the user management page.
Counter: "This creates two places to look for orphaned ownership"
Response: Correct, and that is a feature, not a bug. Orphaned identity ownership and orphaned automation ownership are different concerns with different remediation paths:
- Orphaned identity → Reassign owner, review permissions, rotate credentials
- Orphaned automation → Determine if automation is still needed; if yes, assign owner and validate run-as identity; if no, disable automation
These are different workflows. Showing them separately helps the CISO triage correctly.
Counter: "The NormalizedNodeType enum should stay stable per Round 4 recommendation"
Response: Round 4, Question Q3 asked: "Should the NormalizedNodeType enum change?" The team recommended "No" because they were evaluating whether to add OAA-inspired types. The context was adding types for OAA compatibility, not for semantic correctness. The recommendation was "add semantic richness through properties, not new types."
Round 5 is a different question: are the EXISTING type assignments correct? The answer is no — Business Rules should never have been autonomous_identity. This is not about adding types for OAA; it is about fixing a classification error in the foundational data model.
Counter: "Subtypes already solve this — just filter by identitySubtype"
Response: If the subtype is doing all the semantic work and the entity_type is meaningless, then entity_type is a broken abstraction. A type system where the type tells you nothing and the subtype tells you everything is a type system with the wrong type.
The purpose of entity_type is to be the first-level discriminator — the broadest useful categorization. If two entities have the same entity_type but completely different behaviors, characteristics, and UX expectations, the entity_type is wrong.
17. The Demo Walkthrough — Before and After
Before: Current Model
Demo script, slide 1: Dashboard
Presenter: "SecurityV0 has discovered 92 non-human identities in your environment."
CISO: "92? That seems like a lot. What are they?"
Presenter: "Service Principals, OAuth Applications, Business Rules, Flow Designer Flows, Script Includes, REST Messages..."
CISO: "Wait — Business Rules and Script Includes are identities? Those are code artifacts. How many actual service accounts do I have?"
Presenter: "Let me filter... about 15 are actual service principals and integration users."
CISO: "So 77 of your 92 'identities' aren't identities at all?"
Presenter: "They're modeled as identity subtypes because they participate in the execution authority graph..."
CISO: "I don't want to explain to my board that we have 92 identities when we have 15. Can you fix the count?"
This exchange has happened (or will happen) in every CISO demo. The presenter must apologize for the data model.
After: Proposed Model
Demo script, slide 1: Dashboard
Presenter: "SecurityV0 has discovered 15 non-human identities and 65 automations in your environment."
CISO: "15 identities — OK, that aligns roughly with what I expected. What about the 65 automations?"
Presenter: "Those are Business Rules, Flow Designer Flows, and Scheduled Jobs that execute autonomously. Of those 65, one has active external egress to an LLM endpoint, and 9 have dormant authority — configured with external connections but not recently active."
CISO: "Show me the one with the LLM egress."
Presenter: [Clicks on automation detail. Shows: Trigger → Automation → Code → REST API → Credential → Identity → Target Resources]
CISO: "OK, I can see the full chain. Who owns the identity this runs as?"
Presenter: [Clicks the identity node. Shows: sn-ticket-router, Service Principal, owner: departed]
CISO: "Orphaned. What can it reach?"
Presenter: [Shows blast radius: identity_platform domain, Microsoft Graph API]
CISO: "This is exactly what I need. Add it to the remediation queue."
The conversation flows naturally because the concepts match the CISO's mental model. No apologizing for the data model. No explaining why Script Includes are "identities." No filtering to get the real identity count.
18. Implementation Principles
Principle 1: The Entity Type Must Match the Page It Appears On
If we have an "Identities" page, only entities with entity_type: identity appear on it. If we have an "Automations" page, only entities with entity_type: automation appear on it. No cross-contamination. No "show me identities but filter out the non-identity subtypes."
Principle 2: Counts Must Be Meaningful
"15 Identities" must mean 15 things that can authenticate. "65 Automations" must mean 65 things that can execute. The dashboard numbers must be defensible in a board presentation without caveats.
Principle 3: The Decision Tree Must Be Deterministic
Every artifact from every connector must land in exactly one entity type via the decision tree. No ambiguity. No judgment calls. The connector maps source table to entity type mechanically.
Principle 4: Entity Types Are Navigational, Subtypes Are Descriptive
Entity type determines which page/section the entity appears on. Subtype provides detail within that section. This is a two-level taxonomy: type for structure, subtype for semantics.
Principle 5: Relationships Cross Type Boundaries
The graph still connects everything:
- Automation → RUNS_AS → Identity
- Automation → CALLS → Configuration
- Configuration → AUTHENTICATES_VIA → Credential
- Credential → BINDS_TO → Identity
- Identity → HAS_ROLE → Role
- Role → GRANTS → Permission
- Permission → APPLIES_TO → Resource
Entity types are about classification. Relationships are about connection. They are orthogonal.
Principle 6: No Migration Cost Argument
There are zero active clients. There is no production data that cannot be re-ingested. The connector can re-emit with correct types. The platform can re-ingest. The argument "but migration is expensive" does not apply.
19. Open Questions for Other Roles
For Architect
-
Does the path materializer need changes? Currently it follows RUNS_AS edges from identity to identity. If the source is now "automation" and the target is "identity," does the traversal logic change? My expectation: the edge types drive traversal, not the node types, so the change should be minimal.
-
Should
autonomous_identitybe renamed toidentityor should we introduceidentityas a new value and deprecateautonomous_identity? I prefer a clean rename — no active clients means no deprecation period needed. -
Does the
configurationtype need its own collection, or does it stay in the unifiedentitiescollection? My recommendation: same collection, differententity_typevalue. Per ADR-002, all source entities go in one collection.
For CISO
-
Does the finding taxonomy need to change? If
orphaned_ownershipcan now fire on both identities and automations, should there be distinct finding types (orphaned_identity,orphaned_automation) or one finding type with the entity type as context? -
For compliance exports, should automations be in the identity inventory or in a separate automation inventory section? My recommendation: separate sections with cross-references.
-
Does the "how many identities do I have?" question become a compliance metric? If so, the entity type classification directly affects regulatory reporting.
For Integrator
-
Can the connector determine entity type from source table alone? My analysis says yes —
sys_scriptis always a Business Rule (automation),sys_script_includeis always a Script Include (configuration), etc. Are there edge cases where the same source table could produce different entity types? -
What about the OAuth Profile → AUTHENTICATES_TO matching logic? If OAuth Profiles become credentials instead of identities, the AUTHENTICATES_TO edge construction needs to change. The edge currently goes from one identity to another. If the OAuth Profile is a credential, the edge should go from the identity (that uses this credential) to the target identity, with the credential as an intermediate node linked via AUTHENTICATES_VIA.
-
Does the
system_executionpseudo-identity remain an identity? It appears in audit logs as the actor. It does authenticate (as the system context). But it is a platform-level construct, not a user-created service account. My recommendation: keep as identity with subtypesystem_execution.
For Developer
-
What is the total effort to update the platform for the new entity types? My estimate: 16-24 hours for the platform (types, queries, API, UI pages) + 4-6 hours for the connector = 20-30 hours total. But the Developer should validate this.
-
Should we introduce separate UI pages for each entity type, or a unified entity page with type-based tabs? My recommendation: separate top-level pages for Identities and Automations (the two primary navigational categories). Configuration and Credentials are secondary — accessible from entity detail views and a "full inventory" page.
-
Can we implement this as a "Phase 0" before the execution_chains work (Round 3)? If entity types are correct first, the execution_chains builder can reference typed entities from the start.
20. Recommendation
The Decision
Reclassify automation artifacts out of the identity entity type. Introduce automation and configuration as new NormalizedNodeType values. Rename autonomous_identity to identity and human_identity to owner.
The New NormalizedNodeType Enum
export type NormalizedNodeType =
| "identity" // Things that authenticate (SP, Integration User, Machine Account)
| "automation" // Things that execute autonomously (BR, Flow, Job)
| "configuration" // Code/templates invoked by others (SI, REST Message)
| "owner" // Accountability entities (humans, teams, BUs)
| "role" // Permission groupings
| "permission" // Atomic capabilities
| "resource" // Things acted upon (tables, APIs, buckets)
| "credential" // Authentication material (secrets, certs, tokens, OAuth profiles)
| "execution_evidence" // Proof of execution
Implementation Priority
This should be done BEFORE the execution_chains implementation (Round 3 Phase 1). The execution_chains builder references entity types. If entity types are wrong when chains are built, the chain model inherits the category error.
Recommended sequence:
- Entity type reclassification (this recommendation) — 20-30 hours
- Execution chains collection (Round 3 Phase 1) — 94 hours
- OAA export layer (Round 4 Phase 3) — 24 hours
What This Buys the Product
-
Honest dashboard metrics. "15 identities" is the real number. "65 automations" is the real number. The board presentation does not require caveats.
-
Correct UX structure. Identities page shows identities. Automations page shows automations. No mental filtering required.
-
Clean API contracts.
entity_type=identityreturns authenticating entities.entity_type=automationreturns automation artifacts. Downstream tooling gets what it expects. -
Future-proof for new connectors. When GitHub Actions, AWS Lambda, and Kubernetes CronJobs arrive, they are
automationentities from day one. The identity count stays clean. -
OAA export alignment. Automations export as OAA resources, not as OAA users. The export is semantically correct by default.
-
Foundation for execution_chains. The chain builder knows which entities are entry points (automations), which are code (configurations), which are auth (credentials), and which are actors (identities). This makes chain role assignment (
entry_point,executor,auth_credential,destination_identity) mechanically derivable from entity type. -
Credibility. When the CISO asks "what is a Script Include doing on the Identities page?", the answer is "it's not — it's on the Components page within the execution chain view." That is the right answer.
The Cost of Not Doing This
If we ship with the current model and acquire clients, the cost of reclassification becomes:
- Data migration for every client instance
- API versioning (v1 returns mixed identities; v2 returns separated types)
- Connector updates with backward compatibility requirements
- SCIM/OAA export rework with client notification
- Dashboard metric recalibration with customer explanation
- Documentation rewrite
The cost today: 20-30 hours of engineering work with zero client impact.
The cost in 6 months with 10 clients: 200+ hours of migration, communication, and backward compatibility work.
This is the single cheapest structural fix we will ever make. The window of zero-cost reclassification closes the moment we onboard our first client.
Appendix A: Subtype Registries
Identity Subtypes
| Subtype | Source System | Source Table | Description |
|---|---|---|---|
service_principal | Entra ID | servicePrincipals | Azure AD Service Principal |
oauth_app | Entra ID | applications | Azure AD App Registration |
integration_user | ServiceNow | sys_user | Non-human user account |
machine_account | Various | Various | Generic machine identity |
system_execution | ServiceNow | (synthetic) | Platform-level execution identity |
github_app | GitHub | installations | GitHub Application |
iam_role | AWS | roles | IAM Role (as identity) |
pat_holder | Various | Various | Personal Access Token holder |
Automation Subtypes
| Subtype | Source System | Source Table | Description |
|---|---|---|---|
business_rule | ServiceNow | sys_script | Server-side script on record events |
flow_designer_flow | ServiceNow | sys_flow | No-code/low-code workflow |
scheduled_job | ServiceNow | sysauto | Cron-style scheduled task |
github_action | GitHub | workflows | CI/CD workflow definition |
lambda_function | AWS | functions | Event-driven serverless function |
azure_function | Azure | functions | Event-driven serverless function |
step_function | AWS | stateMachines | State machine orchestration |
Configuration Subtypes
| Subtype | Source System | Source Table | Description |
|---|---|---|---|
script_include | ServiceNow | sys_script_include | Reusable JavaScript library |
rest_message | ServiceNow | sys_rest_message | Outbound API call template |
mid_server_config | ServiceNow | ecc_agent | MID Server configuration |
transform_map | ServiceNow | sys_transform_map | Data import mapping |
terraform_module | Terraform | modules | Infrastructure-as-code module |
Appendix B: Relationship Impact Analysis
Relationships FROM Automation Entities
| Relationship | From | To | Current | Proposed | Change |
|---|---|---|---|---|---|
| RUNS_AS | BR/Flow/Job | SP/User | Identity → Identity | Automation → Identity | Source type changes |
| TRIGGERS_ON | BR/Flow | Table | Identity → Resource | Automation → Resource | Source type changes |
| CALLS | BR | SI | Identity → Identity | Automation → Configuration | Both types change |
| CREATED_BY | BR/Flow | Owner | Identity → Owner | Automation → Owner | Source type changes |
| OWNED_BY | BR/Flow | Owner | Identity → Owner | Automation → Owner | Source type changes |
| EXECUTES_ON | BR/Flow | Resource | Identity → Resource | Automation → Resource | Source type changes |
Relationships FROM Configuration Entities
| Relationship | From | To | Current | Proposed | Change |
|---|---|---|---|---|---|
| CALLS (reverse) | BR | SI | Identity → Identity | Automation → Configuration | Target type changes |
| AUTHENTICATES_VIA | SI/REST | OAuth | Identity → Identity | Configuration → Credential | Both types change |
| EXECUTES_ON | SI | Resource | Identity → Resource | Configuration → Resource | Source type changes |
| CREATED_BY | SI | Owner | Identity → Owner | Configuration → Owner | Source type changes |
Key Observation
None of the EDGE TYPES change. Only the entity types on each end change. This means the path materializer's traversal logic (which follows edge types) should not need fundamental changes — only the type filters on queries may need updating.
Appendix C: Competitive Validation
How Other Products Classify Automation Artifacts
| Product | Business Rules / Lambda | Script Libraries | REST Configs | OAuth Profiles | Service Principals |
|---|---|---|---|---|---|
| Veza (OAA) | Resource | Resource | Resource | Custom Property | Local User |
| CyberArk Identity | Not tracked | Not tracked | Not tracked | Credential | Identity |
| Astrix Security | "Non-Human Entity" | Not tracked | Not tracked | Connection | Identity |
| Silverfort | Not tracked | Not tracked | Not tracked | Not tracked | Service Account |
| Oasis Security | Automation Rule | Not tracked | Integration | Credential | NHI |
No product in the NHI space classifies Business Rules, Script Includes, or REST Messages as "identities." SecurityV0 is the only one doing this. Our classification is the outlier.
Round 5 analysis complete. 666 lines. Position: reclassify automation artifacts from identity to automation, configuration, and credential entity types. Zero active clients. Zero migration cost. The window for a free structural fix closes on first customer onboarding. Recommendation: implement BEFORE execution_chains (Round 3 Phase 1).