@shield-agent/kya 0.5.0 → 0.6.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.
Files changed (74) hide show
  1. package/.mcp.json +1 -1
  2. package/LIMITATIONS.md +1 -0
  3. package/README.md +30 -3
  4. package/amp/settings.example.json +1 -1
  5. package/catalog/agent-trust-baseline-v0.json +103 -0
  6. package/claude/claude_desktop_config.example.json +1 -1
  7. package/cline/cline_mcp_settings.example.json +1 -1
  8. package/copilot/mcp-config.example.json +1 -1
  9. package/dist/certify/attest.d.ts +10 -0
  10. package/dist/certify/attest.d.ts.map +1 -0
  11. package/dist/certify/attest.js +105 -0
  12. package/dist/certify/attest.js.map +1 -0
  13. package/dist/certify/catalog.d.ts +70 -0
  14. package/dist/certify/catalog.d.ts.map +1 -0
  15. package/dist/certify/catalog.js +245 -0
  16. package/dist/certify/catalog.js.map +1 -0
  17. package/dist/certify/evaluate.d.ts +85 -0
  18. package/dist/certify/evaluate.d.ts.map +1 -0
  19. package/dist/certify/evaluate.js +175 -0
  20. package/dist/certify/evaluate.js.map +1 -0
  21. package/dist/certify/render.d.ts +6 -0
  22. package/dist/certify/render.d.ts.map +1 -0
  23. package/dist/certify/render.js +242 -0
  24. package/dist/certify/render.js.map +1 -0
  25. package/dist/cli.d.ts +1 -1
  26. package/dist/cli.d.ts.map +1 -1
  27. package/dist/cli.js +56 -3
  28. package/dist/cli.js.map +1 -1
  29. package/dist/commands/certify.d.ts +48 -0
  30. package/dist/commands/certify.d.ts.map +1 -0
  31. package/dist/commands/certify.js +229 -0
  32. package/dist/commands/certify.js.map +1 -0
  33. package/dist/commands/connect.d.ts +1 -8
  34. package/dist/commands/connect.d.ts.map +1 -1
  35. package/dist/commands/connect.js +3 -39
  36. package/dist/commands/connect.js.map +1 -1
  37. package/dist/commands/wire-hooks.d.ts.map +1 -1
  38. package/dist/commands/wire-hooks.js +2 -1
  39. package/dist/commands/wire-hooks.js.map +1 -1
  40. package/dist/config.d.ts +21 -1
  41. package/dist/config.d.ts.map +1 -1
  42. package/dist/config.js +42 -9
  43. package/dist/config.js.map +1 -1
  44. package/dist/fs-atomic.d.ts +9 -0
  45. package/dist/fs-atomic.d.ts.map +1 -0
  46. package/dist/fs-atomic.js +44 -0
  47. package/dist/fs-atomic.js.map +1 -0
  48. package/dist/index.d.ts +8 -1
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +8 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/receipt/enrich.d.ts +6 -0
  53. package/dist/receipt/enrich.d.ts.map +1 -1
  54. package/dist/receipt/enrich.js +22 -0
  55. package/dist/receipt/enrich.js.map +1 -1
  56. package/dist/sign/evidence-bundle.d.ts +59 -0
  57. package/dist/sign/evidence-bundle.d.ts.map +1 -0
  58. package/dist/sign/evidence-bundle.js +193 -0
  59. package/dist/sign/evidence-bundle.js.map +1 -0
  60. package/droid/README.md +1 -1
  61. package/droid/mcp.example.json +1 -1
  62. package/gemini/settings.example.json +1 -1
  63. package/grok/README.md +1 -1
  64. package/kilo/kilo.example.json +1 -1
  65. package/kimi/mcp.example.json +1 -1
  66. package/kiro/mcp.example.json +1 -1
  67. package/manifest.json +1 -1
  68. package/mastracode/mcp.example.json +1 -1
  69. package/mcp.json +1 -1
  70. package/openai/codex.config.example.toml +1 -1
  71. package/opencode/opencode.example.json +1 -1
  72. package/package.json +2 -1
  73. package/qwen/settings.example.json +1 -1
  74. package/server.json +2 -2
package/.mcp.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "shield-kya": {
4
4
  "command": "npx",
5
- "args": ["--no-install", "@shield-agent/kya@0.5.0", "serve-mcp", "--stdio"],
5
+ "args": ["--no-install", "@shield-agent/kya@0.6.0", "serve-mcp", "--stdio"],
6
6
  "env": {
7
7
  "KYA_BASE_URL": "https://shield-agent.com",
8
8
  "KYA_API_KEY": "${KYA_API_KEY}",
package/LIMITATIONS.md CHANGED
@@ -32,3 +32,4 @@
32
32
  - Grok (grok.com) custom connectors need a public HTTPS URL. Localhost and private IPs are rejected. If the UI only offers OAuth or no auth, use Codex, Gemini CLI, or the xAI SDK instead of turning auth off.
33
33
  - Local HTTP MCP remains loopback-only. Do not bind it to a public interface.
34
34
  - Optional `kya sandbox` (Firecracker) is a runtime wrap beside the gate, not a hypervisor product. Binaries are not in npm. Default off (`KYA_SANDBOX`). MCP still never executes spawn/exec. Kernel bugs and unwrapped shells remain residual risk (see ADR 0007).
35
+ - `kya certify` is evidence-only: it reads local state (trail, ORR, wired hosts, sandboxes, receipts, showback, attestations) and writes a gap report. It never ALLOWs, DENYs, approves, or blocks a tool call, and it is not a second PEP. `pass` means "local evidence shows no gap" — not certification by any third party. Signed evidence bundles (`--sign`) are self-signed: they prove integrity and continuity of a key, not identity; identity binding is the hosted verification product. Attestations are unverified operator statements recorded locally in `.kya/attestations.json`.
package/README.md CHANGED
@@ -130,10 +130,18 @@ Tag sessions with `KYA_HOST=ide` or `KYA_HOST=runtime`. Same policy path either
130
130
  | `KYA_AGENT_ID` | After register | Agent principal id |
131
131
  | `KYA_MCP_PORT` | No (default `3920`) | HTTP MCP listen port |
132
132
  | `KYA_OFFLINE` | No | `1`/`true` for sample evaluate |
133
+ | `KYA_HOLD` | No | `1`/`true` for the org Hold path (REQUIRE_APPROVE opens a human ticket) |
133
134
  | `KYA_DASH_PLAN` | No | `enterprise` unlocks licensed TUI panes |
134
135
  | `KYA_DIFF_PREVIEW` | No | `0` disables clipped change previews on the receipt |
135
136
  | `KYA_RECEIPT_AUTO` | No | `0` disables auto-open receipt after wrap; `1` forces |
136
137
 
138
+ Gate mode can also be pinned in the project's `.kya/config.json`:
139
+ `{"gateMode": "hold"}` or `{"gateMode": "offline"}` (exact values only —
140
+ anything else is ignored). The gate itself (wrap / hook / eval) honors it
141
+ with the precedence flags > env > config > observe, and `kya certify`
142
+ reports through the same resolver, so a certify pass on gate mode always
143
+ reflects how the gate actually runs.
144
+
137
145
  ## MCP tools
138
146
 
139
147
  | Tool | Role |
@@ -149,7 +157,7 @@ MCP Registry entry: `server.json` plus package `mcpName` `io.github.The-Pixel-Bo
149
157
  "mcpServers": {
150
158
  "shield-kya": {
151
159
  "command": "npx",
152
- "args": ["--no-install", "@shield-agent/kya@0.5.0", "serve-mcp", "--stdio"],
160
+ "args": ["--no-install", "@shield-agent/kya@0.6.0", "serve-mcp", "--stdio"],
153
161
  "env": {
154
162
  "KYA_BASE_URL": "http://127.0.0.1:8090",
155
163
  "KYA_API_KEY": "${KYA_API_KEY}",
@@ -176,7 +184,7 @@ npx @shield-agent/kya reject --id <approval-id>
176
184
 
177
185
  ```bash
178
186
  # Prefer a preinstalled package (no registry auto-install):
179
- npx --no-install @shield-agent/kya@0.5.0 serve-mcp --stdio
187
+ npx --no-install @shield-agent/kya@0.6.0 serve-mcp --stdio
180
188
  # Or after npm i -g / local install:
181
189
  kya serve-mcp --stdio
182
190
  ```
@@ -187,7 +195,7 @@ Copy `claude/claude_desktop_config.example.json` into Claude Desktop MCP setting
187
195
 
188
196
  ## OpenAI (Codex / Responses)
189
197
 
190
- **Codex CLI / IDE:** copy `openai/codex.config.example.toml` into `~/.codex/config.toml`. Local stdio uses `npx --no-install @shield-agent/kya@0.5.0 serve-mcp --stdio`. Hosted Codex uses `url = "https://shield-agent.com/mcp"` with `bearer_token_env_var = "KYA_API_KEY"`.
198
+ **Codex CLI / IDE:** copy `openai/codex.config.example.toml` into `~/.codex/config.toml`. Local stdio uses `npx --no-install @shield-agent/kya@0.6.0 serve-mcp --stdio`. Hosted Codex uses `url = "https://shield-agent.com/mcp"` with `bearer_token_env_var = "KYA_API_KEY"`.
191
199
 
192
200
  **Responses API:** see `openai/responses-mcp.example.json` (`server_url` + `Authorization: Bearer <KYA_API_KEY>`).
193
201
 
@@ -237,6 +245,24 @@ npx @shield-agent/kya orr run --path . --out ./orr-report --producer harness.age
237
245
 
238
246
  ORR is a reporting board. Scanners, `--scorecard`, and `harness.agentshield` are evidence. They never ALLOW a high-stakes side effect, so they are not a second policy gate. AgentShield is optional and read-only: no `--fix`, no MiniClaw, no runtime hook. This package does not depend on `ecc-agentshield`. If you pass `--producer harness.agentshield` and have neither `--agentshield-json` nor an `agentshield` binary, ORR records a coverage gap and still exits 0. Explicit `--producer` always attempts; `--skip-optional-producers` only skips producers you did not ask for.
239
247
 
248
+ ## Certify (continuous agent assurance)
249
+
250
+ `kya certify` evaluates the open **Agent Trust Baseline** catalog (`catalog/agent-trust-baseline-v0.json` — 30 requirements across Data & Privacy, Security, Safety, Reliability, Accountability, Society) against local evidence: the global trail, ORR output, wired hosts, sandbox inventory, receipts, showback, and your recorded attestations. It writes a gap report to `.kya/certify/` (JSON + Markdown + HTML). The gap list is your work plan.
251
+
252
+ ```bash
253
+ npx @shield-agent/kya certify # gap report; exit 1 when gaps exist (CI-friendly)
254
+ npx @shield-agent/kya certify --open # open the HTML report
255
+ npx @shield-agent/kya certify --fail-on never # report only, always exit 0
256
+ npx @shield-agent/kya certify --attest SOC-01 --text "Acceptable-use policy: https://example.com/aup"
257
+ npx @shield-agent/kya certify --sign # also emit a signed evidence-bundle.json
258
+ ```
259
+
260
+ Certify is **evidence-only**. It never ALLOWs, DENYs, or blocks anything — the sole PEP remains Shield KYA. Trail-based machine checks never pass on an empty trail (they report `insufficient_evidence`). What a machine cannot check is covered by explicit local attestations (`--attest`), recorded in `.kya/attestations.json` — unverified operator statements, labeled as such.
261
+
262
+ `--sign` emits `evidence-bundle.json`: canonical JSON, ed25519-signed by a locally generated key (`~/.kya/keys/evidence-ed25519.json`, mode 0600, auto-created on first use). Each `--sign` run prints the signing key fingerprint; when the key was just created the CLI notes that key continuity resets there (earlier bundles stay verifiable only under the old pubkey). A self-signed developer key proves bundle **integrity** and **continuity of a key** — **not identity**. Identity binding and the verified badge are the hosted verification product (separate). The bundle format is open and documented in `docs/certify.md`; anyone can verify a bundle offline with the embedded pubkey.
263
+
264
+ Everything here is local, free, and offline: no account, no network calls, no license check. The catalog is MIT-licensed and PRs are welcome.
265
+
240
266
  ## Optional sandbox wrap (Firecracker)
241
267
 
242
268
  Beside the gate, not inside MCP. Opt-in only:
@@ -272,6 +298,7 @@ pnpm build
272
298
  - [Per-host recipes (23 hosts)](docs/hosts/)
273
299
  - [OTLP metrics (OSS + hosted)](docs/otlp.md)
274
300
  - [OWASP MCP governance map](docs/owasp-mcp-governance.md)
301
+ - [kya certify — Agent Trust Baseline gap reports](docs/certify.md)
275
302
  - [Hosted operator SSO / SCIM (not in OSS CLI)](docs/hosted-operator-sso.md)
276
303
  - See also `LIMITATIONS.md` in this repo
277
304
 
@@ -2,7 +2,7 @@
2
2
  "amp.mcpServers": {
3
3
  "shield-kya": {
4
4
  "command": "npx",
5
- "args": ["--no-install", "@shield-agent/kya@0.5.0", "serve-mcp", "--stdio"],
5
+ "args": ["--no-install", "@shield-agent/kya@0.6.0", "serve-mcp", "--stdio"],
6
6
  "env": {
7
7
  "KYA_BASE_URL": "https://shield-agent.com",
8
8
  "KYA_API_KEY": "${KYA_API_KEY}",
@@ -0,0 +1,103 @@
1
+ {
2
+ "id": "agent-trust-baseline",
3
+ "version": "0.1.0",
4
+ "updated": "2026-09-18",
5
+ "domains": ["data-privacy", "security", "safety", "reliability", "accountability", "society"],
6
+ "requirements": [
7
+ { "id": "DP-01", "domain": "data-privacy", "title": "Agent tool calls are intercepted and recorded",
8
+ "text": "At least one coding host is wired to the KYA gate or PreToolUse hook, so agent tool calls are evaluated and written to the trail before execution.",
9
+ "severity": "high", "check": { "kind": "hooks_wired", "min": 1 } },
10
+ { "id": "DP-02", "domain": "data-privacy", "title": "No high-stakes writes allowed in hold mode",
11
+ "text": "In hold mode the gate must not ALLOW high-stakes writes; every HIGH_STAKES_WRITE must be DENY or REQUIRE_APPROVE.",
12
+ "severity": "critical", "check": { "kind": "trail_zero", "windowDays": 30, "match": { "verdict": "ALLOW", "reasonCode": "HIGH_STAKES_WRITE", "mode": "hold" } } },
13
+ { "id": "DP-03", "domain": "data-privacy", "title": "No shell execution allowed in hold mode",
14
+ "text": "In hold mode the gate must not ALLOW shell execution; every SHELL_EXEC must be DENY or REQUIRE_APPROVE.",
15
+ "severity": "critical", "check": { "kind": "trail_zero", "windowDays": 30, "match": { "verdict": "ALLOW", "reasonCode": "SHELL_EXEC", "mode": "hold" } } },
16
+ { "id": "DP-04", "domain": "data-privacy", "title": "Redaction by construction",
17
+ "text": "Raw tool arguments and secrets are never persisted to the trail, receipts, or reports; only hashes and clipped redacted previews.",
18
+ "severity": "high", "check": { "kind": "attest", "prompt": "Confirm the agent configuration never persists raw secrets or full arguments to local evidence stores." } },
19
+ { "id": "DP-05", "domain": "data-privacy", "title": "Bounded local evidence retention",
20
+ "text": "Trail and receipt retention is bounded (rotation or operator deletion policy) and the retention window is known.",
21
+ "severity": "medium", "check": { "kind": "attest", "prompt": "Confirm trail/receipt retention is bounded and state the retention window." } },
22
+
23
+ { "id": "SEC-01", "domain": "security", "title": "Gate enforces, not observe-only",
24
+ "text": "The policy gate runs in an enforcing mode (hold or offline), not observe-only.",
25
+ "severity": "high", "check": { "kind": "config_mode", "not": "observe" } },
26
+ { "id": "SEC-02", "domain": "security", "title": "Deny path is exercised",
27
+ "text": "At least one DENY verdict in the last 90 days proves enforcement is live, not theoretical.",
28
+ "severity": "medium", "check": { "kind": "trail_min", "windowDays": 90, "match": { "verdict": "DENY" }, "min": 1 } },
29
+ { "id": "SEC-03", "domain": "security", "title": "Human approval path is exercised",
30
+ "text": "At least one REQUIRE_APPROVE verdict in the last 90 days proves the human gate is reachable.",
31
+ "severity": "medium", "check": { "kind": "trail_min", "windowDays": 90, "match": { "verdict": "REQUIRE_APPROVE" }, "min": 1 } },
32
+ { "id": "SEC-04", "domain": "security", "title": "ORR security posture is not red",
33
+ "text": "The security_platform category of the latest ORR report is green or amber.",
34
+ "severity": "high", "check": { "kind": "orr_category", "id": "security_platform", "max": "amber" } },
35
+ { "id": "SEC-05", "domain": "security", "title": "No unknown tools allowed in hold mode",
36
+ "text": "In hold mode the gate must not ALLOW calls it cannot classify (UNKNOWN_TOOL).",
37
+ "severity": "high", "check": { "kind": "trail_zero", "windowDays": 30, "match": { "verdict": "ALLOW", "reasonCode": "UNKNOWN_TOOL", "mode": "hold" } } },
38
+
39
+ { "id": "SAFE-01", "domain": "safety", "title": "Never-events are never allowed",
40
+ "text": "Tools on the never-list were never ALLOWed in the last 365 days.",
41
+ "severity": "critical", "check": { "kind": "trail_zero", "windowDays": 365, "match": { "verdict": "ALLOW", "neverEvent": true } } },
42
+ { "id": "SAFE-02", "domain": "safety", "title": "Sandbox available for high-risk execution",
43
+ "text": "At least one sandbox record exists for isolating high-risk tool execution.",
44
+ "severity": "medium", "check": { "kind": "sandbox_inventory", "min": 1 } },
45
+ { "id": "SAFE-03", "domain": "safety", "title": "Latest ORR overall is not red",
46
+ "text": "The overall rating of the latest ORR report is green or amber.",
47
+ "severity": "high", "check": { "kind": "orr_overall", "max": "amber" } },
48
+ { "id": "SAFE-04", "domain": "safety", "title": "Destructive operations require human approval",
49
+ "text": "Destructive or irreversible tool calls map to REQUIRE_APPROVE or DENY in policy, never silent ALLOW.",
50
+ "severity": "critical", "check": { "kind": "attest", "prompt": "Confirm destructive/irreversible tool calls map to REQUIRE_APPROVE or DENY, never silent ALLOW." } },
51
+ { "id": "SAFE-05", "domain": "safety", "title": "Blocked actions have a retry policy",
52
+ "text": "A documented policy bounds automated retries of DENYed or held actions so blocks cannot be brute-forced.",
53
+ "severity": "medium", "check": { "kind": "attest", "prompt": "Confirm a policy bounds automated retries of blocked actions (no bypass loops)." } },
54
+
55
+ { "id": "REL-01", "domain": "reliability", "title": "Trail recording is active",
56
+ "text": "At least one trail event in the last 7 days proves the evidence pipeline is live.",
57
+ "severity": "high", "check": { "kind": "trail_min", "windowDays": 7, "match": {}, "min": 1 } },
58
+ { "id": "REL-02", "domain": "reliability", "title": "Unknown-tool rate is bounded",
59
+ "text": "UNKNOWN_TOOL calls stay at or below 5% of evaluations over the last 30 days.",
60
+ "severity": "medium", "check": { "kind": "trail_ratio", "windowDays": 30, "match": { "reasonCode": "UNKNOWN_TOOL" }, "maxRatio": 0.05 } },
61
+ { "id": "REL-03", "domain": "reliability", "title": "ORR evidence is fresh (30 days)",
62
+ "text": "The latest ORR report is at most 30 days old.",
63
+ "severity": "medium", "check": { "kind": "orr_fresh", "maxAgeDays": 30 } },
64
+ { "id": "REL-04", "domain": "reliability", "title": "Evidence pipeline continuity is monitored",
65
+ "text": "The report daemon / evidence pipeline restarts on failure under a named supervisor (pm2, systemd, monit, launchd).",
66
+ "severity": "low", "check": { "kind": "attest", "prompt": "Name the supervisor that restarts the evidence pipeline on failure." } },
67
+ { "id": "REL-05", "domain": "reliability", "title": "Receipts are generated",
68
+ "text": "At least one activity receipt exists in .kya/receipts.",
69
+ "severity": "low", "check": { "kind": "receipts_present", "min": 1 } },
70
+
71
+ { "id": "ACC-01", "domain": "accountability", "title": "Session identity on all events",
72
+ "text": "Every trail event carries a sessionId (schema-enforced) traceable to an operator or run.",
73
+ "severity": "high", "check": { "kind": "attest", "prompt": "Confirm every trail event carries a sessionId (schema-enforced) and sessions are traceable to an operator or run." } },
74
+ { "id": "ACC-02", "domain": "accountability", "title": "Project attribution on events",
75
+ "text": "Trail events carry project attribution (working-directory basename) and projects map to owned repositories.",
76
+ "severity": "medium", "check": { "kind": "attest", "prompt": "Confirm trail events carry project attribution and projects map to owned repositories." } },
77
+ { "id": "ACC-03", "domain": "accountability", "title": "ORR evidence exists (90 days)",
78
+ "text": "An ORR report exists and is at most 90 days old.",
79
+ "severity": "medium", "check": { "kind": "orr_fresh", "maxAgeDays": 90 } },
80
+ { "id": "ACC-04", "domain": "accountability", "title": "Cost showback is available",
81
+ "text": "A showback card (.kya/usage.json) exists for cost review.",
82
+ "severity": "low", "check": { "kind": "showback_present" } },
83
+ { "id": "ACC-05", "domain": "accountability", "title": "Named human policy owner",
84
+ "text": "A named human is accountable for the agent's policy configuration and is reachable.",
85
+ "severity": "high", "check": { "kind": "attest", "prompt": "Name the human accountable for the agent's policy configuration and how they are reached." } },
86
+
87
+ { "id": "SOC-01", "domain": "society", "title": "Acceptable-use policy",
88
+ "text": "An acceptable-use policy bounds what the agent may do.",
89
+ "severity": "high", "check": { "kind": "attest", "prompt": "Link or quote the acceptable-use policy bounding what the agent may do." } },
90
+ { "id": "SOC-02", "domain": "society", "title": "Incident-response runbook",
91
+ "text": "An incident-response runbook covers agent misbehavior.",
92
+ "severity": "high", "check": { "kind": "attest", "prompt": "Link or quote the incident-response runbook covering agent misbehavior." } },
93
+ { "id": "SOC-03", "domain": "society", "title": "Third-party model disclosure",
94
+ "text": "The third-party models/providers the agent uses are disclosed to users.",
95
+ "severity": "medium", "check": { "kind": "attest", "prompt": "List the third-party models/providers the agent uses and where this is disclosed." } },
96
+ { "id": "SOC-04", "domain": "society", "title": "Data-processing terms",
97
+ "text": "Data-processing terms cover user data handled by the agent.",
98
+ "severity": "high", "check": { "kind": "attest", "prompt": "Link or quote the data-processing terms covering user data handled by the agent." } },
99
+ { "id": "SOC-05", "domain": "society", "title": "Human escalation contact",
100
+ "text": "A human escalation contact is published for agent-caused harm.",
101
+ "severity": "medium", "check": { "kind": "attest", "prompt": "Publish the human escalation contact for agent-caused harm." } }
102
+ ]
103
+ }
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "shield-kya": {
4
4
  "command": "npx",
5
- "args": ["--no-install", "@shield-agent/kya@0.5.0", "serve-mcp", "--stdio"],
5
+ "args": ["--no-install", "@shield-agent/kya@0.6.0", "serve-mcp", "--stdio"],
6
6
  "env": {
7
7
  "KYA_BASE_URL": "https://shield-agent.com",
8
8
  "KYA_API_KEY": "${KYA_API_KEY}",
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "shield-kya": {
4
4
  "command": "npx",
5
- "args": ["--no-install", "@shield-agent/kya@0.5.0", "serve-mcp", "--stdio"],
5
+ "args": ["--no-install", "@shield-agent/kya@0.6.0", "serve-mcp", "--stdio"],
6
6
  "env": {
7
7
  "KYA_BASE_URL": "https://shield-agent.com",
8
8
  "KYA_API_KEY": "${KYA_API_KEY}",
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "shield-kya": {
4
4
  "command": "npx",
5
- "args": ["--no-install", "@shield-agent/kya@0.5.0", "serve-mcp", "--stdio"],
5
+ "args": ["--no-install", "@shield-agent/kya@0.6.0", "serve-mcp", "--stdio"],
6
6
  "env": {
7
7
  "KYA_BASE_URL": "https://shield-agent.com",
8
8
  "KYA_API_KEY": "${KYA_API_KEY}",
@@ -0,0 +1,10 @@
1
+ export interface AttestationRecord {
2
+ readonly requirementId: string;
3
+ readonly text: string;
4
+ readonly at: string;
5
+ }
6
+ export declare function attestationsPath(cwd: string): string;
7
+ /** Latest attestation per requirement id wins. Corrupt/oversize file → empty map. */
8
+ export declare function loadAttestations(cwd: string): Map<string, AttestationRecord>;
9
+ export declare function recordAttestation(cwd: string, requirementId: string, text: string): AttestationRecord;
10
+ //# sourceMappingURL=attest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attest.d.ts","sourceRoot":"","sources":["../../src/certify/attest.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AASD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAqC5E;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,iBAAiB,CAwCnB"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Operator attestations — the explicit escape hatch for requirements a machine
3
+ * cannot check. Local file only (<cwd>/.kya/attestations.json); never uploaded,
4
+ * never a PEP. Latest attestation per requirement id wins.
5
+ */
6
+ import { chmodSync, mkdirSync, readFileSync, statSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { configDir } from "../config.js";
9
+ import { assertNoSecrets, stripEscapes } from "../dash/render.js";
10
+ import { UsageError } from "../errors.js";
11
+ import { atomicWriteSync } from "../fs-atomic.js";
12
+ const MAX_ATTESTATIONS_BYTES = 256 * 1024;
13
+ const MAX_TEXT_CHARS = 2000;
14
+ const REQ_ID = /^[A-Z]+-\d+$/;
15
+ // Attestation text and timestamps are interpolated into single-line evidence
16
+ // strings, reports, and signed bundles — same rule as catalog attest prompts.
17
+ const CONTROL_CHARS = /[\x00-\x1f\x7f]/;
18
+ export function attestationsPath(cwd) {
19
+ return join(configDir(cwd), "attestations.json");
20
+ }
21
+ /** Latest attestation per requirement id wins. Corrupt/oversize file → empty map. */
22
+ export function loadAttestations(cwd) {
23
+ const map = new Map();
24
+ try {
25
+ const path = attestationsPath(cwd);
26
+ const st = statSync(path); // ENOENT/EACCES land in catch → empty map
27
+ if (!st.isFile() || st.size > MAX_ATTESTATIONS_BYTES)
28
+ return map;
29
+ const raw = JSON.parse(readFileSync(path, "utf8"));
30
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
31
+ return map;
32
+ const list = raw.attestations;
33
+ if (!Array.isArray(list))
34
+ return map;
35
+ for (const item of list) {
36
+ if (!item || typeof item !== "object" || Array.isArray(item))
37
+ continue;
38
+ const r = item;
39
+ if (typeof r.requirementId !== "string" ||
40
+ typeof r.text !== "string" ||
41
+ typeof r.at !== "string") {
42
+ continue;
43
+ }
44
+ // The store file is operator-editable; refuse entries that would break
45
+ // the single-line evidence contract downstream, and apply the same
46
+ // bidi/zero-width sanitization record applies so hand-edited entries
47
+ // cannot smuggle display spoofing into evidence.
48
+ if (CONTROL_CHARS.test(r.text) || CONTROL_CHARS.test(r.at))
49
+ continue;
50
+ const text = stripEscapes(r.text).trim();
51
+ if (text.length === 0)
52
+ continue;
53
+ map.set(r.requirementId, {
54
+ requirementId: r.requirementId,
55
+ text,
56
+ at: stripEscapes(r.at),
57
+ });
58
+ }
59
+ }
60
+ catch {
61
+ return new Map();
62
+ }
63
+ return map;
64
+ }
65
+ export function recordAttestation(cwd, requirementId, text) {
66
+ const id = requirementId.trim();
67
+ if (!REQ_ID.test(id)) {
68
+ throw new UsageError(`--attest id must look like DP-01, got ${JSON.stringify(requirementId)}`);
69
+ }
70
+ if (CONTROL_CHARS.test(text)) {
71
+ throw new UsageError("--text must not contain control characters");
72
+ }
73
+ const clean = stripEscapes(text).trim();
74
+ if (clean.length === 0)
75
+ throw new UsageError("--text must not be empty");
76
+ if (clean.length > MAX_TEXT_CHARS) {
77
+ throw new UsageError(`--text exceeds ${MAX_TEXT_CHARS} chars`);
78
+ }
79
+ try {
80
+ assertNoSecrets(clean);
81
+ }
82
+ catch {
83
+ throw new UsageError("--text looks like it contains a secret — attestations describe policy, never credentials");
84
+ }
85
+ const record = {
86
+ requirementId: id,
87
+ text: clean,
88
+ at: new Date().toISOString(),
89
+ };
90
+ const others = [...loadAttestations(cwd).values()].filter((r) => r.requirementId !== id);
91
+ const store = { version: 1, attestations: [...others, record] };
92
+ mkdirSync(configDir(cwd), { recursive: true, mode: 0o700 });
93
+ // Atomic tmp+rename: a crash or concurrent run must never truncate the
94
+ // store — the fail-safe load would silently discard all attestation history.
95
+ const path = attestationsPath(cwd);
96
+ atomicWriteSync(path, `${JSON.stringify(store, null, 2)}\n`);
97
+ try {
98
+ chmodSync(path, 0o600); // operator statements are sensitive — evidence-bundle perms
99
+ }
100
+ catch {
101
+ /* best-effort on non-POSIX */
102
+ }
103
+ return record;
104
+ }
105
+ //# sourceMappingURL=attest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attest.js","sourceRoot":"","sources":["../../src/certify/attest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ export type RequirementSeverity = "critical" | "high" | "medium" | "low";
2
+ export interface TrailMatch {
3
+ readonly verdict?: string;
4
+ readonly reasonCode?: string;
5
+ readonly mode?: "observe" | "hold" | "offline";
6
+ readonly neverEvent?: boolean;
7
+ }
8
+ export type RequirementCheck = {
9
+ readonly kind: "config_mode";
10
+ readonly not: "observe";
11
+ } | {
12
+ readonly kind: "hooks_wired";
13
+ readonly min: number;
14
+ } | {
15
+ readonly kind: "trail_zero";
16
+ readonly windowDays: number;
17
+ readonly match: TrailMatch;
18
+ } | {
19
+ readonly kind: "trail_min";
20
+ readonly windowDays: number;
21
+ readonly match: TrailMatch;
22
+ readonly min: number;
23
+ } | {
24
+ readonly kind: "trail_ratio";
25
+ readonly windowDays: number;
26
+ readonly match: TrailMatch;
27
+ readonly maxRatio: number;
28
+ } | {
29
+ readonly kind: "orr_overall";
30
+ readonly max: "green" | "amber";
31
+ } | {
32
+ readonly kind: "orr_category";
33
+ readonly id: string;
34
+ readonly max: "green" | "amber";
35
+ } | {
36
+ readonly kind: "orr_fresh";
37
+ readonly maxAgeDays: number;
38
+ } | {
39
+ readonly kind: "sandbox_inventory";
40
+ readonly min: number;
41
+ } | {
42
+ readonly kind: "receipts_present";
43
+ readonly min: number;
44
+ } | {
45
+ readonly kind: "showback_present";
46
+ } | {
47
+ readonly kind: "attest";
48
+ readonly prompt: string;
49
+ };
50
+ export interface CatalogRequirement {
51
+ readonly id: string;
52
+ readonly domain: string;
53
+ readonly title: string;
54
+ readonly text: string;
55
+ readonly severity: RequirementSeverity;
56
+ readonly check: RequirementCheck;
57
+ }
58
+ export interface Catalog {
59
+ readonly id: string;
60
+ readonly version: string;
61
+ readonly updated: string;
62
+ readonly domains: readonly string[];
63
+ readonly requirements: readonly CatalogRequirement[];
64
+ }
65
+ export declare const MAX_CATALOG_BYTES: number;
66
+ /** Bundled catalog: <pkg>/catalog/… — works from both src/ (vitest) and dist/ (built). */
67
+ export declare function defaultCatalogPath(): string;
68
+ export declare function loadCatalog(path?: string): Catalog;
69
+ export declare function validateCatalog(raw: unknown): Catalog;
70
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/certify/catalog.ts"],"names":[],"mappings":"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"}