@remcp/remcp 0.2.28 → 0.2.30

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 (3) hide show
  1. package/README.md +19 -0
  2. package/package.json +1 -1
  3. package/src/cli.mjs +32 -1
package/README.md CHANGED
@@ -33,6 +33,25 @@ remcp --version
33
33
  `remcp status` reports the runtime the device would install, whether the agent service is running,
34
34
  and the current usage-metrics state, so a support request can be answered with one paste.
35
35
 
36
+ ## Unrestricted mode
37
+
38
+ `remcp godmode on` removes the runtime's own safety rails on **this** computer: file access is no
39
+ longer confined to the allowed roots, the configured command blocklist is ignored, and the
40
+ catastrophic-command guardrail is set to allow. `remcp godmode off` puts them back; `remcp godmode
41
+ status` says which state you are in and where it comes from.
42
+
43
+ Two things it deliberately does not do:
44
+
45
+ - **It is not reachable from a model.** `set_config_value` lists the settings a model may change and
46
+ this is not one of them; only a person at the computer (this command, `REMCP_RUNTIME_UNRESTRICTED=1`,
47
+ or `unrestricted: true` in `~/.config/remcp/runtime.json`) can turn it on. That is what keeps a
48
+ prompt injection from becoming root.
49
+ - **It does not make the agent root.** Commands run as the user the agent runs as. `sudo` is no longer
50
+ blocked, but it still needs your sudoers rules; to run everything as root, run the agent as root.
51
+
52
+ While it is on, `get_runtime_info` reports `policy.unrestricted: true`, so the model can see it and
53
+ say so instead of assuming the guardrails are still there.
54
+
36
55
  ## macOS folder permissions
37
56
 
38
57
  macOS protects Desktop, Documents, Downloads and iCloud Drive. Until it is granted access, ReMCP
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/remcp",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "description": "ReMCP device client: pair a computer with ReMCP and run the outbound-only agent that hosts the local MCP runtime.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/cli.mjs CHANGED
@@ -487,7 +487,7 @@ function assertRuntimeTrust(server, flags) {
487
487
  }
488
488
 
489
489
  function printHelp() {
490
- console.log(`ReMCP ${VERSION}\n\nCommands:\n remcp start\n remcp status\n remcp doctor\n remcp update\n remcp install\n remcp uninstall\n remcp uninstall --purge\n remcp telemetry [status|on|off]\n remcp --version\n\nPairing commands are generated in the ReMCP workspace.\n\nUsage metrics are opt-out (tool names, timings, outcomes only, sent to your own ReMCP\naccount through the paired agent). Disable them at any time with: remcp telemetry off`);
490
+ console.log(`ReMCP ${VERSION}\n\nCommands:\n remcp start\n remcp status\n remcp doctor\n remcp update\n remcp install\n remcp uninstall\n remcp uninstall --purge\n remcp telemetry [status|on|off]\n remcp godmode [status|on|off]\n remcp --version\n\nPairing commands are generated in the ReMCP workspace.\n\nUsage metrics are opt-out (tool names, timings, outcomes only, sent to your own ReMCP\naccount through the paired agent). Disable them at any time with: remcp telemetry off\n\nUnrestricted mode (remcp godmode on) lifts the access roots and the command guardrails for this\ncomputer only. It is deliberately not reachable from a model or an MCP tool.`);
491
491
  }
492
492
 
493
493
  export async function main(argv = process.argv.slice(2)) {
@@ -602,6 +602,37 @@ export async function main(argv = process.argv.slice(2)) {
602
602
  throw new Error('Usage: remcp telemetry [status|on|off]');
603
603
  }
604
604
 
605
+ // The switch that lifts every guardrail: no access roots, no command blocklist, no catastrophic
606
+ // command protection. It lives here, on the machine, because a model that could turn it on would
607
+ // turn any prompt injection into root. The runtime refuses to set it through MCP
608
+ // (`set_config_value` lists what it accepts, and this is not on the list).
609
+ if (command === 'godmode' || command === 'unrestricted') {
610
+ const action = String(positional[0] || 'status').toLowerCase();
611
+ if (action === 'status') {
612
+ const runtimeFile = readJsonFile(runtimeConfigFile);
613
+ const state = runtimeFile.unrestricted === true || process.env.REMCP_RUNTIME_UNRESTRICTED === '1';
614
+ console.log(JSON.stringify({
615
+ unrestricted: state,
616
+ source: runtimeFile.unrestricted === true ? runtimeConfigFile : process.env.REMCP_RUNTIME_UNRESTRICTED === '1' ? 'REMCP_RUNTIME_UNRESTRICTED' : 'default',
617
+ meaning: state
618
+ ? 'Every path and every command is allowed. Commands run as the user the agent runs as; run the agent as root (sudo remcp install --system) if you want root.'
619
+ : 'The runtime confines file access to its allowed roots and applies its command guardrails.',
620
+ }, null, 2));
621
+ return;
622
+ }
623
+ if (!['on', 'off', 'enable', 'disable'].includes(action)) throw new Error('Usage: remcp godmode [status|on|off]');
624
+ const enabled = action === 'on' || action === 'enable';
625
+ writeJsonFile(runtimeConfigFile, { ...readJsonFile(runtimeConfigFile), unrestricted: enabled });
626
+ const restarted = restartPersistentServiceIfInstalled();
627
+ if (enabled) {
628
+ console.error('Unrestricted mode is ON for this computer: every path and every command is allowed, including sudo. Anything the model is asked to do -- and anything a prompt injection asks it to do -- can now change this machine. Turn it off with `remcp godmode off` when you are done.');
629
+ } else {
630
+ console.log('Unrestricted mode is off: the runtime is back to its allowed roots and command guardrails.');
631
+ }
632
+ console.log(JSON.stringify({ unrestricted: enabled, configFile: runtimeConfigFile, restarted }, null, 2));
633
+ return;
634
+ }
635
+
605
636
  if (command === 'status' || command === 'doctor') {
606
637
  const cfg = loadConfig();
607
638
  const health = await fetch(`${cfg.serverUrl}/health?fresh=${Date.now()}`, { cache: 'no-store' }).then(r => r.json()).catch(error => ({ error: error.message }));