Graph model¶
AgentHound stores collector observations as raw nodes and edges, then builds composite analysis relationships on the server. Every public node uses a deterministic objectid and one identity-owning concrete kind.
Nodes¶
| Group | Kinds |
|---|---|
| MCP | MCPServer, MCPTool, MCPResource, MCPPrompt |
| A2A | A2AAgent, A2ASkill |
| Local context | AgentInstance, Host, ConfigFile, InstructionFile |
| Authentication | Identity, Credential |
| AI services | OllamaInstance, VLLMInstance, QdrantInstance, MLflowServer, LiteLLMGateway, JupyterServer, LangServeApp, OpenWebUIInstance |
| Served models | AIModel |
| Typed resources | VectorCollection, VectorPoint, WorkspaceFile, ModelArtifact, ArtifactStore |
| Query umbrella | AIService |
Concrete AI-service nodes also carry the AIService label. The umbrella label is for queries and does not own identity.
AIModel is a model served by a runtime such as Ollama. A persisted MLflow model version is a ModelArtifact, not an AIModel. VectorCollection represents a Qdrant collection; bounded deep reads retain individual point references as VectorPoint without treating them as MCP resources. WorkspaceFile represents either a notebook or a regular file through its entry_type property.
Resource identities use the owning service identity plus the resource's stable key: exact Qdrant collection name, the source-reported Jupyter workspace path, or MLflow registered-model name and immutable version. Mutable storage URIs never define ModelArtifact identity. Jupyter path identity preserves whitespace and literal backslashes because the contents API can report them as filename material.
Credential material¶
| Property | Meaning |
|---|---|
value |
Concrete raw material observed by the collector. |
value_hash |
SHA-256 identity used for deduplication and evidence joins. |
material_status |
Whether the material is observed, masked, hashed, or otherwise unavailable. |
exposure_status |
Whether collection observed the credential as exposed. |
source/sources and provenance fields |
Where the material or reference came from; repeated observations retain sorted plural values. |
Only concrete value material becomes planner input. The normal dashboard property view masks it; explicit query output and JSON export remain literal.
Credential identity is role- and scope-aware:
| Observation | Identity rule | Result |
|---|---|---|
| Concrete material repeated across agent configurations | value_hash within the collection point |
One Credential retains sorted plural provenance and every graph association. |
| Concrete material accepted for a service credential role | Service endpoint, role, and value_hash |
Repeating the same secret is idempotent; different secrets remain different nodes. |
| Masked, hashed, or unresolved material | Source-specific identity with merge_key=identity |
Reference evidence never merges with observed executable material. |
| The same material observed in different evidence roles | Distinct role-owned nodes correlated by value_hash |
Topology and provenance remain intact without presenting the same value twice to one endpoint. |
Planner candidate identity includes the endpoint and value_hash, so duplicate observations of one concrete secret do not produce duplicate credential-bearing requests to the same surface.
Instruction evidence¶
InstructionFile records the canonical path, source type, content hash, captured size_bytes, and modified_at. Its classification fields are:
| Property | Meaning |
|---|---|
instruction_verdict |
clean, signal, or poisoning. |
instruction_scope |
exact_project, exact_user, or recursive deep. |
instruction_signal_count |
Total classified signals before retention limits. |
instruction_signal_truncated |
Whether some signals were omitted from the bounded evidence. |
instruction_evidence_version |
Structured evidence contract version. |
instruction_evidence_json |
Up to 32 ordered signals and 64 KiB of source-exact matched excerpts, local context, positions, and optional decoded previews. Large encoded tokens retain the bounded raw excerpt that maps to the decisive decoded semantics. |
The evidence contains only bounded excerpts, not the complete instruction file.
Raw edges¶
Raw edges come from collectors or same-scan proof actions.
| Area | Edge kinds |
|---|---|
| MCP topology | TRUSTS_SERVER, PROVIDES_TOOL, PROVIDES_RESOURCE, PROVIDES_PROMPT |
| A2A topology | ADVERTISES_SKILL, DELEGATES_TO, SAME_AUTH_DOMAIN |
| Authentication | AUTHENTICATES_WITH, USES_CREDENTIAL, HAS_ENV_VAR, EXPOSES_CREDENTIAL |
| Host and configuration | RUNS_ON, CONFIGURED_IN, LOADS_INSTRUCTIONS |
| Service inventory | EXPOSES (historical), PROVIDES_MODEL, PROVIDES_RESOURCE, USES_BACKEND, STORED_IN |
| Untrusted input | INGESTS_UNTRUSTED |
| Access observations | CREDENTIAL_ACCESS_OBSERVED, PUBLIC_ACCESS_OBSERVED |
CREDENTIAL_ACCESS_OBSERVED connects a Credential to the exact MCPResource read successfully after the anonymous control was denied. PUBLIC_ACCESS_OBSERVED connects the MCPServer to a resource read anonymously. Both are supporting evidence rather than general traversal shortcuts.
PROVIDES_RESOURCE retains historical service-to-MCPResource variants for V1 artifacts. New collection emits typed pairs: QdrantInstance→VectorCollection, VectorCollection→VectorPoint, JupyterServer→WorkspaceFile, and MLflowServer→ModelArtifact. USES_BACKEND records an explicit service dependency; Open WebUI currently emits it for configured Ollama and enabled Qdrant backends. STORED_IN records a model artifact's reported physical store. EXPOSES remains accepted for historical V1 artifacts but is no longer emitted for Open WebUI backends.
ArtifactStore is created only for a safely canonicalized physical root such as a cloud bucket, container, filesystem, DBFS root, or HDFS authority. Azure ABFS/WASB filesystem or container names are part of that root identity. Object-store key paths are retained as reported rather than cleaned as local filesystem paths. Local filesystem and DBFS roots are scoped to their owning MLflow service. Indirect models:, runs:, and mlflow-artifacts: locators remain sanitized ModelArtifact metadata and do not create a store node.
New typed-resource and backend edges include evidence_state: configured proves only that the source contains the reference, observed means the source API reported it, and verified requires authoritative enumeration or a bounded request through the source. Probing a destination separately does not upgrade a configured backend relationship.
Composite edges¶
| Edge | Source → target | Meaning |
|---|---|---|
HAS_ACCESS_TO |
MCPTool → MCPResource | Capability and resource evidence support access. |
CAN_EXECUTE |
MCPTool → Host | Tool capability supports shell or code execution. |
CAN_REACH |
AgentInstance/A2AAgent → MCPResource/Credential | A deterministic trust, capability, credential, or cross-protocol path exists. |
CAN_EXFILTRATE_VIA |
AgentInstance → MCPTool | Sensitive access combines with an outbound channel. |
SHADOWS |
MCPTool → MCPTool | A tool name or description can shadow another tool. |
POISONED_DESCRIPTION |
MCPTool → MCPTool | Description content contains an injection signal. |
INSTRUCTION_SIGNAL |
InstructionFile → InstructionFile | A standalone local signal, or strong evidence seen only in recursive deep scope, requires review. |
POISONED_INSTRUCTIONS |
InstructionFile → InstructionFile | Strong, locally correlated poisoning evidence occurs in an exact project or user instruction scope. |
POISONS_CONTEXT |
MCPTool → MCPTool | An injection-bearing tool shares agent context with a high-impact tool. |
TAINTS |
MCPTool → MCPTool | Untrusted input can flow between compatible tool schemas. |
IFC_VIOLATION |
MCPTool → MCPTool | Untrusted input can reach a high-impact sink through shared resources. |
CAN_IMPERSONATE |
A2AAgent → A2AAgent | Skill similarity exceeds the impersonation threshold. |
CONFUSED_DEPUTY |
A2AAgent → A2AAgent | A weaker caller can delegate into a stronger callee. |
MCP_ORIGIN_VALIDATION_FAILED |
MCPServer → MCPServer | The endpoint processed a bounded MCP request carrying a deliberately invalid Origin instead of returning HTTP 403. |
Composite edges carry source_collector, confidence, risk weight, and processor-specific evidence. They are regenerated from the current raw projection rather than accepted from collector input.
Identity and scope¶
Raw IDs are deterministic SHA-256 values derived from kind-specific identity fields. Ingest adds collection scope so unrelated environments cannot collide:
- Collection-point scope represents one collector installation or host context.
- Network-context scope represents shared reachable infrastructure.
- Global value identity is reserved for explicit merge primitives such as concrete credential hashes.
Display names, timestamps, and mutable descriptions do not define identity. Reference-only contributions follow the authoritative observation for the same raw ID.
Typed-resource migration¶
The current server continues to accept historical V1 JupyterServer, MLflowServer, and QdrantInstance relationships to MCPResource. A new complete observation replaces those current-projection rows with the corresponding typed resource; partial or failed observations preserve them. Historical scan artifacts and captured finding evidence remain attached to their original scan and IDs.
The migrated resource kinds are not inputs to the existing finding processors, so this change does not create or rename a finding fingerprint and does not move cross-scan triage. A copied raw graph objectid is an identity for that representation, not a permanent alias: use the typed resource's source key (path, model name plus version, collection name, or point uri) to locate the same reported object after migration.
Coverage and lifecycle¶
Each collector reports outcomes such as complete, partial, failed, truncated, or not_applicable. Service resources belong to a stable service-instance inventory surface. Only a complete surface can reconcile its children; failed credential guesses, truncation, and partial traversal preserve earlier facts. For Open WebUI, one authorized exhaustive configuration pass completes only that service's configuration surface and stops later credential guesses; earlier failed attempts remain journal evidence without downgrading it. The shared autonomous-scan root becomes complete only when every blocking inventory surface is complete.
Composite analysis is rebuilt as one epoch after raw reconciliation. Published scan metadata records both submitted counts and the resulting graph totals.
Finding evidence¶
Findings classify evidence as observed, inferred, verified, hypothesis, reference-only, or unknown. A verified CAN_REACH finding contains evidence.proof with the action ID, verification time, differential control and credential stages, outcome, and cleanup status.
Finding detail returns the exact evidence subgraph captured at publication. Instruction finding detail additionally returns structured instruction_evidence extracted from that immutable snapshot. It does not rerun graph discovery when the detail page is opened.