Entity Type Classification — OAA Specialist
Date: 2026-02-13 (Round 5)
Role: Veza OAA Specialist
Core Question: What entity type should Business Rules, Script Includes, REST Messages, OAuth Profiles, Flow Designer Flows, and Scheduled Jobs actually be? Is the current entity_type: "identity" classification correct?
Table of Contents
- Executive Summary
- The Identity Test — Applied Rigorously
- OAA Entity Classification for Each Artifact
- The Dual-Nature Problem — Critical Examination
- How Other Platforms Classify These Artifacts
- Proposed Decision Tree
- Proposed Type System
- Relationship Implications
- The "Runs As" Relationship as the Bridge
- Impact on Execution Path Traversal
- Impact on Findings and Evidence
- OAA Export Implications
- Migration Path
- Open Questions
- Recommendation
- Appendix A: Complete Entity Classification Matrix
- Appendix B: OAA Community Connector Entity Classification Evidence
- Appendix C: Comparison with Current Model
1. Executive Summary
The current model is wrong. After rigorous application of both the Identity Test (authentication, authorization context, audit actor, ACL subject) and OAA's own entity semantics, I conclude:
-
Business Rules, Script Includes, REST Messages, and OAuth Profiles are NOT identities. They fail 4/4 identity criteria. They are artifacts — configurable things that exist within a platform, owned and managed by administrators, acted upon by platform processes.
-
Flow Designer Flows and Scheduled Jobs are NOT identities either, though they come closer. They have a "run as" configuration, which gives them an indirect relationship to an identity. But the flow itself does not authenticate. The identity it runs as authenticates. The flow is an automation artifact that delegates execution to a real identity.
-
OAuth Profiles are credentials, not identities. An OAuth profile stores client_id, client_secret, token_url — it is authentication material, analogous to a certificate or API key. In SecurityV0's own data model, this maps to the existing
credentialentity type.
The founder's instinct is correct. The Round 4 Architect's claim that "automations are simultaneously identities AND resources" was a rationalization to avoid model change. This round provides the evidence.
Proposed Classification
| Artifact | Current Type | Correct Type | OAA Mapping |
|---|---|---|---|
| Service Principal | autonomous_identity | autonomous_identity | Local User |
| Machine Account | autonomous_identity | autonomous_identity | Local User |
| Business Rule | autonomous_identity (subtype) | automation_artifact | Resource |
| Script Include | autonomous_identity (subtype) | automation_artifact | Resource |
| Flow Designer Flow | autonomous_identity (subtype) | automation_artifact | Resource |
| Scheduled Job | autonomous_identity (subtype) | automation_artifact | Resource |
| REST Message | autonomous_identity (subtype) | automation_artifact | Resource |
| OAuth Profile | autonomous_identity (subtype) | credential | Custom Property on Local User |
| System Execution | autonomous_identity (subtype) | autonomous_identity | Local User |
2. The Identity Test — Applied Rigorously
The prompt provides a 4-criterion Identity Test. Let me apply each criterion to each artifact with evidence, not assertion.
2.1 Criterion 1: Authenticates (Has Credentials)
What this means: The entity presents credentials (password, certificate, token, API key) to a system to prove it is who it claims to be. The system verifies the credential and establishes a session or grants access.
| Artifact | Authenticates? | Evidence |
|---|---|---|
| Service Principal | YES | Has client_id + client_secret or certificate. Presents them to Entra's token endpoint. Receives an access token. Appears in sign-in logs with authentication details. |
Machine Account (svc-integration-user) | YES | Has a username + password (or token). Logs into ServiceNow. Session established. Appears in sys_user_session table. |
| Business Rule | NO | A Business Rule has no credentials. It does not log in. It is triggered by a database event (insert, update, delete on a table). When it executes, it runs in the context of whatever session triggered it, or in the "system" context. The BR itself presents no credentials. |
| Script Include | NO | A Script Include is a reusable code library. It is called by other server-side scripts (BRs, Flows, Scheduled Jobs). It has no credentials, no authentication context. It inherits the caller's execution context. |
| REST Message | NO | A REST Message is a configuration artifact — it defines an HTTP endpoint URL, method, headers, and body template. It does not authenticate. When invoked by a script, it may use an authentication profile (OAuth, basic auth, mutual auth) configured separately. The REST Message itself is not an identity. |
| OAuth Profile | NO — and critically, it is not an identity that authenticates, it is authentication material used BY an identity | An OAuth Profile stores client_id, client_secret, token_url, scope. It is a credential configuration. It does not authenticate itself — it enables authentication. The distinction is: a certificate does not authenticate; an identity presents a certificate to authenticate. Same with an OAuth Profile. |
| Flow Designer Flow | NO | A Flow does not authenticate. It has a "Run As" configuration that specifies which user or system context it executes under. That user authenticates. The Flow is an orchestration artifact. |
| Scheduled Job | NO | Same as Flow. A Scheduled Job has a "Run As" field. The job definition does not authenticate — the configured user authenticates. |
Summary: Only Service Principals and Machine Accounts authenticate. Everything else either has no authentication context or borrows it from a real identity.
2.2 Criterion 2: Has Its Own Authorization Context (Roles, Permissions)
What this means: The entity is directly assigned roles or permissions. When a system evaluates "can X do Y?", X is this entity.
| Artifact | Has own authz? | Evidence |
|---|---|---|
| Service Principal | YES | Directly assigned roles in Entra (Application.ReadWrite.All, etc.). The SP's access token carries these claims. |
| Machine Account | YES | Directly assigned roles in ServiceNow (itil, hr_admin, etc.) via sys_user_has_role. |
| Business Rule | NO | A BR is not assigned roles. It has an "Advanced" security check (sys_security_acl evaluation), but that check evaluates the session user's roles, not the BR's roles. A BR has no sys_user_has_role entries. |
| Script Include | NO | A Script Include has no roles. It executes with whatever privileges its caller has. The isAccessible() method on Script Includes checks the caller's roles. |
| REST Message | NO | A REST Message has no roles or permissions. It is a configuration record. |
| OAuth Profile | NO | An OAuth Profile stores scopes (like "openid", "user.read"), but these are the requested scopes for the token request, not roles assigned to the profile itself. The profile is a credential template. |
| Flow Designer Flow | PARTIALLY — but indirectly | A Flow has a "Run As" configuration. If "Run As" is set to "System User", the flow executes with system-level privileges. But these are not roles assigned to the Flow — they are roles of the user it runs as. The Flow does not appear in sys_user_has_role. |
| Scheduled Job | PARTIALLY — but indirectly | Same as Flow. A Scheduled Job's "Run As User" field points to a real user. That user has roles. The job does not have its own roles. |
Summary: Only Service Principals and Machine Accounts have their own authorization context. Flows and Scheduled Jobs have indirect authorization via their "Run As" target, but this is delegation, not direct authorization.
2.3 Criterion 3: Appears as the Actor in Audit Logs
What this means: When you look at audit logs, security logs, or transaction logs, this entity appears as the "who" — the actor that performed the action.
| Artifact | Actor in logs? | Evidence |
|---|---|---|
| Service Principal | YES | Appears in Entra sign-in logs (servicePrincipalId). Appears in ServiceNow syslog_transaction if it authenticates via OAuth to ServiceNow. |
| Machine Account | YES | Appears in syslog_transaction.user_name, sys_audit.user, sys_user_session. The machine account IS the actor. |
| Business Rule | PARTIALLY — as a trigger, not as an actor | In sys_audit, the actor is the user whose action triggered the BR. The BR itself may appear in the source field (e.g., "Business Rule: AzureGraphRouter"). But the BR is not the actor — it is the mechanism. The distinction matters: "User admin triggered a change; Business Rule AzureGraphRouter executed in response" is different from "Business Rule AzureGraphRouter performed an action." |
| Script Include | NO | Script Includes do not appear as actors in logs. They may appear in stack traces or debug logs, but never as the "who." |
| REST Message | NO | REST Messages do not appear as actors. When an outbound REST call is made, the log entry records the user context, not the REST Message definition. |
| OAuth Profile | NO | OAuth Profiles do not appear in logs as actors. The authenticated identity (SP or user) appears. |
| Flow Designer Flow | PARTIALLY | In sys_flow_context, the flow's sys_id appears. But sys_flow_context.started_by references a user. The flow is the process; the user is the actor. Some logs record sys_flow_context.run_as to show which user context the flow used. Again, the flow is mechanism, not actor. |
| Scheduled Job | PARTIALLY | In syslog_transaction, a scheduled job's execution may show the "Run As" user as the actor. The job itself may appear in the source field. Similar to BR — the job is the trigger/mechanism, the user is the actor. |
Summary: Service Principals and Machine Accounts ARE actors in logs. BRs, Flows, and Scheduled Jobs are mechanisms/triggers that appear in logs as sources or contexts, but the actual "actor" is the identity they run as. Script Includes, REST Messages, and OAuth Profiles never appear as actors.
2.4 Criterion 4: Can Be the Subject of "Can X Do Y?"
What this means: Can you meaningfully ask "Can this entity do Y?" and get a deterministic answer from the authorization system?
| Artifact | Subject of ACL? | Evidence |
|---|---|---|
| Service Principal | YES | "Can sp-graph-router read Application.ReadWrite.All scope?" — answered by Entra's role assignment check. |
| Machine Account | YES | "Can svc-integration-user write to the incident table?" — answered by ServiceNow's ACL evaluation against the user's roles. |
| Business Rule | NO | "Can Business Rule AzureGraphRouter write to the incident table?" — this question does not make sense in ServiceNow's authorization model. A BR does not have roles. The ACL system evaluates the session user, not the BR. A BR is code that runs, not an entity that has permissions. |
| Script Include | NO | Same reasoning. "Can Script Include AzureGraphHelper call the Graph API?" — this is not an ACL question. The Script Include has no permissions of its own. |
| REST Message | NO | "Can REST Message AzureGraphAPI access graph.microsoft.com?" — this is a configuration question, not an authorization question. The REST Message defines where to call, not whether the caller is allowed. |
| OAuth Profile | NO | "Can OAuth Profile EntraOAuth access Entra?" — again, not an authorization question. The OAuth Profile stores credentials for accessing Entra, but authorization is evaluated for the SP, not the profile. |
| Flow Designer Flow | NO | "Can Flow HR_Onboarding create HR cases?" — this is not how ServiceNow evaluates authorization. ServiceNow evaluates "Can [Run As User] create HR cases?" The flow is the what, not the who. |
| Scheduled Job | NO | Same reasoning. "Can Scheduled Job NightlySync read CMDB?" — ServiceNow evaluates the Run As User's permissions, not the job's. |
2.5 Identity Test Results Summary
| Artifact | Auth? | Own Authz? | Actor? | ACL Subject? | Score | Identity? |
|---|---|---|---|---|---|---|
| Service Principal | YES | YES | YES | YES | 4/4 | YES |
| Machine Account | YES | YES | YES | YES | 4/4 | YES |
| System Execution | YES* | YES | YES | YES | 3-4/4 | YES |
| Business Rule | NO | NO | Partial | NO | 0.5/4 | NO |
| Script Include | NO | NO | NO | NO | 0/4 | NO |
| REST Message | NO | NO | NO | NO | 0/4 | NO |
| OAuth Profile | NO | NO | NO | NO | 0/4 | NO |
| Flow Designer Flow | NO | Indirect | Partial | NO | 0.5/4 | NO |
| Scheduled Job | NO | Indirect | Partial | NO | 0.5/4 | NO |
*System Execution is a platform-level identity (the "system" user context). It authenticates implicitly as the system, has full privileges, and appears as "system" in audit logs.
Verdict: Six of the nine current "identity subtypes" fail the Identity Test categorically. Only Service Principals, Machine Accounts, and System Execution are identities.
3. OAA Entity Classification for Each Artifact
Now I apply OAA's own entity semantics to classify each artifact. OAA defines four entity categories within an Application:
- Local User — An identity that authenticates to the application and has permissions
- Local Group — A collection of local users for group-based permission assignment
- Local Role — A named bundle of permissions
- Resource — A domain object that users interact with; something users have permissions ON
The decision is: for each ServiceNow automation artifact, which of these is it?
3.1 Business Rule → OAA Resource
Reasoning:
A Business Rule is a configurable artifact within ServiceNow. It is a thing that administrators create, modify, enable, disable, and delete. Administrators have permissions on Business Rules (create, read, update, delete via sys_script table ACLs). A BR does not have permissions on other things — it does not hold roles or exercise permissions.
In OAA terms:
- Is it something that a user interacts with? YES — admins configure, enable, disable BRs.
- Does it have permissions? NO — it has no roles assigned to it.
- Can users have permissions on it? YES — via
sys_scripttable ACLs (read, write, delete).
Classification: Resource (type: business_rule)
# OAA mapping
servicenow_app.add_resource(
name="AzureGraphRouter",
resource_type="business_rule"
)
Custom properties carry the execution-relevant metadata:
br_resource.set_property("table", "incident")
br_resource.set_property("trigger_condition", "insert")
br_resource.set_property("active", True)
br_resource.set_property("execution_mode", "autonomous")
br_resource.set_property("egress_category", "external")
br_resource.set_property("run_as", "system")
3.2 Script Include → OAA Resource
Reasoning:
A Script Include is a reusable code library in ServiceNow. It is stored in the sys_script_include table. Administrators create, modify, and manage Script Includes. A Script Include has no identity properties — it does not authenticate, hold roles, or appear as an actor.
It is a dependency of other automation artifacts. In execution chain terms, it is called by Business Rules, Flows, or other Script Includes. It is code-as-resource, analogous to a library or module.
Classification: Resource (type: script_include)
In OAA, Script Includes would be sub-resources of the ServiceNow application, potentially nested under the calling automation:
servicenow_app.add_resource(
name="AzureGraphHelper",
resource_type="script_include"
)
3.3 REST Message → OAA Resource
Reasoning:
A REST Message is an outbound HTTP integration configuration stored in sys_rest_message. It defines:
- Endpoint URL
- HTTP method
- Authentication profile (reference to OAuth Profile or basic auth config)
- Request/response templates
It is a pure configuration artifact. It is not an identity, not a credential, and not executable logic. It is an integration definition — a resource that describes how to reach an external system.
In the execution chain, a REST Message is a dependency of a Script Include or Business Rule — it is the configuration that tells the calling code where and how to make an HTTP request.
Classification: Resource (type: rest_message)
servicenow_app.add_resource(
name="AzureGraphAPI",
resource_type="rest_message"
)
Custom properties:
rm_resource.set_property("endpoint", "https://graph.microsoft.com")
rm_resource.set_property("auth_type", "oauth2")
rm_resource.set_property("auth_profile_ref", "oauth-profile-sys-id")
3.4 OAuth Profile → Credential (SecurityV0) / Custom Property (OAA)
Reasoning:
This is the most nuanced classification. An OAuth Profile stores:
- Client ID
- Client Secret (encrypted)
- Token URL
- Grant Type
- Scopes
This is authentication material. In SecurityV0's own data model, there is already a credential entity type that captures exactly this:
Credential: Authentication material that an identity uses to prove itself to a target system — an OAuth client secret, certificate, personal access token, API key, or federation trust.
The OAuth Profile IS a credential. Specifically, it is a credential of type oauth_client_secret with grant_type: "client_credentials". It has issuing_system (Entra ID), target_system (ServiceNow's outbound config), and scopes_granted.
In OAA, credentials have no first-class entity type. The closest mapping is custom properties on the Local User (the SP that uses the credential) or a custom resource type.
Classification: credential (in SecurityV0 NormalizedNodeType) / Resource with type oauth_profile in OAA export
# OAA: model as a resource since OAA has no credential entity
servicenow_app.add_resource(
name="EntraOAuth",
resource_type="oauth_profile"
)
But in the internal SecurityV0 model:
{
nodeType: "credential",
properties: {
credential_type: "oauth_client_secret",
grant_type: "client_credentials",
issuing_system: "entra_id",
target_system: "servicenow"
}
}
3.5 Flow Designer Flow → OAA Resource
Reasoning:
A Flow Designer Flow is an orchestration artifact. It defines a sequence of actions (steps) that execute in response to a trigger. It is stored in sys_hub_flow.
The Flow has a "Run As" configuration. This is the key property that makes it seem like an identity — it executes actions. But:
- The Flow does not authenticate. The "Run As" user authenticates.
- The Flow has no roles. The "Run As" user's roles determine what the flow can do.
- The Flow is not the ACL subject. ServiceNow evaluates "Can [Run As User] do X?", not "Can [Flow] do X?"
- The Flow appears in logs as the mechanism, not the actor.
sys_flow_context.started_byis the actor; the flow is the process.
The Flow is a configurable process — a resource within ServiceNow that defines automation behavior. Administrators create, modify, enable, disable, and delete Flows. Users have permissions on Flows (via table ACLs on sys_hub_flow).
A real-world analogy: A Terraform configuration file is not an identity, even though it provisions infrastructure. The identity is the AWS IAM role that Terraform assumes. The Terraform file is a resource — a code artifact that defines what to do. A Flow is the same: a configuration artifact that defines what to execute, delegating identity to its "Run As" target.
Classification: Resource (type: flow_designer_flow)
servicenow_app.add_resource(
name="HR Onboarding Workflow",
resource_type="flow_designer_flow"
)
Custom properties:
flow_resource.set_property("run_as", "system")
flow_resource.set_property("run_as_user_id", "sys-user-id-xyz")
flow_resource.set_property("trigger_type", "record_created")
flow_resource.set_property("trigger_table", "hr_case")
flow_resource.set_property("active", True)
flow_resource.set_property("execution_mode", "autonomous")
3.6 Scheduled Job → OAA Resource
Reasoning:
A Scheduled Job (sysauto_script or sys_trigger) is a time-triggered automation. It has:
- A schedule (cron expression or interval)
- A "Run As" user
- A script or reference to executable logic
The same reasoning as Flow Designer Flow applies:
- The job does not authenticate — its "Run As" user does.
- The job has no roles — its "Run As" user's roles apply.
- The job is not the ACL subject.
- The job appears in logs as the mechanism/trigger, not the actor.
Classification: Resource (type: scheduled_job)
servicenow_app.add_resource(
name="Nightly CMDB Sync",
resource_type="scheduled_job"
)
4. The Dual-Nature Problem — Critical Examination
4.1 The Claim
In Round 4, the Architect stated:
"Automation artifacts are simultaneously identities AND resources. They execute (identity), and they are configured (resource). OAA cannot represent this duality because OAA's world is strictly divided."
4.2 Why This Is Wrong
This claim conflates two distinct things:
-
"They execute" — Correct, automation artifacts cause execution to happen. But causing execution is not the same as being an identity. A trigger on a database table causes execution. A cron schedule causes execution. A webhook URL causes execution. None of these are identities.
-
"They are configured" — Correct, they are configurable artifacts that administrators manage. This makes them resources.
The error is in equating "causes execution" with "is an identity." By this logic, a database trigger is an identity. A cron expression is an identity. An event listener is an identity. This leads to absurdity.
The correct decomposition is:
- The automation artifact (BR, Flow, Job) is a resource — a configurable thing that defines behavior.
- The identity that the artifact runs as is a separate entity — a user, service principal, or system context.
- The relationship between them is
RUNS_AS— the artifact delegates execution to the identity.
This decomposition is:
- Semantically correct (the BR is not the identity; the identity is the identity)
- OAA-compatible (resource with properties, not a local user)
- Consistent with how every other OAA connector classifies analogous artifacts
4.3 The RUNS_AS Relationship Resolves the "Dual Nature"
The perceived "dual nature" is actually a composition of two entities connected by a relationship:
[Automation Artifact] --RUNS_AS--> [Identity]
(Resource) (autonomous_identity)
This is not duality — it is a graph relationship. The artifact has execution semantics because it delegates to an identity that has authentication and authorization. You do not need to make the artifact an identity to capture this. You need a relationship.
SecurityV0 already has the RUNS_AS relationship type. The current model uses RUNS_AS from one identity to another (automation identity → SP). The corrected model uses RUNS_AS from an automation artifact (resource) to an identity:
[Business Rule: AzureGraphRouter] --RUNS_AS--> [Identity: system]
(automation_artifact) (autonomous_identity)
This is more honest about what is happening in the real system.
4.4 The Rationalization Problem
I believe the "dual nature" claim in Round 4 was a rationalization to avoid the cost of changing the data model. The synthesis states:
"Team recommendation: No. All 6 roles agree to keep the existing type enum stable."
But the question in Round 4 was "how should execution chains map to OAA concepts?" — not "should we change the entity type classification?" The Round 4 team was solving a different problem (execution chains collection + OAA export) and inherited the identity assumption without questioning it.
Round 5's question — "is the current identity classification semantically correct?" — was not addressed by Round 4. The answer is no, it is not.
5. How Other Platforms Classify These Artifacts
5.1 Evidence from Every OAA Community Connector
I have reviewed all 9 OAA community connectors (GitHub, GitLab, Jira, PagerDuty, Bitbucket, Slack, Rollbar, Cerby, Looker). Here is the complete evidence:
Entities classified as Local Users (identities):
| Connector | Local Users | Why They Are Identities |
|---|---|---|
| GitHub | Org members, outside collaborators | Have GitHub accounts, authenticate, appear as commit authors |
| GitLab | GitLab users | Have accounts, authenticate, have access levels |
| Jira | Atlassian accounts | Have accounts, authenticate, are actors on issues |
| PagerDuty | PagerDuty users | Have accounts, authenticate, are on-call actors |
| Bitbucket | Workspace members | Have Atlassian accounts, authenticate, push code |
| Slack | Slack users, bots | Have accounts, authenticate, send messages |
| Rollbar | Rollbar users | Have accounts, authenticate, manage projects |
| Looker | Looker users | Have accounts, authenticate, run queries |
Entities classified as Resources:
| Connector | Resources | Why They Are Resources |
|---|---|---|
| GitHub | Repositories | Things users have permissions on (read, write, admin) |
| GitLab | Groups, Projects | Organizational containers users have access levels on |
| Jira | Projects | Containers users have permission schemes on |
| PagerDuty | Teams | Organizational units users belong to and have roles in |
| Bitbucket | Projects, Repos | Things users have permissions on (read, write, admin) |
| Rollbar | Projects | Things users have access levels on (owner, standard, light, view) |
| Looker | Model Sets, Models, Connections | Data objects users have permissions on |
Entities classified as Automation/Execution artifacts: NONE.
Not a single OAA community connector models:
- GitHub Actions workflows as Local Users
- GitLab CI pipelines as Local Users
- Jira workflows or automations as Local Users
- PagerDuty escalation policies as Local Users
- Slack workflow automations as Local Users
If these existed in the connectors, they would be Resources — because they are configurable artifacts that users interact with, not identities that authenticate.
5.2 The GitLab Bot Pattern
GitLab's connector provides a relevant edge case. GitLab bots are modeled as Local Users:
if user_info.get("bot") is True:
local_user.set_property("bot", True)
But note: GitLab bots ARE identities. They have accounts. They authenticate. They appear as actors on merge requests and CI jobs. The bot is a user — an automation identity, not an automation artifact.
This is the distinction: a bot (identity that automates) vs. a workflow (artifact that defines automation). SecurityV0's Service Principals and Machine Accounts are bots — they are identities. Business Rules and Flows are workflows — they are artifacts.
5.3 The Slack Bot Pattern
Similarly, Slack models bots as Local Users with a bot_id property:
if is_bot and bot_id is not None:
oaa_user.set_property("bot_id", bot_id)
Slack bots have accounts, authenticate via tokens, and are actors in channels. They are identities, not artifacts.
5.4 Pattern Summary
Across all 9 OAA connectors, the pattern is consistent and universal:
| Category | OAA Classification | Examples |
|---|---|---|
| Things that authenticate and act | Local User | Users, bots, service accounts |
| Things that users interact with | Resource | Repos, projects, teams, connections |
| Things that bundle permissions | Local Role | Admin, read, write, maintain |
| Collections of users | Local Group | Teams, groups, user groups |
No connector models automation artifacts (workflows, pipelines, rules, policies) as Local Users. None.
6. Proposed Decision Tree
This decision tree is deterministic. Any developer can follow it to classify a new entity from any source system.
6.1 Primary Classification
START: Given entity E from source system S
Q1: Does E authenticate to S using its own credentials?
(Has username/password, client_id/secret, certificate, token, API key)
YES → Q2: Does E have its own role/permission assignments in S?
(Not inherited, not borrowed — directly assigned)
YES → Q3: Does E appear as the "actor" in S's audit logs?
(Not as a trigger source or mechanism, but as the identity
that performed the action)
YES → IDENTITY (autonomous_identity or human_identity)
NO → IDENTITY (may be a service account with limited logging)
NO → This is unusual. An entity that authenticates but has no
direct permissions may be a credential or a proxy.
→ Q4: Does E store authentication material for other entities?
YES → CREDENTIAL
NO → IDENTITY (edge case — may be a delegated identity)
NO → Q5: Does E store authentication material (secrets, keys, certificates, tokens)?
(client_secret, private_key, api_key, password — not just references)
YES → CREDENTIAL
NO → Q6: Does E define executable behavior (code, workflow, process)?
(Scripts, rules, flows, pipelines, jobs)
YES → Q7: Does E have a "run as" or "execute as" identity?
YES → AUTOMATION_ARTIFACT
(with RUNS_AS relationship to the identity)
NO → AUTOMATION_ARTIFACT
(runs in caller context or system context)
NO → Q8: Is E something that identities have permissions ON?
(A table, API endpoint, configuration, integration)
YES → RESOURCE
NO → Q9: Does E bundle permissions together?
YES → ROLE
NO → RESOURCE (default for classifiable artifacts)
6.2 Application of Decision Tree to Each Artifact
| Artifact | Q1 (Auth?) | Path | Result |
|---|---|---|---|
| Service Principal | YES | Q1→YES, Q2→YES, Q3→YES | autonomous_identity |
| Machine Account | YES | Q1→YES, Q2→YES, Q3→YES | autonomous_identity |
| System Execution | YES* | Q1→YES, Q2→YES, Q3→YES | autonomous_identity |
| OAuth Profile | NO | Q1→NO, Q5→YES | credential |
| Business Rule | NO | Q1→NO, Q5→NO, Q6→YES, Q7→YES (system context) | automation_artifact |
| Script Include | NO | Q1→NO, Q5→NO, Q6→YES, Q7→NO (inherits caller) | automation_artifact |
| REST Message | NO | Q1→NO, Q5→NO, Q6→NO (configuration, not executable), Q8→YES | resource (integration config) |
| Flow Designer Flow | NO | Q1→NO, Q5→NO, Q6→YES, Q7→YES (run as user) | automation_artifact |
| Scheduled Job | NO | Q1→NO, Q5→NO, Q6→YES, Q7→YES (run as user) | automation_artifact |
Note on REST Message: A REST Message is a borderline case. It defines how to call an external system (URL, headers, body template), but the executable behavior is in the calling Script Include or Business Rule. I classify it as a resource (specifically, an integration configuration resource) rather than an automation_artifact because it does not define autonomous executable behavior — it defines a reusable HTTP call template that is invoked by other code. However, it could reasonably be classified as automation_artifact with subtype integration_config if the team prefers a broader definition.
6.3 Decision Tree for Future Entities
This tree handles entities SecurityV0 may encounter in future connectors:
| Future Entity | Q Path | Classification |
|---|---|---|
| GitHub Actions Workflow | Q1→NO, Q5→NO, Q6→YES, Q7→YES (GITHUB_TOKEN identity) | automation_artifact |
| GitHub App | Q1→YES (has private key + JWT), Q2→YES (permissions), Q3→YES | autonomous_identity |
| AWS Lambda Function | Q1→NO, Q5→NO, Q6→YES, Q7→YES (execution role) | automation_artifact |
| AWS IAM Role | Q1→YES (can be assumed), Q2→YES (policies), Q3→YES | autonomous_identity |
| Terraform State File | Q1→NO, Q5→NO, Q6→NO, Q8→YES | resource |
| Kubernetes CronJob | Q1→NO, Q5→NO, Q6→YES, Q7→YES (service account) | automation_artifact |
| Kubernetes Service Account | Q1→YES (token), Q2→YES (RBAC), Q3→YES | autonomous_identity |
| PagerDuty Escalation Policy | Q1→NO, Q5→NO, Q6→YES (defines escalation), Q7→NO | automation_artifact |
| Jira Workflow | Q1→NO, Q5→NO, Q6→YES (defines transitions), Q7→NO | automation_artifact |
7. Proposed Type System
7.1 New NormalizedNodeType Enum
export type NormalizedNodeType =
| "autonomous_identity" // Service Principals, Machine Accounts, System Execution, GitHub Apps
| "human_identity" // Human owners (unchanged)
| "automation_artifact" // Business Rules, Script Includes, Flows, Scheduled Jobs, REST Messages
| "role" // Roles (unchanged)
| "permission" // Permissions (unchanged)
| "resource" // Tables, APIs, repos, cloud resources (unchanged)
| "credential" // OAuth Profiles, certificates, API keys, PATs (unchanged)
| "execution_evidence"; // Proof of execution (unchanged)
Change from current model: Added automation_artifact. Removed the notion that automation subtypes are identities.
7.2 Subtype System
Each automation_artifact carries a subtype property for classification within the type:
// On nodes with nodeType: "automation_artifact"
interface AutomationArtifactProperties {
// Subtype classification
subtype:
| "business_rule"
| "script_include"
| "rest_message"
| "flow_designer_flow"
| "scheduled_job"
| "integration_config" // REST Message alternative classification
| "github_actions_workflow"
| "lambda_function"
| "kubernetes_cronjob";
// Execution context
execution_mode: "autonomous" | "operator_assisted" | "human_triggered" | "unknown";
security_relevance: "active_external" | "dormant_authority" | "internal_inventory";
// Trigger information
trigger_type?: "schedule" | "event" | "manual" | "api_call" | "record_change";
trigger_table?: string;
trigger_condition?: string;
schedule?: string;
// Run-as reference (resolved via RUNS_AS edge)
run_as_type?: "configured" | "inherited" | "system" | "caller_context";
// Egress information
egress_category?: "none" | "internal" | "external";
destination_domain?: string;
// Lifecycle
active: boolean;
created_by?: string;
last_modified_at?: string;
}
7.3 What Each Type Means
| Type | Semantic | Identity Test Score | OAA Mapping | Examples |
|---|---|---|---|---|
autonomous_identity | An entity that authenticates, holds roles, and acts autonomously | 4/4 or 3/4 | Local User | Service Principal, Machine Account, GitHub App, K8s Service Account |
human_identity | A human who owns or manages other entities | N/A (not tested) | IdP Identity | Human owner, admin, operator |
automation_artifact | A configurable artifact that defines executable behavior, delegating identity to a "run as" target | 0-0.5/4 | Resource | Business Rule, Flow, Scheduled Job, Script Include, REST Message |
role | A named bundle of permissions | N/A | Local Role | ServiceNow roles, Entra roles, AWS policies |
permission | A single normalized capability (action + scope) | N/A | Custom Permission | read on incident, write on hr_case |
resource | Something that identities act upon | N/A | Resource | Tables, APIs, repos, buckets |
credential | Authentication material used by an identity | N/A | Custom Property / Resource | OAuth Profile, certificate, PAT, API key |
execution_evidence | Immutable proof of actual execution | N/A | Not in OAA | syslog_transaction entry, sys_flow_context record |
7.4 Why Not More Types?
One might ask: why not add integration_config for REST Messages, or code_library for Script Includes? The answer is parsimony:
automation_artifactcovers all executable/configurable automation things — the subtype property provides specificity.- Too many types fragment the query model — every UI filter, API endpoint, and evaluation rule that references
nodeTypemust handle each type. - Subtypes are flexible — new subtypes can be added without breaking the type system. New types require changes everywhere.
The one exception I considered was separating integration_config for REST Messages, since they do not define executable behavior themselves. But since they participate in execution chains and carry security-relevant properties (endpoint URLs, auth references), keeping them under automation_artifact with a descriptive subtype is cleaner.
8. Relationship Implications
8.1 Current Relationships (Identity → Identity Model)
Currently, the execution chain for AzureGraphRouter looks like:
BR:AzureGraphRouter (identity) --RUNS_AS--> Identity:system (identity)
BR:AzureGraphRouter (identity) --TRIGGERS_ON--> Resource:incident_table
BR:AzureGraphRouter (identity) --calls--> SI:AzureGraphHelper (identity)
SI:AzureGraphHelper (identity) --calls--> RM:AzureGraphAPI (identity)
RM:AzureGraphAPI (identity) --AUTHENTICATES_VIA--> Cred:EntraOAuth (credential)
Problems with this:
- A Business Rule does not RUNS_AS anything. It runs in the triggering context.
- A Script Include does not authenticate. It is called code.
- A REST Message does not authenticate. It defines how to make a call.
- The OAuth Profile is already modeled as a credential, but it was also an identity subtype.
8.2 Corrected Relationships (Artifact → Identity Model)
Artifact:AzureGraphRouter (automation_artifact) --RUNS_AS--> Identity:system (autonomous_identity)
Artifact:AzureGraphRouter (automation_artifact) --TRIGGERS_ON--> Resource:incident_table (resource)
Artifact:AzureGraphRouter (automation_artifact) --CALLS--> Artifact:AzureGraphHelper (automation_artifact)
Artifact:AzureGraphHelper (automation_artifact) --CALLS--> Artifact:AzureGraphAPI (automation_artifact)
Artifact:AzureGraphAPI (automation_artifact) --USES_CREDENTIAL--> Credential:EntraOAuth (credential)
Credential:EntraOAuth (credential) --AUTHENTICATES_TO--> Identity:sp-graph-router (autonomous_identity)
Identity:sp-graph-router (autonomous_identity) --HAS_ROLE--> Role:Application.ReadWrite.All
This is more honest:
- The BR (artifact) runs in system context (RUNS_AS → system identity)
- The BR (artifact) calls a Script Include (artifact) — this is a CALLS relationship, not identity delegation
- The Script Include calls a REST Message (artifact) — again a CALLS relationship
- The REST Message uses a credential (OAuth Profile) — this is USES_CREDENTIAL
- The credential authenticates to the external identity — AUTHENTICATES_TO
- The external identity has roles — HAS_ROLE
8.3 New Edge Type Needed: CALLS
The corrected model requires a CALLS edge type for artifact-to-artifact dependencies:
export type NormalizedEdgeType =
| "OWNED_BY"
| "BELONGS_TO"
| "HAS_ROLE"
| "GRANTS"
| "APPLIES_TO"
| "AUTHENTICATES_TO"
| "AUTHENTICATES_VIA"
| "EXECUTES_ON"
| "RUNS_AS" // automation_artifact → autonomous_identity
| "TRIGGERS_ON" // automation_artifact → resource
| "CREATED_BY"
| "DELEGATES_TO"
| "APPROVED_BY"
| "MEMBER_OF"
| "CALLS" // NEW: automation_artifact → automation_artifact
| "USES_CREDENTIAL"; // NEW: automation_artifact → credential
8.4 Updated RUNS_AS Semantics
Currently: Identity → Identity ("this automation identity executes as this identity")
Corrected: automation_artifact → autonomous_identity ("this artifact executes in this identity's context")
The semantics are the same, but the source entity type changes from identity to artifact. The path materializer follows the same traversal logic.
9. The "Runs As" Relationship as the Bridge
The RUNS_AS relationship is the key architectural element that makes this classification work. It bridges the gap between artifacts (which define behavior) and identities (which have authorization).
9.1 How RUNS_AS Enables Execution Path Traversal
The execution path question is: "What can this automation ultimately do?"
With the corrected model:
Artifact:AzureGraphRouter
--RUNS_AS--> Identity:system
--HAS_ROLE--> Role:admin
--GRANTS--> Permission:*
--APPLIES_TO--> Resource:* (all tables)
Artifact:AzureGraphRouter
--CALLS--> Artifact:AzureGraphHelper
--CALLS--> Artifact:AzureGraphAPI
--USES_CREDENTIAL--> Credential:EntraOAuth
--AUTHENTICATES_TO--> Identity:sp-graph-router
--HAS_ROLE--> Role:Application.ReadWrite.All
--GRANTS--> Permission:read_write on scope:application
--APPLIES_TO--> Resource:Microsoft Graph API
The path materializer follows:
- From the artifact, find RUNS_AS → get the local identity's execution path
- From the artifact, follow CALLS → CALLS → USES_CREDENTIAL → AUTHENTICATES_TO → get the cross-system execution path
- Union the results = complete blast radius
9.2 RUNS_AS Variants
| Run As Type | Meaning | Example |
|---|---|---|
configured | Explicitly configured "Run As" user | Flow with "Run As: svc-integration-user" |
system | Runs in system context (highest privilege) | Business Rule running as "system" |
inherited | Inherits the triggering user's context | Business Rule in "current" user context |
caller_context | Runs in the calling artifact's context | Script Include called by a Business Rule |
For inherited and caller_context types, RUNS_AS is optional or points to a placeholder "session_user" identity. The actual execution authority depends on who/what triggered the chain.
10. Impact on Execution Path Traversal
10.1 Current Path Traversal (Identity Model)
Artifact-as-Identity → RUNS_AS → Identity → HAS_ROLE → ...
The path materializer starts at an "identity" node and follows RUNS_AS to "borrow" another identity's execution paths.
10.2 Corrected Path Traversal (Artifact Model)
automation_artifact → RUNS_AS → autonomous_identity → HAS_ROLE → ...
automation_artifact → CALLS → automation_artifact → USES_CREDENTIAL → credential → AUTHENTICATES_TO → autonomous_identity → HAS_ROLE → ...
The path materializer starts at an artifact node and follows:
RUNS_AS→ to get the local execution authorityCALLS→ to traverse the dependency chainUSES_CREDENTIAL→ to find the authentication materialAUTHENTICATES_TO→ to reach the cross-system identityHAS_ROLE→ to get the target system's permissions
10.3 What Changes in the Path Materializer
The path materializer needs to know that automation_artifact nodes are traversal starting points, just like autonomous_identity nodes. Specifically:
- Entry point expansion: When listing "things that have execution paths", include both
autonomous_identityandautomation_artifactnodes. - Edge traversal: Add
CALLSandUSES_CREDENTIALto the set of edges the materializer follows. - Depth budget:
CALLSedges should consume the depth budget (they are internal dependency chains).RUNS_ASshould not (it is identity binding, as per the current model).
10.4 Impact on Existing Queries
Any query that currently selects nodeType: "autonomous_identity" to find "things that execute" will need to also include nodeType: "automation_artifact". This is a breaking change, but the prompt states:
CONSTRAINT: Do NOT consider migration cost. The team has no active clients and can rewrite from scratch if needed. Focus ONLY on semantic correctness.
Therefore, the breaking change is acceptable.
11. Impact on Findings and Evidence
11.1 Findings Currently Generated for Automation Identities
The current model generates findings like:
orphaned_ownershipfor a Business Rule whose creator leftdormant_authorityfor a Flow that has not executed in 90 daysscope_driftfor a Scheduled Job whose "Run As" user gained new roles
11.2 How Findings Change
With the corrected model:
Orphaned ownership:
- Still valid. An
automation_artifactcan haveOWNED_BYedges to owners. If all owners have decayed, the finding fires. - The finding text changes from "This identity has no owner" to "This automation artifact has no owner."
Dormant authority:
- Still valid, but the subject changes. The finding is about the artifact's execution path (via RUNS_AS → identity → roles → permissions → resources).
- The finding text: "This automation artifact has not executed in 90 days but its execution identity (system) still has admin access to all tables."
Scope drift:
- The drift occurs on the identity, not the artifact. If the "Run As" identity gains new roles, the artifact's blast radius expands.
- The finding text: "The identity 'system' that automation 'AzureGraphRouter' runs as gained 3 new roles since last baseline."
11.3 New Finding Types Enabled
The corrected model enables findings that the identity model cannot express:
Artifact misconfiguration:
- "Automation artifact 'AzureGraphRouter' has execution_mode: autonomous but its Run As identity 'system' has admin access to all tables including restricted resources."
Credential chain exposure:
- "Automation artifact 'AzureGraphHelper' uses credential 'EntraOAuth' which authenticates as Service Principal 'sp-graph-router' with Application.ReadWrite.All permission — a transitive privilege chain from a Business Rule to Microsoft Graph API admin access."
Artifact-identity binding audit:
- "12 automation artifacts RUNS_AS identity 'system' — this concentrates all automation risk in a single identity context."
12. OAA Export Implications
12.1 Artifact as OAA Resource
In the OAA export layer (from Round 4's recommendation), automation artifacts map cleanly to Resources:
# ServiceNow as OAA Application
sn_app = CustomApplication("ServiceNow - corp.service-now.com", "ServiceNow")
# Automation artifacts as Resources
sn_app.add_resource("AzureGraphRouter", resource_type="business_rule")
sn_app.add_resource("AzureGraphHelper", resource_type="script_include")
sn_app.add_resource("AzureGraphAPI", resource_type="rest_message")
sn_app.add_resource("HR Onboarding Workflow", resource_type="flow_designer_flow")
sn_app.add_resource("Nightly CMDB Sync", resource_type="scheduled_job")
# Identities as Local Users
sp = sn_app.add_local_user("svc-integration-user")
sp.add_identity("svc-integration-user@corp.service-now.com")
# The "system" identity
system = sn_app.add_local_user("system")
# Permission binding: identity has permissions on automation artifacts
system.add_permission("execute", resources=[
sn_app.resources["AzureGraphRouter"],
sn_app.resources["AzureGraphHelper"]
])
12.2 Custom Properties for SecurityV0-Specific Metadata
# On automation artifact resources
br = sn_app.resources["AzureGraphRouter"]
br.set_property("execution_mode", "autonomous")
br.set_property("security_relevance", "active_external")
br.set_property("trigger_type", "record_change")
br.set_property("trigger_table", "incident")
br.set_property("run_as_user", "system")
br.set_property("egress_category", "external")
br.set_property("destination_domain", "graph.microsoft.com")
br.set_property("sv0_artifact_subtype", "business_rule")
12.3 Compatibility
This OAA mapping is more compatible than the current model because:
- It follows the same pattern as every existing OAA connector (domain objects as Resources).
- It does not create entity proliferation (no micro-Applications).
- It preserves SecurityV0-specific semantics via custom properties.
- It works with Veza's authorization graph natively (Identity → Permission → Resource).
13. Migration Path
Although the prompt says to ignore migration cost, I will briefly note the logical migration steps for completeness, since they demonstrate the feasibility of the change.
13.1 NormalizedNodeType Enum
Add automation_artifact to the union type. No existing types change.
13.2 Connector (sv0-connectors)
In the transformer, nodes currently emitted as:
{
"nodeType": "autonomous_identity",
"properties": {
"identity_type": "business_rule",
...
}
}
Change to:
{
"nodeType": "automation_artifact",
"properties": {
"subtype": "business_rule",
...
}
}
OAuth Profile nodes change from autonomous_identity to credential.
13.3 Ingestion (sv0-platform)
The entity normalizer must accept automation_artifact as a valid nodeType.
13.4 Storage
The entities collection stores entity_type from nodeType. No schema change needed — the field already accepts any string from the union type.
13.5 Path Materializer
Must follow CALLS, USES_CREDENTIAL edges from automation_artifact nodes. Must include automation_artifact as a valid starting point for execution path materialization.
13.6 Evaluator
Findings that reference autonomous_identity for automation subtypes must be updated to reference automation_artifact.
13.7 UI
Filters that show "identities" may need to also show "automation artifacts" depending on the view. The Graph Explorer and Entity Detail components need to handle the new type.
13.8 Tests
All tests that create Business Rule, Script Include, Flow, Scheduled Job, or REST Message nodes with nodeType: "autonomous_identity" must be updated.
14. Open Questions
Q1: Should REST Message be automation_artifact or resource?
Argument for automation_artifact: REST Messages participate in execution chains and carry security-relevant configuration (endpoint URLs, auth references). They are part of the execution flow.
Argument for resource: REST Messages do not define executable behavior. They are templates. The executable behavior is in the calling Script Include.
My recommendation: automation_artifact with subtype rest_message. This keeps all execution chain participants under one type, simplifying path traversal. The subtype distinguishes configuration artifacts from executable artifacts.
Q2: Should Script Includes with caller_context execution get RUNS_AS edges?
Analysis: A Script Include that runs in the caller's context does not have its own identity binding. It inherits whatever context called it. A RUNS_AS edge would be misleading because the Script Include does not "run as" a specific identity — it runs as whoever called it.
My recommendation: No RUNS_AS edge for caller_context Script Includes. The path materializer discovers their execution authority by traversing the CALLS chain backward to find the nearest artifact with a RUNS_AS edge.
Q3: Should system_execution remain an autonomous_identity?
Analysis: The "system" user in ServiceNow is a legitimate identity. It has maximum privileges, appears as an actor in logs, and is the effective identity for many automated processes. It is the target of RUNS_AS edges from Business Rules and Flows.
My recommendation: Yes, system_execution should remain autonomous_identity. It is the identity that automation artifacts delegate to. It passes the Identity Test (authenticates as system context, has full roles, appears as actor, is the ACL subject for system-level operations).
Q4: What about automation artifacts that have no RUNS_AS target?
Analysis: Some automation artifacts (like Script Includes or REST Messages) do not have an explicit "Run As" configuration. They inherit their execution context from their caller.
My recommendation: These artifacts have no RUNS_AS edge. Their execution authority is determined by traversing the CALLS chain to the root artifact (the Business Rule, Flow, or Scheduled Job that started the chain), which does have a RUNS_AS edge.
15. Recommendation
15.1 Definitive Answer
Business Rules, Script Includes, REST Messages, Flow Designer Flows, and Scheduled Jobs should be automation_artifact entity types, NOT autonomous_identity subtypes.
OAuth Profiles should be credential entity types, NOT autonomous_identity subtypes.
The evidence is unambiguous:
-
Identity Test: These artifacts score 0-0.5/4 on the Identity Test. Identities score 3-4/4.
-
OAA Classification: Every OAA community connector classifies domain artifacts as Resources, never as Local Users. No connector models automation workflows, CI pipelines, or configurable processes as identities.
-
Semantic Correctness: Classifying a Business Rule as an identity conflates "defines behavior" with "has identity." A database trigger defines behavior. A cron expression defines behavior. A Kubernetes deployment manifest defines behavior. None of these are identities.
-
The Dual-Nature Claim Is a Rationalization: The Round 4 Architect's claim that automations are "simultaneously identities AND resources" was an argument to avoid model change. The RUNS_AS relationship already resolves the "execution semantics" concern without misclassifying the entity type.
-
RUNS_AS Is the Bridge: The automation artifact → RUNS_AS → identity relationship gives artifacts their execution authority without making them identities. This is the correct decomposition.
15.2 Changes Required
| Component | Change |
|---|---|
NormalizedNodeType | Add automation_artifact to the union type |
NormalizedEdgeType | Add CALLS and USES_CREDENTIAL edge types |
| Connector (transformer.py) | Emit BR, SI, RM, Flow, Job as automation_artifact; emit OAuth Profile as credential |
| Platform (ingestion) | Accept automation_artifact as valid nodeType |
| Path Materializer | Follow CALLS, USES_CREDENTIAL from artifact nodes; include artifacts as entry points |
| Evaluator | Update finding generators to reference automation_artifact for automation-specific findings |
| Data Model (01-data-model.md) | Document new type with full semantic definition |
| UI | Add artifact type to Graph Explorer, Entity Detail, DataTable |
15.3 What This Does NOT Change
- The
execution_chainscollection (Round 3) remains as-is — it stores chain composition regardless of entity types. - The OAA export layer (Round 4) works better with this change — artifacts map to Resources naturally.
- The RUNS_AS relationship continues to exist — its source type changes from identity to artifact.
- The TRIGGERS_ON relationship continues to exist — same edge, different source type.
Appendix A: Complete Entity Classification Matrix
| Entity | Source System | Current nodeType | Current Subtype | Proposed nodeType | Proposed Subtype | Identity Test | OAA Mapping |
|---|---|---|---|---|---|---|---|
| Service Principal | Entra ID | autonomous_identity | service_principal | autonomous_identity | service_principal | 4/4 | Local User |
| OAuth App | Entra ID | autonomous_identity | oauth_app | autonomous_identity | oauth_app | 4/4 | Local User |
| GitHub App | GitHub | autonomous_identity | github_app | autonomous_identity | github_app | 4/4 | Local User |
| Machine Account | ServiceNow | autonomous_identity | machine_account | autonomous_identity | machine_account | 4/4 | Local User |
| System Execution | ServiceNow | autonomous_identity | system_execution | autonomous_identity | system_execution | 3-4/4 | Local User |
| PAT | GitHub/SN | autonomous_identity | pat | credential | pat | N/A | Custom Property |
| Business Rule | ServiceNow | autonomous_identity | business_rule | automation_artifact | business_rule | 0.5/4 | Resource |
| Script Include | ServiceNow | autonomous_identity | script_include | automation_artifact | script_include | 0/4 | Resource |
| REST Message | ServiceNow | autonomous_identity | rest_message | automation_artifact | rest_message | 0/4 | Resource |
| OAuth Profile | ServiceNow | autonomous_identity | oauth_profile | credential | oauth_client_config | 0/4 | Resource |
| Flow Designer Flow | ServiceNow | autonomous_identity | flow_designer_flow | automation_artifact | flow_designer_flow | 0.5/4 | Resource |
| Scheduled Job | ServiceNow | autonomous_identity | scheduled_job | automation_artifact | scheduled_job | 0.5/4 | Resource |
| Human Owner | Entra/SN | human_identity | human | human_identity | human | N/A | IdP Identity |
| Team | Entra/SN | human_identity | team | human_identity | team | N/A | Local Group |
| Business Unit | Org | human_identity | business_unit | human_identity | business_unit | N/A | N/A |
| Role | Entra/SN | role | — | role | — | N/A | Local Role |
| Permission | Entra/SN | permission | — | permission | — | N/A | Custom Permission |
| Table/API Resource | ServiceNow | resource | table/api | resource | table/api | N/A | Resource |
| Credential | Entra/SN | credential | — | credential | — | N/A | Custom Property |
| Execution Evidence | SN/Entra | execution_evidence | — | execution_evidence | — | N/A | N/A |
Appendix B: OAA Community Connector Entity Classification Evidence
B.1 GitHub (oaa_github.py)
Entities discovered:
- Org members →
add_local_user()(Local User) - Outside collaborators →
add_local_user()(Local User) - Teams →
add_local_group()(Local Group) - Repositories →
add_resource(resource_type="repository")(Resource)
What is NOT modeled:
- GitHub Actions workflows — not discovered, not modeled
- GitHub Apps — not discovered (would be Local User if modeled)
- Secrets — not discovered (would be credential if modeled)
- Deployment environments — not discovered (would be Resource)
Key evidence: Repositories are Resources. GitHub does not model any automation/workflow artifact as a Local User.
B.2 GitLab (oaa_gitlab.py)
Entities discovered:
- Users →
add_local_user()(Local User) - Bots →
add_local_user()withbot: Trueproperty (Local User) - Groups →
add_resource(resource_type="group")(Resource) - Projects →
add_sub_resource(resource_type="project")(Sub-Resource of Group)
What is NOT modeled:
- CI/CD pipelines — not discovered, not modeled
- CI/CD variables (secrets) — not discovered
- GitLab runners — not discovered
Key evidence: GitLab bots are Local Users because they authenticate (have tokens) and act (push code, merge MRs). Groups and Projects are Resources. No CI/CD artifact is modeled.
B.3 Jira (oaa_jira.py)
Entities discovered:
- Users →
add_local_user()(Local User) - Groups →
add_local_group()(Local Group) - Projects →
add_resource(resource_type="project")(Resource) - Permission schemes → mapped to roles per project
What is NOT modeled:
- Jira workflows — not discovered (would be Resource)
- Jira automations — not discovered (would be Resource)
- Webhooks — not discovered
Key evidence: Projects are Resources. Users have permissions on Projects. No workflow or automation concept is modeled.
B.4 PagerDuty (oaa_pagerduty.py)
Entities discovered:
- Users →
add_local_user()(Local User) - Teams →
add_local_group()(Local Group) ANDadd_resource(resource_type="team")(Resource)
What is NOT modeled:
- Escalation policies — not discovered (would be Resource/automation_artifact)
- Schedules — not discovered (would be Resource)
- Incident workflows — not discovered
Key evidence: Teams are simultaneously groups (for membership) and resources (for permission scoping). No automation concept is modeled. Escalation policies, which are the closest PagerDuty analog to ServiceNow Business Rules, are not modeled at all — and if they were, they would be Resources, not Users.
B.5 Bitbucket (oaa_bitbucket.py)
Entities discovered:
- Workspace members →
add_local_user()(Local User) - Groups →
add_local_group()(Local Group) - Projects →
add_resource(resource_type="Project")(Resource) - Repos →
add_sub_resource(resource_type="Repo")(Sub-Resource of Project)
Key evidence: Bitbucket follows the same pattern as GitHub. Code repositories are Resources. Users are identities. No pipelines or automation artifacts modeled.
B.6 Pattern Across All Connectors
| Local User (Identity) | Local Group | Resource | Automation Artifact as User | |
|---|---|---|---|---|
| GitHub | Members, collaborators | Teams | Repos | NEVER |
| GitLab | Users, bots | — | Groups, Projects | NEVER |
| Jira | Users | Groups | Projects | NEVER |
| PagerDuty | Users | Teams | Teams | NEVER |
| Bitbucket | Members | Groups | Projects, Repos | NEVER |
| Slack | Users, bots | User Groups | — | NEVER |
| Rollbar | Users | Teams | Projects | NEVER |
| Looker | Users | Groups | Model Sets, Models, Connections | NEVER |
The evidence is unanimous across 8 connectors spanning 8 different platforms: Automation artifacts are NEVER classified as identities (Local Users) in OAA. They are either Resources or not modeled at all.
Appendix C: Comparison with Current Model
C.1 What the Current Model Gets Right
-
Service Principals, Machine Accounts, and System Execution are correctly classified as identities. They pass the Identity Test.
-
The RUNS_AS relationship is correct in concept. The idea that automation things "run as" an identity is architecturally sound. The issue is that both the source and target of RUNS_AS are currently identities, when only the target should be.
-
The execution_mode and security_relevance properties are correct and should be preserved. These properties are meaningful for automation artifacts — they describe the artifact's behavior, not its identity.
-
TRIGGERS_ON is correct in concept. Automation artifacts are triggered by events on resources. This relationship should flow from
automation_artifact→resource.
C.2 What the Current Model Gets Wrong
-
Six automation artifacts are misclassified as identities. This is the core error.
-
The identity_type property overloads two different concepts.
identity_type: "service_principal"(a real identity type) is on the same field asidentity_type: "business_rule"(an artifact type). These are categorically different things. -
OAuth Profile is misclassified. It is authentication material (credential), not an identity.
-
The model cannot distinguish "what has execution authority" from "what is a configurable artifact." Because everything is an identity, you cannot query "show me all identities" without getting Business Rules, Script Includes, and REST Messages in the results. This dilutes the meaning of "identity" to the point of uselessness for compliance and audit purposes.
-
Blast radius calculations are inflated. Because BRs and SIs are identities with "execution paths," the blast radius includes all artifacts in the chain. In reality, the blast radius should start at the identity (the Run As target) and traverse its permissions — not at the artifact.
C.3 The Identity Count Problem
Current model: A ServiceNow instance with 15 Business Rules, 30 Script Includes, 10 REST Messages, 5 OAuth Profiles, 8 Flows, and 4 Scheduled Jobs produces 72 "identities" plus the actual identities (say 5 Service Principals + 3 Machine Accounts).
Total "identity" count: 80 Actual identity count: 8
When a CISO asks "how many non-human identities does this ServiceNow instance have?", the answer should be 8, not 80. The current model inflates the identity count by 10x with artifacts that are not identities.
Corrected model:
autonomous_identitycount: 8 (5 SPs + 3 machine accounts)automation_artifactcount: 67 (15 BRs + 30 SIs + 10 RMs + 8 Flows + 4 Jobs)credentialcount: 5 (OAuth Profiles)
The CISO gets the correct answer: 8 non-human identities, 67 automation artifacts that execute through those identities, 5 credentials that enable cross-system authentication.
Round 5 analysis complete. The entity type classification error in the current model is clear, evidence-based, and fixable. The proposed changes (adding automation_artifact type, reclassifying 6 subtypes, reclassifying OAuth Profile as credential) produce a model that is semantically correct, OAA-compatible, and consistent with how every Veza community connector classifies analogous entities. The RUNS_AS relationship remains the bridge between artifacts and identities, preserving execution path traversal. The founder's instinct was right.