Skip to content

[Bug] ACS events can't fill the fields their OCSF classes require #235

Open

Description

@bar-capsule

What is broken

ocsf-mapping.json assigns every ACS event an OCSF class, and most of those classes need fields ACS events don't carry: process, device, user, src_endpoint, or one of a required set such as service / dst_endpoint. An exporter that follows the mapping has to invent these values or leave them out, and OCSF validators reject events that leave them out.

Where

specification/v0.1.0/trace/ocsf-mapping.json; docs/spec/trace/events.md; docs/spec/trace/extend_ocsf.md

What the specification says, and what happens instead

ocsf-mapping.json: "v0.1.0 conformance requires that a deployment emitting OCSF for the Trace pillar uses these class UIDs and severity rules verbatim."

events.md, under the OCSF class table: "Required class-specific attributes are populated from the ACS payload."

For most classes the payload has nothing to populate them with. What OCSF 1.9.0, the version #121 pins, requires beyond the base event:

ACS events OCSF class OCSF 1.9.0 requires What ACS lacks
toolCallRequest, toolCallResult 1007 Process Activity actor, device, process device; a tool call is not an OS process
sessionStart, sessionEnd, subagentStart, subagentStop 3002 Authentication user, plus at least one of service / dst_endpoint service and dst_endpoint; user whenever the session has none (user_identity and metadata.user_context are optional, and agentTrigger's scheduled, external_event, a2a_inbound and system triggers can start sessions with no user)
knowledgeRetrieval, memoryStore, memoryContextRetrieval, preCompact, postCompact 6005 Datastore Activity actor, src_endpoint, plus at least one of database / databucket / table src_endpoint; store and source names are optional
agbom/snapshot, agbom/changed (and skillRegister once #63 lands) 5001 Device Inventory Info device device; an AgBOM describes agent components, not a host
skillLoad, skillUnload (once #63 lands) 1005 Module Activity actor, device, module device

actor can come from metadata.agent_id, which every request carries, and so can the app that 6002 Application Lifecycle needs (prompts and turns), though the mapping doesn't say so. 2004 Detection Finding needs only finding_info, which ACS can fill.

This isn't new with the 1.9.0 pin. process, user and src_endpoint were already class-required in 1.5.0, and device was required on 1007 and 5001 whenever OCSF's host profile applied. OCSF 1.8.0 moved actor and device into the System Activity classes (1007, 1005), actor into Datastore Activity and device into Device Inventory Info, so under 1.9.0 they're required unconditionally.

The official validator (https://schema.ocsf.io/api/v2/validate) confirms it at 1.9.0. Minimal events built only from fields ACS carries fail for every class in the table. 2004 passes, and so does 6002 once the agent fills its app. Our own page fails too: the tool-call example in extend_ocsf.md is rejected with Required attribute "device" is missing., and it fills process with the tool ("name": "database_query", "cmd_line": "tools/call:database_query"). The AgBOM example fills device with the agent ("uid": "agent-123", "type": "AI Agent"). None of the three examples on that page passes; the remaining errors (type_uid, metadata.product, device.type_id) are plain example fixes.

Decisions have a related gap in our own mapping: decision_class.required_fields requires evidences and verdict_id, which OCSF only recommends, and no page says what goes in them. In OCSF, verdict_id is an analyst's verdict on a finding (False Positive, True Positive, Benign, ...). The Guardian's decision fits disposition_id (Allowed, Blocked, ...).

Impact on implementers

An OCSF exporter either leaves these fields out, and validators reject its events, or invents them. Exporters will invent differently (the tool or the agent process as process, the host or the agent as device), so the same deny reads differently in two SIEMs, and detection content written against one deployment misses the other. events.md says the Trace vocabulary "ensures cross-vendor agents and Guardians produce comparable traces"; for these classes it can't yet.

Proposed direction: for every OCSF-required field, ocsf-mapping.json states where the value comes from, either an ACS field or the runtime hosting the Observed Agent (for example device as that host and src_endpoint as its network endpoint), or the event moves to a class ACS can fill, as the #63 decision did for skillLoad. The AI Operation profile in 1.9.0 adds ai_agent, ai_model, delegation and message_context to 1007, 3002, 6005, 6002 and 1005, which gives the agent's identity a native field. The same pass can settle two class-fit questions: prompts and turns sit on 6002, which OCSF describes as "installation, removal, start, stop of an application or service", and the inventory rows could move to 5020 Software Inventory Info, which also requires device but carries an sbom object.

Related: #121 (version pin), #63 (skill classes), #228 (decision fields on the OTel span), #198 (allow class), #234 (examples on other pages).

Current Priority Scope

Feeds conformance evidence

Activity

  1. rocklambros commented on Oct 12, 2026

    @rocklambros
    Collaborator

    One more gap of the same kind, for whoever picks this up. OCSF requires activity_id on every event, and several of our rows still don't set one.

    Rows Class activity_id today
    userMessage, agentResponse, agentTrigger, turnStart, turnEnd 6002 Application Lifecycle none
    toolCallRequest, toolCallResult 1007 Process Activity none
    knowledgeRetrieval, memoryStore, memoryContextRetrieval 6005 Datastore Activity none

    #258 (for #121) adds activity_id 2 (Collect) to the three 5001 rows, because that was a clear fit. I left these alone on purpose. Picking an id for them is a class-fit call, not a rename. 6002's activities are install, remove, start, stop and similar, and none of them describes a user message or a turn. So this probably gets settled together with the 6002 question above.

    Status on two other things this issue lists:

  2. rocklambros commented on Oct 12, 2026

    @rocklambros
    Collaborator

    Status on the device gap, the one OCSF error left in our own examples after #258.

    Where it stands. After #258, the deny example (2004) and the AgBOM example (5001) validate clean at 1.9.0. The tool-call example (1007 Process Activity) still fails with one error: Required attribute "device" is missing. 1007, 1005 and 5001 all require device unconditionally since OCSF 1.8.0, and no ACS payload carries one.

    What ACS has to build device from. The request envelope's metadata has platform, platform_version and environment, but only agent_id and session_id are required. A conformant request can therefore carry nothing describing the host.

    The 5001 example's workaround. It fills device with the agent itself (uid = the agent id, type_id 99 "Other", type "AI Agent"), and the validator accepts that. It works, but it describes the agent, not a device. Two deployments could fill it differently: one with the agent, one with the host.

    OCSF's AI Operation profile (ai_operation, in 1.9.0) adds ai_agent, ai_model, delegation and message_context to these classes. That gives the agent a proper home. It does not remove the device requirement.

    Options, for a Spec lead to pick:

    1. device is the host running the Observed Agent. The mapping says so, and says to fill it from metadata.platform and environment when present, with type_id 0 (Unknown) when absent. This is honest, but the field will often be empty.
    2. device is the agent, as the 5001 example already does. The mapping makes that normative (uid = agent_id, type_id 99, type "AI Agent"), and the agent's details also go in ai_agent under the AI Operation profile. This is consistent across classes and always fillable, but it stretches what OCSF means by a device.
    3. Move tool calls off 1007 to a class ACS can fill without inventing a host. This is the bigger change, and it ties into the class-fit questions above.

    Option 2 is the smallest change that makes every example validate and gives two exporters one answer. It also matches what our 5001 example already does. Whichever option wins, the same rule applies to 1005 (skill load and unload) and 5001. The missing activity_id on the 6002, 1007 and 6005 rows (my earlier comment here) is the other half of what these rows need.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:needs-triageNot yet triaged. Applied by the issue formstype:bugSomething isn't working

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions