@vaultys/mcp-agent 0.1.0
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 +246 -0
- package/dist/bin/audit.d.ts +12 -0
- package/dist/bin/audit.js +137 -0
- package/dist/bin/grant-policy.d.ts +10 -0
- package/dist/bin/grant-policy.js +110 -0
- package/dist/bin/init.d.ts +15 -0
- package/dist/bin/init.js +77 -0
- package/dist/bin/server.d.ts +9 -0
- package/dist/bin/server.js +9 -0
- package/dist/grant-policy.d.ts +16 -0
- package/dist/grant-policy.js +117 -0
- package/dist/src/audit.d.ts +12 -0
- package/dist/src/audit.js +133 -0
- package/dist/src/autoInit.d.ts +40 -0
- package/dist/src/autoInit.js +169 -0
- package/dist/src/policyMiddleware.d.ts +55 -0
- package/dist/src/policyMiddleware.js +194 -0
- package/dist/src/resources/index.d.ts +13 -0
- package/dist/src/resources/index.js +80 -0
- package/dist/src/server.d.ts +19 -0
- package/dist/src/server.js +87 -0
- package/dist/src/tools/filesystem.d.ts +10 -0
- package/dist/src/tools/filesystem.js +44 -0
- package/dist/src/tools/network.d.ts +10 -0
- package/dist/src/tools/network.js +25 -0
- package/dist/src/tools/shell.d.ts +11 -0
- package/dist/src/tools/shell.js +26 -0
- package/dist/srp-grant-policy.d.ts +24 -0
- package/dist/srp-grant-policy.js +177 -0
- package/package.json +55 -0
- package/policy.example.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 VaultysID
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# @vaultys/mcp-agent
|
|
2
|
+
|
|
3
|
+
**Policy-enforced MCP server powered by VaultysID** — cryptographic authorization and tamper-evident audit trail for every AI tool call.
|
|
4
|
+
|
|
5
|
+
Every action your AI agent takes is:
|
|
6
|
+
1. **Authorized** against a signed policy bundle
|
|
7
|
+
2. **Executed** only if allowed
|
|
8
|
+
3. **Receipted** with a cryptographic signature for tamper-evident audit
|
|
9
|
+
|
|
10
|
+
## Quick Start — 2 minutes to Claude Desktop
|
|
11
|
+
|
|
12
|
+
### 1. Add to Claude Desktop
|
|
13
|
+
|
|
14
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"vaultys-agent": {
|
|
20
|
+
"command": "npx",
|
|
21
|
+
"args": ["-y", "@vaultys/mcp-agent"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That's it. Restart Claude Desktop — the server auto-generates a default policy on first run.
|
|
28
|
+
|
|
29
|
+
### 2. (Optional) Set a workspace directory
|
|
30
|
+
|
|
31
|
+
By default the agent uses your current working directory. To restrict file access to a specific project:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"vaultys-agent": {
|
|
37
|
+
"command": "npx",
|
|
38
|
+
"args": ["-y", "@vaultys/mcp-agent"],
|
|
39
|
+
"env": {
|
|
40
|
+
"WORKSPACE_ROOT": "/Users/you/projects/my-app"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 3. (Optional) Customize the policy
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx @vaultys/mcp-agent init --workspace /path/to/project --hours 48
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This creates/updates the signed policy in `~/.vaultys-mcp/`.
|
|
54
|
+
|
|
55
|
+
## What It Does
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
┌──────────────┐ ┌────────────────────────────────────┐
|
|
59
|
+
│ Claude / │◄──MCP──►│ @vaultys/mcp-agent │
|
|
60
|
+
│ Cursor / │ stdio │ │
|
|
61
|
+
│ any client │ │ ┌──────────────────────────────┐ │
|
|
62
|
+
│ │ │ │ Policy Middleware │ │
|
|
63
|
+
│ │ │ │ map tool → capability │ │
|
|
64
|
+
│ │ │ │ evaluate against policy │ │
|
|
65
|
+
│ │ │ │ sign receipt │ │
|
|
66
|
+
│ │ │ └──────────────────────────────┘ │
|
|
67
|
+
│ │ │ │
|
|
68
|
+
│ │ │ Tools: │
|
|
69
|
+
│ │ │ read_file → fs.read:path │
|
|
70
|
+
│ │ │ write_file → fs.write:path │
|
|
71
|
+
│ │ │ list_directory → fs.list:path │
|
|
72
|
+
│ │ │ run_command → proc.exec:bin │
|
|
73
|
+
│ │ │ fetch_url → net.egress:host │
|
|
74
|
+
│ │ │ │
|
|
75
|
+
│ │ │ Audit: ~/.vaultys-mcp/ + ./audit/ │
|
|
76
|
+
└──────────────┘ └────────────────────────────────────┘
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Policy Enforcement
|
|
80
|
+
|
|
81
|
+
Every tool call is mapped to a taxonomy capability and checked against the signed policy:
|
|
82
|
+
|
|
83
|
+
| Tool | Capability | Example Scope |
|
|
84
|
+
| ---------------- | ----------------- | ------------------------- |
|
|
85
|
+
| `read_file` | `fs.read` | `/workspace/src/index.ts` |
|
|
86
|
+
| `write_file` | `fs.write` | `/workspace/output.json` |
|
|
87
|
+
| `list_directory` | `fs.list` | `/workspace/src` |
|
|
88
|
+
| `run_command` | `proc.exec` | `ls`, `grep`, `cat` |
|
|
89
|
+
| `fetch_url` | `net.egress.http` | `api.example.com` |
|
|
90
|
+
|
|
91
|
+
Denied calls return an error message to the AI and are logged with a signed denial receipt.
|
|
92
|
+
|
|
93
|
+
### Default Policy
|
|
94
|
+
|
|
95
|
+
Auto-generated on first run — allows:
|
|
96
|
+
- File read/write/list within `WORKSPACE_ROOT`
|
|
97
|
+
- Safe commands: `ls`, `cat`, `echo`, `wc`, `head`, `tail`, `grep`, `find`, `pwd`, `date`, `whoami`
|
|
98
|
+
- HTTP requests to any host
|
|
99
|
+
- Blocks: `secrets.*`, `pkg.system`, `proc.privilege`
|
|
100
|
+
|
|
101
|
+
## CLI Commands
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Start the MCP server (called by Claude Desktop via npx)
|
|
105
|
+
npx @vaultys/mcp-agent
|
|
106
|
+
|
|
107
|
+
# Interactive setup — customize policy, workspace, validity
|
|
108
|
+
npx @vaultys/mcp-agent init
|
|
109
|
+
npx @vaultys/mcp-agent init --workspace /path --hours 48
|
|
110
|
+
|
|
111
|
+
# Sign a custom policy
|
|
112
|
+
npx @vaultys/mcp-agent grant --policy custom.json --hours 2
|
|
113
|
+
|
|
114
|
+
# Verify the audit trail
|
|
115
|
+
npx @vaultys/mcp-agent audit
|
|
116
|
+
npx @vaultys/mcp-agent audit --tamper <job-id> # demo tampering detection
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or using globally installed commands:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
vaultys-mcp-agent # start server
|
|
123
|
+
vaultys-mcp-init # setup
|
|
124
|
+
vaultys-mcp-grant # sign policy
|
|
125
|
+
vaultys-mcp-audit # verify receipts
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## MCP Resources
|
|
129
|
+
|
|
130
|
+
The server exposes these resources that Claude can read:
|
|
131
|
+
|
|
132
|
+
| Resource URI | Description |
|
|
133
|
+
| --------------------------- | --------------------------- |
|
|
134
|
+
| `vaultys://policy/current` | Active signed policy (JSON) |
|
|
135
|
+
| `vaultys://identity/server` | Server DID and public key |
|
|
136
|
+
| `vaultys://receipts/list` | All receipt summaries |
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
### Environment Variables
|
|
141
|
+
|
|
142
|
+
| Variable | Default | Description |
|
|
143
|
+
| -------------------- | ----------------- | ------------------------------------------- |
|
|
144
|
+
| `WORKSPACE_ROOT` | Current directory | Root directory for file operations |
|
|
145
|
+
| `POLICY_FILE` | (auto-managed) | Path to a custom signed policy file |
|
|
146
|
+
| `AUTHORITY_FILE` | (auto-managed) | Path to authority identity for verification |
|
|
147
|
+
| `VAULTYS_CONFIG_DIR` | `~/.vaultys-mcp` | Configuration directory |
|
|
148
|
+
|
|
149
|
+
### Config Directory (`~/.vaultys-mcp/`)
|
|
150
|
+
|
|
151
|
+
| File | Purpose |
|
|
152
|
+
| ------------------------- | ----------------------------------- |
|
|
153
|
+
| `server.identity.json` | Server's VaultysID (auto-generated) |
|
|
154
|
+
| `authority.secret.json` | Authority private key (keep safe!) |
|
|
155
|
+
| `authority.identity.json` | Authority public key |
|
|
156
|
+
| `policy.signed.json` | Active signed policy |
|
|
157
|
+
|
|
158
|
+
### Custom Policy File
|
|
159
|
+
|
|
160
|
+
Create a JSON policy and sign it:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"version": "1.0",
|
|
165
|
+
"scopes": {
|
|
166
|
+
"fs.read": ["{{WORKSPACE}}/**"],
|
|
167
|
+
"fs.write": ["{{WORKSPACE}}/output/**"],
|
|
168
|
+
"fs.list": ["{{WORKSPACE}}/**"],
|
|
169
|
+
"proc.exec": ["ls", "cat", "grep"],
|
|
170
|
+
"net.egress.http": ["api.mycompany.com"]
|
|
171
|
+
},
|
|
172
|
+
"denied": ["secrets.*", "pkg.system"],
|
|
173
|
+
"constraints": {
|
|
174
|
+
"max_runtime": 60,
|
|
175
|
+
"no_shell_features": true
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`{{WORKSPACE}}` is replaced with the actual workspace path at signing time.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
vaultys-mcp-grant --policy my-policy.json --workspace /my/project --hours 8
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Audit Trail
|
|
187
|
+
|
|
188
|
+
Every tool call produces a signed receipt in `./audit/`:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"job_id": "a1b2c3d4-...",
|
|
193
|
+
"timestamp": "2025-02-12T...",
|
|
194
|
+
"tool": "read_file",
|
|
195
|
+
"decision": "allow",
|
|
196
|
+
"allowed_caps": ["fs.read:/workspace/README.md"],
|
|
197
|
+
"receipt": {
|
|
198
|
+
"intent_hash": "sha256...",
|
|
199
|
+
"policy_hash": "sha256...",
|
|
200
|
+
"exec": { "started": "...", "ended": "...", "exit_code": 0 },
|
|
201
|
+
"broker_signature": "<base64>"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Verify the entire audit trail:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
vaultys-mcp-audit
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Tampered receipts are detected — the cryptographic signature becomes invalid.
|
|
213
|
+
|
|
214
|
+
## Security Model
|
|
215
|
+
|
|
216
|
+
- **Server identity**: Ed25519 keypair generated on first run, persisted in `~/.vaultys-mcp/`
|
|
217
|
+
- **Authority identity**: Separate keypair that signs policies — can be held by a different person/system
|
|
218
|
+
- **Policy signing**: Authority signs the policy bundle; server verifies before loading
|
|
219
|
+
- **Intent signing**: Server signs each execution intent before policy evaluation
|
|
220
|
+
- **Receipt signing**: Server signs each execution outcome for tamper-evident audit
|
|
221
|
+
- **Scope matching**: File paths resolved to absolute paths; glob matching against policy patterns
|
|
222
|
+
- **Denied capabilities**: Explicit deny list checked before scope matching (deny wins)
|
|
223
|
+
|
|
224
|
+
## Development
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
git clone https://github.com/nicmusic/vaultysid
|
|
228
|
+
cd typescript/demos/mcp-agent
|
|
229
|
+
pnpm install
|
|
230
|
+
|
|
231
|
+
# Run tests (29 E2E tests)
|
|
232
|
+
pnpm test
|
|
233
|
+
|
|
234
|
+
# Start server directly (dev mode, no build needed)
|
|
235
|
+
pnpm start
|
|
236
|
+
|
|
237
|
+
# Sign a policy (dev mode)
|
|
238
|
+
pnpm grant-policy
|
|
239
|
+
|
|
240
|
+
# Build for production
|
|
241
|
+
pnpm build
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vaultys-mcp-audit — Verify the cryptographic audit trail.
|
|
4
|
+
*
|
|
5
|
+
* Walks through all signed receipts and verifies broker signatures.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* vaultys-mcp-audit # verify all receipts
|
|
9
|
+
* vaultys-mcp-audit --tamper <job-id> # tamper then verify (demo)
|
|
10
|
+
* vaultys-mcp-audit --dir /path/to/audit # custom audit directory
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vaultys-mcp-audit — Verify the cryptographic audit trail.
|
|
4
|
+
*
|
|
5
|
+
* Walks through all signed receipts and verifies broker signatures.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* vaultys-mcp-audit # verify all receipts
|
|
9
|
+
* vaultys-mcp-audit --tamper <job-id> # tamper then verify (demo)
|
|
10
|
+
* vaultys-mcp-audit --dir /path/to/audit # custom audit directory
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { ExecutionManager, IdManager, MemoryStorage } from "@vaultys/id";
|
|
15
|
+
import { configPath } from "../src/autoInit.js";
|
|
16
|
+
// ── Parse args ──
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
function getArg(name, defaultValue) {
|
|
19
|
+
const idx = args.indexOf(`--${name}`);
|
|
20
|
+
if (idx !== -1 && args[idx + 1])
|
|
21
|
+
return args[idx + 1];
|
|
22
|
+
return defaultValue;
|
|
23
|
+
}
|
|
24
|
+
const AUDIT_DIR = path.resolve(getArg("dir", "audit"));
|
|
25
|
+
const tamperId = getArg("tamper", "");
|
|
26
|
+
async function main() {
|
|
27
|
+
console.log("╔══════════════════════════════════════════════════╗");
|
|
28
|
+
console.log("║ VaultysID Audit Trail Verifier ║");
|
|
29
|
+
console.log("╚══════════════════════════════════════════════════╝");
|
|
30
|
+
console.log();
|
|
31
|
+
// Load server identity for verification
|
|
32
|
+
let serverVaultysId = null;
|
|
33
|
+
const identityPath = configPath("server.identity.json");
|
|
34
|
+
if (fs.existsSync(identityPath)) {
|
|
35
|
+
const data = fs.readFileSync(identityPath, "utf-8");
|
|
36
|
+
const store = MemoryStorage().fromString(data);
|
|
37
|
+
const idm = await IdManager.fromStore(store);
|
|
38
|
+
if (idm) {
|
|
39
|
+
serverVaultysId = idm.vaultysId;
|
|
40
|
+
console.log(`Server DID: ${serverVaultysId.did}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Fallback: try local server.identity.json
|
|
44
|
+
if (!serverVaultysId && fs.existsSync("server.identity.json")) {
|
|
45
|
+
const data = fs.readFileSync("server.identity.json", "utf-8");
|
|
46
|
+
const store = MemoryStorage().fromString(data);
|
|
47
|
+
const idm = await IdManager.fromStore(store);
|
|
48
|
+
if (idm) {
|
|
49
|
+
serverVaultysId = idm.vaultysId;
|
|
50
|
+
console.log(`Server DID: ${serverVaultysId.did}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!serverVaultysId) {
|
|
54
|
+
console.error("⚠ Cannot load server identity — signature verification disabled");
|
|
55
|
+
}
|
|
56
|
+
console.log();
|
|
57
|
+
// Load receipts
|
|
58
|
+
if (!fs.existsSync(AUDIT_DIR)) {
|
|
59
|
+
console.log("No audit records found. Run some tool calls first!");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const files = fs.readdirSync(AUDIT_DIR).filter((f) => f.endsWith(".json")).sort();
|
|
63
|
+
if (files.length === 0) {
|
|
64
|
+
console.log("No audit records found. Run some tool calls first!");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.log(`Found ${files.length} receipt(s)\n`);
|
|
68
|
+
// Optionally tamper
|
|
69
|
+
if (tamperId) {
|
|
70
|
+
const tamperFile = path.join(AUDIT_DIR, `${tamperId}.json`);
|
|
71
|
+
if (fs.existsSync(tamperFile)) {
|
|
72
|
+
const record = JSON.parse(fs.readFileSync(tamperFile, "utf-8"));
|
|
73
|
+
record.receipt.exec.exit_code = 999;
|
|
74
|
+
fs.writeFileSync(tamperFile, JSON.stringify(record, null, 2));
|
|
75
|
+
console.log(`🔧 TAMPERED with receipt ${tamperId} (changed exit_code to 999)\n`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.error(`⚠ Receipt ${tamperId} not found`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let passed = 0;
|
|
82
|
+
let failed = 0;
|
|
83
|
+
let skipped = 0;
|
|
84
|
+
for (const file of files) {
|
|
85
|
+
const filePath = path.join(AUDIT_DIR, file);
|
|
86
|
+
let record;
|
|
87
|
+
try {
|
|
88
|
+
record = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
console.log(` ⚠ ${file}: invalid JSON`);
|
|
92
|
+
skipped++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const receipt = record.receipt;
|
|
96
|
+
const jobId = record.job_id;
|
|
97
|
+
const decision = record.decision;
|
|
98
|
+
const tool = record.tool;
|
|
99
|
+
if (receipt.broker_signature && typeof receipt.broker_signature === "string") {
|
|
100
|
+
receipt.broker_signature = Buffer.from(receipt.broker_signature, "base64");
|
|
101
|
+
}
|
|
102
|
+
let verified = false;
|
|
103
|
+
let sigStatus = "⏭ no key";
|
|
104
|
+
if (serverVaultysId) {
|
|
105
|
+
verified = ExecutionManager.verifyReceipt(receipt, serverVaultysId);
|
|
106
|
+
sigStatus = verified ? "✅ VALID" : "❌ INVALID";
|
|
107
|
+
if (verified)
|
|
108
|
+
passed++;
|
|
109
|
+
else
|
|
110
|
+
failed++;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
skipped++;
|
|
114
|
+
}
|
|
115
|
+
const decisionIcon = decision === "allow" ? "🟢" : "🔴";
|
|
116
|
+
console.log(` ${sigStatus} ${decisionIcon} ${tool.padEnd(16)} ${jobId} ${record.timestamp}`);
|
|
117
|
+
if (!verified && serverVaultysId) {
|
|
118
|
+
console.log(` ⚠ Signature verification FAILED — possible tampering!`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
console.log();
|
|
122
|
+
console.log("─".repeat(52));
|
|
123
|
+
console.log(` Results: ${passed} verified, ${failed} FAILED, ${skipped} skipped`);
|
|
124
|
+
if (failed > 0) {
|
|
125
|
+
console.log();
|
|
126
|
+
console.log(" ⚠ TAMPERING DETECTED in one or more receipts!");
|
|
127
|
+
}
|
|
128
|
+
else if (passed > 0) {
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(" ✅ All receipts verified — audit trail is intact.");
|
|
131
|
+
}
|
|
132
|
+
console.log();
|
|
133
|
+
}
|
|
134
|
+
main().catch((err) => {
|
|
135
|
+
console.error("Error:", err);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vaultys-mcp-grant — Sign a custom policy for the MCP agent.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* vaultys-mcp-grant # default policy, 1 year
|
|
7
|
+
* vaultys-mcp-grant --policy custom.json --hours 2 # custom policy, 2 hours
|
|
8
|
+
* vaultys-mcp-grant --workspace /my/project --hours 48 # custom workspace
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vaultys-mcp-grant — Sign a custom policy for the MCP agent.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* vaultys-mcp-grant # default policy, 1 year
|
|
7
|
+
* vaultys-mcp-grant --policy custom.json --hours 2 # custom policy, 2 hours
|
|
8
|
+
* vaultys-mcp-grant --workspace /my/project --hours 48 # custom workspace
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { ensureConfigDir, configPath, loadOrCreateIdentity, } from "../src/autoInit.js";
|
|
13
|
+
import { ExecutionManager } from "@vaultys/id";
|
|
14
|
+
// ── Parse args ──
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
function getArg(name, defaultValue) {
|
|
17
|
+
const idx = args.indexOf(`--${name}`);
|
|
18
|
+
if (idx !== -1 && args[idx + 1])
|
|
19
|
+
return args[idx + 1];
|
|
20
|
+
return defaultValue;
|
|
21
|
+
}
|
|
22
|
+
const policyFile = getArg("policy", "");
|
|
23
|
+
const hours = parseInt(getArg("hours", String(24 * 365)), 10);
|
|
24
|
+
const workspaceRoot = path.resolve(getArg("workspace", process.cwd()));
|
|
25
|
+
async function main() {
|
|
26
|
+
console.log("╔══════════════════════════════════════════════════╗");
|
|
27
|
+
console.log("║ VaultysID MCP Agent — Grant Policy ║");
|
|
28
|
+
console.log("╚══════════════════════════════════════════════════╝");
|
|
29
|
+
console.log();
|
|
30
|
+
ensureConfigDir();
|
|
31
|
+
// 1. Load or create authority
|
|
32
|
+
const { idm: authorityIdm, created } = await loadOrCreateIdentity(configPath("authority.secret.json"));
|
|
33
|
+
if (created) {
|
|
34
|
+
console.log(`✓ Generated authority identity: ${authorityIdm.vaultysId.did}`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log(`✓ Loaded authority identity: ${authorityIdm.vaultysId.did}`);
|
|
38
|
+
}
|
|
39
|
+
// Export public identity
|
|
40
|
+
const vid = authorityIdm.vaultysId;
|
|
41
|
+
fs.writeFileSync(configPath("authority.identity.json"), JSON.stringify({
|
|
42
|
+
did: vid.did,
|
|
43
|
+
id: Buffer.from(vid.id).toString("base64"),
|
|
44
|
+
fingerprint: vid.fingerprint,
|
|
45
|
+
}, null, 2));
|
|
46
|
+
// 2. Load policy template
|
|
47
|
+
let policyTemplate;
|
|
48
|
+
if (policyFile && fs.existsSync(policyFile)) {
|
|
49
|
+
policyTemplate = JSON.parse(fs.readFileSync(policyFile, "utf-8"));
|
|
50
|
+
console.log(`✓ Loaded custom policy: ${policyFile}`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// Use built-in default
|
|
54
|
+
policyTemplate = {
|
|
55
|
+
version: "1.0",
|
|
56
|
+
scopes: {
|
|
57
|
+
"fs.read": ["{{WORKSPACE}}/**"],
|
|
58
|
+
"fs.write": ["{{WORKSPACE}}/**"],
|
|
59
|
+
"fs.list": ["{{WORKSPACE}}/**"],
|
|
60
|
+
"proc.exec": ["ls", "cat", "echo", "wc", "head", "tail", "grep", "find", "pwd", "date", "whoami"],
|
|
61
|
+
"net.egress.http": ["*"],
|
|
62
|
+
},
|
|
63
|
+
denied: ["secrets.*", "pkg.system", "proc.privilege"],
|
|
64
|
+
constraints: {
|
|
65
|
+
max_runtime: 300,
|
|
66
|
+
no_shell_features: true,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
console.log("✓ Using default policy template");
|
|
70
|
+
}
|
|
71
|
+
// 3. Substitute {{WORKSPACE}} placeholder
|
|
72
|
+
if (policyTemplate.scopes && typeof policyTemplate.scopes === "object") {
|
|
73
|
+
const scopes = policyTemplate.scopes;
|
|
74
|
+
for (const [key, patterns] of Object.entries(scopes)) {
|
|
75
|
+
if (Array.isArray(patterns)) {
|
|
76
|
+
scopes[key] = patterns.map((p) => p.replace(/\{\{WORKSPACE\}\}/g, workspaceRoot));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// 4. Add time bounds
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
const policy = {
|
|
83
|
+
...policyTemplate,
|
|
84
|
+
not_before: now,
|
|
85
|
+
not_after: now + hours * 3600_000,
|
|
86
|
+
};
|
|
87
|
+
console.log(` Workspace: ${workspaceRoot}`);
|
|
88
|
+
console.log(` Valid from: ${new Date(now).toISOString()}`);
|
|
89
|
+
console.log(` Expires: ${new Date(now + hours * 3600_000).toISOString()}`);
|
|
90
|
+
console.log(` Duration: ${hours} hours`);
|
|
91
|
+
// 5. Sign
|
|
92
|
+
const em = new ExecutionManager(authorityIdm);
|
|
93
|
+
const signed = await em.signPolicy(policy);
|
|
94
|
+
// 6. Persist
|
|
95
|
+
const serializable = {
|
|
96
|
+
...signed,
|
|
97
|
+
signature: signed.signature
|
|
98
|
+
? Buffer.from(signed.signature).toString("base64")
|
|
99
|
+
: undefined,
|
|
100
|
+
};
|
|
101
|
+
fs.writeFileSync(configPath("policy.signed.json"), JSON.stringify(serializable, null, 2));
|
|
102
|
+
console.log();
|
|
103
|
+
console.log(`✓ Signed policy saved to: ${configPath("policy.signed.json")}`);
|
|
104
|
+
console.log();
|
|
105
|
+
console.log("Run 'npx @vaultys/mcp-agent' to start the server with this policy.");
|
|
106
|
+
}
|
|
107
|
+
main().catch((err) => {
|
|
108
|
+
console.error("Error:", err);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vaultys-mcp-init — Interactive configuration for the MCP agent.
|
|
4
|
+
*
|
|
5
|
+
* Creates or updates configuration in ~/.vaultys-mcp/:
|
|
6
|
+
* - Server identity (auto-generated if missing)
|
|
7
|
+
* - Authority identity (auto-generated if missing)
|
|
8
|
+
* - Signed policy (customizable)
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx @vaultys/mcp-agent init # interactive setup
|
|
12
|
+
* vaultys-mcp-init --hours 48 # 48-hour policy
|
|
13
|
+
* vaultys-mcp-init --workspace /my/dir # custom workspace root
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
package/dist/bin/init.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vaultys-mcp-init — Interactive configuration for the MCP agent.
|
|
4
|
+
*
|
|
5
|
+
* Creates or updates configuration in ~/.vaultys-mcp/:
|
|
6
|
+
* - Server identity (auto-generated if missing)
|
|
7
|
+
* - Authority identity (auto-generated if missing)
|
|
8
|
+
* - Signed policy (customizable)
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx @vaultys/mcp-agent init # interactive setup
|
|
12
|
+
* vaultys-mcp-init --hours 48 # 48-hour policy
|
|
13
|
+
* vaultys-mcp-init --workspace /my/dir # custom workspace root
|
|
14
|
+
*/
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import { ensureConfigDir, loadOrCreateIdentity, configPath, signAndPersistPolicy, } from "../src/autoInit.js";
|
|
17
|
+
// ── Parse args ──
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
function getArg(name, defaultValue) {
|
|
20
|
+
const idx = args.indexOf(`--${name}`);
|
|
21
|
+
if (idx !== -1 && args[idx + 1])
|
|
22
|
+
return args[idx + 1];
|
|
23
|
+
return defaultValue;
|
|
24
|
+
}
|
|
25
|
+
const hours = parseInt(getArg("hours", String(24 * 365)), 10);
|
|
26
|
+
const workspaceRoot = path.resolve(getArg("workspace", process.cwd()));
|
|
27
|
+
async function main() {
|
|
28
|
+
console.log("╔══════════════════════════════════════════════════╗");
|
|
29
|
+
console.log("║ VaultysID MCP Agent — Setup ║");
|
|
30
|
+
console.log("╚══════════════════════════════════════════════════╝");
|
|
31
|
+
console.log();
|
|
32
|
+
const dir = ensureConfigDir();
|
|
33
|
+
console.log(`Config directory: ${dir}`);
|
|
34
|
+
console.log();
|
|
35
|
+
// 1. Server identity
|
|
36
|
+
const { idm: serverIdm, created: serverCreated } = await loadOrCreateIdentity(configPath("server.identity.json"));
|
|
37
|
+
if (serverCreated) {
|
|
38
|
+
console.log(`✓ Generated server identity: ${serverIdm.vaultysId.did}`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.log(`✓ Server identity exists: ${serverIdm.vaultysId.did}`);
|
|
42
|
+
}
|
|
43
|
+
// 2. Authority + Policy
|
|
44
|
+
const { signedPolicy, authorityIdm } = await signAndPersistPolicy(workspaceRoot, hours);
|
|
45
|
+
console.log(`✓ Authority DID: ${authorityIdm.vaultysId.did}`);
|
|
46
|
+
console.log(`✓ Policy signed (${hours} hours validity)`);
|
|
47
|
+
console.log(` Workspace: ${workspaceRoot}`);
|
|
48
|
+
if (signedPolicy.not_before) {
|
|
49
|
+
console.log(` Valid from: ${new Date(signedPolicy.not_before).toISOString()}`);
|
|
50
|
+
}
|
|
51
|
+
if (signedPolicy.not_after) {
|
|
52
|
+
console.log(` Expires: ${new Date(signedPolicy.not_after).toISOString()}`);
|
|
53
|
+
}
|
|
54
|
+
console.log();
|
|
55
|
+
console.log("┌──────────────────────────────────────────────────┐");
|
|
56
|
+
console.log("│ Setup complete! │");
|
|
57
|
+
console.log("├──────────────────────────────────────────────────┤");
|
|
58
|
+
console.log("│ │");
|
|
59
|
+
console.log("│ Add to Claude Desktop config: │");
|
|
60
|
+
console.log("│ │");
|
|
61
|
+
console.log('│ "mcpServers": { │');
|
|
62
|
+
console.log('│ "vaultys-agent": { │');
|
|
63
|
+
console.log('│ "command": "npx", │');
|
|
64
|
+
console.log('│ "args": ["@vaultys/mcp-agent"] │');
|
|
65
|
+
console.log("│ } │");
|
|
66
|
+
console.log("│ } │");
|
|
67
|
+
console.log("│ │");
|
|
68
|
+
console.log("│ Config file location (macOS): │");
|
|
69
|
+
console.log("│ ~/Library/Application Support/Claude/ │");
|
|
70
|
+
console.log("│ claude_desktop_config.json │");
|
|
71
|
+
console.log("│ │");
|
|
72
|
+
console.log("└──────────────────────────────────────────────────┘");
|
|
73
|
+
}
|
|
74
|
+
main().catch((err) => {
|
|
75
|
+
console.error("Error:", err);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Grant Policy CLI
|
|
4
|
+
*
|
|
5
|
+
* Creates an authority identity and signs a policy bundle.
|
|
6
|
+
* Outputs:
|
|
7
|
+
* - policy.signed.json — the signed policy (loaded by the MCP server)
|
|
8
|
+
* - authority.identity.json — the authority's public key (for verification)
|
|
9
|
+
* - authority.secret.json — the authority's full identity (keep safe!)
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* pnpm grant-policy # uses policy.example.json, 24h validity
|
|
13
|
+
* pnpm grant-policy -- --policy custom.json # custom policy file
|
|
14
|
+
* pnpm grant-policy -- --hours 2 # 2-hour validity window
|
|
15
|
+
*/
|
|
16
|
+
export {};
|