chain-insights 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/bin/cli.js +10 -0
- package/bin/install.cjs +252 -0
- package/bin/mcp-proxy.cjs +10 -0
- package/dist/active-BSrxLKwn.mjs +50 -0
- package/dist/active-BSrxLKwn.mjs.map +1 -0
- package/dist/active-Dv7Tu-O4.cjs +68 -0
- package/dist/app-BjjuQM0B.mjs +155 -0
- package/dist/app-BjjuQM0B.mjs.map +1 -0
- package/dist/app-Dq1TdB6p.cjs +161 -0
- package/dist/artifact-server-DoxJ7fCx.cjs +47 -0
- package/dist/artifact-server-Dxz5YbuQ.mjs +48 -0
- package/dist/artifact-server-Dxz5YbuQ.mjs.map +1 -0
- package/dist/assets/bg-pattern.png +0 -0
- package/dist/assets/logo.png +0 -0
- package/dist/call-args-DQA2QcRA.cjs +27 -0
- package/dist/call-args-Lk_wOJxd.mjs +29 -0
- package/dist/call-args-Lk_wOJxd.mjs.map +1 -0
- package/dist/capabilities-CB97WMA5.cjs +83 -0
- package/dist/capabilities-DliMBim-.mjs +84 -0
- package/dist/capabilities-DliMBim-.mjs.map +1 -0
- package/dist/cases-By7INiOa.mjs +6 -0
- package/dist/cases-CDcNU91B.cjs +9 -0
- package/dist/chunk-CZWwpsFl.cjs +43 -0
- package/dist/cli.cjs +752 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +753 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-D4Bq0rp9.mjs +111 -0
- package/dist/client-D4Bq0rp9.mjs.map +1 -0
- package/dist/client-D4fZgIaO.cjs +132 -0
- package/dist/config-Bmdl5hdk.cjs +67 -0
- package/dist/config-BwrBYmiC.mjs +44 -0
- package/dist/config-BwrBYmiC.mjs.map +1 -0
- package/dist/data-extractor-BNGj7ECT.cjs +347 -0
- package/dist/data-extractor-DFzsa5CS.mjs +336 -0
- package/dist/data-extractor-DFzsa5CS.mjs.map +1 -0
- package/dist/dossier-BsroDgD3.mjs +76 -0
- package/dist/dossier-BsroDgD3.mjs.map +1 -0
- package/dist/dossier-DtxREpPm.cjs +76 -0
- package/dist/evidence-BGcdKxuV.cjs +200 -0
- package/dist/evidence-BhvFW-y_.mjs +195 -0
- package/dist/evidence-BhvFW-y_.mjs.map +1 -0
- package/dist/format-Ce1RObVl.mjs +22 -0
- package/dist/format-Ce1RObVl.mjs.map +1 -0
- package/dist/format-DOrPvXEr.cjs +20 -0
- package/dist/frontmatter-D8wWCeOa.mjs +26 -0
- package/dist/frontmatter-D8wWCeOa.mjs.map +1 -0
- package/dist/frontmatter-DgAuai7E.cjs +35 -0
- package/dist/graph-normalizer-Cv9yK9Pg.mjs +130 -0
- package/dist/graph-normalizer-Cv9yK9Pg.mjs.map +1 -0
- package/dist/graph-normalizer-DeIj6Ses.cjs +133 -0
- package/dist/graph-reports-C4TBjCkM.mjs +63 -0
- package/dist/graph-reports-C4TBjCkM.mjs.map +1 -0
- package/dist/graph-reports-DU05YCei.cjs +64 -0
- package/dist/html-generator-CAv81IWH.cjs +85 -0
- package/dist/html-generator-V6Bp0uRb.mjs +68 -0
- package/dist/html-generator-V6Bp0uRb.mjs.map +1 -0
- package/dist/index.cjs +31 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +187 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +9 -0
- package/dist/init-BjuFt54X.cjs +232 -0
- package/dist/init-CaOsHTIo.mjs +232 -0
- package/dist/init-CaOsHTIo.mjs.map +1 -0
- package/dist/mcp-proxy.cjs +1257 -0
- package/dist/mcp-proxy.d.cts +12 -0
- package/dist/mcp-proxy.d.cts.map +1 -0
- package/dist/mcp-proxy.d.mts +12 -0
- package/dist/mcp-proxy.d.mts.map +1 -0
- package/dist/mcp-proxy.mjs +1255 -0
- package/dist/mcp-proxy.mjs.map +1 -0
- package/dist/output-root-CFYms3ad.cjs +43 -0
- package/dist/output-root-CmWM7aV2.mjs +33 -0
- package/dist/output-root-CmWM7aV2.mjs.map +1 -0
- package/dist/parser-BUIWW1OH.cjs +182 -0
- package/dist/parser-DO0_SssG.mjs +182 -0
- package/dist/parser-DO0_SssG.mjs.map +1 -0
- package/dist/public-tools-D4UI-Zb0.mjs +2554 -0
- package/dist/public-tools-D4UI-Zb0.mjs.map +1 -0
- package/dist/public-tools-XSpkz2ky.cjs +2556 -0
- package/dist/resolver-C2ZS7oC8.mjs +201 -0
- package/dist/resolver-C2ZS7oC8.mjs.map +1 -0
- package/dist/resolver-zYbu4wDV.cjs +203 -0
- package/dist/rolldown-runtime-wcPFST8Q.mjs +13 -0
- package/dist/runner-1Eq55OYb.cjs +148 -0
- package/dist/runner-BhUHbiHG.mjs +149 -0
- package/dist/runner-BhUHbiHG.mjs.map +1 -0
- package/dist/schema-4XpzDFQM.cjs +55 -0
- package/dist/schema-8d0rVIdZ.mjs +37 -0
- package/dist/schema-8d0rVIdZ.mjs.map +1 -0
- package/dist/schema-cache-9CksD7tX.mjs +34 -0
- package/dist/schema-cache-9CksD7tX.mjs.map +1 -0
- package/dist/schema-cache-CgWRCN2N.cjs +36 -0
- package/dist/selector-CkFcTXzz.cjs +10 -0
- package/dist/selector-xjm6NTHI.mjs +12 -0
- package/dist/selector-xjm6NTHI.mjs.map +1 -0
- package/dist/server-BkM5xrXb.mjs +45 -0
- package/dist/server-BkM5xrXb.mjs.map +1 -0
- package/dist/server-DXowbpfi.cjs +54 -0
- package/dist/session-BpNylyuJ.cjs +115 -0
- package/dist/session-CcTgYxsj.mjs +115 -0
- package/dist/session-CcTgYxsj.mjs.map +1 -0
- package/dist/setup-DOpKPrlx.cjs +81 -0
- package/dist/setup-DyrWHuwQ.mjs +80 -0
- package/dist/setup-DyrWHuwQ.mjs.map +1 -0
- package/dist/store-BiUhQOIf.cjs +230 -0
- package/dist/store-BoWE-Gtl.mjs +225 -0
- package/dist/store-BoWE-Gtl.mjs.map +1 -0
- package/dist/templates/graph.html +1406 -0
- package/dist/tool-visibility-3Z_KvO9Q.mjs +28 -0
- package/dist/tool-visibility-3Z_KvO9Q.mjs.map +1 -0
- package/dist/tool-visibility-CwgY205r.cjs +36 -0
- package/dist/tools-Cp2jAAAb.mjs +100 -0
- package/dist/tools-Cp2jAAAb.mjs.map +1 -0
- package/dist/tools-f_vJUZAF.cjs +139 -0
- package/dist/topup-server-BZuQifvh.cjs +940 -0
- package/dist/topup-server-DUjyFftI.mjs +919 -0
- package/dist/topup-server-DUjyFftI.mjs.map +1 -0
- package/dist/version-1gP19Lhi.mjs +8 -0
- package/dist/version-1gP19Lhi.mjs.map +1 -0
- package/dist/version-BNGtdpmH.cjs +18 -0
- package/dist/viz-BlCJe6Tk.mjs +35 -0
- package/dist/viz-BlCJe6Tk.mjs.map +1 -0
- package/dist/viz-ClezVXrJ.cjs +44 -0
- package/dist/wallet-BMelXBYP.mjs +104 -0
- package/dist/wallet-BMelXBYP.mjs.map +1 -0
- package/dist/wallet-RnvvSpV2.cjs +146 -0
- package/docs/architecture.md +145 -0
- package/docs/contributing.md +68 -0
- package/docs/debugging.md +68 -0
- package/docs/development.md +44 -0
- package/docs/graph-tools.md +251 -0
- package/docs/images/graph-mcp-iframe.png +0 -0
- package/docs/images/graph-visualization.png +0 -0
- package/docs/images/topup-page.png +0 -0
- package/docs/investigation-workspaces.md +151 -0
- package/docs/mcp-proxy.md +180 -0
- package/package.json +59 -0
- package/skills/chain-insights-developer-experience/SKILL.md +101 -0
- package/skills/chain-insights-investigation/SKILL.md +285 -0
- package/skills/chain-insights-investigation/agents/openai.yaml +4 -0
- package/skills/chain-insights-investigation/scripts/run-target-uat.sh +197 -0
- package/skills/chain-insights-trace-funds/SKILL.md +249 -0
- package/skills/ci-case/SKILL.md +43 -0
- package/skills/ci-status/SKILL.md +45 -0
- package/skills/test-chain-insights-graphrag-mcp/SKILL.md +75 -0
- package/skills/test-chain-insights-graphrag-mcp/agents/openai.yaml +4 -0
- package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +414 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: chain-insights-investigation
|
|
3
|
+
description: Use when operating in a Chain Insights investigation workspace or when the user asks to investigate blockchain activity, trace funds, analyze AML risk, use the cia/chain-insights CLI, work with cases, sessions, evidence, dossiers, graph_query, graph_query_batch, Bittensor, Ethereum, or Base investigation data. This skill is mandatory for Codex-led Chain Insights investigations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Chain Insights Investigation
|
|
7
|
+
|
|
8
|
+
Use the Chain Insights framework. Do not improvise an investigation workflow in chat.
|
|
9
|
+
|
|
10
|
+
## Debug Mode
|
|
11
|
+
|
|
12
|
+
For local development and UAT, enable Graph MCP debug mode before graph calls:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cia debug on --token chain-insights-dev-debug --endpoint http://localhost:8012/mcp
|
|
16
|
+
cia debug status
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Debug mode disables x402 payments for Graph MCP calls and requires a configured debug token.
|
|
20
|
+
|
|
21
|
+
Turn it off before paid-path testing:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cia debug off
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## First Moves
|
|
28
|
+
|
|
29
|
+
1. Confirm the current directory is an initialized Chain Insights workspace:
|
|
30
|
+
```bash
|
|
31
|
+
test -f .chain-insights/workspace.json && cat .chain-insights/workspace.json
|
|
32
|
+
```
|
|
33
|
+
If this fails, stop and tell the user to run:
|
|
34
|
+
```bash
|
|
35
|
+
cia init .
|
|
36
|
+
```
|
|
37
|
+
No investigation output belongs under ~/.chain-insights.
|
|
38
|
+
2. Inspect live network support before choosing tools:
|
|
39
|
+
```bash
|
|
40
|
+
cia mcp networks
|
|
41
|
+
```
|
|
42
|
+
Use the advertised `Topology`, `Risk`, `Dataset`, and `Available tools`
|
|
43
|
+
columns as the source of truth for the current GraphRAG endpoint. The
|
|
44
|
+
`Dataset` column is the graph coverage range, usually
|
|
45
|
+
`<first_height>..<last_height> / <first_date>..<last_date>`. If an incident,
|
|
46
|
+
address activity, or requested chain falls outside that range, state that
|
|
47
|
+
limitation before querying. Do not call `address_risk` unless the selected
|
|
48
|
+
network advertises risk support and `address_risk` is available. If only
|
|
49
|
+
topology is available, use `track_funds`, `scam_topology`, or
|
|
50
|
+
`graph_query_batch` with `USE live_topology` as appropriate. Use
|
|
51
|
+
`graph_query_batch` with `USE archive_topology`
|
|
52
|
+
for historical money-flow topology, and `USE facts`
|
|
53
|
+
for graph-language StarRocks facts exposed through `facts_*_view`.
|
|
54
|
+
3. Read workspace runtime schema notes:
|
|
55
|
+
```bash
|
|
56
|
+
test -f .chain-insights/runtime-skill/SKILL.md && sed -n '1,220p' .chain-insights/runtime-skill/SKILL.md
|
|
57
|
+
```
|
|
58
|
+
4. If `.chain-insights/schema/<network>.graph-schema.json` does not exist, capture schema before case queries:
|
|
59
|
+
```bash
|
|
60
|
+
mkdir -p .chain-insights/schema
|
|
61
|
+
cia mcp call graph_query_batch network=<network> 'queries=[{"id":"node_labels","query":"USE live_topology MATCH (n:Address) RETURN \"Address\" AS node_label, count(n) AS sample_count LIMIT 1"},{"id":"relationship_types","query":"USE live_topology MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN \"FLOWS_TO\" AS rel_name, count(r) AS sample_count LIMIT 1"},{"id":"address_sample","query":"USE live_topology MATCH (n:Address) RETURN n.address AS address, n.labels AS labels LIMIT 20"},{"id":"flow_sample","query":"USE live_topology MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN r.amount_sum AS amount_sum, r.amount_usd_sum AS amount_usd_sum LIMIT 20"},{"id":"archive_flow_sample","query":"USE archive_topology MATCH (:Address)-[f:FLOWS_TO]->(:Address) RETURN f.period_granularity AS granularity, f.amount_sum AS amount_sum LIMIT 20"}]' > .chain-insights/schema/<network>.graph-schema.raw.json
|
|
62
|
+
```
|
|
63
|
+
Reduce the raw schema into `.chain-insights/schema/<network>.graph-schema.json` and update `.chain-insights/runtime-skill/SKILL.md` with the observed fields.
|
|
64
|
+
5. List cases:
|
|
65
|
+
```bash
|
|
66
|
+
cia case list
|
|
67
|
+
```
|
|
68
|
+
6. If a case exists and the user did not choose one, ask which numbered case to use.
|
|
69
|
+
7. If no case exists but the user gave an address/topic, create one with a descriptive name:
|
|
70
|
+
```bash
|
|
71
|
+
cia case open "Tracking stolen funds from <full-address>" --tags <network-or-domain>
|
|
72
|
+
```
|
|
73
|
+
8. Show selected case before investigating:
|
|
74
|
+
```bash
|
|
75
|
+
cia case show <case-number>
|
|
76
|
+
```
|
|
77
|
+
9. Start or reuse the active session:
|
|
78
|
+
```bash
|
|
79
|
+
cia case session start <case-number> "short session title"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`cia case show` displays context. It does not start work.
|
|
83
|
+
|
|
84
|
+
## Hard Rules
|
|
85
|
+
|
|
86
|
+
- Always preserve full blockchain addresses exactly.
|
|
87
|
+
- Python GraphRAG MCP is the golden behavior for `address_risk` and
|
|
88
|
+
`track_funds`. Chain Insights MCP may expose its own high-level tools, but
|
|
89
|
+
their graph semantics must be a faithful port of the Python tools.
|
|
90
|
+
- When the upstream server is Go Graph MCP, high-level Chain Insights tools
|
|
91
|
+
must implement Python-compatible orchestration by calling `graph_query` or
|
|
92
|
+
`graph_query_batch`. Prefer `USE live_topology` for Memgraph RAM topology,
|
|
93
|
+
`USE archive_topology` for StarRocks historical topology, and `USE facts`
|
|
94
|
+
for StarRocks facts. Do not replace Python probe semantics with
|
|
95
|
+
simplified local recipes.
|
|
96
|
+
- For exchange-deposit discovery, preserve Python `BFSOps`/`StolenFundsProbe`
|
|
97
|
+
semantics: forward search to `Address` nodes where `is_exchange IS NOT NULL`,
|
|
98
|
+
stop at exchange nodes, treat `path[-2]` as the deposit candidate, then run
|
|
99
|
+
backward/source and reverse-lead stages. Current MemGQL does not parse
|
|
100
|
+
Memgraph `*BFS` or variable-length relationship syntax, so Go Graph MCP
|
|
101
|
+
deployments reproduce this with generated fixed-depth `FLOWS_TO` query batches.
|
|
102
|
+
- For `address_risk`, Python `GraphRAGQueryEngine.check_address_risk` is the
|
|
103
|
+
golden behavior: bounded neighborhood expansion with exchange-stopped waves,
|
|
104
|
+
risk/scoring fields, lookalikes, forward exchange
|
|
105
|
+
discovery, and backward `BFSOps.bfs_backward` source-exchange discovery.
|
|
106
|
+
Chain Insights may call Go MCP primitives, but it must not reduce
|
|
107
|
+
`address_risk` to only a profile lookup.
|
|
108
|
+
- Never call graph tools without an explicit `network`.
|
|
109
|
+
- Never assume network support. Run `cia mcp networks` first and respect the
|
|
110
|
+
advertised `Available tools` plus the `Dataset` height/date range.
|
|
111
|
+
- Never treat user claims as facts until tool output supports them.
|
|
112
|
+
- Never leave material tool output only in chat. Save it as evidence.
|
|
113
|
+
- Keep evidence compact and use original graph field names. Evidence Markdown should summarize and point to files; large JSON belongs in `reports/tables/`.
|
|
114
|
+
- Store visualization data in `reports/graphs/` and analyst tables in `reports/tables/` under the initialized workspace.
|
|
115
|
+
- Prefer Mermaid/table reports over long prose dossiers.
|
|
116
|
+
- Prefer `cia` commands over direct file edits.
|
|
117
|
+
- Do not use `cia case resume`; use `cia case show`.
|
|
118
|
+
- Use numbered case selectors from `cia case list`.
|
|
119
|
+
- Use `graph_query_batch` for related graph reads.
|
|
120
|
+
- Use read-only Cypher only: no `CREATE`, `MERGE`, `SET`, `DELETE`, `REMOVE`, `DROP`, or `DETACH`.
|
|
121
|
+
- Bound graph reads with `LIMIT`.
|
|
122
|
+
|
|
123
|
+
## Tool Selection
|
|
124
|
+
|
|
125
|
+
Tool selection is network-dependent:
|
|
126
|
+
|
|
127
|
+
- `Topology: yes` means graph topology tools can run for that network.
|
|
128
|
+
- `Risk: yes` means risk-aware tools such as `address_risk` can be used.
|
|
129
|
+
- `Dataset` defines the graph coverage window. Use it to qualify conclusions;
|
|
130
|
+
do not imply coverage before the first advertised height/date or after the
|
|
131
|
+
last advertised height/date.
|
|
132
|
+
- `Available tools` is authoritative. If a tool is not listed as available,
|
|
133
|
+
fall back to the available lower-level graph tools or tell the user the
|
|
134
|
+
network is not supported for that workflow yet.
|
|
135
|
+
|
|
136
|
+
Use `address_risk` first for ordinary address screening. It measures risk,
|
|
137
|
+
neighborhood context, and exchange behavior together, including exchange inflow
|
|
138
|
+
and outflow paths. Do not use retired separate exchange-flow tools as a primary
|
|
139
|
+
workflow; that behavior belongs in `address_risk`.
|
|
140
|
+
|
|
141
|
+
Use `address_risk` with `compare_address` when the user asks whether two
|
|
142
|
+
addresses are connected or whether a relationship is risky. Use compare mode
|
|
143
|
+
as the unified connection-risk path.
|
|
144
|
+
|
|
145
|
+
Use `track_funds` when the user has victim/source addresses and may also have
|
|
146
|
+
known scammer addresses. It accepts up to five `trusted_addresses` and up to
|
|
147
|
+
five `untrusted_addresses`, preserves those roles, and traces each through the
|
|
148
|
+
local tracing engine.
|
|
149
|
+
|
|
150
|
+
Use `track_funds` for stolen-fund and fund-flow work, including single-address
|
|
151
|
+
fund-flow tracing by passing one address as the only `trusted_addresses` value.
|
|
152
|
+
|
|
153
|
+
Use `scam_topology` when known victim incident ground truth should become
|
|
154
|
+
ML-ready `scam_labels` plus reviewable laundering context. The victim-only
|
|
155
|
+
traversal is outward from victim/source funds, so do not query or promote
|
|
156
|
+
victim inbound transfers as scam infrastructure. The primary traversal is a
|
|
157
|
+
node-relative novelty wave: each new node expands only once, repeated targets
|
|
158
|
+
are retained as non-expanding `convergence_edge` context, and downstream edges
|
|
159
|
+
must have `first_seen_timestamp` or `last_seen_timestamp` greater than or equal
|
|
160
|
+
to the current node's wave-arrival timestamp. The tool can instead use
|
|
161
|
+
`activity_policy=global_incident_only`, where every wave is filtered against
|
|
162
|
+
`incident_timestamp_ms`. Exchange terminal safety is the only hard-coded
|
|
163
|
+
terminal behavior; other labels are generic context hints. The graph report follows the
|
|
164
|
+
same `chain-insights.graph.v1` layout as
|
|
165
|
+
`track_funds`: primary victim-flow edges are `flows`, exchange deposits are
|
|
166
|
+
`deposits`, and deposit-cluster enrichment is `reverse_leads`.
|
|
167
|
+
Victim/source addresses,
|
|
168
|
+
exchange endpoints, and generic labeled context nodes are not automatic scam
|
|
169
|
+
labels; laundering intermediates and exchange deposit candidates are reviewable,
|
|
170
|
+
not automatic writes to `core_address_labels`.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
cia mcp scam-topology --network bittensor --victim-address 5... --incident-timestamp-ms 1715532228001 --max-hops 16
|
|
174
|
+
cia mcp scam-topology --network bittensor --victim-address 5... --incident-timestamp-ms 1715532228001 --max-hops 16 --activity-policy global_incident_only
|
|
175
|
+
cia mcp scam-topology --network bittensor --victim-address 5... --incident-timestamp-ms 1715532228001 --case 1
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
If `case_id` or CLI `--case` is provided, `scam_topology` writes a compact
|
|
179
|
+
`chain-insights.evidence_pointer.v1` entry to the case. The pointer references
|
|
180
|
+
workspace-local compact evidence JSON, graph JSON, graph HTML,
|
|
181
|
+
label-candidate CSV, and Markdown report files under `reports/`.
|
|
182
|
+
|
|
183
|
+
Use manual `graph_query_batch` for custom topology or fact questions. Use
|
|
184
|
+
`USE live_topology` for Memgraph RAM topology, `USE archive_topology` for
|
|
185
|
+
StarRocks historical topology, and `USE facts` for StarRocks facts.
|
|
186
|
+
Use `graph_query` or `graph_query_batch` for all graph-language reads.
|
|
187
|
+
|
|
188
|
+
## Query And Evidence Loop
|
|
189
|
+
|
|
190
|
+
Default to compact evidence. Preserve source schema field names and avoid adding
|
|
191
|
+
derived aliases or unit labels unless the schema or query result explicitly
|
|
192
|
+
supports them. Select only fields needed to support the claim, such as source,
|
|
193
|
+
destination, relationship type, amount fields, tx counts, tx ids, labels, and
|
|
194
|
+
degrees. Avoid storing whole node or relationship property blobs in evidence
|
|
195
|
+
unless the query is explicitly for schema discovery or debugging.
|
|
196
|
+
|
|
197
|
+
For every material graph/tool query:
|
|
198
|
+
|
|
199
|
+
1. Write output to a temp file with narrow Cypher projections:
|
|
200
|
+
```bash
|
|
201
|
+
cia mcp call graph_query_batch \
|
|
202
|
+
network=<network> \
|
|
203
|
+
'queries=[{"id":"<stable_id>","query":"USE live_topology MATCH ... RETURN ... LIMIT 50"}]' \
|
|
204
|
+
> /tmp/<stable_id>.json
|
|
205
|
+
```
|
|
206
|
+
2. Inspect the output and reduce it if the tool returned extra fields:
|
|
207
|
+
```bash
|
|
208
|
+
jq '{schema:"chain-insights.compact_evidence.v1", facts:<selected-fields>}' \
|
|
209
|
+
/tmp/<stable_id>.json > /tmp/<stable_id>.compact.json
|
|
210
|
+
```
|
|
211
|
+
3. Save the compact output as evidence. If the JSON is large, Chain Insights stores it under `reports/tables/` and keeps the evidence Markdown as a summary plus pointer:
|
|
212
|
+
```bash
|
|
213
|
+
cia case evidence add <case-number> \
|
|
214
|
+
--source graph_query_batch_compact \
|
|
215
|
+
--query-params "network=<network> id=<stable_id> ..." \
|
|
216
|
+
--content "$(cat /tmp/<stable_id>.compact.json)"
|
|
217
|
+
```
|
|
218
|
+
4. Only after evidence is saved, summarize what the output supports and what remains unknown.
|
|
219
|
+
5. Write graph/table/report outputs when the query describes fund movement:
|
|
220
|
+
```bash
|
|
221
|
+
mkdir -p reports/graphs reports/tables
|
|
222
|
+
```
|
|
223
|
+
Save `chain-insights.graph.v1` JSON to `reports/graphs/` for visualization, tabular extracts to `reports/tables/`, and Mermaid/table analyst reports to `reports/`.
|
|
224
|
+
6. If an address/entity finding becomes durable, update the dossier with one short pointer, not a full report:
|
|
225
|
+
```bash
|
|
226
|
+
cia case dossier update <case-number> <full-address> \
|
|
227
|
+
--type unknown \
|
|
228
|
+
--finding "See reports/<report>.md and evidence <filename>."
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Quoting Pattern
|
|
232
|
+
|
|
233
|
+
Keep MCP JSON arguments on one shell line. Raw newlines inside JSON strings break parsing.
|
|
234
|
+
|
|
235
|
+
Good:
|
|
236
|
+
```bash
|
|
237
|
+
cia mcp call graph_query_batch network=bittensor 'queries=[{"id":"address_exists","query":"USE live_topology MATCH (n:Address {address: \"5...\"}) RETURN n.address AS address, n.labels AS labels LIMIT 1"},{"id":"address_feature","query":"USE facts MATCH (:Address {address: \"5...\"})-[:HAS_FEATURE]->(feature:AddressFeature) RETURN feature.degree_in AS degree_in, feature.degree_out AS degree_out, feature.tx_total_count AS tx_total_count LIMIT 1"}]'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Bad:
|
|
241
|
+
```bash
|
|
242
|
+
'queries=[{"id":"address_exists","query":"MATCH (n {address:
|
|
243
|
+
\"5...\"}) RETURN ..."}]'
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Session Closeout
|
|
247
|
+
|
|
248
|
+
At the end of a work pass:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
cia case session end <case-number> \
|
|
252
|
+
--findings "Evidence-backed findings from this session." \
|
|
253
|
+
--next-steps "Concrete next graph queries or checks."
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Then show the case:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
cia case show <case-number>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## When Asked "What Next?"
|
|
263
|
+
|
|
264
|
+
Give one concrete next command, not a broad menu. Prefer the next topology or fact query, or the next evidence-save step.
|
|
265
|
+
|
|
266
|
+
## Fresh Workspace UAT
|
|
267
|
+
|
|
268
|
+
When validating the skill or Chain Insights investigation flow, run the bundled script from the Chain Insights repo:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
skills/chain-insights-investigation/scripts/run-target-uat.sh
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The script creates a fresh investigation workspace, enables debug mode, captures runtime graph schema, opens a case for `5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5`, starts a session, runs `graph_query_batch`, saves compact evidence with original field names, updates a lightweight dossier pointer, ends the session, and verifies the resulting case state.
|
|
275
|
+
|
|
276
|
+
Environment overrides:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
TARGET_ADDRESS=5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5 \
|
|
280
|
+
NETWORK=bittensor \
|
|
281
|
+
GRAPH_MCP_ENDPOINT=http://localhost:8012/mcp \
|
|
282
|
+
GRAPH_MCP_DEBUG_TOKEN=chain-insights-dev-debug \
|
|
283
|
+
WORKSPACE_ROOT=/tmp/ci-investigation-uat \
|
|
284
|
+
skills/chain-insights-investigation/scripts/run-target-uat.sh
|
|
285
|
+
```
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Chain Insights Investigation"
|
|
3
|
+
short_description: "Run evidence-backed blockchain investigations with cia"
|
|
4
|
+
default_prompt: "Use the Chain Insights investigation workflow for this case: select or create a case, start a session, run bounded graph queries, save raw outputs as evidence, update dossiers for durable findings, and close with next steps."
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -Eeuo pipefail
|
|
3
|
+
|
|
4
|
+
TARGET_ADDRESS="${TARGET_ADDRESS:-5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5}"
|
|
5
|
+
NETWORK="${NETWORK:-bittensor}"
|
|
6
|
+
GRAPH_MCP_ENDPOINT="${GRAPH_MCP_ENDPOINT:-http://localhost:8012/mcp}"
|
|
7
|
+
GRAPH_MCP_DEBUG_TOKEN="${GRAPH_MCP_DEBUG_TOKEN:-chain-insights-dev-debug}"
|
|
8
|
+
WORKSPACE_ROOT="${WORKSPACE_ROOT:-$(mktemp -d /tmp/chain-insights-investigation-uat.XXXXXX)}"
|
|
9
|
+
GLOBAL_REPORTS="${HOME}/.chain-insights/reports"
|
|
10
|
+
GLOBAL_CASES="${HOME}/.chain-insights/cases"
|
|
11
|
+
GLOBAL_SNAPSHOT_BEFORE="$(mktemp /tmp/chain-insights-global-before.XXXXXX)"
|
|
12
|
+
GLOBAL_SNAPSHOT_AFTER="$(mktemp /tmp/chain-insights-global-after.XXXXXX)"
|
|
13
|
+
CONFIG_SNAPSHOT_READY=0
|
|
14
|
+
|
|
15
|
+
log() {
|
|
16
|
+
printf '[chain-insights-investigation-uat] %s\n' "$*"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
require_cmd() {
|
|
20
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
21
|
+
printf '[chain-insights-investigation-uat] missing command: %s\n' "$1" >&2
|
|
22
|
+
exit 127
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
snapshot_global_outputs() {
|
|
27
|
+
local output_file="$1"
|
|
28
|
+
: >"${output_file}"
|
|
29
|
+
for dir in "${GLOBAL_REPORTS}" "${GLOBAL_CASES}"; do
|
|
30
|
+
{
|
|
31
|
+
printf '[%s]\n' "${dir}"
|
|
32
|
+
if [[ -d "${dir}" ]]; then
|
|
33
|
+
(
|
|
34
|
+
cd "${dir}"
|
|
35
|
+
find . -mindepth 1 -type d -print | LC_ALL=C sort | sed 's/^/dir /'
|
|
36
|
+
find . -mindepth 1 -type f -print0 \
|
|
37
|
+
| LC_ALL=C sort -z \
|
|
38
|
+
| xargs -0 -r sha256sum \
|
|
39
|
+
| sed 's/^/file /'
|
|
40
|
+
)
|
|
41
|
+
else
|
|
42
|
+
printf '<missing>\n'
|
|
43
|
+
fi
|
|
44
|
+
} >>"${output_file}"
|
|
45
|
+
done
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
assert_no_global_outputs_changed() {
|
|
49
|
+
snapshot_global_outputs "${GLOBAL_SNAPSHOT_AFTER}"
|
|
50
|
+
if ! cmp -s "${GLOBAL_SNAPSHOT_BEFORE}" "${GLOBAL_SNAPSHOT_AFTER}"; then
|
|
51
|
+
log "global investigation output roots changed; reports/cases must stay workspace-local"
|
|
52
|
+
diff -u "${GLOBAL_SNAPSHOT_BEFORE}" "${GLOBAL_SNAPSHOT_AFTER}" >&2 || true
|
|
53
|
+
return 1
|
|
54
|
+
fi
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
restore_mode() {
|
|
58
|
+
if [[ "${CONFIG_SNAPSHOT_READY}" != "1" ]]; then
|
|
59
|
+
return
|
|
60
|
+
fi
|
|
61
|
+
if [[ -n "${OLD_GRAPH_MCP_MODE:-}" ]]; then
|
|
62
|
+
cia config set graphMcpMode "${OLD_GRAPH_MCP_MODE}" >/dev/null || true
|
|
63
|
+
fi
|
|
64
|
+
if [[ -n "${OLD_GRAPH_MCP_ENDPOINT:-}" ]]; then
|
|
65
|
+
cia config set graphMcpEndpoint "${OLD_GRAPH_MCP_ENDPOINT}" >/dev/null || true
|
|
66
|
+
fi
|
|
67
|
+
cia config set graphMcpAuthToken "${OLD_GRAPH_MCP_AUTH_TOKEN:-}" >/dev/null || true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
finish() {
|
|
71
|
+
local status="$?"
|
|
72
|
+
set +e
|
|
73
|
+
if [[ -f "${GLOBAL_SNAPSHOT_BEFORE}" ]]; then
|
|
74
|
+
assert_no_global_outputs_changed || status=1
|
|
75
|
+
fi
|
|
76
|
+
restore_mode
|
|
77
|
+
rm -f "${GLOBAL_SNAPSHOT_BEFORE}" "${GLOBAL_SNAPSHOT_AFTER}"
|
|
78
|
+
exit "${status}"
|
|
79
|
+
}
|
|
80
|
+
trap finish EXIT
|
|
81
|
+
|
|
82
|
+
require_cmd cia
|
|
83
|
+
require_cmd node
|
|
84
|
+
require_cmd jq
|
|
85
|
+
require_cmd sha256sum
|
|
86
|
+
|
|
87
|
+
snapshot_global_outputs "${GLOBAL_SNAPSHOT_BEFORE}"
|
|
88
|
+
|
|
89
|
+
OLD_GRAPH_MCP_MODE="$(cia config get graphMcpMode || true)"
|
|
90
|
+
OLD_GRAPH_MCP_ENDPOINT="$(cia config get graphMcpEndpoint || true)"
|
|
91
|
+
OLD_GRAPH_MCP_AUTH_TOKEN="$(cia config get graphMcpAuthToken || true)"
|
|
92
|
+
CONFIG_SNAPSHOT_READY=1
|
|
93
|
+
|
|
94
|
+
log "workspace: ${WORKSPACE_ROOT}"
|
|
95
|
+
log "target: ${NETWORK}:${TARGET_ADDRESS}"
|
|
96
|
+
log "enabling Graph MCP debug mode for UAT"
|
|
97
|
+
cia debug on --token "${GRAPH_MCP_DEBUG_TOKEN}" --endpoint "${GRAPH_MCP_ENDPOINT}" >/dev/null
|
|
98
|
+
|
|
99
|
+
cia init "${WORKSPACE_ROOT}" --force >/dev/null
|
|
100
|
+
cd "${WORKSPACE_ROOT}"
|
|
101
|
+
|
|
102
|
+
CASE_NAME="Tracking stolen funds from ${TARGET_ADDRESS}"
|
|
103
|
+
cia case open "${CASE_NAME}" --tags "${NETWORK},uat" --description "Fresh-folder Chain Insights investigation UAT." >/tmp/chain-insights-uat-case-open.txt
|
|
104
|
+
CASE_ID="$(sed -n 's/^Case opened: //p' /tmp/chain-insights-uat-case-open.txt | head -n1)"
|
|
105
|
+
if [[ -z "${CASE_ID}" ]]; then
|
|
106
|
+
log "failed to parse case id"
|
|
107
|
+
cat /tmp/chain-insights-uat-case-open.txt >&2
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
cia case session start "${CASE_ID}" "UAT graph evidence for ${TARGET_ADDRESS}" >/dev/null
|
|
112
|
+
mkdir -p reports/uat reports/graphs .chain-insights/schema
|
|
113
|
+
|
|
114
|
+
SCHEMA_RAW=".chain-insights/schema/${NETWORK}.graph-schema.raw.json"
|
|
115
|
+
SCHEMA_FILE=".chain-insights/schema/${NETWORK}.graph-schema.json"
|
|
116
|
+
log "capturing ${NETWORK} graph schema"
|
|
117
|
+
cia mcp call graph_query_batch \
|
|
118
|
+
network="${NETWORK}" \
|
|
119
|
+
'queries=[{"id":"node_labels","query":"USE live_topology MATCH (n:Address) RETURN \"Address\" AS node_label, count(n) AS sample_count LIMIT 1"},{"id":"relationship_types","query":"USE live_topology MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN \"FLOWS_TO\" AS rel_name, count(r) AS sample_count LIMIT 1"},{"id":"address_sample","query":"USE live_topology MATCH (n:Address) RETURN n.address AS address, n.labels AS labels LIMIT 20"},{"id":"flow_sample","query":"USE live_topology MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN r.amount_sum AS amount_sum, r.amount_usd_sum AS amount_usd_sum LIMIT 20"}]' \
|
|
120
|
+
> "${SCHEMA_RAW}"
|
|
121
|
+
|
|
122
|
+
jq --arg network "${NETWORK}" '{
|
|
123
|
+
schema:"chain-insights.runtime_graph_schema.v1",
|
|
124
|
+
network:$network,
|
|
125
|
+
source:"graph_query_batch",
|
|
126
|
+
node_labels:(.facts.queries[]|select(.id=="node_labels")|.results),
|
|
127
|
+
relationship_types:(.facts.queries[]|select(.id=="relationship_types")|.results),
|
|
128
|
+
address_property_keys:(.facts.queries[]|select(.id=="address_property_keys")|.results|map(.property_key)),
|
|
129
|
+
flows_to_property_keys:(.facts.queries[]|select(.id=="flows_to_property_keys")|.results|map(.property_key))
|
|
130
|
+
}' "${SCHEMA_RAW}" > "${SCHEMA_FILE}"
|
|
131
|
+
|
|
132
|
+
if ! jq -e '.flows_to_property_keys | index("amount_sum")' "${SCHEMA_FILE}" >/dev/null; then
|
|
133
|
+
log "schema did not include FLOWS_TO.amount_sum"
|
|
134
|
+
cat "${SCHEMA_FILE}" >&2
|
|
135
|
+
exit 1
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
RESULT_FILE="reports/uat/address_exists.json"
|
|
139
|
+
COMPACT_FILE="reports/uat/address_exists.compact.json"
|
|
140
|
+
|
|
141
|
+
log "running graph_query_batch address_exists"
|
|
142
|
+
cia mcp call graph_query_batch \
|
|
143
|
+
network="${NETWORK}" \
|
|
144
|
+
"queries=[{\"id\":\"address_exists\",\"query\":\"USE live_topology MATCH (n:Address {address: \\\"${TARGET_ADDRESS}\\\"}) RETURN n.address AS address, n.labels AS labels, n.degree_in AS degree_in, n.degree_out AS degree_out, n.tx_total_count AS tx_total_count, n.total_volume_usd AS total_volume_usd LIMIT 1\"}]" \
|
|
145
|
+
> "${RESULT_FILE}"
|
|
146
|
+
|
|
147
|
+
if ! grep -q "${TARGET_ADDRESS}" "${RESULT_FILE}"; then
|
|
148
|
+
log "graph result did not contain target address"
|
|
149
|
+
cat "${RESULT_FILE}" >&2
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
jq --arg network "${NETWORK}" '{
|
|
154
|
+
schema:"chain-insights.compact_evidence.v1",
|
|
155
|
+
source:"graph_query_batch",
|
|
156
|
+
network:$network,
|
|
157
|
+
query_ids:["address_exists"],
|
|
158
|
+
addresses:(.facts.queries[]|select(.id=="address_exists")|.results)
|
|
159
|
+
}' "${RESULT_FILE}" > "${COMPACT_FILE}"
|
|
160
|
+
|
|
161
|
+
EVIDENCE_OUT="$(cia case evidence add "${CASE_ID}" \
|
|
162
|
+
--source graph_query_batch_compact \
|
|
163
|
+
--query-params "network=${NETWORK} address=${TARGET_ADDRESS} query=address_exists compact=true schema=${SCHEMA_FILE}" \
|
|
164
|
+
--content "$(cat "${COMPACT_FILE}")")"
|
|
165
|
+
printf '%s\n' "${EVIDENCE_OUT}" > reports/uat/evidence-add.txt
|
|
166
|
+
|
|
167
|
+
SHOW_OUT="$(cia case show "${CASE_ID}")"
|
|
168
|
+
printf '%s\n' "${SHOW_OUT}" > reports/uat/case-show.txt
|
|
169
|
+
if ! printf '%s\n' "${SHOW_OUT}" | grep -q 'Evidence files: 1'; then
|
|
170
|
+
log "case show did not report one evidence file"
|
|
171
|
+
printf '%s\n' "${SHOW_OUT}" >&2
|
|
172
|
+
exit 1
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
cia case dossier update "${CASE_ID}" "${TARGET_ADDRESS}" \
|
|
176
|
+
--type unknown \
|
|
177
|
+
--finding "UAT confirmed the target address exists in ${NETWORK}; see compact address_exists evidence and ${SCHEMA_FILE}." >/dev/null
|
|
178
|
+
|
|
179
|
+
cia case session end "${CASE_ID}" \
|
|
180
|
+
--findings "UAT confirmed schema capture and compact graph_query_batch evidence for the target address." \
|
|
181
|
+
--next-steps "Run narrow FLOWS_TO projections and save graph JSON under reports/graphs/." >/dev/null
|
|
182
|
+
|
|
183
|
+
FINAL_SHOW="$(cia case show "${CASE_ID}")"
|
|
184
|
+
printf '%s\n' "${FINAL_SHOW}" > reports/uat/final-case-show.txt
|
|
185
|
+
if ! printf '%s\n' "${FINAL_SHOW}" | grep -q 'Dossiers: 1'; then
|
|
186
|
+
log "case show did not report one dossier"
|
|
187
|
+
printf '%s\n' "${FINAL_SHOW}" >&2
|
|
188
|
+
exit 1
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
log "PASS"
|
|
192
|
+
log "workspace: ${WORKSPACE_ROOT}"
|
|
193
|
+
log "case: ${CASE_ID}"
|
|
194
|
+
log "result: ${WORKSPACE_ROOT}/${RESULT_FILE}"
|
|
195
|
+
log "schema: ${WORKSPACE_ROOT}/${SCHEMA_FILE}"
|
|
196
|
+
log "evidence:"
|
|
197
|
+
find "cases/${CASE_ID}/evidence" -maxdepth 1 -type f -print | sort
|