opmsec 0.1.3 → 0.1.5
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/.env.example +1 -0
- package/.husky/pre-commit +1 -0
- package/README.md +71 -275
- package/bun.lock +5 -5
- package/docs/architecture/agents.mdx +11 -59
- package/docs/architecture/benchmarks.mdx +20 -46
- package/docs/architecture/overview.mdx +31 -38
- package/docs/architecture/scanner.mdx +11 -37
- package/docs/cli/audit.mdx +9 -12
- package/docs/cli/check.mdx +12 -26
- package/docs/cli/fix.mdx +10 -30
- package/docs/cli/info.mdx +12 -19
- package/docs/cli/install.mdx +27 -39
- package/docs/cli/push.mdx +40 -57
- package/docs/cli/register-agent.mdx +21 -53
- package/docs/cli/view.mdx +12 -29
- package/docs/concepts/ens-records.mdx +44 -0
- package/docs/concepts/multi-agent-consensus.mdx +18 -36
- package/docs/concepts/on-chain-registry.mdx +22 -49
- package/docs/concepts/security-model.mdx +20 -52
- package/docs/concepts/zk-agent-verification.mdx +26 -64
- package/docs/contract/events.mdx +13 -74
- package/docs/contract/functions.mdx +40 -126
- package/docs/contract/overview.mdx +17 -36
- package/docs/introduction.mdx +22 -25
- package/docs/mint.json +3 -2
- package/docs/quickstart.mdx +34 -70
- package/docs/system-design.png +0 -0
- package/package.json +7 -6
- package/packages/cli/src/commands/author-view.tsx +87 -2
- package/packages/cli/src/commands/check.tsx +18 -5
- package/packages/cli/src/commands/fix.tsx +25 -12
- package/packages/cli/src/commands/info.tsx +92 -4
- package/packages/cli/src/commands/install.tsx +327 -23
- package/packages/cli/src/commands/push.tsx +112 -0
- package/packages/cli/src/commands/register-agent.tsx +72 -31
- package/packages/cli/src/index.tsx +7 -5
- package/packages/cli/src/services/ens-records.ts +525 -0
- package/packages/cli/src/services/version.ts +156 -5
- package/packages/core/src/benchmarks.ts +116 -0
- package/packages/core/src/constants.ts +18 -6
- package/packages/core/src/model-rankings.ts +40 -15
- package/packages/core/src/types.ts +10 -0
- package/packages/core/src/utils.ts +136 -1
- package/packages/scanner/src/index.ts +2 -1
- package/packages/scanner/src/queue/memory-queue.ts +7 -2
- package/packages/scanner/src/services/benchmark-runner.ts +86 -1
- package/packages/scanner/src/services/fileverse.ts +61 -12
- package/packages/scanner/src/services/openrouter.ts +18 -7
- package/packages/web/.next/BUILD_ID +1 -0
- package/packages/web/.next/app-path-routes-manifest.json +4 -0
- package/packages/web/.next/diagnostics/build-diagnostics.json +6 -0
- package/packages/web/.next/diagnostics/framework.json +1 -0
- package/packages/web/.next/export-marker.json +6 -0
- package/packages/web/.next/images-manifest.json +58 -0
- package/packages/web/.next/next-minimal-server.js.nft.json +1 -0
- package/packages/web/.next/next-server.js.nft.json +1 -0
- package/packages/web/.next/prerender-manifest.json +54 -4
- package/packages/web/.next/required-server-files.json +320 -0
- package/packages/web/.next/routes-manifest.json +53 -1
- package/packages/web/.next/server/app/_not-found/page.js +2 -0
- package/packages/web/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/packages/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/packages/web/.next/server/app/_not-found.html +1 -0
- package/packages/web/.next/server/app/_not-found.meta +8 -0
- package/packages/web/.next/server/app/_not-found.rsc +18 -0
- package/packages/web/.next/server/app/index.html +6 -0
- package/packages/web/.next/server/app/index.meta +7 -0
- package/packages/web/.next/server/app/index.rsc +22 -0
- package/packages/web/.next/server/app/page.js +24 -24
- package/packages/web/.next/server/app/page.js.nft.json +1 -0
- package/packages/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/packages/web/.next/server/chunks/611.js +6 -0
- package/packages/web/.next/server/chunks/778.js +30 -0
- package/packages/web/.next/server/functions-config-manifest.json +4 -0
- package/packages/web/.next/server/interception-route-rewrite-manifest.js +1 -1
- package/packages/web/.next/server/next-font-manifest.js +1 -1
- package/packages/web/.next/server/next-font-manifest.json +1 -1
- package/packages/web/.next/server/pages/404.html +1 -0
- package/packages/web/.next/server/pages/500.html +1 -0
- package/packages/web/.next/server/pages/_app.js +1 -0
- package/packages/web/.next/server/pages/_app.js.nft.json +1 -0
- package/packages/web/.next/server/pages/_document.js +1 -0
- package/packages/web/.next/server/pages/_document.js.nft.json +1 -0
- package/packages/web/.next/server/pages/_error.js +19 -0
- package/packages/web/.next/server/pages/_error.js.nft.json +1 -0
- package/packages/web/.next/server/webpack-runtime.js +2 -2
- package/packages/web/.next/static/0esGzFBCzREfVwijEGDfL/_buildManifest.js +1 -0
- package/packages/web/.next/static/0esGzFBCzREfVwijEGDfL/_ssgManifest.js +1 -0
- package/packages/web/.next/static/chunks/174-5b5efcb3b8efcc01.js +1 -0
- package/packages/web/.next/static/chunks/255-0dc49b7a6e8e5c05.js +1 -0
- package/packages/web/.next/static/chunks/4bd1b696-382748cc942d8a14.js +1 -0
- package/packages/web/.next/static/chunks/app/_not-found/page-0da542be7eb33a64.js +1 -0
- package/packages/web/.next/static/chunks/app/layout-de8e841104500505.js +1 -0
- package/packages/web/.next/static/chunks/app/layout.js +37 -7
- package/packages/web/.next/static/chunks/app/page-7e086379698b9fb0.js +1 -0
- package/packages/web/.next/static/chunks/app/page.js +297 -1
- package/packages/web/.next/static/chunks/framework-ac73abd125e371fe.js +1 -0
- package/packages/web/.next/static/chunks/main-4e8d71b5ef7ee7e3.js +1 -0
- package/packages/web/.next/static/chunks/main-app-dd261207182e5a23.js +1 -0
- package/packages/web/.next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/packages/web/.next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/packages/web/.next/static/chunks/webpack-0dcd67569eb46132.js +1 -0
- package/packages/web/.next/static/chunks/webpack.js +2 -2
- package/packages/web/.next/static/css/102562cf2d0ae9b0.css +3 -0
- package/packages/web/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/packages/web/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/packages/web/.next/static/media/8d697b304b401681-s.woff2 +0 -0
- package/packages/web/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/packages/web/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
- package/packages/web/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- package/packages/web/.next/static/webpack/16f18baa938a434c.webpack.hot-update.json +1 -0
- package/packages/web/.next/static/webpack/5fe9fe8578f9c3d2.webpack.hot-update.json +1 -0
- package/packages/web/.next/static/webpack/73c7d02260cc80e4.webpack.hot-update.json +1 -0
- package/packages/web/.next/static/webpack/a2d85d19aa028de1.webpack.hot-update.json +1 -0
- package/packages/web/.next/static/webpack/app/{layout.73e341375c8d429e.hot-update.js → layout.16f18baa938a434c.hot-update.js} +1 -1
- package/packages/web/.next/static/webpack/app/{layout.6fee6306e0f98869.hot-update.js → layout.5fe9fe8578f9c3d2.hot-update.js} +1 -1
- package/packages/web/.next/static/webpack/app/layout.653e365406c0d9ac.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/layout.6800169a899e3a8b.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/layout.73c7d02260cc80e4.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/layout.a2d85d19aa028de1.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/page.653e365406c0d9ac.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/page.6800169a899e3a8b.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/page.73c7d02260cc80e4.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/page.a2d85d19aa028de1.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/{webpack.6fee6306e0f98869.hot-update.js → webpack.16f18baa938a434c.hot-update.js} +2 -2
- package/packages/web/.next/static/webpack/{webpack.73e341375c8d429e.hot-update.js → webpack.5fe9fe8578f9c3d2.hot-update.js} +2 -2
- package/packages/web/.next/static/webpack/webpack.653e365406c0d9ac.hot-update.js +12 -0
- package/packages/web/.next/static/webpack/webpack.6800169a899e3a8b.hot-update.js +12 -0
- package/packages/web/.next/static/webpack/webpack.73c7d02260cc80e4.hot-update.js +12 -0
- package/packages/web/.next/static/webpack/webpack.a2d85d19aa028de1.hot-update.js +12 -0
- package/packages/web/.next/trace +2 -5
- package/packages/web/app/globals.css +197 -51
- package/packages/web/app/layout.tsx +6 -3
- package/packages/web/app/page.tsx +791 -309
- package/packages/web/bun.lock +66 -105
- package/packages/web/next.config.ts +8 -1
- package/packages/web/package.json +5 -2
- package/packages/web/postcss.config.mjs +2 -2
- package/packages/web/public/apple-icon.png +1 -0
- package/packages/web/public/dependency-bottleneck.png +0 -0
- package/packages/web/public/icon-dark-32x32.png +1 -0
- package/packages/web/public/icon-light-32x32.png +1 -0
- package/packages/web/public/icon.svg +1 -0
- package/packages/web/public/nextjs-cve-announcement.png +0 -0
- package/packages/web/public/phantomraven-npm-attack.png +0 -0
- package/packages/web/public/placeholder-logo.png +1 -0
- package/packages/web/public/placeholder-logo.svg +1 -0
- package/packages/web/public/placeholder-user.jpg +1 -0
- package/packages/web/public/placeholder.jpg +1 -0
- package/packages/web/public/placeholder.svg +1 -0
- package/packages/web/public/react-cve-meme.png +0 -0
- package/packages/web/public/wallet-drain-exploit.png +0 -0
- package/packages/web/styles/globals.css +125 -0
- package/packages/web/.next/server/vendor-chunks/@swc.js +0 -55
- package/packages/web/.next/server/vendor-chunks/next.js +0 -3010
- package/packages/web/.next/static/chunks/app-pages-internals.js +0 -182
- package/packages/web/.next/static/chunks/main-app.js +0 -1882
- package/packages/web/.next/static/css/app/layout.css +0 -1237
- package/packages/web/.next/static/webpack/633457081244afec._.hot-update.json +0 -1
- package/packages/web/.next/static/webpack/app/page.6fee6306e0f98869.hot-update.js +0 -22
- package/packages/web/.next/static/webpack/app/page.73e341375c8d429e.hot-update.js +0 -22
- package/packages/web/tailwind.config.ts +0 -48
- /package/packages/web/.next/static/chunks/{polyfills.js → polyfills-42372ed130431b0a.js} +0 -0
- /package/packages/web/.next/static/webpack/{6fee6306e0f98869.webpack.hot-update.json → 653e365406c0d9ac.webpack.hot-update.json} +0 -0
- /package/packages/web/.next/static/webpack/{73e341375c8d429e.webpack.hot-update.json → 6800169a899e3a8b.webpack.hot-update.json} +0 -0
package/docs/cli/push.mdx
CHANGED
|
@@ -1,37 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'opm push'
|
|
3
|
-
description: 'Sign, scan, publish,
|
|
3
|
+
description: 'Sign, scan, publish, register on-chain, write ENS records.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# opm push
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
## What It Does
|
|
11
|
-
|
|
12
|
-
<Steps>
|
|
13
|
-
<Step title="Pack tarball">
|
|
14
|
-
Creates an npm tarball via <code>npm pack</code> and computes SHA-256 checksum over the packed file.
|
|
15
|
-
</Step>
|
|
16
|
-
<Step title="Sign checksum">
|
|
17
|
-
Signs the checksum with ECDSA (secp256k1) using your <code>OPM_SIGNING_KEY</code>.
|
|
18
|
-
</Step>
|
|
19
|
-
<Step title="Resolve ENS">
|
|
20
|
-
Resolves your wallet address to an ENS name (Base Sepolia, then Ethereum Mainnet fallback).
|
|
21
|
-
</Step>
|
|
22
|
-
<Step title="Security scan">
|
|
23
|
-
Runs 3 AI agents in parallel (Claude, Gemini, DeepSeek) to analyze the package. Each agent submits a risk score (0–100) and reasoning on-chain.
|
|
24
|
-
</Step>
|
|
25
|
-
<Step title="Risk check">
|
|
26
|
-
If aggregate score ≥ 80 or risk level is CRITICAL, publishing is blocked.
|
|
27
|
-
</Step>
|
|
28
|
-
<Step title="Publish to npm">
|
|
29
|
-
Publishes the tarball to the npm registry (uses <code>--token</code> or <code>NPM_TOKEN</code> for automation).
|
|
30
|
-
</Step>
|
|
31
|
-
<Step title="Register on-chain">
|
|
32
|
-
Calls <code>OPMRegistry.registerPackage()</code> with package name, version, checksum, signature, and ENS name.
|
|
33
|
-
</Step>
|
|
34
|
-
</Steps>
|
|
8
|
+
The full publish pipeline: sign your package, scan with 3 AI agents, publish to npm, register on-chain, upload audit report, and write ENS text records.
|
|
35
9
|
|
|
36
10
|
## Usage
|
|
37
11
|
|
|
@@ -51,49 +25,58 @@ opm push --otp <code>
|
|
|
51
25
|
|
|
52
26
|
</CodeGroup>
|
|
53
27
|
|
|
28
|
+
## What Happens
|
|
29
|
+
|
|
30
|
+
<Steps>
|
|
31
|
+
<Step title="Pack & sign">
|
|
32
|
+
Creates tarball via `npm pack`, computes SHA-256 checksum, signs with ECDSA using `OPM_SIGNING_KEY`.
|
|
33
|
+
</Step>
|
|
34
|
+
<Step title="Resolve ENS">
|
|
35
|
+
Resolves your wallet to an ENS name (Sepolia → Mainnet fallback).
|
|
36
|
+
</Step>
|
|
37
|
+
<Step title="AI scan">
|
|
38
|
+
3 agents (Claude, Gemini, DeepSeek) analyze in parallel. Each submits a risk score on-chain.
|
|
39
|
+
</Step>
|
|
40
|
+
<Step title="Risk gate">
|
|
41
|
+
Aggregate score ≥ 80 or CRITICAL → **publish blocked**.
|
|
42
|
+
</Step>
|
|
43
|
+
<Step title="Publish to npm">
|
|
44
|
+
Publishes tarball to the npm registry.
|
|
45
|
+
</Step>
|
|
46
|
+
<Step title="Register on-chain">
|
|
47
|
+
Calls `OPMRegistry.registerPackage()` with checksum, signature, ENS name.
|
|
48
|
+
</Step>
|
|
49
|
+
<Step title="Upload report">
|
|
50
|
+
Formatted markdown report uploaded to Fileverse (IPFS-backed, encrypted).
|
|
51
|
+
</Step>
|
|
52
|
+
<Step title="Write ENS records">
|
|
53
|
+
Sets `opm.version`, `opm.checksum`, `opm.fileverse`, `opm.risk_score`, and more on your ENS name. Creates package subname (e.g. `express.djpai.eth`) if you own the parent. Sets contenthash to Fileverse IPFS CID.
|
|
54
|
+
</Step>
|
|
55
|
+
</Steps>
|
|
56
|
+
|
|
54
57
|
## Required Environment Variables
|
|
55
58
|
|
|
56
59
|
| Variable | Purpose |
|
|
57
60
|
|----------|---------|
|
|
58
|
-
| `OPM_SIGNING_KEY` | Ethereum private key for
|
|
59
|
-
| `OPENROUTER_API_KEY` or `OPENAI_API_KEY` |
|
|
60
|
-
| `AGENT_PRIVATE_KEY` | Agent wallet for
|
|
61
|
+
| `OPM_SIGNING_KEY` | Ethereum private key for ECDSA signing |
|
|
62
|
+
| `OPENROUTER_API_KEY` or `OPENAI_API_KEY` | AI agent scanning |
|
|
63
|
+
| `AGENT_PRIVATE_KEY` | Agent wallet for on-chain score submission (needs Base Sepolia ETH) |
|
|
61
64
|
|
|
62
|
-
## Optional
|
|
65
|
+
## Optional
|
|
63
66
|
|
|
64
67
|
| Variable | Purpose |
|
|
65
68
|
|----------|---------|
|
|
66
|
-
| `NPM_TOKEN` | npm automation token (alternative to
|
|
67
|
-
| `FILEVERSE_API_KEY` |
|
|
68
|
-
|
|
69
|
-
## Risk Blocking
|
|
69
|
+
| `NPM_TOKEN` | npm automation token (alternative to `--token` flag) |
|
|
70
|
+
| `FILEVERSE_API_KEY` | Audit report uploads |
|
|
70
71
|
|
|
71
72
|
<Warning>
|
|
72
|
-
Packages with **risk score ≥ 80**
|
|
73
|
+
Packages with **risk score ≥ 80** are blocked. The scan fails, npm publish is skipped, and no on-chain registration occurs.
|
|
73
74
|
</Warning>
|
|
74
75
|
|
|
75
|
-
When blocked, the CLI shows which agents scored the package and the aggregate risk. Fix the issues and run <code>opm push</code> again.
|
|
76
|
-
|
|
77
|
-
## What Gets Stored On-Chain
|
|
78
|
-
|
|
79
|
-
After a successful push, the following data is recorded on the OPMRegistry:
|
|
80
|
-
|
|
81
|
-
| Data | Description |
|
|
82
|
-
|------|-------------|
|
|
83
|
-
| Package name | From <code>package.json</code> |
|
|
84
|
-
| Version | Semantic version |
|
|
85
|
-
| Checksum | SHA-256 hash of the tarball |
|
|
86
|
-
| Signature | ECDSA signature of the checksum |
|
|
87
|
-
| ENS name | Author identity (e.g. <code>vitalik.eth</code>) |
|
|
88
|
-
| Agent scores | Per-agent risk scores and reasoning |
|
|
89
|
-
| Report URI | Fileverse/IPFS link to the full audit report |
|
|
90
|
-
|
|
91
76
|
## Output
|
|
92
77
|
|
|
93
|
-
|
|
94
|
-
- **Report URI** link when uploaded to Fileverse
|
|
95
|
-
- **npm package URL** on successful publish
|
|
78
|
+
Every transaction shows a clickable BaseScan link: score submissions, package registration, ENS record writes. Plus the Fileverse report URI and npm package URL.
|
|
96
79
|
|
|
97
80
|
<Note>
|
|
98
|
-
|
|
81
|
+
See [ENS Text Records](/concepts/ens-records) for the full list of records written during push.
|
|
99
82
|
</Note>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'opm register-agent'
|
|
3
|
-
description: 'Register a new security agent with ZK-verified benchmarks.'
|
|
3
|
+
description: 'Register a new AI security agent with ZK-verified benchmarks.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# opm register-agent
|
|
7
7
|
|
|
8
|
-
Register
|
|
8
|
+
Register your own security agent. Must pass 10 benchmark cases with 100% accuracy and produce a valid ZK proof.
|
|
9
9
|
|
|
10
10
|
## Usage
|
|
11
11
|
|
|
@@ -15,66 +15,34 @@ opm register-agent --name <name> --model <model> [--system-prompt <prompt>]
|
|
|
15
15
|
|
|
16
16
|
| Flag | Required | Description |
|
|
17
17
|
|------|----------|-------------|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
18
|
+
| `--name` | Yes | Agent identifier |
|
|
19
|
+
| `--model` | Yes | LLM model (e.g. `anthropic/claude-sonnet-4-20250514`) |
|
|
20
|
+
| `--system-prompt` | No | Custom system prompt (defaults to OPM security auditor) |
|
|
21
|
+
|
|
22
|
+
## Process
|
|
23
|
+
|
|
24
|
+
1. **Validate** — Checks agent name, model, env vars
|
|
25
|
+
2. **Benchmark** — Sends 10 labeled cases in a single LLM call (3 clean, 7 malicious)
|
|
26
|
+
3. **ZK proof** — Generates proof of 100% accuracy without revealing test data
|
|
27
|
+
4. **Register** — Calls `OPMRegistry.registerAgent()` with name, model, system prompt hash, proof hash
|
|
21
28
|
|
|
22
29
|
## Required Environment Variables
|
|
23
30
|
|
|
24
31
|
| Variable | Purpose |
|
|
25
32
|
|----------|---------|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
29
|
-
## Process
|
|
33
|
+
| `AGENT_PRIVATE_KEY` | Wallet that becomes the agent identity (needs Base Sepolia ETH) |
|
|
34
|
+
| `OPENROUTER_API_KEY` or `OPENAI_API_KEY` | LLM access for benchmark calls |
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
<Step title="Validate configuration">
|
|
33
|
-
Checks agent name, model, and required env vars.
|
|
34
|
-
</Step>
|
|
35
|
-
<Step title="Run benchmark suite">
|
|
36
|
-
Executes 10 labeled test cases across categories: clean, typosquat, malicious, cve, obfuscated, exfiltration, dependency_confusion.
|
|
37
|
-
</Step>
|
|
38
|
-
<Step title="Generate ZK proof">
|
|
39
|
-
Produces a zero-knowledge proof that the agent achieved 100% accuracy without revealing test data or expected outputs.
|
|
40
|
-
</Step>
|
|
41
|
-
<Step title="Register on-chain">
|
|
42
|
-
Calls <code>OPMRegistry.registerAgent()</code> with name, model, system prompt hash, and proof hash.
|
|
43
|
-
</Step>
|
|
44
|
-
</Steps>
|
|
36
|
+
## On Success
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
- BaseScan transaction link shown
|
|
39
|
+
- Agent authorized to submit scores
|
|
40
|
+
- Participates in all future package scans
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|----------|-------|-------------|
|
|
50
|
-
| clean | 3 | Legitimate packages (string utils, math, validator) |
|
|
51
|
-
| typosquat | 1 | Typosquat with credential exfiltration |
|
|
52
|
-
| malicious | 2 | Postinstall shell, SSH key exfiltration |
|
|
53
|
-
| cve | 1 | Known prototype pollution CVE |
|
|
54
|
-
| obfuscated | 1 | Obfuscated reverse shell |
|
|
55
|
-
| exfiltration | 1 | Env var exfiltration on import |
|
|
56
|
-
| dependency_confusion | 1 | Internal scope shadowing + exfiltration |
|
|
42
|
+
## On Failure
|
|
57
43
|
|
|
58
|
-
|
|
44
|
+
Shows which cases were misclassified, expected vs actual verdict, and rejection reason.
|
|
59
45
|
|
|
60
46
|
<Warning>
|
|
61
|
-
**100% accuracy
|
|
47
|
+
**100% accuracy required.** No partial passes. The ZK proof hides test data and individual results — only the commitment hash and proof hash go on-chain.
|
|
62
48
|
</Warning>
|
|
63
|
-
|
|
64
|
-
<Note>
|
|
65
|
-
The ZK proof hides test data and individual results. Only the commitment hash and proof hash are stored on-chain.
|
|
66
|
-
</Note>
|
|
67
|
-
|
|
68
|
-
## Success
|
|
69
|
-
|
|
70
|
-
On success:
|
|
71
|
-
- Etherscan transaction link is shown
|
|
72
|
-
- Agent is authorized to submit scores
|
|
73
|
-
- Agent participates in the next package scan alongside existing agents
|
|
74
|
-
|
|
75
|
-
## Failure
|
|
76
|
-
|
|
77
|
-
On failure, the CLI shows:
|
|
78
|
-
- Which benchmark cases failed and why
|
|
79
|
-
- Expected vs actual risk level and score
|
|
80
|
-
- Rejection reason (e.g. "Agent achieved 90% accuracy (100% required)")
|
package/docs/cli/view.mdx
CHANGED
|
@@ -1,52 +1,35 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'opm view / opm whois'
|
|
3
|
-
description: 'Author profile and published packages.'
|
|
3
|
+
description: 'Author profile, reputation, and published packages.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# opm view / opm whois
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Look up an author's ENS profile, OPM reputation, and published packages.
|
|
9
9
|
|
|
10
10
|
## Usage
|
|
11
11
|
|
|
12
12
|
<CodeGroup>
|
|
13
13
|
|
|
14
|
-
```bash
|
|
15
|
-
opm view
|
|
14
|
+
```bash By ENS name
|
|
15
|
+
opm view djpai.eth
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
```bash Whois (auto-appends .eth)
|
|
19
|
-
opm whois
|
|
19
|
+
opm whois djpai
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
</CodeGroup>
|
|
23
23
|
|
|
24
24
|
<Note>
|
|
25
|
-
|
|
25
|
+
`opm view <name.eth>` shows author profile. `opm view <pkg>` (without `.eth`) delegates to `opm info`.
|
|
26
26
|
</Note>
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## What It Shows
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
- **Identity** — ENS name, wallet address, bio, URL, GitHub, Twitter, email, avatar
|
|
31
|
+
- **Author stats** — Packages published, average reputation score
|
|
32
|
+
- **OPM ENS records** — `opm.version`, `opm.fileverse`, `opm.risk_score`, `opm.packages`, contenthash
|
|
33
|
+
- **Published packages** — Name, version, risk score, checksum, signature status, report link
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
- **Address** — Wallet address
|
|
34
|
-
- **Bio** — ENS description text record
|
|
35
|
-
- **URL** — ENS url record
|
|
36
|
-
- **GitHub** — <code>com.github</code> text record
|
|
37
|
-
- **Twitter** — <code>com.twitter</code> text record
|
|
38
|
-
- **Email** — ENS email record
|
|
39
|
-
- **Avatar** — Rendered from ENS avatar record
|
|
40
|
-
|
|
41
|
-
### Author Stats
|
|
42
|
-
|
|
43
|
-
- **Packages published** — Count from OPMRegistry
|
|
44
|
-
- **Avg reputation** — Risk badge (lower = better)
|
|
45
|
-
|
|
46
|
-
### Published Packages
|
|
47
|
-
|
|
48
|
-
For each package: name, version, risk score, checksum, signature status, and report URI link.
|
|
49
|
-
|
|
50
|
-
<Note>
|
|
51
|
-
No environment variables are required. ENS resolution uses public resolvers.
|
|
52
|
-
</Note>
|
|
35
|
+
No environment variables required. Uses public ENS resolvers.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'ENS Text Records'
|
|
3
|
+
description: 'Package metadata stored on ENS for decentralized discovery.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ENS Text Records
|
|
7
|
+
|
|
8
|
+
When you publish via `opm push`, package metadata is written to your ENS name as text records — creating a decentralized discovery layer independent of the smart contract.
|
|
9
|
+
|
|
10
|
+
## Record Keys
|
|
11
|
+
|
|
12
|
+
### Author-Level (on `djpai.eth`)
|
|
13
|
+
|
|
14
|
+
| Key | Example |
|
|
15
|
+
|-----|---------|
|
|
16
|
+
| `opm.version` | `1.2.3` |
|
|
17
|
+
| `opm.checksum` | `0x8a3f...` |
|
|
18
|
+
| `opm.fileverse` | Fileverse report URI |
|
|
19
|
+
| `opm.risk_score` | `12` |
|
|
20
|
+
| `opm.signature` | ECDSA signature |
|
|
21
|
+
| `opm.contract` | Registry contract address |
|
|
22
|
+
| `opm.packages` | `express,lodash` |
|
|
23
|
+
|
|
24
|
+
### Per-Package (namespaced)
|
|
25
|
+
|
|
26
|
+
`opm.pkg.<name>.version`, `opm.pkg.<name>.checksum`, `opm.pkg.<name>.fileverse`, etc.
|
|
27
|
+
|
|
28
|
+
## Subnames
|
|
29
|
+
|
|
30
|
+
OPM creates ENS subnames like `express.djpai.eth` during `opm push` if you own the parent name. Each subname gets its own text records for per-package resolution.
|
|
31
|
+
|
|
32
|
+
## Contenthash
|
|
33
|
+
|
|
34
|
+
The ENS `contenthash` is set to the Fileverse IPFS CID — read directly from the Fileverse Portal smart contract. This makes audit reports discoverable via standard ENS contenthash resolution.
|
|
35
|
+
|
|
36
|
+
## When Records Are Used
|
|
37
|
+
|
|
38
|
+
- **`opm push`** — Writes all records after on-chain registration
|
|
39
|
+
- **`opm info`** / **`opm view`** — Reads and displays records alongside on-chain data
|
|
40
|
+
- **`opm install express@djpai.eth`** — Resolves the ENS author, reads metadata
|
|
41
|
+
|
|
42
|
+
<Note>
|
|
43
|
+
Writing records requires the signer (`OPM_SIGNING_KEY`) to own/manage the ENS name. Reading is permissionless.
|
|
44
|
+
</Note>
|
|
@@ -1,58 +1,40 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'Multi-Agent Consensus'
|
|
3
|
-
description: 'Three LLMs
|
|
3
|
+
description: 'Three LLMs scan in parallel, submit scores on-chain, aggregate via intelligence-weighted averaging.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Multi-Agent Consensus
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Three different AI models analyze every package in parallel. Each submits an independent risk score on-chain. Scores are aggregated using intelligence-weighted averaging.
|
|
9
9
|
|
|
10
|
-
## Agent
|
|
10
|
+
## Agent Lineup
|
|
11
11
|
|
|
12
|
-
| Agent | OpenRouter
|
|
13
|
-
|
|
12
|
+
| Agent | OpenRouter | OpenAI fallback |
|
|
13
|
+
|-------|-----------|----------------|
|
|
14
14
|
| agent-1 | Claude Sonnet 4 | GPT-4.1 |
|
|
15
15
|
| agent-2 | Gemini 2.5 Flash | GPT-4.1 Mini |
|
|
16
16
|
| agent-3 | DeepSeek Chat | GPT-4.1 Nano |
|
|
17
17
|
|
|
18
|
-
Model diversity reduces single-model blind spots
|
|
18
|
+
Model diversity reduces single-model blind spots. Override via `AGENT1_MODEL`, `AGENT2_MODEL`, `AGENT3_MODEL`.
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## What Each Agent Does
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
1. Receives package source, `package.json`, version history, and any known CVEs
|
|
23
|
+
2. Produces structured JSON: risk score (0-100), risk level, reasoning, vulnerability list, supply chain indicators
|
|
24
|
+
3. Submits score on-chain via `OPMRegistry.submitScore()`
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
- `package.json` and dependency metadata
|
|
26
|
-
- Version history and changelog context
|
|
27
|
-
- CVE/OSV advisory data when available
|
|
26
|
+
Each agent scores a version only once.
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
## Intelligence-Weighted Scoring
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
- **Vulnerability enumeration** with severity, category, file path, and evidence
|
|
33
|
-
- **Supply chain indicators**: install scripts, native bindings, obfuscated code, network calls, filesystem access, process spawning, `eval` usage, environment variable access
|
|
34
|
-
- **Version history analysis**: changelog risk, maintainer changes, dependency graph mutations
|
|
35
|
-
- **Recommendation**: SAFE, CAUTION, WARN, or BLOCK
|
|
36
|
-
|
|
37
|
-
## On-Chain Submission
|
|
38
|
-
|
|
39
|
-
Agent wallets call `OPMRegistry.submitScore(name, version, riskScore, reasoning)` for each package version. Each agent may submit only once per version. Scores are stored in the contract's `versionData` mapping.
|
|
40
|
-
|
|
41
|
-
## Intelligence-Weighted Aggregation
|
|
42
|
-
|
|
43
|
-
Scores are aggregated using model weights from the **Artificial Analysis API**:
|
|
44
|
-
|
|
45
|
-
- **Intelligence Index**: General reasoning and knowledge
|
|
46
|
-
- **Coding Index**: Code generation and analysis capability
|
|
47
|
-
|
|
48
|
-
Weights are applied to each agent's score before computing the mean. This favors higher-capability models when consensus is ambiguous.
|
|
30
|
+
When `ARTIFICIAL_ANALYSIS_API_KEY` is set, scores are weighted by each model's Intelligence Index and Coding Index from the Artificial Analysis API. Higher-capability models carry more weight in the final score.
|
|
49
31
|
|
|
50
32
|
<Note>
|
|
51
|
-
|
|
33
|
+
Without the API key, agents are weighted equally (simple mean).
|
|
52
34
|
</Note>
|
|
53
35
|
|
|
54
|
-
##
|
|
36
|
+
## Provider Routing
|
|
55
37
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
38
|
+
- `OPENROUTER_API_KEY` set → routes through OpenRouter (multi-model)
|
|
39
|
+
- Only `OPENAI_API_KEY` → falls back to GPT-4.1 family
|
|
40
|
+
- Force a provider: `LLM_PROVIDER=openrouter` or `LLM_PROVIDER=openai`
|
|
@@ -1,72 +1,45 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'On-chain Registry'
|
|
3
|
-
description: 'OPMRegistry.sol
|
|
3
|
+
description: 'OPMRegistry.sol on Base Sepolia — packages, scores, authors, agents.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# On-chain Registry
|
|
7
7
|
|
|
8
|
-
The **OPMRegistry** smart contract is the
|
|
8
|
+
The **OPMRegistry** smart contract is the source of truth for package metadata, agent scores, author reputation, and registered agents.
|
|
9
9
|
|
|
10
10
|
## Deployment
|
|
11
11
|
|
|
12
12
|
| Property | Value |
|
|
13
13
|
|----------|-------|
|
|
14
|
-
| **Chain** | Base Sepolia |
|
|
15
|
-
| **
|
|
16
|
-
| **
|
|
17
|
-
| **Explorer** | [BaseScan](https://sepolia.basescan.org/address/0x16684391fc9bf48246B08Afe16d1a57BFa181d48) |
|
|
14
|
+
| **Chain** | Base Sepolia (84532) |
|
|
15
|
+
| **Address** | [`0x16684391fc9bf48246B08Afe16d1a57BFa181d48`](https://sepolia.basescan.org/address/0x16684391fc9bf48246B08Afe16d1a57BFa181d48) |
|
|
16
|
+
| **Solidity** | 0.8.20 |
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
## What It Stores
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
| Data | Description |
|
|
21
|
+
|------|-------------|
|
|
22
|
+
| **Packages** | Name → version → checksum, signature, author, ENS name, report URI |
|
|
23
|
+
| **Agent scores** | Per-version risk scores (0-100) with reasoning from each agent |
|
|
24
|
+
| **Authors** | Wallet → ENS name, cumulative reputation, package count |
|
|
25
|
+
| **Agents** | Authorized agents (owner-set or ZK-verified) with name, model, proof hash |
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|--------|--------|
|
|
25
|
-
| `AuthorProfile` | `addr`, `ensName`, `reputationTotal`, `reputationCount`, `packagesPublished` |
|
|
26
|
-
| `AgentScore` | `agent`, `riskScore`, `reasoning` |
|
|
27
|
-
| `VersionData` | `author`, `checksum`, `signature`, `reportURI`, `scores[]`, `exists` |
|
|
28
|
-
| `Package` | `name`, `versions[]`, `exists` |
|
|
29
|
-
| `RegisteredAgent` | `agentAddress`, `name`, `model`, `systemPromptHash`, `proofHash`, `registeredAt`, `active` |
|
|
27
|
+
## Key Operations
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
- **`registerPackage`** — Store a new version with checksum, signature, and ENS binding
|
|
30
|
+
- **`submitScore`** — Agent submits risk score + reasoning (authorized agents only)
|
|
31
|
+
- **`setReportURI`** — Attach Fileverse/IPFS report link to a version
|
|
32
|
+
- **`registerAgent`** — Permissionless agent registration with ZK proof hash
|
|
33
|
+
- **`getSafestVersion`** — Get the lowest-risk version in a lookback window
|
|
34
|
+
- **`getPackageInfo`** — Full metadata + aggregate score for a version
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|----------|--------|-------------|
|
|
35
|
-
| `registerPackage(name, version, checksum, sig, ensName)` | Public | Register a new package version with checksum, signature, and ENS binding |
|
|
36
|
-
| `submitScore(name, version, riskScore, reasoning)` | Authorized agents | Submit a risk score (0–100) and reasoning for a package version |
|
|
37
|
-
| `setReportURI(name, version, uri)` | Authorized agents | Attach a Fileverse report URI to a package version |
|
|
38
|
-
| `getPackageInfo(name, version)` | View | Retrieve full metadata and aggregate score for a package version |
|
|
39
|
-
| `getScores(name, version)` | View | Return all individual agent scores for a version |
|
|
40
|
-
| `getAggregateScore(name, version)` | View | Compute mean risk score across all agent submissions |
|
|
41
|
-
| `getSafestVersion(name, lookback)` | View | Return the lowest-risk version within a configurable lookback window |
|
|
42
|
-
| `getVersions(name)` | View | List all registered versions of a package |
|
|
43
|
-
| `getAuthorByAddress(addr)` | View | Retrieve author profile by Ethereum address |
|
|
44
|
-
| `getAuthorByENS(ensName)` | View | Resolve author profile by ENS name |
|
|
45
|
-
| `getAuthorReputation(addr)` | View | Compute author's mean risk score across all packages |
|
|
46
|
-
| `registerAgent(name, model, systemPromptHash, proofHash)` | Public | Permissionless agent registration (requires valid ZK proof) |
|
|
47
|
-
|
|
48
|
-
## Events
|
|
49
|
-
|
|
50
|
-
| Event | Parameters |
|
|
51
|
-
|-------|------------|
|
|
52
|
-
| `PackageRegistered` | `name`, `version`, `author`, `ensName` |
|
|
53
|
-
| `ScoreSubmitted` | `name`, `version`, `agent`, `riskScore`, `reasoning` |
|
|
54
|
-
| `ReportURISet` | `name`, `version`, `uri` |
|
|
55
|
-
| `AuthorRegistered` | `addr`, `ensName` |
|
|
56
|
-
| `AgentAuthorized` | `agent`, `status` |
|
|
57
|
-
| `AgentRegistered` | `agent`, `name`, `model`, `systemPromptHash`, `proofHash`, `timestamp` |
|
|
36
|
+
See [Contract Functions](/contract/functions) for the complete API reference.
|
|
58
37
|
|
|
59
38
|
## Author Reputation
|
|
60
39
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
reputation = reputationTotal / reputationCount
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Where `reputationTotal` accumulates each agent's score for every version of every package authored by that address. This provides a cumulative risk score average across all published packages.
|
|
40
|
+
Computed as `reputationTotal / reputationCount` — the average of all agent scores received across all packages published by an author. Lower is better.
|
|
68
41
|
|
|
69
|
-
## Risk Thresholds
|
|
42
|
+
## Risk Thresholds
|
|
70
43
|
|
|
71
44
|
| Constant | Value |
|
|
72
45
|
|----------|-------|
|
|
@@ -1,76 +1,44 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'Security Model'
|
|
3
|
-
description: '
|
|
3
|
+
description: 'Five layers of defense: crypto, AI, CVEs, supply chain checks, and ENS trust.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Security Model
|
|
7
7
|
|
|
8
|
-
OPM
|
|
8
|
+
OPM layers five independent defenses. No single layer is sufficient — together they catch supply chain injection, typosquatting, dependency confusion, maintainer takeover, and known CVEs.
|
|
9
9
|
|
|
10
|
-
## 1. Cryptographic
|
|
10
|
+
## 1. Cryptographic Signing
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
- **ECDSA signature**: secp256k1 signature over the checksum, derived from the author's Ethereum private key
|
|
14
|
-
- **On-chain registration**: Checksum, signature, and author binding stored in `OPMRegistry` on Base Sepolia
|
|
12
|
+
Every package gets a SHA-256 checksum and ECDSA signature (secp256k1) from the author's Ethereum wallet. Both are stored on-chain and verified at install time. Tampered tarballs are rejected.
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
## 2. Multi-Agent AI Scanning
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
Three LLMs (Claude, Gemini, DeepSeek) analyze source code, dependency metadata, and version history in parallel. Each submits a risk score (0-100) on-chain. Scores are aggregated using intelligence-weighted averaging via the Artificial Analysis API.
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
Agents flag: install scripts, native bindings, obfuscated code, network calls, filesystem access, process spawning, `eval` usage, and env var access.
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
- Each agent submits a risk score (0–100) and reasoning on-chain
|
|
24
|
-
- **Intelligence-weighted aggregation** via the Artificial Analysis API (Intelligence Index, Coding Index)
|
|
25
|
-
- Fallback to equal weighting if the API is unavailable
|
|
20
|
+
## 3. CVE Detection
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
<Card title="Agent 1" icon="robot">
|
|
29
|
-
Claude Sonnet 4 (OpenRouter) / GPT-4.1 (OpenAI fallback)
|
|
30
|
-
</Card>
|
|
31
|
-
<Card title="Agent 2" icon="robot">
|
|
32
|
-
Gemini 2.5 Flash (OpenRouter) / GPT-4.1 Mini (OpenAI fallback)
|
|
33
|
-
</Card>
|
|
34
|
-
<Card title="Agent 3" icon="robot">
|
|
35
|
-
DeepSeek Chat (OpenRouter) / GPT-4.1 Nano (OpenAI fallback)
|
|
36
|
-
</Card>
|
|
37
|
-
</CardGroup>
|
|
22
|
+
Real-time lookup against the OSV database (CVE + GHSA advisories) with CVSS v3 severity scoring. CRITICAL CVEs block installation. HIGH CVEs trigger warnings with suggested fix versions.
|
|
38
23
|
|
|
39
|
-
##
|
|
24
|
+
## 4. Supply Chain Checks
|
|
40
25
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
26
|
+
| Check | How |
|
|
27
|
+
|-------|-----|
|
|
28
|
+
| Typosquatting | Levenshtein distance against top npm packages + AI name similarity assessment |
|
|
29
|
+
| Dependency confusion | Scoped vs unscoped name conflicts surfaced during `opm check` |
|
|
30
|
+
| ChainPatrol blocklist | Fallback for packages not in the on-chain registry |
|
|
46
31
|
|
|
47
|
-
##
|
|
32
|
+
## 5. ENS Trust Layer
|
|
48
33
|
|
|
49
|
-
|
|
50
|
-
|-------|-------------|
|
|
51
|
-
| **Typosquat detection** | Package names compared against npm search results and download-count differentials; AI agents assess name similarity |
|
|
52
|
-
| **Dependency confusion** | Scoped vs unscoped name conflicts and internal package shadowing surfaced during `opm check` |
|
|
53
|
-
| **ChainPatrol blocklist** | Fallback blocklist for packages absent from the on-chain registry (requires `CHAINPATROL_API_KEY`) |
|
|
54
|
-
|
|
55
|
-
AI agents also flag: install scripts, native bindings, obfuscated code, network calls, filesystem access, process spawning, `eval` usage, and environment variable access.
|
|
56
|
-
|
|
57
|
-
## 5. Trust Layer
|
|
58
|
-
|
|
59
|
-
- **ENS identity resolution**: Author addresses resolved to ENS names (Sepolia, Mainnet fallback)
|
|
60
|
-
- **On-chain author reputation**: Cumulative risk score average across all published packages
|
|
61
|
-
- **Author profiles**: ENS text records (avatar, description, URL, GitHub, Twitter, email) for human verification
|
|
34
|
+
Author addresses resolve to ENS names. Reputation is computed as the average risk score across all published packages. ENS text records (avatar, GitHub, Twitter) provide human-verifiable identity signals.
|
|
62
35
|
|
|
63
36
|
## Risk Thresholds
|
|
64
37
|
|
|
65
|
-
|
|
|
66
|
-
|
|
38
|
+
| Score | Level | What happens |
|
|
39
|
+
|-------|-------|-------------|
|
|
67
40
|
| 0–20 | LOW | Safe to install |
|
|
68
41
|
| 21–40 | MEDIUM | Flagged for caution |
|
|
69
42
|
| 41–70 | HIGH | Warnings triggered |
|
|
70
43
|
| 71–100 | CRITICAL | High risk |
|
|
71
|
-
|
|
72
|
-
| Threshold | Value | Behavior |
|
|
73
|
-
|-----------|-------|----------|
|
|
74
|
-
| Block threshold (CLI) | 80 | `opm push` blocks publication; `opm install` blocks installation |
|
|
75
|
-
| `HIGH_RISK_THRESHOLD` (contract) | 70 | Packages above trigger warnings |
|
|
76
|
-
| `MEDIUM_RISK_THRESHOLD` (contract) | 40 | Packages above flagged for caution |
|
|
44
|
+
| **≥ 80** | — | **Blocks `opm push` and `opm install`** |
|