Securing 2M+ accounts & $15B+ in assets, protected & secure·B5 Secure™ — per data-element authorization for .NET platforms

Decision Evidence Fabric

← Platform
Platform

A log says something happened. Evidence proves it.

An audit trail that the audited system can rewrite is not evidence. The decision evidence fabric is the difference between a record of what B5 decided and a record an examiner can verify independently.

ExplainabilitySigningHash chainsExternal verifier
Q4 2026 Roadmap ExtensionPreviewQ4 2026 Roadmap Extension

This capability is available in Preview today. The Q4 2026 roadmap extension adds the capabilities described below; production availability has not yet been declared. Both badges above are rendered from the Product Truth Registry.

The schema, and why we wrote it

Every SIEM can record who acted and whether it was allowed. None of them can record the authority under which it was allowed. We checked: the Open Cybersecurity Schema Framework has Authorize Session for session-level privilege assignment and API Activity for requests, and no class at all for a decision about an individual record.

So we wrote one. The OCSF Authority extension defines a data-element authorization decision carrying the actor, the record, the decision, the reason code, the delegation in force, the enforcement point, the aggregate limits consulted, the age of the state consulted, and whether the decision was a simulation. It also defines a profile — a mixin any vendor can apply to their own event classes to carry authority context without adopting ours.

An event stream that cannot distinguish a simulated decision from a real one will produce detections built on traffic that never happened. That is why is_shadow is in the schema and not an afterthought.

Status: Q4 2026 Roadmap Extension

One decision, both sides of the seam

An advisor with a real, unexpired grant tries to move money from an account the grant does not cover.

This is the case worth showing, because it is the one every other layer gets wrong. The caller is authenticated. The session is valid. The endpoint is permitted. A gateway sees a well-formed request from a known user and passes it. The account is simply not in the grant — and that is a fact about authority, which only the layer holding the grant can know.

A8 Core™ — the action

Submit a transfer instruction

Custody.Instructions.InstructionService.Submit is called for account AC-77310 by advisor-338, acting for user-1042 under grant grant-4471.

B5 Secure — the answer

Denied, with a reason

DENY_SCOPE_ACCOUNT_NOT_IN_GRANT. The grant covers AC-99120; the instruction targeted AC-77310. Decided in-process, before the method body ran.

The decision is emitted in the OCSF authority extension B5 authored, because the Open Cybersecurity Schema Framework has no class for “may this actor perform this action on this record, under which grant, and why”. Authorize Session is session-level; API Activity is request-shaped. Neither can carry a grant.

authorization_decision · OCSF extension “authority”
{
  "category_uid": 3,
  "category_name": "Identity & Access Management",
  "class_name": "Authorization Decision",
  "metadata": {
    "extension": { "name": "authority", "uid": "PENDING-OCSF-REGISTRATION", "version": "0.1.0" }
  },
  "time": 1785225601456,
  "activity_id": 9,
  "activity_name": "Transfer",
  "is_permitted": false,
  "reason_code": "DENY_SCOPE_ACCOUNT_NOT_IN_GRANT",
  "actor":    { "user": { "uid": "advisor-338", "name": "j.okafor" } },
  "resource": { "uid": "AC-77310", "type": "custody_account" },
  "enforcement_point": {
    "type": "in_process_method",
    "runtime": "aspnet_core",
    "name": "Custody.Instructions.InstructionService.Submit"
  },
  "delegation": {
    "uid": "grant-4471",
    "grantor": "user-1042",
    "grantee": "advisor-338",
    "purpose": "portfolio_rebalance",
    "scope": "account:AC-99120;action:transfer",
    "on_revoke_in_flight": "fail",
    "is_revoked": false
  },
  "purpose": "portfolio_rebalance",
  "policy": { "uid": "pol-cust-14", "name": "Custody Servicing", "version": "14" },
  "decision_evidence": {
    "uid": "ev-01J8ZA",
    "hash": "b7c1f0e2a9",
    "prev_hash": "4d0aa71cc3",
    "chain_uid": "chain-t-8f21c0",
    "sequence": 918234,
    "signing_key_uid": "key-2026-07-a"
  },
  "is_shadow": false,
  "state_age": 240,
  "duration": 2,
  "message": "The grant covers AC-99120; the instruction targeted AC-77310."
}

The extension identifier is deliberately unregistered. OCSF assigns each extension a reserved identifier range so two vendors cannot ship colliding schemas. Until that range is issued the sample carries PENDING-OCSF-REGISTRATION, which is not a valid integer — so this cannot be mistaken for a registered schema, including by us.

Four fields nobody writes unless they have built this

The argument for authoring the schema rather than mapping into someone else’s.

is_shadow

Was this decision real?

A simulated decision must be distinguishable from an enforced one, or shadow traffic silently poisons every detection and every metric built on the stream.

state_age

How fresh was the state?

A permit against three-second-old state is not the same permit as one against three-hour-old state. Same outcome, different assurance.

limits[]

Which aggregate was consulted?

Without it, a denial on a spending threshold is indistinguishable from a denial on scope — and the remedies are opposite.

on_revoke_in_flight

What happens to work already moving?

fail or allow_completion. It is a property of the grant, so it belongs on the record, decided when the grant was written rather than during the incident.

For a Microsoft security team

Where this lands today, and where it is lossy.

ASIM, Sentinel’s normalisation layer, has schemas for Authentication, Audit Event, DHCP, DNS, File Event, Network Session, Process Event, Registry Event, User Management and Web Session. There is no authorization-decision schema. The nearest fit is Audit Event, and it is lossy in two specific ways:

Loss 1

The reason collapses

EventResult reduces a denial to pass or fail, so the reason code — the field an analyst actually needs — survives only as free text in EventOriginalResultDetails.

Loss 2

The grant has nowhere to go

There is no field for the delegated authority in force. The one thing that distinguishes this event class from an audit line is the thing that cannot be normalised.

That is not an integration gap to be worked around with custom fields; it is a missing schema. Emitting OCSF and mapping to ASIM alongside it is the route we intend — OCSF for portability, ASIM because it is what makes the data work with Sentinel’s built-in analytics rules. The mapping notes mark every inferred field name as unconfirmed rather than presenting a half-guessed table as authoritative. Q4 2026 Roadmap ExtensionThird-Party Integration

Verify it yourself, offline

Twenty-one checks, no network, no login, nothing of ours to trust.

The evidence format is published, and the verifier is a single Python file using only the standard library. It has no B5 dependency and makes no network calls. Each record’s digest binds its own body and its position in the chain, so altering one record changes every hash after it, which moves the chain head, which breaks the signature.

the demo, in three commands
$ python3 b5verify.py ./example-export-ed25519
b5verify — ./example-export-ed25519

  ok  every record carries the required fields
  ok  every record hashes to its stated digest
  ok  the hash chain is unbroken
  ok  sequence numbers are contiguous and in order
  ok  the manifest's declared head is the last record's hash :: manifest 281e0ac6b700, actual 281e0ac6b700
  ok  the chain head is signed by the key the manifest names :: ed25519
  ok  every record states whether it was a simulation
  ok  NOTE: 1 of 5 records are shadow decisions and did not affect any response

VERIFIED — 21 checks passed.                                   # exit 0

$ sed -i 's/AC-77310/AC-99120/' example-export-ed25519/records.jsonl

$ python3 b5verify.py ./example-export-ed25519

 FAIL every record hashes to its stated digest :: [(2, 'ev-01J8ZA', 'stated 5a7c1ab486cd, computed 581ebbefff4f')]

VERIFICATION FAILED — 1 of 21 checks did not pass.              # exit 1

Two example exports ship, and the reason is not convenience. One is signed with HMAC and carries its secret in the manifest so it verifies with the standard library alone — which makes it demonstrably forgeable. The other carries an ed25519 public key only, which is what production looks like. Shipping just the convenient one while claiming signatures protect the chain would be a claim the artefact itself contradicts. When the signing library is absent the tool reports the signature check as SKIPPED, never as passed.

The format specification was attacked before it was published. Nine attempts — flipping a denial to a permit, deleting a record mid-chain, reordering two, re-chaining after a tamper, forging the head signature, stripping the shadow flag — and one of them worked on the first draft, which is how the digest came to bind the chain linkage as well as the body. A format that has not been attacked is a draft.

Scroll to Top