@i4ctime/q-ring 0.13.1 → 0.14.1

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/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  <div align="center">
2
- <img src="https://unpkg.com/@i4ctime/q-ring@latest/assets/social-card-optimized.jpg" alt="q-ring logo" width="100%" />
2
+ <img src="https://raw.githubusercontent.com/I4cTime/q-ring/main/assets/social-card-optimized.jpg" alt="q-ring — never paste an API key into .env again" width="100%" />
3
3
  </div>
4
4
 
5
5
  # q-ring
6
6
 
7
- **The first quantum-inspired keyring built specifically for AI coding agents.**
7
+ **OS keychain secrets for AI coding agents, over MCP.**
8
8
 
9
9
  [![NPM Version](https://img.shields.io/npm/v/@i4ctime/q-ring?style=flat-square&color=0ea5e9)](https://www.npmjs.com/package/@i4ctime/q-ring)
10
10
  [![Docs](https://img.shields.io/badge/docs-website-0ea5e9?style=flat-square)](https://qring.i4c.studio/docs)
11
11
  [![MCP Tools](https://img.shields.io/badge/MCP_tools-44-0ea5e9?style=flat-square)](https://glama.ai/mcp/servers/I4cTime/q-ring)
12
12
  [![License](https://img.shields.io/npm/l/@i4ctime/q-ring?style=flat-square&color=0ea5e9)](https://github.com/I4cTime/q-ring/blob/main/LICENSE)
13
+ [![Discord](https://img.shields.io/badge/discord-join%20the%20studio-5865F2?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/5uEApw5uEz)
13
14
 
14
15
  <a href="https://glama.ai/mcp/servers/I4cTime/q-ring">
15
16
  <img src="https://glama.ai/mcp/servers/I4cTime/q-ring/badges/card.svg" alt="q-ring MCP server" width="400" />
@@ -382,7 +383,7 @@ qring exec -- npm run deploy
382
383
  # Inject only specific tags
383
384
  qring exec --tags backend -- node server.js
384
385
 
385
- # Run with a restricted profile (blocks curl/wget/ssh, 30s timeout)
386
+ # Run with a restricted profile (blocks network tools and interpreters/shells, 30s timeout)
386
387
  qring exec --profile restricted -- npm test
387
388
  ```
388
389
 
@@ -532,6 +533,8 @@ Define project-level governance rules in `.q-ring.json` to control which MCP too
532
533
 
533
534
  Over MCP, policy is resolved from the directory the server was **launched** in — not from the `projectPath` a caller passes — so an agent can't sidestep restrictions by pointing at a directory with no policy. Launch the MCP server from your project root (where `.q-ring.json` lives). Edits to `.q-ring.json` are picked up automatically (the policy cache invalidates on file change), so you don't need to restart the server.
534
535
 
536
+ Policy files are schema-validated and **fail closed**: an invalid `policy` object (say, a typo like `denytools`) raises a `PolicyConfigError` instead of being silently ignored, so a malformed rule can never widen access.
537
+
535
538
  ```bash
536
539
  # View the active policy
537
540
  qring policy
@@ -567,7 +570,7 @@ Example policy in `.q-ring.json`:
567
570
  Restrict command execution with named profiles that control allowed commands, network access, timeouts, and environment sanitization.
568
571
 
569
572
  ```bash
570
- # Run with the "restricted" profile (blocks curl, wget, ssh; 30s timeout)
573
+ # Run with the "restricted" profile (blocks network tools and interpreters/shells; 30s timeout)
571
574
  qring exec --profile restricted -- npm test
572
575
 
573
576
  # Run with the "ci" profile (5min timeout, allows network)
@@ -577,11 +580,11 @@ qring exec --profile ci -- npm run deploy
577
580
  qring exec -- echo "hello"
578
581
  ```
579
582
 
580
- **Built-in profiles:** `unrestricted`, `restricted` (no network tools, 30s limit), `ci` (5min limit, blocks destructive commands).
583
+ **Built-in profiles:** `unrestricted`, `restricted` (denies network tools *and* interpreters/shells — `python -c`, `node -e`, `bash` and friends can't exfiltrate injected secrets; 30s limit), `ci` (5min limit, blocks destructive commands).
581
584
 
582
585
  ### Tamper-Evident Audit
583
586
 
584
- Every audit event includes a SHA-256 hash of the previous event, creating a tamper-evident chain. Verify integrity and export logs in multiple formats.
587
+ Every audit event includes a SHA-256 hash of the previous event, creating a tamper-evident chain. Since v0.14 the chain is also anchored with a keyed HMAC stored in the OS keyring, so `qring audit:verify` detects truncation and whole-file rewrites — not just in-place edits. Verify integrity and export logs in multiple formats.
585
588
 
586
589
  ```bash
587
590
  # Verify the entire audit chain
@@ -986,6 +989,12 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide (dev environment, conv
986
989
  - **Claude Code:** `pnpm run plugin:sync:claude` copies `claude-code-plugin/` into the current directory (or pass a project path; add `--user` to install at `~/.claude/`).
987
990
  - See also [docs/cli-mcp-parity.md](docs/cli-mcp-parity.md).
988
991
 
992
+ ## 🔒 Security
993
+
994
+ - **Local-first.** Core storage is your OS keychain — there is no q-ring cloud and no account. The MCP surface, audit log, and agent memory live on your machine (audit and memory files are written owner-only, `0600`).
995
+ - **Hardened by adversarial review.** v0.14.0 shipped the results of an internal adversarial audit — policy-bypass, approval-scoping, and exec-profile findings all fixed, each with regression tests. Details are in the [CHANGELOG](CHANGELOG.md) `Security` sections (house style since 0.12.0: fix first, then disclose there).
996
+ - **Reporting a vulnerability.** Use [GitHub private vulnerability reporting](https://github.com/I4cTime/q-ring/security/advisories/new) — see [SECURITY.md](SECURITY.md) for the supported-versions table and response commitments (48-hour acknowledgement, 7-day assessment).
997
+
989
998
  ## 📜 License
990
999
 
991
1000
  [AGPL-3.0](LICENSE) - Free to use, modify, and share. Any derivative work or hosted service must release its source code under the same license.
Binary file
Binary file
@@ -0,0 +1,27 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
2
+ <!-- q-ring brand mark, single-color cut (badges/stickers/engraving). -->
3
+ <defs>
4
+ <mask id="qhole" maskUnits="userSpaceOnUse" x="0" y="0" width="512" height="512">
5
+ <rect width="512" height="512" fill="#fff"/>
6
+ <ellipse cx="256" cy="243" rx="78" ry="73" fill="#000"/>
7
+ </mask>
8
+ </defs>
9
+ <g stroke="#f8f8ff" fill="none" stroke-linecap="round">
10
+ <path d="M256 84C204 110 146 116 94 122C92 242 130 364 256 452C382 364 420 242 418 122C366 116 308 110 256 84Z" stroke-width="9"/>
11
+ <path d="M256 102C209.2 125.4 157 130.8 110.2 136.2C108.4 244.2 142.6 354 256 433.2C369.4 354 403.6 244.2 401.8 136.2C355 130.8 302.8 125.4 256 102Z" stroke-width="4.5"/>
12
+ <g mask="url(#qhole)">
13
+ <ellipse cx="256" cy="243" rx="118" ry="46" transform="rotate(-62 256 243)" stroke-width="4"/>
14
+ <ellipse cx="256" cy="243" rx="118" ry="46" transform="rotate(0 256 243)" stroke-width="4"/>
15
+ <ellipse cx="256" cy="243" rx="118" ry="46" transform="rotate(62 256 243)" stroke-width="4"/>
16
+ </g>
17
+ <ellipse cx="256" cy="243" rx="72" ry="72" stroke-width="6.5"/>
18
+ <ellipse cx="256" cy="243" rx="76.5" ry="65.5" transform="rotate(30 256 243)" stroke-width="5.5"/>
19
+ <ellipse cx="256" cy="243" rx="76.5" ry="65.5" transform="rotate(-30 256 243)" stroke-width="5.5"/>
20
+ <circle cx="323.37" cy="157.7" r="8" fill="#f8f8ff" stroke="none"/>
21
+ <circle cx="188.63" cy="328.3" r="8" fill="#f8f8ff" stroke="none"/>
22
+ <circle cx="155.93" cy="267.38" r="8" fill="#f8f8ff" stroke="none"/>
23
+ <circle cx="356.07" cy="218.62" r="8" fill="#f8f8ff" stroke="none"/>
24
+ <circle cx="189.28" cy="192.57" r="8" fill="#f8f8ff" stroke="none"/>
25
+ <circle cx="322.72" cy="293.43" r="8" fill="#f8f8ff" stroke="none"/>
26
+ </g>
27
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
2
+ <!-- q-ring brand mark, small-size cut (favicon-legible): shield + ring.
3
+ Colors canonical; source of truth: apps/qring/lib/palette.ts. -->
4
+ <defs>
5
+ <linearGradient id="qg" gradientUnits="userSpaceOnUse" x1="72" y1="256" x2="440" y2="256">
6
+ <stop offset="0" stop-color="#00D1FF"/>
7
+ <stop offset="0.52" stop-color="#7B2DFF"/>
8
+ <stop offset="1" stop-color="#D946EF"/>
9
+ </linearGradient>
10
+ </defs>
11
+ <g stroke="url(#qg)" fill="none" stroke-linecap="round">
12
+ <path d="M256 84C204 110 146 116 94 122C92 242 130 364 256 452C382 364 420 242 418 122C366 116 308 110 256 84Z" stroke-width="24"/>
13
+ <circle cx="256" cy="246" r="96" stroke-width="28"/>
14
+ </g>
15
+ </svg>
@@ -0,0 +1,44 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
2
+ <!-- q-ring brand mark. Colors are canonical brand hex values; source of
3
+ truth: apps/qring/lib/palette.ts + app/globals.css @theme. -->
4
+ <defs>
5
+ <linearGradient id="qg" gradientUnits="userSpaceOnUse" x1="72" y1="256" x2="440" y2="256">
6
+ <stop offset="0" stop-color="#00D1FF"/>
7
+ <stop offset="0.52" stop-color="#7B2DFF"/>
8
+ <stop offset="1" stop-color="#D946EF"/>
9
+ </linearGradient>
10
+ <mask id="qhole" maskUnits="userSpaceOnUse" x="0" y="0" width="512" height="512">
11
+ <rect width="512" height="512" fill="#fff"/>
12
+ <ellipse cx="256" cy="243" rx="78" ry="73" fill="#000"/>
13
+ </mask>
14
+ <filter id="qglow-wide" x="-30%" y="-30%" width="160%" height="160%">
15
+ <feGaussianBlur stdDeviation="9"/>
16
+ </filter>
17
+ <filter id="qglow-tight" x="-30%" y="-30%" width="160%" height="160%">
18
+ <feGaussianBlur stdDeviation="2.5"/>
19
+ </filter>
20
+ <g id="qart">
21
+ <g stroke="url(#qg)" fill="none" stroke-linecap="round">
22
+ <path d="M256 84C204 110 146 116 94 122C92 242 130 364 256 452C382 364 420 242 418 122C366 116 308 110 256 84Z" stroke-width="9"/>
23
+ <path d="M256 102C209.2 125.4 157 130.8 110.2 136.2C108.4 244.2 142.6 354 256 433.2C369.4 354 403.6 244.2 401.8 136.2C355 130.8 302.8 125.4 256 102Z" stroke-width="4.5"/>
24
+ <g mask="url(#qhole)">
25
+ <ellipse cx="256" cy="243" rx="118" ry="46" transform="rotate(-62 256 243)" stroke-width="4"/>
26
+ <ellipse cx="256" cy="243" rx="118" ry="46" transform="rotate(0 256 243)" stroke-width="4"/>
27
+ <ellipse cx="256" cy="243" rx="118" ry="46" transform="rotate(62 256 243)" stroke-width="4"/>
28
+ </g>
29
+ <ellipse cx="256" cy="243" rx="72" ry="72" stroke-width="6.5"/>
30
+ <ellipse cx="256" cy="243" rx="76.5" ry="65.5" transform="rotate(30 256 243)" stroke-width="5.5"/>
31
+ <ellipse cx="256" cy="243" rx="76.5" ry="65.5" transform="rotate(-30 256 243)" stroke-width="5.5"/>
32
+ <circle cx="323.37" cy="157.7" r="8" fill="url(#qg)" stroke="none"/>
33
+ <circle cx="188.63" cy="328.3" r="8" fill="url(#qg)" stroke="none"/>
34
+ <circle cx="155.93" cy="267.38" r="8" fill="url(#qg)" stroke="none"/>
35
+ <circle cx="356.07" cy="218.62" r="8" fill="url(#qg)" stroke="none"/>
36
+ <circle cx="189.28" cy="192.57" r="8" fill="url(#qg)" stroke="none"/>
37
+ <circle cx="322.72" cy="293.43" r="8" fill="url(#qg)" stroke="none"/>
38
+ </g>
39
+ </g>
40
+ </defs>
41
+ <use href="#qart" filter="url(#qglow-wide)" opacity="0.75"/>
42
+ <use href="#qart" filter="url(#qglow-tight)" opacity="0.9"/>
43
+ <use href="#qart"/>
44
+ </svg>
Binary file
Binary file