Public source documents
Fingerprints describe the source files at export time. They do not verify the claims or their evidence.
exchange/gates/policy.yaml
Source SHA-256: 7d7cd6cc263a1e438fde812f78808816c1d9d4ff8736a4ecf8033a326d4e8dc0
# Gate policy as code. This file is the encoding of the deployment's risk
# appetite: which claim types exist, who emits and consumes them, and what a
# promotion must clear.
#
# Changing a tier is an ADR, not a config tweak. A mounted cartridge may
# tighten a tier (verified to human), never loosen one; graph check contracts
# fails if response-request is below human (invariant I-10).
version: 1
tiers:
auto: schema, provenance, and hash checks pass
verified: auto, plus at least one receipt present and re-verifiable
human: verified, plus a named person approves (the human gate)
cells: [detection, investigation, threat-sandbox, response, detection-eng, hunt]
# Consumers outside the cell topology. detection-repo is the org's
# detection repository; PR review there is the human check at merge.
external_consumers: [detection-repo]
claims:
lead:
emitted_by: [detection, hunt]
consumed_by: [investigation]
tier: verified
tasking:
emitted_by: [investigation]
consumed_by: [threat-sandbox, detection, response, detection-eng, hunt]
tier: auto
verdict:
emitted_by: [threat-sandbox]
consumed_by: [investigation, detection-eng]
tier: verified
response-request:
emitted_by: [investigation]
consumed_by: [response]
tier: human
detection-pr:
emitted_by: [detection-eng]
consumed_by: [detection-repo] # cartridge-bound repository; PR review is the human check at merge
tier: verified
exchange/graph/schema/node-types.yaml
Source SHA-256: 09b98e6278d6d52fb901ff44a4a63333a2e986891d2ed3a7d1bf1edd2a0173ab
# The canonical graph's node kinds. Small and fixed on purpose: a compact
# schema keeps queries writable, adapters finishable, and drift visible.
# Adding a kind requires an ADR. Claims proposing undeclared kinds fail
# validation, graph check contracts, and the gate.
version: 1
kinds:
Asset:
description: A host, service, or resource. crown_jewel is a property, not a kind.
key_properties: [hostname, platform, crown_jewel]
Identity:
description: A human, service, or workload identity.
key_properties: [type, directory]
LogSource:
description: A telemetry source and its onboarding state.
key_properties: [product, onboarded, ocsf_classes]
Detection:
description: A detection rule and its lifecycle state.
key_properties: [format, status, repo_path]
Technique:
description: An ATT&CK technique reference.
key_properties: [framework]
Finding:
description: A normalized event, alert, or analysis result promoted as evidence.
key_properties: [ocsf_class, severity, verdict]
Case:
description: An investigation record; details live in the workspace, the node carries the pointer.
key_properties: [status, workspace_uri]
Function:
description: >-
A reusable automation/response function; a versioned code building block
that playbook logic invokes. Response engineering's analog of Detection.
ADR-0019.
key_properties: [language, status, repo]
exchange/graph/schema/edge-types.yaml
Source SHA-256: b07545466412e3d26c30be8ae2eeea7b022f039daa7ecf55696c8dd7784c7de7
# The canonical graph's edge kinds. Endpoints reference node kinds declared in
# node-types.yaml; graph check enforces the pairing. Adding a kind
# requires an ADR.
#
# 'exposes' exists to make the platform's founding risk path a first-class,
# queryable chain: detection gap plus unmonitored log source plus crown-jewel
# asset. Materialized chain edges keep that query cheap.
version: 1
kinds:
detects: { start: Detection, end: Technique }
monitors: { start: LogSource, end: Asset }
observed_on: { start: Finding, end: Asset }
involves: { start: Finding, end: Identity }
maps_to: { start: Finding, end: Technique }
derived_from: { start: Case, end: Finding }
exposes: { start: Technique, end: Asset }
# ADR-0019: the response-automation call/change layer beneath playbooks.
calls: { start: Function, end: Function }
modifies: { start: Case, end: Function }
exchange/claims/claim.schema.json
Source SHA-256: f7ddf0b5cdb6b0a582a53c1bfafb48b071f928382651640fe4df64c0bb028aa4
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "urn:security-graph:claim:1",
"title": "security-graph claim envelope",
"description": "The promotion envelope. Every write that crosses a gate into the exchange is exactly one claim. Normative; changes require an ADR. Free-text is confined to the length-capped 'summary' field and is treated as data by consumers, never as instructions.",
"type": "object",
"additionalProperties": false,
"required": ["claim_type", "graph_delta", "evidence", "provenance", "signature"],
"properties": {
"claim_type": {
"type": "string",
"enum": ["lead", "tasking", "verdict", "response-request", "detection-pr"],
"description": "Tier routing per exchange/gates/policy.yaml."
},
"summary": {
"type": "string",
"maxLength": 500,
"description": "The only free-text field. Length-capped; consumers treat it as data."
},
"graph_delta": { "$ref": "#/$defs/graph_delta" },
"evidence": {
"type": "array",
"minItems": 1,
"items": {
"oneOf": [
{ "$ref": "#/$defs/workspace_evidence" },
{ "$ref": "#/$defs/ocsf_evidence" }
]
}
},
"provenance": {
"type": "object",
"additionalProperties": false,
"required": ["run_id", "cell", "receipts", "created"],
"properties": {
"run_id": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-]{2,63}$",
"description": "Stable id of the Run that produced this claim, e.g. det-2026-0901-114."
},
"cell": {
"type": "string",
"pattern": "^[a-z][a-z-]{1,31}$",
"description": "Emitting cell. Gate policy checks it against the claim type's emitted_by set."
},
"receipts": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9_-]+:[A-Za-z0-9_.-]+=.+$",
"description": "system:key=value. A re-checkable pointer proving an act happened."
},
"description": "May be empty only for claims whose gate tier is 'auto'; verified and human tiers require at least one receipt (enforced by gate policy, not this schema)."
},
"created": {
"type": "string",
"format": "date-time",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$"
},
"content_trust": {
"type": "string",
"enum": ["human-reviewed", "agent-verified", "AI-RAW"],
"description": "ADR-0016. AI-RAW material is reviewed before promotion."
}
}
},
"signature": {
"type": "string",
"minLength": 1,
"description": "Detached signature over the canonical claim body under the emitting cell's identity. Verification lands with the gate runner (roadmap phase 2); the field is required now so no unsigned claim shape ever becomes load-bearing."
}
},
"$defs": {
"graph_delta": {
"type": "object",
"additionalProperties": false,
"required": ["nodes", "edges"],
"description": "OpenGraph-compatible fragment proposed for the canonical graph. Node and edge kinds must be declared in exchange/graph/schema/. Upstream BloodHound OpenGraph is authoritative for the interchange shape; conformance testing against a live BloodHound ingest is a roadmap phase 2 eval.",
"properties": {
"nodes": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "kinds"],
"properties": {
"id": { "type": "string", "minLength": 1 },
"kinds": {
"type": "array",
"minItems": 1,
"items": { "type": "string" }
},
"properties": { "type": "object" }
}
}
},
"edges": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["kind", "start", "end"],
"properties": {
"kind": { "type": "string", "minLength": 1 },
"start": { "$ref": "#/$defs/edge_endpoint" },
"end": { "$ref": "#/$defs/edge_endpoint" },
"properties": { "type": "object" }
}
}
}
}
},
"edge_endpoint": {
"type": "object",
"additionalProperties": false,
"required": ["value"],
"properties": {
"value": { "type": "string", "minLength": 1 },
"match_by": { "type": "string", "enum": ["id", "name"], "default": "id" }
}
},
"workspace_evidence": {
"type": "object",
"additionalProperties": false,
"required": ["uri", "sha256"],
"properties": {
"uri": {
"type": "string",
"pattern": "^workspace://[a-z][a-z-]{1,31}/(runs|casework)/[A-Za-z0-9._-]+/[A-Za-z0-9./_-]+$"
},
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
}
},
"ocsf_evidence": {
"type": "object",
"additionalProperties": false,
"required": ["ocsf"],
"properties": {
"ocsf": {
"type": "object",
"minProperties": 1,
"description": "An embedded OCSF object at the schema version pinned in harness/pins.lock. Structural validation against the pinned OCSF schema lands with the adapter (exchange/graph/adapters/ocsf/)."
}
}
}
}
}
harness/registry.yaml
Source SHA-256: ac32921727c64f3cda95c2775abdbe3ccc202fb39b607fb563c5d5dd8ffa6d1f
# The tool registry, canonical and machine-readable. docs/registry.md is
# generated from this file by graph registry; edit here, never there.
#
# The registry is normative (ADR-0009): every external tool gets a
# disposition, two risk scores, and a pin before an agent may call it.
# graph check enforces the schema (registry.schema.json), id
# uniqueness, and the placement rules against every cell allow-list.
#
# Scores. sc is supply-chain risk: is the code safe to run (org, license,
# install method, maintenance). pi is prompt-injection surface: are the
# tool's OUTPUTS attacker-influenceable (log lines, intel comments, case
# artifacts, malware strings all are). High-PI output is sandboxed,
# sanitized, and treated as data; high-PI tools sit only in cells without
# production write scopes (invariant I-5).
#
# Dispositions. build: no adequate external option. fork: fork and harden,
# track upstream on delay. wrap: thin adapter we own. use: pin as published.
# have: already implemented somewhere we control. integrate: external
# system, not bundled. pattern: learn from, never depend on.
#
# Status. adopted: in the plan. candidate: named, not yet vetted.
# evaluate: a decision between alternatives is pending.
#
# Capability rows (category: capability) are the org-agnostic bind points
# cell allow-lists reference; a mounted cartridge selects the concrete
# product and server for each.
version: 1
entries:
# ---- schema standards: the vocabulary layer (ADR-0004, ADR-0005) ----
- id: ocsf-schema
name: OCSF
url: https://github.com/ocsf/ocsf-schema
category: standard
disposition: use
status: adopted
sc: low
pi: none
license: Apache-2.0
license_verified: true
placement: any
notes: >-
Linux Foundation schema for events and findings. Normalization at
ingestion; observable and event vocabulary; claims embed OCSF objects
as evidence. Semver: verify the current release at schema.ocsf.io
before pinning. Adapter contract: exchange/graph/adapters/ocsf/.
- id: opengraph
name: BloodHound OpenGraph
url: https://github.com/SpecterOps/BloodHound
category: standard
disposition: use
status: adopted
sc: low
pi: none
license: Apache-2.0
license_verified: true
placement: any
notes: >-
The graph interchange. The canonical graph is assembled only from
gated OpenGraph deltas; subgraphs render in BloodHound for free.
- id: pysigma
name: Sigma / pySigma
url: https://github.com/SigmaHQ/pySigma
category: standard
disposition: use
status: adopted
sc: low
pi: low
license: LGPL-2.1
license_verified: false
placement: any
notes: >-
Detection lingua franca. Rule validation and conversion to SIEM query
languages; feeds hayabusa and chainsaw.
- id: attack-stix-data
name: MITRE ATT&CK STIX data
url: https://github.com/mitre-attack/attack-stix-data
category: standard
disposition: use
status: adopted
sc: low
pi: low
license: MITRE terms
license_verified: false
placement: any
notes: >-
Technique vocabulary. STIX data indexed and folded into the graph as
an ingestion source; coverage emitted as detects edges.
# ---- spine: what this project builds ----
- id: exchange-core
name: Context graph, gates, exchange
url: null
category: spine
disposition: build
status: adopted
sc: none
pi: none
license: Apache-2.0
license_verified: true
placement: any
notes: >-
Neo4j store plus OpenGraph interchange (ADR-0004); gate runner per
exchange/gates/. The core of the platform.
- id: ocsf-adapter
name: OCSF to OpenGraph adapter
url: null
category: spine
disposition: build
status: adopted
sc: none
pi: none
license: Apache-2.0
license_verified: true
placement: any
notes: >-
exchange/graph/adapters/ocsf/. Start with the three to five OCSF
classes the deployment actually ingests first.
- id: mcp-gateway
name: MCP gateway
url: null
category: spine
disposition: build
status: adopted
sc: none
pi: none
license: Apache-2.0
license_verified: true
placement: any
notes: >-
Thin auth-fronting MCP server with per-cell identity; the owned
enterprise seam (ADR-0007). Contract: deploy/mcp/gateway/README.md.
- id: vetting-harness
name: Vetting and packaging harness
url: null
category: spine
disposition: build
status: adopted
sc: none
pi: none
license: Apache-2.0
license_verified: true
placement: any
notes: >-
pins.lock, update queue, policy tooling; enforces this registry.
- id: cartridge-loader
name: Cartridge loader and lint
url: null
category: spine
disposition: build
status: adopted
sc: none
pi: none
license: Apache-2.0
license_verified: true
placement: any
notes: >-
docs/cartridge.md load protocol; lint shipped today as
graph check cartridge.
- id: agent-memory
name: neo4j-labs/agent-memory
url: https://github.com/neo4j-labs/agent-memory
category: spine
disposition: fork
status: adopted
sc: med
pi: low
license: Apache-2.0
license_verified: false
placement: any
notes: >-
POLE+O entity model fits security. Neo4j Labs means not commercially
supported and APIs may change: vendor a fork, self-hosted Bolt only.
- id: bloodhound
name: SpecterOps/BloodHound
url: https://github.com/SpecterOps/BloodHound
category: spine
disposition: use
status: adopted
sc: low
pi: low
license: Apache-2.0
license_verified: true
placement: any
notes: >-
Pathfinding plus OpenGraph ingestion. Rewrite neither.
# ---- infrastructure ----
- id: uv
name: uv
url: https://github.com/astral-sh/uv
category: infrastructure
disposition: use
status: adopted
sc: low
pi: low
license: Apache-2.0 OR MIT
license_verified: true
placement: any
notes: >-
Python packaging. Pinned installer; route through the org package
proxy when the cartridge defines one.
- id: beads
name: beads
url: https://github.com/gastownhall/beads
category: infrastructure
disposition: use
status: adopted
tier: tasking
sc: med
pi: low
license: MIT
license_verified: true
placement: any
notes: >-
Reference tasking backend; optional unless the deployment selects it.
SC med: active upstream, substantial embedded Dolt dependencies;
tagged source and release archive pinned independently, MIT read at
f45b249ce6b40ba62aecc03949e6371e8f7c79d8.
PI low: issue text is untrusted data, never gate authority.
v1.3.0 supports embedded single-writer operation without a server;
serialize commands, export issues for review, use Dolt backup for
full history. Tasking remains backend-agnostic.
install:
kind: github-release
source: https://github.com/gastownhall/beads/releases/download/v1.3.0/beads_1.3.0_darwin_arm64.tar.gz
version: "1.3.0"
pin: sha256:7cc77367d0b84c50243a1108bc1f73648699211257d414b917540bf868e6bb85
verify: sha256
run_as: separate-process
platforms: [macos-arm64]
size_mb: 49
notes: >-
Initial adoption, not a pin bump. macOS arm64 release only;
other platforms need their own reviewed archive pin before use.
Extract into vendor/beads, never execute a network installer.
graph install onboards only a selected private cartridge store.
Package-proxy deployments need an explicitly approved release
source or a matching verified offline archive; no mirror bypass.
- id: sops-age
name: SOPS + age
url: https://github.com/getsops/sops
category: infrastructure
disposition: use
status: adopted
sc: low
pi: low
license: MPL-2.0
license_verified: true
placement: any
notes: >-
Shared-secret layer, per-cell scoped; decrypted output never
committed. Enterprise seam is the org secrets manager (ADR-0008).
- id: opentofu
name: OpenTofu
url: https://opentofu.org
category: infrastructure
disposition: use
status: adopted
sc: low
pi: low
license: MPL-2.0
license_verified: true
placement: any
notes: >-
Default IaC for deploy/enterprise (ADR-0010).
- id: terraform
name: Terraform
url: https://github.com/hashicorp/terraform
category: infrastructure
disposition: integrate
status: candidate
sc: low
pi: low
license: BUSL-1.1
license_verified: true
placement: external
notes: >-
Not open source since 2023. Only where a downstream requires it and
license review clears the use.
- id: cloud-clis
name: aws-cli / azure-cli
url: null
category: infrastructure
disposition: use
status: adopted
sc: low
pi: low
license: Apache-2.0 / MIT
license_verified: false
placement: any
notes: >-
Pinned; org proxy where possible.
# ---- workstation tier: the operator's own shell (opt-in, never default) ----
- id: blackdot
name: blackdot
url: https://github.com/blackwell-systems/blackdot
category: infrastructure
tier: workstation
disposition: use
status: adopted
sc: low
pi: none
license: Apache-2.0
license_verified: true
placement: any
notes: >-
Dotfiles framework (Go CLI plus zsh/tmux config) with vault-backed
secret sync (1Password, Bitwarden, pass) and a `doctor` health
check. Touches only the operator's $HOME; reads no graph, no cell,
no credential the exchange sees. Provides the shell an Operator
runs the CLI from, nothing the graph depends on.
install:
kind: git
source: https://github.com/blackwell-systems/blackdot.git
version: "4.0.0"
pin: 17d9aaee4b6f986ccb835cb0670c7db46054244c
verify: commit
run_as: separate-process
platforms: [macos-arm64, macos-x86_64, linux-x86_64, linux-aarch64]
size_mb: 15
notes: >-
Pinned checkout into vendor/blackdot only. Adopting it as your shell
(BLACKDOT_DIR, ~/.zshrc symlink, brew or release binary) is a
separate, manual step; the toolkit never rewrites $HOME.
- id: onepassword-cli
name: 1Password CLI
url: https://developer.1password.com/docs/cli/
category: infrastructure
tier: workstation
disposition: wrap
status: candidate
sc: med
pi: none
license: Proprietary
license_verified: false
placement: external
notes: >-
Optional preinstalled op executable for reference-only, explicitly
approved runtime credentials; no installer, vault sync, or Blackdot
dependency. Human desktop and explicit service-account modes remain
separate. The initial consumer supports development MCP discovery;
source-system writes and production execution require separate gates.
Review the vendor source and release before installation; never bundle
unverified binaries. See docs/onepassword.md.
# ---- capabilities: org-agnostic bind points allow-lists reference ----
- id: siem
name: SIEM query surface
url: null
category: capability
disposition: integrate
status: adopted
sc: none
pi: med
license: n/a
license_verified: true
placement: any
notes: >-
Bound by the cartridge to a concrete product and MCP server. For
Splunk, both an official in-instance app (honors RBAC) and community
servers exist; run a coverage diff and record the call in the
cartridge. Read-only in every cell that carries it.
- id: edr
name: EDR read surface
url: null
category: capability
disposition: integrate
status: adopted
sc: none
pi: med
license: n/a
license_verified: true
placement: any
notes: >-
Bound by the cartridge. Concrete public option for CrowdStrike:
falcon-mcp (own row). Read scope only.
- id: ti-enrichment
name: Threat-intel enrichment
url: null
category: capability
disposition: integrate
status: adopted
sc: none
pi: high
license: n/a
license_verified: true
placement: low-privilege
notes: >-
VT-class lookups. Public comment fields are attacker-writable:
whatever server the cartridge selects, its output is hostile data,
its deps are pinned, and it sits only in cells without production
write scopes.
- id: scm
name: Source-control API
url: null
category: capability
disposition: integrate
status: adopted
sc: none
pi: low
license: n/a
license_verified: true
placement: any
notes: >-
Branch and PR operations for PR-driven flows (detection-pr). Merge
remains a human act in the target repo.
- id: identity-graph
name: Directory / cloud-identity read
url: null
category: capability
disposition: integrate
status: candidate
sc: none
pi: med
license: n/a
license_verified: true
placement: any
notes: >-
Read-scoped credentials only. Concrete public option for Microsoft
Graph: lokka (own row).
- id: knowledge-base
name: Wikis and document stores
url: null
category: capability
disposition: integrate
status: candidate
sc: none
pi: med
license: n/a
license_verified: true
placement: any
notes: >-
Pages are provenance sources, never instructions; harvested content
is AI-RAW until reviewed (ADR-0016). Read scope by default. Design
D-5 in docs/design/toolkit.md; the first bound server is
microsoft-learn (own row).
# ---- integrations catalog: concrete public servers a cartridge may select ----
- id: falcon-mcp
name: CrowdStrike falcon-mcp
url: https://github.com/CrowdStrike/falcon-mcp
category: integration
tier: integrations
disposition: use
status: candidate
sc: med
pi: med
license: MIT
license_verified: true
placement: low-privilege
binds: edr
notes: >-
MIT LICENSE read at v0.19.0, commit
17fea062558312dfbf981ea09f7ce58a11ea31fb. SC medium: maintained by
CrowdStrike but public preview, with a multi-package Python dependency
chain and open version ranges. PI medium: host and alert text can be
attacker-influenced. Investigation cell, read scope only; never a cell
with production write scopes. Use the upstream --read-only filter plus
an exact --tools list and read-scoped API credentials; modules alone
also expose mutations. Client credentials are not delegated user SSO.
Initial pin and recipe are proposed through
harness/update-queue/falcon-mcp-2026-09-16.md; no runtime approval.
install:
kind: git
source: https://github.com/CrowdStrike/falcon-mcp.git
version: "0.19.0"
pin: 17fea062558312dfbf981ea09f7ce58a11ea31fb
verify: commit
run_as: separate-process
notes: >-
Checkout only into vendor/falcon-mcp, after delayed review approval.
Does not install Python dependencies, authenticate, or start a server.
Runtime provisioning must retain the upstream dependency lock and
deployment package-sourcing policy; never use uvx or an unpinned image.
Enable only for an explicitly approved read-only session, dev first.
- id: lokka
name: Lokka
url: https://github.com/merill/lokka
category: integration
tier: integrations
disposition: use
status: candidate
sc: med
pi: med
license: MIT
license_verified: true
placement: low-privilege
binds: identity-graph
notes: >-
MIT LICENSE read at commit b3790f7d3fdb8636703de82f88e74f9e822a099c,
main on 2026-06-19, the merge of the SSRF and token-exfiltration fix
in pull request 47. SC medium: one maintainer, no tags or GitHub
releases, and npm 2.1.2 declares a gitHead absent from the public
repository, so only a public commit is pinnable and the npm tarball is
never fetched. PI medium: directory attributes, sign-in and audit text
can be attacker-influenced. One generic Lokka-Microsoft tool forwards
any Graph or Azure RM path with get, post, put, patch or delete, and
add-graph-permission requests further scopes: read-only is a property
of the principal's granted permissions, never of the tool. Read-scoped
cells only; an app-only or delegated principal holding read
permissions alone and no Azure RBAC role; never the maintainer's
default multi-tenant client id. Initial pin and recipe are proposed
through harness/update-queue/lokka-2026-09-16.md; no runtime approval.
install:
kind: git
source: https://github.com/merill/lokka.git
version: "0.3.0-untagged"
pin: b3790f7d3fdb8636703de82f88e74f9e822a099c
verify: commit
run_as: separate-process
notes: >-
Checkout only into vendor/lokka after delayed review approval. Does
not run npm install, build, authenticate, or start a server; a build
must resolve dependencies through the deployment package-sourcing
policy with a lockfile, never npx against the public registry.
Enable only for an explicitly approved read-only session against a
non-production principal first.
- id: recorded-future-mcp
name: Recorded Future MCP
url: https://support.recordedfuture.com/hc/en-us/articles/51803098002835
category: integration
disposition: integrate
status: candidate
sc: med
pi: high
license: commercial subscription; pilot terms
license_verified: false
placement: low-privilege
binds: ti-enrichment
notes: >-
Vendor-hosted server over the Intelligence Cloud, offered as a pilot
programme to customers. The article is behind the customer login,
so the endpoint and tool list are recorded by a person with pilot
access through graph tools discover, never pasted from memory.
Intelligence text, references, and analyst notes are
attacker-influenceable: high PI, low-privilege cells only, output
sanitized. Read scope only; no server-side writes are bound.
- id: microsoft-learn
name: Microsoft Learn MCP
url: https://learn.microsoft.com/api/mcp
category: integration
disposition: integrate
status: candidate
sc: low
pi: med
license: Microsoft terms of use
license_verified: false
placement: any
binds: knowledge-base
notes: >-
Vendor-hosted, unauthenticated documentation search and fetch. Its
tool list is public, which makes it the reference target for
testing graph tools discover. Page content is evidence, not
instruction.
- id: soar-fork
name: Hardened SOAR MCP fork
url: null
category: integration
disposition: fork
status: evaluate
sc: high
pi: med
license: TBD by upstream choice
license_verified: false
placement: any
notes: >-
The response actuator. Fork one public candidate, strip to read plus
a single pipeline-trigger tool, sanitize output (ADR-0013); that
narrowed surface is what the med PI scores. Upstream candidates for
Splunk SOAR, all unvetted and high PI as published: huseynAgazade
(broadest, ~70 tools), Ewiges-M (playbook lifecycle), abuis78
(per-tool enable, closest to read-only-by-default).
- id: mcp-for-splunk
name: deslicer/mcp-for-splunk
url: https://github.com/deslicer/mcp-for-splunk
category: integration
disposition: pattern
status: adopted
sc: med
pi: med
license: Apache-2.0
license_verified: false
placement: external
notes: >-
Reference only.
- id: telemetry-pipelines
name: Telemetry pipelines and proxies
url: null
category: integration
disposition: integrate
status: adopted
sc: none
pi: none
license: n/a
license_verified: true
placement: ingestion-only
notes: >-
Cribl-class pipelines and secure web gateways integrate at ingestion
as OCSF sources, never as agent tools.
# ---- incident-response lane ----
- id: hayabusa
name: hayabusa
url: https://github.com/Yamato-Security/hayabusa
category: ir
disposition: use
status: adopted
sc: low
pi: med
license: AGPL-3.0
license_verified: false
placement: any
notes: >-
Rust EVTX forensics, Sigma-compatible. Run as a separate program;
never linked into distributed binaries (compliance/licenses.md).
- id: chainsaw
name: chainsaw
url: https://github.com/WithSecureLabs/chainsaw
category: ir
disposition: use
status: adopted
sc: low
pi: med
license: GPL-3.0
license_verified: false
placement: any
notes: >-
Sigma hunting over event logs; shared with detection engineering.
- id: deepbluecli
name: DeepBlueCLI
url: https://github.com/sans-blue-team/DeepBlueCLI
category: ir
disposition: use
status: candidate
sc: low
pi: med
license: GPL-3.0
license_verified: false
placement: any
notes: >-
PowerShell event-log analysis.
- id: kansa
name: kansa
url: https://github.com/davehull/kansa
category: ir
disposition: wrap
status: evaluate
sc: med
pi: med
license: Apache-2.0
license_verified: false
placement: any
notes: >-
Older IR collection; wrap and pin, or replace the role.
- id: timesketch
name: Timesketch
url: https://github.com/google/timesketch
category: ir
disposition: use
status: candidate
sc: low
pi: med
license: Apache-2.0
license_verified: false
placement: any
notes: >-
Collaborative timelines; matches the collaboration goal.
- id: ghidra-mcp
name: GhidraMCP
url: https://github.com/LaurieWired/GhidraMCP
category: ir
disposition: fork
status: candidate
sc: med
pi: high
license: Apache-2.0
license_verified: false
placement: detonation-only
notes: >-
Malware strings and decompiled code are hostile input. Detonation
cell only (invariants I-5, I-9).
- id: wtfis
name: wtfis
url: https://github.com/pirxthepilot/wtfis
category: ir
disposition: wrap
status: candidate
sc: med
pi: med
license: MIT
license_verified: false
placement: any
notes: >-
Single-pane enrichment pivot; wrap or fork, never rebuild.
- id: playwright-mcp
name: playwright-mcp
url: https://github.com/microsoft/playwright-mcp
category: ir
disposition: use
status: adopted
sc: low
pi: high
license: Apache-2.0
license_verified: true
placement: detonation-only
notes: >-
Live pages are hostile. Sandbox and controlled-egress cells only.
# ---- threat-hunting lane ----
- id: bloodhound-mcp-ai
name: BloodHound-MCP-AI
url: https://github.com/MorDavid/BloodHound-MCP-AI
category: threat-hunting
disposition: fork
status: evaluate
sc: med
pi: med
license: MIT
license_verified: false
placement: any
notes: >-
Re-evaluate against a thin adapter of our own once the OpenGraph
adapters exist; one of the two survives.
- id: misp
name: MISP
url: https://github.com/MISP/MISP
category: threat-hunting
disposition: integrate
status: candidate
sc: low
pi: med
license: AGPL-3.0
license_verified: false
placement: external
notes: >-
Later-phase intel feed; STIX via MISP if a threat-intel lane opens.
- id: ctinexus
name: CTINexus
url: https://github.com/peng-gao-lab/CTINexus
category: threat-hunting
disposition: pattern
status: adopted
sc: med
pi: med
license: MIT
license_verified: false
placement: external
notes: >-
Learn the LLM entity-extraction approach; never depend on research
code.
- id: cartography
name: Cartography
url: https://github.com/lyft/cartography
category: threat-hunting
disposition: pattern
status: evaluate
sc: low
pi: low
license: Apache-2.0
license_verified: true
placement: any
notes: >-
Neo4j asset-context ingestion (cloud and EDR modules). Promote from
pattern to candidate ingestion engine now that the store decision
(ADR-0004) names Neo4j.
# ---- validation ----
- id: atomic-red-team
name: Atomic Red Team
url: https://github.com/redcanaryco/atomic-red-team
category: validation
disposition: use
status: adopted
sc: low
pi: low
license: MIT
license_verified: true
placement: detonation-only
notes: >-
Emulate, detection fires, verdict carries the receipts.
- id: atomic-red-team-mcp
name: atomic-red-team-mcp
url: https://github.com/cyberbuff/atomic-red-team-mcp
category: validation
disposition: fork
status: candidate
sc: med
pi: med
license: MIT
license_verified: false
placement: detonation-only
notes: >-
Executes techniques: fork, pin, explicit atomic allow-list,
detonation cell only.
- id: caldera
name: Caldera
url: https://github.com/mitre/caldera
category: validation
disposition: integrate
status: evaluate
sc: low
pi: med
license: Apache-2.0
license_verified: false
placement: detonation-only
notes: >-
Scenario chaining against a second emulation system: decide, or
demote to pattern.
- id: ocsf-simulator
name: OCSF-Simulator
url: https://github.com/timeplus-io/OCSF-Simulator
category: validation
disposition: pattern
status: candidate
sc: med
pi: low
license: Apache-2.0
license_verified: false
placement: any
notes: >-
Synthetic OCSF event streams for evals and ingestion tests. Vet
before any dependency.
- id: cyberbattlesim
name: CyberBattleSim
url: https://github.com/microsoft/CyberBattleSim
category: validation
disposition: pattern
status: adopted
sc: low
pi: low
license: MIT
license_verified: true
placement: external
notes: >-
Adversary-sim reference for evals fixtures and attack-path modeling;
quiescent research code, never a dependency.
# ---- content patterns for lanes ----
- id: adk-runbooks
name: ADK Runbooks
url: https://github.com/dandye/adk_runbooks
category: content
disposition: pattern
status: candidate
sc: med
pi: high
license: MIT
license_verified: false
placement: external
notes: >-
A manager-orchestrated security agent team on Google's ADK: tiered
SOC analysts, hunter, CTI researcher, responder, detection engineer,
and an LLM judge, with sixty-plus SKILL.md procedures loaded by
progressive disclosure, dynamic MCP tool discovery, and a
rubric-scored eval harness over dozens of end-to-end workflows.
Complementary, not competing: it supplies agents and procedures, we
supply the memory and the boundary. The seams, in order: its
sub-agents run inside one cell each and never across cells (I-2);
its skills overlay runbooks/ through a cartridge (frontmatter
validated, org content private); its MCP meta-tools reach only what
the hosting cell's allow-list permits through deploy/mcp/gateway (I-5); its
reports become claims with receipts or stay in the cell's
workspace; its rubrics score Runs. PI is high because its agents
read attacker-writable text (alert bodies, TI comments, case notes)
by design, so it hosts in low-privilege cells only. Vet the pip
dependency set and the Google SecOps servers separately before any
runtime dependency; the adapter lands with the first cell pair.
- id: athf
name: Agentic Threat Hunting Framework
url: https://github.com/Nebulock-Inc/agentic-threat-hunting-framework
category: content
disposition: pattern
status: candidate
sc: med
pi: low
license: MIT
license_verified: false
placement: external
notes: >-
The LOCK hunt loop (Learn, Observe, Check, Keep) parallels the Run;
hunt-file format and hypothesis loop harvest into lanes/hunt.
Framework markdown is low-risk to mine; vet the pip package and MCP
server separately before any dependency.
- id: nebulock-ade
name: Agentic detection engineering framework
url: https://github.com/Nebulock-Inc
category: content
disposition: pattern
status: evaluate
sc: med
pi: low
license: MIT
license_verified: false
placement: external
notes: >-
ATHF's detection-engineering sibling: durable memory plus a
five-stage lifecycle. Evaluate for lanes/detection-eng.
- id: joern
name: Joern
url: https://github.com/joernio/joern
category: content
disposition: pattern
status: candidate
sc: low
pi: low
license: Apache-2.0
license_verified: true
placement: external
notes: >-
Code-property-graph reference, held for a future code lane.
README.md
Source SHA-256: 735a201b3b40905fde8bfe591e9c6521fde7506b52aba5b4d3232ee2f860b9dc
<p align="center">
<img src="docs/assets/security-graph-header.svg" width="100%" alt="security-graph: six sealed security cells exchange typed claims through a policy gate. Contracts-first, self-hostable, pre-runtime.">
</p>
<h3 align="center">Your security context as a graph. Agents work it. People gate it. Every answer carries its receipts.</h3>
<p align="center">
<a href="#one-command">Install</a> /
<a href="docs/start-here.md">Start here</a> /
<a href="#the-toolchain-it-ships-with">Toolchain</a> /
<a href="docs/architecture.md">Architecture</a> /
<a href="#a-visual-first-look">Visual guide</a> /
<a href="docs/roadmap.md">Roadmap</a>
</p>
A security graph answers three questions tabular tools cannot: **which
paths lead to what matters**, **what else falls if this one thing is
compromised**, and **is this alert routine or the middle of something**.
Microsoft, Datadog, and Illumio sell that as a feature of their cloud.
security-graph is the same idea for the network you actually have, with
two differences that matter more than the graph itself:
- **Agents are the operators.** GitHub Copilot CLI, Claude Code, or an
ADK agent team read the graph, run typed procedures against it, and
return claims, not chat. The graph is the memory they ground in.
Today they maintain this tree through its skills, run its checks, and
file its vault; operating the graph is what the first cell pair
proves.
- **Evidence is the currency.** Nothing enters the graph without
provenance; nothing leaves a Run without a receipt someone else can
re-check; nothing touches production without a gate a person holds.
A hijacked agent can emit garbage claims, and nothing else.
It runs on a laptop, a homelab box, or a SOC's compose host with the same
contracts, and it knows nothing about your organisation until you mount
a private cartridge. Adopt it anywhere; the agents behave the same way
everywhere.
Where it stands, in one sentence: the contracts, the installer, the
check suite, the cartridge seam, and the toolchain work today and are
machine-enforced; the running graph store and the gate runner are the
next phase, and nothing here pretends otherwise.
```
ask -> ground -> act -> verify -> commit
```
## Three questions, as graph queries
| Question | Edges it walks | What you get back |
|---|---|---|
| **Attack path.** Which techniques reach this asset, and does anything detect them? | `Technique exposes Asset`, `Detection detects Technique`, `LogSource monitors Asset` | the route, the detections on it, and the hop where your telemetry goes dark |
| **Blast radius.** This identity is compromised. What does it touch? | `Finding involves Identity`, `Identity` to `Asset` reach, `Case modifies Function` | the assets, findings, and open cases that share the identity, ranked by what is monitored |
| **Contextual detection.** Is this alert alone? | `Detection` to its `Technique`, then to `Finding` and `Case` neighbours | the alert placed among what fired before, what was already investigated, and what an adversary would do next |
The vocabulary is small on purpose: eight node kinds, nine edge kinds,
in `exchange/graph/schema/`. Exchange with BloodHound is native through
OpenGraph; events arrive as OCSF; detections are Sigma; techniques are
ATT&CK. Nothing here is a term one of those already defines.
## What makes it different
Plenty of projects give you security agents. Plenty of vendors give you
a graph of their own telemetry. This one gives agents somewhere to stand
and something they cannot do, on data you own.
- **Public core, private cartridge.** This repository knows nothing
about you. Your SIEM, approvers, decisions, and evidence live in a
private cartridge that mounts at install time and is never committed
here; `graph check leaks` proves it before every push.
- **Compromise contained by construction.** Agents run in sealed cells
that never talk to each other. Only schema-validated claims cross
policy gates. The human tier cannot be lowered by configuration.
Prompt injection is a containment problem here, not a prompting one.
- **A gate on the write, not on the transcript.** Guardrail products
police what an agent says and which tools it calls. This gate sits
where the damage would land: nothing changes the record until policy
passes the delta, free text in a claim is one length-capped field
treated as data, and production actions wait for a person. Each claim
carries a signature slot the gate runner will verify; until it lands,
the claims are typed and checked, not yet cryptographically signed.
- **Receipts, not answers.** A detection claim arrives with the SIEM
search id, the sandbox detonation record, and the PR URL, each
re-checkable without the emitting agent's help. Anything without a
receipt is visibly unverified.
- **A scored toolchain, not a vendor's.** Fifty-two open-source tools,
standards, and servers are scored for supply-chain and
prompt-injection risk, SHA-pinned, and placed where their risk allows.
High-injection tools never share a cell with production write scopes;
a check enforces it. Agents see tools one line at a time, bounded by
the cell, and open a schema only when they are about to call.
- **One command, machine-enforced.** What is not a check does not exist
as process. `graph check` runs everything CI runs; the layout table,
the invariants, the fixtures, and the pins are all tests.
## One command
```console
$ mkdir -p ~/Graph && cd ~/Graph
$ git clone https://github.com/<owner>/security-graph
$ cd security-graph
$ ./graph install
```
`./graph` builds `.venv`, installs this checkout into it, and runs the
install steps: machine, contracts, cartridge, toolkit, optional tasking,
agents, connections, exchange, command, hooks. Each says what it found, what it
did, and what it left for you. Every prompt has a default; nothing is
fetched that was not listed first and SHA-pinned. The `command` step
links `graph` into `~/.local/bin`, so from here on it is `graph <verb>`
from any directory. The cartridge step also wires `~/Graph` as the
shared agent working folder: platform instructions and skills come
from this checkout; private context and skills come from the selected
cartridge. Nothing private is copied into public files. Then:
```console
$ graph open # the visual guide, in your browser; works offline
$ graph help # a short starting point, not the whole reference
$ graph copilot # Graph workspace; Fable 5.1, xhigh, allow-all, autopilot
$ graph jit TOKEN -- cmd # one credential, one process: hidden prompt, op://, blackdot://
$ graph tour # one claim, one gate, one break, in five minutes
$ graph doctor # re-check everything, read-only
$ graph check # what CI runs: contracts, leaks, cartridge, feedback
$ graph tools # choose by purpose: learn, investigate, detect, explore, connect
$ graph tools plan investigate # preview the tools for one job; changes nothing
$ graph tools catalog --cell hunt # what an agent in that cell may see, one line per tool
$ graph workspace status # the actual platform, cartridge, and agent skill wiring
```
Start Copilot CLI or Claude Code in **`~/Graph`**, not just inside the
public checkout. The installer creates `AGENTS.md`, `CLAUDE.md`, and
native skill links there. An existing or edited file is protected, not
overwritten. A checkout elsewhere can use the same home workspace;
`--workspace PATH` chooses another dedicated working folder.
`--yes` runs unattended, `--step` confirms each change, `--dry-run`
shows the plan and writes nothing. Python 3.11 is the only requirement.
[docs/quickstart.md](docs/quickstart.md) has the flags, the toolkit
tiers, and how to point an agent at the tree.
[`graph copilot`](docs/copilot.md) is an opt-in launcher with explicit
startup defaults, a quick `--model astra` switch, and setup instructions
for macOS, Linux, and native Windows.
## A visual first look
**`graph open` puts the front door in your browser.** Follow an example
through ask, ground, act, verify, commit; select a node to inspect its
properties; open a receipt's source; find tools by purpose; see what
each cell may access and where a person must decide.
It is one self-contained HTML file, with no server, account, external
fonts, or network requests. The terminal stays underneath: every
surface gives you its command. `graph export site --output guide.html`
makes the same portable artifact without opening a browser, and
`graph publish cloudflare --hostname guide.example.org` puts it on a
hostname you own: public example data only, hash-verified after upload
([deploy/cloudflare](deploy/cloudflare/README.md)).
The **Setup** page lets you name your workspace, approver, and systems,
then download private setup answers. Run the command it provides to
create and connect your cartridge. The browser and
`graph cartridge init` share the same questions; neither invents
approvals or enables a connection.
This is a **public-example snapshot**, not the live console. Contract
checks run during export, but example evidence and signatures remain
visibly unverified. Nothing reads your private cartridge or local
credentials; nothing approves, executes, or writes to the graph.
Setup now starts with the work: `graph install --purpose investigate`,
for example. `--purpose learn` needs no additional tools. The five
purposes share one catalog between the browser and terminal; expert
tiers remain available through `graph tools list --all` and `--tools`.
## Pick your door
| | Start with | You will see |
|---|---|---|
| **I triage alerts** | `graph open`, then `graph tools plan investigate` | an example claim, its evidence trail, and the policy before an action |
| **I write detections** | [exchange/graph/](exchange/graph/README.md) and the Sigma rows in the [registry](docs/registry.md) | detections as nodes with `detects` edges to ATT&CK; coverage as a query |
| **I run a homelab** | `graph install --profile workstation`, then [deploy/](deploy/README.md) | the same graph over your own hosts, identities, and logs, with no cloud account anywhere |
| **I run a team** | [docs/architecture.md](docs/architecture.md) and the cell table below | the boundary drawn: what each cell holds, and its worst case if lost |
| **I am evaluating this** | `graph open` | a visual walkthrough and the boundary between examples and the unfinished runtime |
## The toolchain it ships with
security-graph competes with none of these. It gives them a shared
memory and a boundary. Every row is an entry in
[harness/registry.yaml](harness/registry.yaml) with a supply-chain and
prompt-injection score, a disposition, and a placement rule; the seam
column says where it attaches. `graph tools list` shows the same table
from the shell.
| For | Tools | What the graph adds | Seam |
|---|---|---|---|
| **Seeing the graph** | [BloodHound CE](https://github.com/SpecterOps/BloodHound), Neo4j, [BloodHound-MCP-AI](https://github.com/MorDavid/BloodHound-MCP-AI) | the canonical graph exported as OpenGraph, so attack paths render in a browser people already trust | `exchange/graph/`, ADR-0004 |
| **Detecting** | [Sigma / pySigma](https://github.com/SigmaHQ/pySigma), [MITRE ATT&CK](https://github.com/mitre-attack/attack-stix-data), [OCSF](https://github.com/ocsf/ocsf-schema) | Detection nodes with `detects` edges; coverage gaps as a saved traversal; `detection-pr` claims that land as reviewed PRs | the detection-eng cell; `docs/lanes.md` |
| **Proving detections work** | [Atomic Red Team](https://github.com/redcanaryco/atomic-red-team), [Caldera](https://github.com/mitre/caldera), [ATHF](https://github.com/Nebulock-Inc/agentic-threat-hunting-framework) | a zero-credential sandbox cell that emits verdicts only; hunts as Runs with receipts; emulation results as `Finding` nodes against the `Technique` they tested | the threat-sandbox and hunt cells |
| **Investigating** | [hayabusa](https://github.com/Yamato-Security/hayabusa), [chainsaw](https://github.com/WithSecureLabs/chainsaw), [Timesketch](https://github.com/google/timesketch), [wtfis](https://github.com/pirxthepilot/wtfis), [DeepBlueCLI](https://github.com/sans-blue-team/DeepBlueCLI) | forensic output filed as claims against `Asset` and `Identity`, so a timeline and a case share one graph | the investigation cell |
| **Running agents** | [adk_runbooks](https://github.com/dandye/adk_runbooks), GitHub Copilot CLI, Claude Code | the org graph to ground in, claims and receipts for what they produce, a gate before anything touches production, and a cartridge so org knowledge stops living in prompt text | one cell per sub-agent; skills overlay `runbooks/`; `graph agent install` |
| **Reaching systems** | MCP servers: [falcon-mcp](https://github.com/CrowdStrike/falcon-mcp), [Lokka](https://github.com/merill/lokka), [mcp-for-splunk](https://github.com/deslicer/mcp-for-splunk), your own | per-cell allow-lists, disabled-by-default manifests, JIT credentials, and placement rules a check enforces | `deploy/mcp/`, invariant I-5 |
| **Threat intel** | [MISP](https://github.com/MISP/MISP), [Cartography](https://github.com/lyft/cartography), [CTINexus](https://github.com/peng-gao-lab/CTINexus) | enrichment as gated graph deltas with provenance, never as free text | the hunt cell |
The pairing that matters most today is adk_runbooks. It is rich in
agents and procedures, with sixty-plus `SKILL.md` runbooks under
progressive disclosure and rubric-scored evals, and thin on
organisation memory: its org knowledge lives in persona prompts and its
outputs are reports. security-graph is the reverse. Together, its
agents get somewhere to ground and somewhere to commit, and their
reports become claims a gate can check.
[docs/design/adk-gap.md](docs/design/adk-gap.md) is the honest
comparison, including what we take from it next.
## Status
| Works today, machine-enforced | Being built, specified next to where it lands |
|---|---|
| claim schemas with valid and invalid fixtures | the gate runner and graph store, roadmap phase 2 |
| gate policy as code; six cell definitions; the graph kind set | install recipes for the rest of the registry; the schema and first pinned row landed with ADR-0025 |
| the scored tool registry with SC and PI scores and placement rules | automated cartridge pin loading and the extended authoring lifecycle |
| Graph-root agent context and a linted private starter cartridge from CLI or browser setup answers | runtime-backed cartridge editing in the live console |
| the cartridge interface, its lint, and the worked example with its landing page | the live console, after the graph runtime: ADR-0021 |
| `graph open` and `graph export site`: offline visual examples, sources, toolkit, and boundaries | live Runs, gate queues, and approvals; [portal options](docs/design/portal-options.md) |
| `graph`: install, doctor, check, tools, agent, connect, feedback, and a check suite CI runs on every change | the first cell pair end to end with real receipts, phase 3 |
> [!IMPORTANT]
> **The contracts and the command work today; the graph runtime is next.**
> This is not a production-ready security automation service. Integrations
> stay disabled until selected, pinned, and given appropriate scopes.
> [docs/roadmap.md](docs/roadmap.md) and [docs/plans/](docs/plans/README.md)
> say what comes next and why.
## How it fits together
Humans see a pipeline; the trust topology is hub-and-spoke. Cells never
talk to each other. They publish typed claims through gates into a
shared exchange, and the next cell picks work up from there.
```
detection ─┐ ┌─ response, human-gated
hunt ──────┤ │
├─ gate ─> EXCHANGE ── gate ─> ────┤
detection- │ canonical graph + tasking │
eng ───────┘ append-only, gated writes └─ investigation ─> threat-sandbox
```
| Cell | Holds | Emits | Worst case if fully compromised |
|---|---|---|---|
| detection | read-only SIEM | leads | bogus leads that die at the verified gate |
| investigation | EDR read, case write | tasking, response-requests | polluted casework in its own workspace |
| threat-sandbox | **zero credentials** | verdicts only | garbage verdicts; nothing else reachable |
| response | SOAR read + pipeline trigger | action receipts | blocked by the human gate |
| detection-eng | detection repo write via PR | detection-prs | a bad PR; merge review is the gate |
| hunt | graph read, enrichment | leads | same as detection |
How people will see all of this, the Runs, the gate queue, the graph,
and the cells, is the console: [docs/design/console.md](docs/design/console.md),
with the alternatives weighed in
[docs/design/portal-options.md](docs/design/portal-options.md).
## Adapt it to your org: the cartridge
This is the reusable core. Your SIEM and EDR selections, package proxy,
approvers, and current integrations arrive in a **cartridge**, resolved
at install time and never committed here.
| | Core, this repository | Cartridge, yours |
|---|---|---|
| Contains | code, contracts, registry, charters, deploy profiles, the example | org naming, current integrations, approver identity, private evidence, org decisions, org skills |
| Distribution | public | private to your org |
| Contracts | defines them | parameterises only; may tighten, never weaken |
Start with **`graph cartridge init`**, or **Setup** in `graph open`.
The builder creates a separate Git repository, lints it, and selects
it for your Graph workspace. It creates no remote, copies no fictional
cases or decisions, and never replaces an existing directory. Agents
can use the same command unattended with `--org`, `--gate`, and
`--yes`. Already have a cartridge? Use
`graph workspace init --cartridge /path/to/your-cartridge`.
[cartridges/example/](cartridges/example/README.md) remains the
complete fictional reference, including the fuller landing-page
pattern. `graph check leaks` is the structural and
private-marker proof before publishing; it does not replace review of
content or history. The interface is [docs/cartridge.md](docs/cartridge.md).
## Improve it by using it
A deployment should improve the next person's experience, not just
accumulate private notes. `graph feedback` records deploy, test, and
share cycles and routes each to a core change, a cartridge-only change,
a follow-on task, or a reasoned no-change. Core changes need regression
artifacts. `graph check` rejects unresolved cycles. Evidence stays
private; fixes come back here. See [docs/feedback.md](docs/feedback.md).
## Repository map
```
exchange/ claim schemas + fixtures, gate policy, graph kinds, OCSF adapter contract, tasking
cells/ six sealed cells + template; four YAML artifacts each
runbooks/ typed procedures per cell, personas, and skills/ for agent CLIs
harness/ the scored registry, dependency pins, the update queue, leak-marker policy
deploy/ workstation / compose / cloudflare / enterprise bindings; mcp/ gateway and manifests
cartridges/ the worked example cartridge, with its landing page
docs/ architecture, spec, roadmap, designs, ADRs, agent charters, compliance, glossary
src/ security_graph/, the graph command; checks/ is the enforcement code
workspace/ the local data plane contract; runtime contents never committed
```
The map is enforced: `graph check` fails on a tracked top-level
directory that AGENTS.md does not declare.
## Documentation
[docs/README.md](docs/README.md) is the index. For people:
[start here](docs/start-here.md) -> [quickstart](docs/quickstart.md) ->
[glossary](docs/glossary.md) -> [architecture](docs/architecture.md) ->
[cartridge interface](docs/cartridge.md). For contributors and agents:
[AGENTS.md](AGENTS.md) -> [the contract](docs/spec.md) ->
[ADRs](docs/adr/README.md) -> [registry](docs/registry.md). Where the
tree stands and what is next: [plans](docs/plans/README.md),
[designs](docs/design/README.md), and the
[landscape](docs/research/landscape.md) of what else exists.
## Contributing
Contributions welcome, from humans and from agents under review; see
[.github/CONTRIBUTING.md](.github/CONTRIBUTING.md). Contracts before
cells, every change keeps `graph check` green, schema and policy changes
need an ADR, and nothing merges unverified.
## License
[Apache-2.0](LICENSE). Bundled or vendored tools keep their own
licenses, tracked per entry in the registry and
[docs/compliance/licenses.md](docs/compliance/licenses.md).
cells/detection/identity.yaml
Source SHA-256: 60d44bdd5430b4f7d1ff8b6246465ed5d521373296718a0713dd45c6727f406a
# detection: workload identity and credential surface (invariant I-6 lives here).
# Local profiles: JIT injection per session. Enterprise: a per-cell workload
# identity in the org IdP; the secrets manager issues only what is named
# below, by name, never by value.
cell: detection
scopes:
- system: siem
access: read
tier: production
secrets: ['siem-token']
cells/detection/allow-list.yaml
Source SHA-256: e33a1395427fc3ce3c10ef340a337b2fdcd86847728b30e6fb1aabb766fda6a6
# detection: MCP allow-list. The gateway enforces this at the protocol layer.
# Every id must exist in harness/registry.yaml; graph check enforces
# PI placement (invariant I-5) against the identity file next door.
# A cartridge overlay may add rows under the same rules; it may not remove
# the constraints here.
cell: detection
servers:
- id: siem
scope: read
note: SIEM query surface. Bound to a concrete server by the cartridge; read-only always.
- id: pysigma
scope: read
note: Rule validation and query conversion for backtesting a hit before claiming it.
cells/detection/subgraph.yaml
Source SHA-256: 5f1c8b045d3615131c0eeb6e6655cf55564cdf518b19c526882d5c00ed438016
# detection: graph view. What this cell reads at ground, and what its claims
# may propose at commit. Kinds must be declared in exchange/graph/schema/.
cell: detection
reads: ['Detection', 'LogSource', 'Technique', 'Asset']
proposes:
nodes: ['Finding', 'Identity']
edges: ['involves', 'observed_on', 'maps_to']
cells/investigation/identity.yaml
Source SHA-256: 43cd5580db718042987882aca2c559a6068b7a4ea254a6ebceab2ae0f672beaf
# investigation: workload identity and credential surface (invariant I-6 lives here).
# Local profiles: JIT injection per session. Enterprise: a per-cell workload
# identity in the org IdP; the secrets manager issues only what is named
# below, by name, never by value.
cell: investigation
scopes:
- system: edr
access: read
tier: production
- system: casework
access: write
tier: local
secrets: ['edr-token']
cells/investigation/allow-list.yaml
Source SHA-256: e3620a6c529002b52749cd32a8326538ca7a604410ffa05004b07f9ea62cde30
# investigation: MCP allow-list. The gateway enforces this at the protocol layer.
# Every id must exist in harness/registry.yaml; graph check enforces
# PI placement (invariant I-5) against the identity file next door.
# A cartridge overlay may add rows under the same rules; it may not remove
# the constraints here.
cell: investigation
servers:
- id: edr
scope: read
note: "EDR read surface: host state, process trees, artifacts. Bound by the cartridge."
- id: timesketch
scope: read
note: Collaborative timeline analysis over workspace casework.
- id: wtfis
scope: read
note: Single-pane enrichment pivot; output sanitized before agent context.
cells/investigation/subgraph.yaml
Source SHA-256: 21d680da1fa3926198f3ff548ee8eef5502041078447103ab0a8aabfab585753
# investigation: graph view. What this cell reads at ground, and what its claims
# may propose at commit. Kinds must be declared in exchange/graph/schema/.
cell: investigation
reads: ['Asset', 'Identity', 'LogSource', 'Detection', 'Technique', 'Finding', 'Case']
proposes:
nodes: ['Case', 'Finding', 'Asset']
edges: ['derived_from', 'observed_on', 'involves']
cells/threat-sandbox/identity.yaml
Source SHA-256: 95facadb3d77ceea4348bb31202f315b951c99e9911bce7e2e6bdd61a3edb806
# threat-sandbox: workload identity and credential surface (invariant I-6 lives here).
# Local profiles: JIT injection per session. Enterprise: a per-cell workload
# identity in the org IdP; the secrets manager issues only what is named
# below, by name, never by value.
cell: threat-sandbox
scopes: []
secrets: []
# Zero production credentials in any profile is invariant I-9.
# graph check contracts fails if anything is ever added here.
cells/threat-sandbox/allow-list.yaml
Source SHA-256: 4dd2d7b4edae496011807c044bfe4939144c0d95eeb9dbf2716e099c17c7f998
# threat-sandbox: MCP allow-list. The gateway enforces this at the protocol layer.
# Every id must exist in harness/registry.yaml; graph check enforces
# PI placement (invariant I-5) against the identity file next door.
# A cartridge overlay may add rows under the same rules; it may not remove
# the constraints here.
cell: threat-sandbox
servers:
- id: ghidra-mcp
scope: read
note: Reverse engineering. Decompiled code and strings are hostile input; high PI is acceptable here because this cell holds nothing to steal.
- id: atomic-red-team-mcp
scope: execute
note: Technique emulation against the sandbox itself, restricted to the explicit atomic allow-list in this cell's lane.
- id: playwright-mcp
scope: read
note: Live URL detonation. Live pages are hostile; same placement logic as ghidra-mcp.
- id: ti-enrichment
scope: read
note: Threat intel lookups. Public comment fields are attacker-writable; treated as hostile data.
cells/threat-sandbox/subgraph.yaml
Source SHA-256: bfe6d6d0fb8fba683267668e4c9b51ee33e370ddedd130bc4ccf25ecd8c0d913
# threat-sandbox: graph view. What this cell reads at ground, and what its claims
# may propose at commit. Kinds must be declared in exchange/graph/schema/.
cell: threat-sandbox
reads: ['Technique', 'Finding']
proposes:
nodes: ['Finding', 'Technique']
edges: ['maps_to']
cells/response/identity.yaml
Source SHA-256: d8182d2650939fa6891bb416e037f4a85bc36492b3173d49270c0e1a3441ca89
# response: workload identity and credential surface (invariant I-6 lives here).
# Local profiles: JIT injection per session. Enterprise: a per-cell workload
# identity in the org IdP; the secrets manager issues only what is named
# below, by name, never by value.
cell: response
scopes:
- system: soar
access: read
tier: production
- system: playbook-pipeline
access: write
tier: production
secrets: ['soar-token', 'pipeline-token']
cells/response/allow-list.yaml
Source SHA-256: 6490664e05b13d19b7883435dade0ba73c0a5c21e06aeacef30978385674000f
# response: MCP allow-list. The gateway enforces this at the protocol layer.
# Every id must exist in harness/registry.yaml; graph check enforces
# PI placement (invariant I-5) against the identity file next door.
# A cartridge overlay may add rows under the same rules; it may not remove
# the constraints here.
cell: response
servers:
- id: soar-fork
scope: write
note: "The hardened SOAR fork: read case and playbook status, plus a single pipeline-trigger tool. Never arbitrary SOAR write scope (ADR-0013)."
cells/response/subgraph.yaml
Source SHA-256: c73a45649713c18746b9fec45c930d591ea68f535b5b08d7f03f880ed74c9d65
# response: graph view. What this cell reads at ground, and what its claims
# may propose at commit. Kinds must be declared in exchange/graph/schema/.
cell: response
reads: ['Asset', 'Case', 'Finding']
proposes:
nodes: ['Case']
edges: []
cells/detection-eng/identity.yaml
Source SHA-256: a73d4ff6ca25f90ce9ce71c680ee155b75b5998d08253e1e35595df3e824c678
# detection-eng: workload identity and credential surface (invariant I-6 lives here).
# Local profiles: JIT injection per session. Enterprise: a per-cell workload
# identity in the org IdP; the secrets manager issues only what is named
# below, by name, never by value.
cell: detection-eng
scopes:
- system: siem
access: read
tier: production
- system: detection-repo
access: write
tier: production
secrets: ['siem-token', 'scm-token']
cells/detection-eng/allow-list.yaml
Source SHA-256: 8d23d5ea5eb2ff67c83d2bf1486fe9d0b8ecb584f0cab9efa8351b24b3853528
# detection-eng: MCP allow-list. The gateway enforces this at the protocol layer.
# Every id must exist in harness/registry.yaml; graph check enforces
# PI placement (invariant I-5) against the identity file next door.
# A cartridge overlay may add rows under the same rules; it may not remove
# the constraints here.
cell: detection-eng
servers:
- id: pysigma
scope: read
note: Rule authoring, validation, and conversion to the target query language.
- id: chainsaw
scope: read
note: Sigma hunting over sample event logs for backtests.
- id: siem
scope: read
note: Historical backtesting of a candidate rule; read-only always.
- id: scm
scope: write
note: Opens detection-pr branches and pull requests against the detection repo; merge stays human.
cells/detection-eng/subgraph.yaml
Source SHA-256: 3caccc1d159d93900f9ee9a4ce6476a88dcca9bfb244b2e7f5013845f8367c87
# detection-eng: graph view. What this cell reads at ground, and what its claims
# may propose at commit. Kinds must be declared in exchange/graph/schema/.
cell: detection-eng
reads: ['Detection', 'Technique', 'LogSource', 'Finding']
proposes:
nodes: ['Detection', 'Technique']
edges: ['detects']
cells/hunt/identity.yaml
Source SHA-256: 076f056aa5f26a730c672ecd629397d9119945c143ecd0b8b36560d8ea499fc7
# hunt: workload identity and credential surface (invariant I-6 lives here).
# Local profiles: JIT injection per session. Enterprise: a per-cell workload
# identity in the org IdP; the secrets manager issues only what is named
# below, by name, never by value.
cell: hunt
scopes:
- system: ti-enrichment
access: read
tier: production
secrets: ['ti-api-key']
cells/hunt/allow-list.yaml
Source SHA-256: bf0f2adfec93c26eeca799ac9de1d71125bdedcebcb54e4c1a36eeb16d471433
# hunt: MCP allow-list. The gateway enforces this at the protocol layer.
# Every id must exist in harness/registry.yaml; graph check enforces
# PI placement (invariant I-5) against the identity file next door.
# A cartridge overlay may add rows under the same rules; it may not remove
# the constraints here.
cell: hunt
servers:
- id: bloodhound
scope: read
note: Pathfinding over exported subgraphs via OpenGraph.
- id: ti-enrichment
scope: read
note: "Threat intel pivots. High PI is acceptable here: this cell has no production write scope."
- id: wtfis
scope: read
note: Domain and IP pivoting; output sanitized before agent context.
cells/hunt/subgraph.yaml
Source SHA-256: be8e6e6ce82031f5f97f3ae1b47c5105df5070df5111ae43dfc7a483123b0bca
# hunt: graph view. What this cell reads at ground, and what its claims
# may propose at commit. Kinds must be declared in exchange/graph/schema/.
cell: hunt
reads: ['Asset', 'Identity', 'LogSource', 'Detection', 'Technique', 'Finding', 'Case']
proposes:
nodes: ['Finding', 'Technique', 'Asset']
edges: ['maps_to', 'observed_on', 'exposes']
exchange/claims/fixtures/valid/detection-pr.json
Source SHA-256: 4ed4ec026fdbad0b2541afa6ac88c510c410df15419c04c1abbcb91c0d182644
{
"claim_type": "detection-pr",
"summary": "New Sigma rule for service-account interactive logon; converted, backtested over 30 days, atomic validated in sandbox.",
"graph_delta": {
"nodes": [
{ "id": "detection:svc_interactive_logon", "kinds": ["Detection"],
"properties": { "format": "sigma", "status": "proposed" } },
{ "id": "technique:T1078.002", "kinds": ["Technique"],
"properties": { "framework": "attack" } }
],
"edges": [
{ "kind": "detects",
"start": { "value": "detection:svc_interactive_logon" },
"end": { "value": "technique:T1078.002" } }
]
},
"evidence": [
{ "uri": "workspace://detection-eng/runs/de-2026-0902-012/backtest.json",
"sha256": "d1e2f3a4b5c60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8fc2" }
],
"provenance": {
"run_id": "de-2026-0902-012",
"cell": "detection-eng",
"receipts": ["scm:pr_url=https://example.invalid/detections/pull/241", "atomic:execution=de-2026-0902-012-a1"],
"created": "2026-09-02T15:21:37Z"
},
"signature": "fixture-signature-placeholder"
}
exchange/claims/fixtures/valid/lead.json
Source SHA-256: 47458e23e084d07c90de0fc01ec9038fdaf57758380fdb6eb2195dd9e879af23
{
"claim_type": "lead",
"summary": "Sigma rule fired on service-account interactive logon from an unmanaged subnet; two correlated events in a 10 minute window.",
"graph_delta": {
"nodes": [
{ "id": "finding:det-2026-0901-114", "kinds": ["Finding"],
"properties": { "ocsf_class": "detection_finding", "severity": "medium" } },
{ "id": "identity:svc-backup", "kinds": ["Identity"],
"properties": { "type": "service-account" } }
],
"edges": [
{ "kind": "involves",
"start": { "value": "finding:det-2026-0901-114" },
"end": { "value": "identity:svc-backup" } }
]
},
"evidence": [
{ "uri": "workspace://detection/runs/det-2026-0901-114/query-results.json",
"sha256": "9f2b4c1a7e8d3f60b5a4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2" },
{ "ocsf": { "class_uid": 2004, "activity_id": 1, "severity_id": 3 } }
],
"provenance": {
"run_id": "det-2026-0901-114",
"cell": "detection",
"receipts": ["siem:search_id=1756736000.4821", "sigma:rule=svc_interactive_logon"],
"created": "2026-09-01T14:32:00Z"
},
"signature": "fixture-signature-placeholder"
}
exchange/claims/fixtures/valid/response-request.json
Source SHA-256: 90c16108619be6c3525999a027bdd37b8ed6e55caa575fcc89268037942b6d97
{
"claim_type": "response-request",
"summary": "Request isolation of host WS-4417 pending credential rotation for svc-backup; verdict ts-2026-0902-031 attached upstream.",
"graph_delta": {
"nodes": [
{ "id": "asset:ws-4417", "kinds": ["Asset"],
"properties": { "crown_jewel": false } }
],
"edges": []
},
"evidence": [
{ "uri": "workspace://investigation/casework/inv-2026-0902-007/timeline.md",
"sha256": "c1d2e3f4a5b60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8fb1" }
],
"provenance": {
"run_id": "inv-2026-0902-007",
"cell": "investigation",
"receipts": ["edr:request_id=9c4412", "case:id=inv-2026-0902-007"],
"created": "2026-09-02T11:40:03Z"
},
"signature": "fixture-signature-placeholder"
}
exchange/claims/fixtures/valid/tasking.json
Source SHA-256: 6872ce4c5a4350644d93b50617e522124b3a9f98e024bf17ffd61b70e5b86f53
{
"claim_type": "tasking",
"summary": "Detonate the attachment carved from case inv-2026-0902-007 and return a verdict.",
"graph_delta": { "nodes": [], "edges": [] },
"evidence": [
{ "uri": "workspace://investigation/casework/inv-2026-0902-007/attachment.bin.manifest",
"sha256": "0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9" }
],
"provenance": {
"run_id": "inv-2026-0902-007",
"cell": "investigation",
"receipts": [],
"created": "2026-09-02T09:14:11Z"
},
"signature": "fixture-signature-placeholder"
}
exchange/claims/fixtures/valid/verdict.json
Source SHA-256: fd37f08eb402a95d133b09052332a40914361eba8423a5a31ed2f0d17fe6d668
{
"claim_type": "verdict",
"summary": "Sample is a loader; beacons to one domain; no lateral capability observed in a 20 minute detonation.",
"graph_delta": {
"nodes": [
{ "id": "finding:ts-2026-0902-031", "kinds": ["Finding"],
"properties": { "verdict": "malicious", "family": "loader" } },
{ "id": "technique:T1071.001", "kinds": ["Technique"],
"properties": { "framework": "attack" } }
],
"edges": [
{ "kind": "maps_to",
"start": { "value": "finding:ts-2026-0902-031" },
"end": { "value": "technique:T1071.001" } }
]
},
"evidence": [
{ "uri": "workspace://threat-sandbox/runs/ts-2026-0902-031/detonation-report.json",
"sha256": "b1c2d3e4f5a60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8fa0" }
],
"provenance": {
"run_id": "ts-2026-0902-031",
"cell": "threat-sandbox",
"receipts": ["sandbox:detonation_id=ts-2026-0902-031", "sandbox:snapshot=pre-detonation-0902"],
"created": "2026-09-02T10:02:45Z",
"content_trust": "agent-verified"
},
"signature": "fixture-signature-placeholder"
}
exchange/claims/fixtures/invalid/missing-provenance.json
Source SHA-256: cb5fdf54963227927bb988379d3789f19bab2f8961097ffb4c1add5f46956c28
{
"claim_type": "lead",
"graph_delta": { "nodes": [], "edges": [] },
"evidence": [
{ "uri": "workspace://detection/runs/det-2026-0901-114/query-results.json",
"sha256": "9f2b4c1a7e8d3f60b5a4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2" }
],
"signature": "fixture-signature-placeholder"
}