@bivy/bivy 0.16.7-staging.1 → 0.16.7-staging.2

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
@@ -125,8 +125,8 @@ configuration. Re-running the installer updates Bivy and restarts the service.
125
125
  without an account or server. During setup, choose **local only for now** to skip
126
126
  remote access. The browser and phone apps need a control plane: use
127
127
  [app.bivy.sh](https://app.bivy.sh) or
128
- [self-host one](docs/self-host-quickstart.md). You can switch later with
129
- `bivy relay:setup`.
128
+ [self-host one](docs/self-host-quickstart.md). You can sign in later with
129
+ `bivy login` (or use `bivy relay:setup` for self-hosted endpoint options).
130
130
 
131
131
  Self-hosted Bivy Core is open source and has no usage limits. Bivy Cloud offers
132
132
  a managed app, relay, and hosted Machines; see
package/bin/bivy.mjs CHANGED
@@ -13,7 +13,9 @@
13
13
  * bivy stop stop the background service
14
14
  * bivy restart restart the background service (waits for active sessions to finish; --force to skip)
15
15
  * bivy status show config + whether the node is reachable
16
- * bivy login sign into a model provider (native Pi /login)
16
+ * bivy login sign this machine into a Bivy account
17
+ * bivy logout sign this machine out of its Bivy account
18
+ * bivy provider login sign into a model provider
17
19
  * bivy update update Bivy + install deps + restart service (waits for active sessions to finish; --force to skip)
18
20
  * bivy update:log show output of the last (or in-progress) update
19
21
  * bivy open open the browser UI
@@ -1802,7 +1804,7 @@ function cmdCompletions(args = []) {
1802
1804
  const shell = (args[0] || "").toLowerCase();
1803
1805
  const commands = [
1804
1806
  "run", "runs", "sessions", "ls", "resume", "promote", "rename", "nodes", "agent", "agents", "agents:install", "shim", "takeover", "token", "exec",
1805
- "send", "attach", "kill", "setup", "start", "stop", "restart", "status", "doctor", "diagnostics", "capabilities", "logs", "login",
1807
+ "send", "attach", "kill", "setup", "start", "stop", "restart", "status", "doctor", "diagnostics", "capabilities", "logs", "login", "logout", "signout", "provider", "model",
1806
1808
  "update", "update:log", "audit", "automation", "config", "plugin", "open", "service", "secrets", "voice", "link", "relay:setup",
1807
1809
  "github:connect", "github:app-create", "github:app-connect", "github:app-sync", "prune", "uninstall", "help", "version",
1808
1810
  ];
@@ -1820,6 +1822,7 @@ _bivy_completions() {
1820
1822
  fi
1821
1823
  case "$prev" in
1822
1824
  run) COMPREPLY=( $(compgen -W "${agents.join(" ")}" -- "$cur") );;
1825
+ provider|model) COMPREPLY=( $(compgen -W "login" -- "$cur") );;
1823
1826
  esac
1824
1827
  }
1825
1828
  complete -F _bivy_completions bivy`);
@@ -1835,6 +1838,8 @@ _bivy() {
1835
1838
  compadd -- $cmds
1836
1839
  elif [[ \${words[2]} == run ]]; then
1837
1840
  compadd -- $agents
1841
+ elif [[ \${words[2]} == provider || \${words[2]} == model ]]; then
1842
+ compadd -- login
1838
1843
  fi
1839
1844
  }
1840
1845
  compdef _bivy bivy`);
@@ -1844,7 +1849,8 @@ compdef _bivy bivy`);
1844
1849
  console.log(`# bivy fish completion — save to ~/.config/fish/completions/bivy.fish
1845
1850
  complete -c bivy -f
1846
1851
  complete -c bivy -n '__fish_use_subcommand' -a '${commands.join(" ")}'
1847
- complete -c bivy -n '__fish_seen_subcommand_from run' -a '${agents.join(" ")}'`);
1852
+ complete -c bivy -n '__fish_seen_subcommand_from run' -a '${agents.join(" ")}'
1853
+ complete -c bivy -n '__fish_seen_subcommand_from provider model' -a 'login'`);
1848
1854
  return;
1849
1855
  }
1850
1856
  console.error(c.red("Usage: bivy completions <bash|zsh|fish>"));
@@ -3785,7 +3791,7 @@ async function cmdSetup(args = []) {
3785
3791
  const loginCode = await runSetupModelLogin(config);
3786
3792
  rl.resume();
3787
3793
  if (loginCode !== 0 || !hasModelConfig(loadConfig())) {
3788
- console.log(c.yellow("Model sign-in did not complete. The node can start, but an agent reply still requires 'bivy login'."));
3794
+ console.log(c.yellow("Model sign-in did not complete. The node can start, but an agent reply still requires 'bivy provider login'."));
3789
3795
  }
3790
3796
  agentAuthReady = hasModelConfig(loadConfig());
3791
3797
  }
@@ -3848,7 +3854,7 @@ async function cmdSetup(args = []) {
3848
3854
  console.log(c.bold(c.green("\n ✓ Node running. Check first-task readiness below.\n")));
3849
3855
  console.log(` ${c.green("✓")} node reachable at ${url(finalConfig)}`);
3850
3856
  console.log(` ${agentReady ? c.green("✓") : c.yellow("!")} runtime ${agentReady ? `${setupAgent?.label || "Pi"} available` : "not installed — run 'bivy agents:install'"}`);
3851
- console.log(` ${modelReady ? c.green("✓") : c.yellow("!")} model ${modelReady ? (setupAgent?.needsBivyModel ? "credential configured" : "native agent login ready") : (setupAgent?.needsBivyModel ? "not configured — run 'bivy login'" : `${setupAgent?.loginHint || "sign in through the selected agent"}`)}`);
3857
+ console.log(` ${modelReady ? c.green("✓") : c.yellow("!")} model ${modelReady ? (setupAgent?.needsBivyModel ? "credential configured" : "native agent login ready") : (setupAgent?.needsBivyModel ? "not configured — run 'bivy provider login'" : `${setupAgent?.loginHint || "sign in through the selected agent"}`)}`);
3852
3858
  console.log(` ${repoReady ? c.green("✓") : c.dim("○")} repository ${repoReady ? "accessible" : "choose one from the directory where you start Bivy or in the app"}`);
3853
3859
  const ghReady = githubConnected(finalConfig);
3854
3860
  console.log(` ${ghReady ? c.green("✓") : c.dim("○")} GitHub ${ghReady ? "connected — your repos will list in the app" : c.dim("optional — connect later in the app under Settings → GitHub App")}`);
@@ -3951,7 +3957,7 @@ function printFirstRunSteps(modelReady = false, setupAgent = null) {
3951
3957
  console.log(" Start your first session:");
3952
3958
  if (!modelReady) {
3953
3959
  const login = setupAgent?.needsBivyModel
3954
- ? `${c.cyan("bivy login")} ${c.dim("(stored in Bivy's encrypted vault)")}`
3960
+ ? `${c.cyan("bivy provider login")} ${c.dim("(stored in Bivy's encrypted vault)")}`
3955
3961
  : c.cyan(setupAgent?.command || "the selected agent's native CLI");
3956
3962
  console.log(` Model access: ${login}`);
3957
3963
  }
@@ -4225,7 +4231,7 @@ async function cmdDoctor(args = []) {
4225
4231
  console.log(` ${mark(agentAvailable, true)} agent ${runtimeInfo?.displayName || defaultAgent}${agentAvailable ? "" : c.dim(" not available — install it or run 'bivy setup'")}`);
4226
4232
  const credentialReady = readiness?.credential?.ok ?? hasModelConfig(config);
4227
4233
  const credentialKnown = readiness?.credential?.probed || readiness?.credential?.configured;
4228
- console.log(` ${mark(credentialReady, authOwner !== "bivy" || !credentialKnown)} model ${credentialReady ? (readiness?.credential?.probed ? "access verified" : "configured") : authOwner === "bivy" ? c.dim("not ready — run 'bivy login'") : c.dim("agent-native auth — use the agent's CLI login if needed")}`);
4234
+ console.log(` ${mark(credentialReady, authOwner !== "bivy" || !credentialKnown)} model ${credentialReady ? (readiness?.credential?.probed ? "access verified" : "configured") : authOwner === "bivy" ? c.dim("not ready — run 'bivy provider login'") : c.dim("agent-native auth — use the agent's CLI login if needed")}`);
4229
4235
  const repositoryReady = Boolean(readiness?.repository?.ok);
4230
4236
  console.log(` ${mark(repositoryReady, true)} repository ${repositoryReady ? "accessible" : c.dim("not selected or access could not be verified")}`);
4231
4237
  const firstTaskReady = reachable && agentAvailable && credentialReady && repositoryReady;
@@ -4642,9 +4648,9 @@ function cmdUpdateLog(args) {
4642
4648
  });
4643
4649
  }
4644
4650
 
4645
- async function cmdLogin(args) {
4651
+ async function cmdProviderLogin(args) {
4646
4652
  if (args.includes("-h") || args.includes("--help")) {
4647
- console.log("Usage: bivy login [provider]\n\nSign into a model provider (Pi's native /login). With no provider, prompts interactively for the auth method and provider.");
4653
+ console.log("Usage: bivy provider login [provider]\n bivy model login [provider]\n\nSign into a model provider. With no provider, prompts interactively for the authentication method and provider.");
4648
4654
  return;
4649
4655
  }
4650
4656
  if (!(await ensureDeps())) process.exit(1);
@@ -4656,6 +4662,85 @@ async function cmdLogin(args) {
4656
4662
  process.exit(code);
4657
4663
  }
4658
4664
 
4665
+ async function cmdAccountLogin(args) {
4666
+ if (args.includes("-h") || args.includes("--help")) {
4667
+ console.log("Usage: bivy login [--github|--email <email>]\n\nSign this machine into a Bivy account and enable remote access. With no flags, choose GitHub or an emailed magic link interactively.");
4668
+ return;
4669
+ }
4670
+ if (args[0] && !args[0].startsWith("-")) {
4671
+ console.error(c.red(`'bivy login ${args[0]}' is no longer a model-provider command. Use 'bivy provider login ${args[0]}'.`));
4672
+ process.exitCode = 1;
4673
+ return;
4674
+ }
4675
+ await cmdRelaySetup(args);
4676
+ }
4677
+
4678
+ async function cmdAccountLogout(args = []) {
4679
+ if (args.includes("-h") || args.includes("--help")) {
4680
+ console.log("Usage: bivy logout\n bivy signout\n\nSign this machine out of its Bivy account. Local sessions and model-provider credentials are kept.");
4681
+ return;
4682
+ }
4683
+ if (args.length) {
4684
+ console.error(c.red(`Unknown logout option: ${args[0]}`));
4685
+ process.exitCode = 1;
4686
+ return;
4687
+ }
4688
+ if (!fs.existsSync(relayConfigPath)) {
4689
+ console.log(c.dim("This machine is not signed into a Bivy account."));
4690
+ return;
4691
+ }
4692
+
4693
+ let relayConfig;
4694
+ try {
4695
+ relayConfig = JSON.parse(fs.readFileSync(relayConfigPath, "utf8"));
4696
+ } catch (error) {
4697
+ console.log(c.yellow(`Could not read the existing account configuration: ${error instanceof Error ? error.message : String(error)}`));
4698
+ }
4699
+
4700
+ if (relayConfig?.controlPlaneUrl && relayConfig?.enrollmentToken) {
4701
+ try {
4702
+ const res = await fetch(`${String(relayConfig.controlPlaneUrl).replace(/\/$/, "")}/node`, {
4703
+ method: "DELETE",
4704
+ headers: { authorization: `Bearer ${relayConfig.enrollmentToken}` },
4705
+ });
4706
+ if (!res.ok && res.status !== 401 && res.status !== 404) {
4707
+ console.log(c.yellow(`Could not remove the account's machine registration (${res.status}); signing out locally anyway.`));
4708
+ }
4709
+ } catch (error) {
4710
+ console.log(c.yellow(`Could not reach the account service; signing out locally anyway (${error instanceof Error ? error.message : String(error)}).`));
4711
+ }
4712
+ } else if (relayConfig?.room && relayConfig?.roomToken) {
4713
+ console.log(c.dim("This machine used account-free relay access; removing that local relay configuration."));
4714
+ }
4715
+
4716
+ fs.rmSync(relayConfigPath, { force: true });
4717
+ fs.rmSync(setupSessionPath, { force: true });
4718
+ // This key wraps the account-level model credential vault. It must not cross
4719
+ // an account boundary; the local provider credential store itself is kept.
4720
+ fs.rmSync(path.join(appDir, "model-auth-vault.json"), { force: true });
4721
+
4722
+ const config = loadConfig();
4723
+ if (restartService()) {
4724
+ console.log(c.green("Signed out. Service restarted with remote account access disabled."));
4725
+ return;
4726
+ }
4727
+ if (await isReachable(config)) {
4728
+ try {
4729
+ const token = await localDeviceToken(config);
4730
+ await localApi(config, "/api/relay/reload", {
4731
+ method: "POST",
4732
+ headers: { authorization: `Bearer ${token}` },
4733
+ body: "{}",
4734
+ });
4735
+ console.log(c.green("Signed out. The running node disconnected from the remote account."));
4736
+ return;
4737
+ } catch (error) {
4738
+ console.log(c.yellow(`Signed out locally, but could not disconnect the running node immediately: ${error instanceof Error ? error.message : String(error)}`));
4739
+ }
4740
+ }
4741
+ console.log(c.green("Signed out of Bivy on this machine."));
4742
+ }
4743
+
4659
4744
  async function cmdLinkPhone(args = []) {
4660
4745
  if (args.includes("-h") || args.includes("--help")) {
4661
4746
  console.log("Usage: bivy link\n\nShow a remote web/PWA link (and QR) in the terminal, single-use and short-lived (5 minutes). Requires 'bivy relay:setup' first.");
@@ -4937,7 +5022,9 @@ ${c.bold("bivy")} — Bivy node CLI
4937
5022
  ${c.cyan("bivy doctor")} Health check: deps, node, model, remote, agents
4938
5023
  ${c.cyan("bivy capabilities")} [--json] What this Machine unlocks: OS, agents, providers, Docker/GPU, plugins, workspaces
4939
5024
  ${c.cyan("bivy logs")} [-f] Tail the node logs (systemd journal, launchd, or background log)
4940
- ${c.cyan("bivy login")} Sign into a model provider (Pi /login)
5025
+ ${c.cyan("bivy login")} Sign this machine into a Bivy account (GitHub or email)
5026
+ ${c.cyan("bivy logout")} Sign this machine out (alias: signout)
5027
+ ${c.cyan("bivy provider login")} Sign into a model provider (alias: model login)
4941
5028
  ${c.cyan("bivy update")} Update Bivy + install deps + restart service (waits for active sessions to finish a turn; --force to skip)
4942
5029
  ${c.cyan("bivy update:log")} Show output of the last (or in-progress) update
4943
5030
  ${c.cyan("bivy agent add")} Connect an existing user-owned agent
@@ -5144,8 +5231,25 @@ Unlike 'bivy run', these commands operate on governed background Runs with check
5144
5231
  await cmdLogs(args);
5145
5232
  break;
5146
5233
  case "login":
5147
- await cmdLogin(args);
5234
+ await cmdAccountLogin(args);
5148
5235
  break;
5236
+ case "logout":
5237
+ case "signout":
5238
+ await cmdAccountLogout(args);
5239
+ break;
5240
+ case "provider":
5241
+ case "model": {
5242
+ const [action, ...providerArgs] = args;
5243
+ if (action === "login") {
5244
+ await cmdProviderLogin(providerArgs);
5245
+ } else if (!action || ["-h", "--help", "help"].includes(action)) {
5246
+ console.log(`Usage: bivy ${command} login [provider]\n\nSign into a model provider. Both 'bivy provider login' and 'bivy model login' are equivalent.`);
5247
+ } else {
5248
+ console.error(c.red(`Unknown ${command} action: ${action}. Usage: bivy ${command} login [provider]`));
5249
+ process.exitCode = 1;
5250
+ }
5251
+ break;
5252
+ }
5149
5253
  case "update":
5150
5254
  await cmdUpdate(args);
5151
5255
  break;
@@ -102,7 +102,7 @@ async function loginApiKey(provider) {
102
102
  if (provider.id === "anthropic") {
103
103
  const probe = await probeAnthropicAccess(apiKey);
104
104
  if (probe.probed && !probe.ok) {
105
- console.log(`⚠ ${probe.reason || "The key was saved but Anthropic rejected it."} Double-check the key; re-run 'bivy login' to replace it.`);
105
+ console.log(`⚠ ${probe.reason || "The key was saved but Anthropic rejected it."} Double-check the key; re-run 'bivy provider login' to replace it.`);
106
106
  }
107
107
  else if (probe.probed) {
108
108
  console.log("✓ Verified: the key can reach the Anthropic API.");
@@ -11,7 +11,7 @@
11
11
  // At rest the vault is encrypted (AES-256-GCM via the repo's own seal/open — no
12
12
  // third crypto implementation) under a 0600 key minted once. Writes are
13
13
  // serialized twice over: an in-process per-provider promise chain, and a
14
- // cross-process mkdir lock (the `bivy login` CLI writes the same file as the
14
+ // cross-process mkdir lock (the `bivy provider login` CLI writes the same file as the
15
15
  // running daemon). `modify()` is the only write path, so every mutation is a
16
16
  // read-modify-write under the lock — the ordering OAuth refresh depends on
17
17
  // (rotated refresh tokens are single-use; a read-then-write loses that race).
@@ -40,7 +40,7 @@ Agent-native logins:
40
40
  Config file (${path.relative(process.cwd(), configPath) || configPath}):
41
41
  bivy credentials config path|show|edit Print path, show, or open in $EDITOR
42
42
 
43
- Note: run 'bivy login' to add a provider's default OAuth/API-key login.`);
43
+ Note: run 'bivy provider login' to add a provider's default OAuth/API-key login.`);
44
44
  }
45
45
  async function askHidden(question) {
46
46
  if (!input.isTTY) {
@@ -89,7 +89,7 @@ function kindLabel(r) {
89
89
  async function cmdList() {
90
90
  const records = [...(await listCredentialRecords(credsDir))].sort((a, b) => `${a.provider}:${a.label}`.localeCompare(`${b.provider}:${b.label}`));
91
91
  if (records.length === 0) {
92
- console.log("No credentials. Add one with 'bivy credentials add', or 'bivy login'.");
92
+ console.log("No credentials. Add one with 'bivy credentials add', or 'bivy provider login'.");
93
93
  return;
94
94
  }
95
95
  const presets = getCredentialPresets(credsDir);
package/dist/server.js CHANGED
@@ -2976,14 +2976,27 @@ async function handleRelayMessage(msg) {
2976
2976
  console.warn("[relay] failed to handle client message:", error);
2977
2977
  }
2978
2978
  }
2979
+ function stopRelayConnection() {
2980
+ relay?.stop();
2981
+ relay = undefined;
2982
+ // Disconnecting drops any remote clients the old tunnel carried; release the
2983
+ // shared relay size slot so local PTYs it may have shrunk grow back.
2984
+ terminals.dropClient(RELAY_CLIENT_ID);
2985
+ sessionAdvertiseTarget = undefined;
2986
+ if (advertiseResyncTimer)
2987
+ clearInterval(advertiseResyncTimer);
2988
+ advertiseResyncTimer = undefined;
2989
+ if (nodeHeartbeatTimer)
2990
+ clearInterval(nodeHeartbeatTimer);
2991
+ nodeHeartbeatTimer = undefined;
2992
+ controlPlanePoller?.stop();
2993
+ controlPlanePoller = undefined;
2994
+ }
2979
2995
  function startRelayIfConfigured() {
2996
+ stopRelayConnection();
2980
2997
  const config = loadRelayConfig(appDir);
2981
2998
  if (!config)
2982
2999
  return false;
2983
- relay?.stop();
2984
- // Reconnecting drops any remote clients the old tunnel carried; release the
2985
- // shared relay size slot so local PTYs it may have shrunk grow back.
2986
- terminals.dropClient(RELAY_CLIENT_ID);
2987
3000
  relay = new RelayConnector(config, (msg) => void handleRelayMessage(msg), {
2988
3001
  pairing: pairingStore,
2989
3002
  onWorkAvailable: (hint) => {
@@ -7231,7 +7244,7 @@ function actionableAgentError(runtimeId, error) {
7231
7244
  if (id.startsWith("codex"))
7232
7245
  return "Codex is not signed in. Run `codex login`, then retry; the same login works from Bivy and the PWA.";
7233
7246
  if (id === "pi" || id === "aider")
7234
- return "No model credential is configured. Run `bivy login`, then retry. This is only required once and compatible credentials sync E2E-encrypted to your other Bivy nodes.";
7247
+ return "No model credential is configured. Run `bivy provider login`, then retry. This is only required once and compatible credentials sync E2E-encrypted to your other Bivy nodes.";
7235
7248
  return "The selected agent needs model authentication. Sign in through its native CLI, then retry.";
7236
7249
  }
7237
7250
  return raw;
@@ -8742,8 +8755,12 @@ app.use("/api", authMiddleware(identity));
8742
8755
  // Reload relay.json without forcing the user to restart the whole node. This is
8743
8756
  // used by `bivy relay:setup` after it enrolls the node.
8744
8757
  app.post("/api/relay/reload", (_req, res) => {
8745
- const ok = startRelayIfConfigured();
8746
- res.status(ok ? 200 : 400).json(ok ? { ok: true } : { error: "Relay not configured" });
8758
+ const enabled = startRelayIfConfigured();
8759
+ if (enabled)
8760
+ startControlPlaneTasksIfConfigured();
8761
+ // A missing relay.json is a valid reload state: `bivy logout` removes it and
8762
+ // calls this endpoint to disconnect a foreground node without restarting it.
8763
+ res.json({ ok: true, enabled });
8747
8764
  });
8748
8765
  // Link a remote web/PWA device through the relay. Mints a short-lived,
8749
8766
  // node-scoped client grant from the control plane and packages it with the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.16.7-staging.1",
3
+ "version": "0.16.7-staging.2",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",
@@ -66,6 +66,6 @@
66
66
  "nanoid": "3.3.18",
67
67
  "undici": "8.10.0"
68
68
  },
69
- "readme": "# Bivy\n\n[![npm](https://img.shields.io/npm/v/@bivy/bivy?color=2b6cb0&label=%40bivy%2Fbivy)](https://www.npmjs.com/package/@bivy/bivy)\n[![license: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-2b6cb0)](LICENSE)\n[![node](https://img.shields.io/badge/node-%E2%89%A520-2b6cb0)](https://nodejs.org)\n\n**Run coding agents on your machines and use them from anywhere — from a phone,\nbrowser, terminal, GitHub issue, Slack message, schedule, or webhook.**\n\nStart Claude Code on your workstation, next to the repo, dev server, and\ndatabase you already use. Walk away. From your phone, you can see what it did,\nanswer a question, or approve a migration. CI or a webhook can start the next\njob on the right Machine without waiting for you to return.\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash # install + guided setup\ncd your-repo\nbivy run claude # start an agent in this repo\nbivy open # open it in a browser or on your phone\n```\n\nBivy does not replace Claude Code, Codex, or the other agents you use. It keeps\ntheir Sessions running, routes work to the right Machine, and gives you one place\nto start, join, approve, and review work.\n\nFirst thing to try: ask the agent to explain the repository, make one small safe\nchange, then open the same Session in the web app or on your phone while it runs.\n\n**[Quickstart](docs/quickstart.md)** ·\n**[Docs](docs/README.md)** ·\n**[Why Bivy](docs/why-bivy.md)** ·\n**[Security model](docs/security-model.md)** ·\n**[bivy.sh](https://bivy.sh)**\n\n> **Bivy is 0.x software.** Claude Code, Codex, Pi, and OpenCode are the\n> release-tested paths. Support for other agents varies; check the\n> [runtime support matrix](docs/runtime-support-matrix.md) before relying on a\n> specific feature.\n\n## Why not just a cloud sandbox?\n\nA hosted sandbox clones your repo into a clean environment. Bivy runs in the\nenvironment you already use: the current working tree, running services, and\nwarm caches.\n\n| | Cloud sandbox | Bivy Machine |\n|---|---|---|\n| Your repository | a cloned copy | the real working tree, uncommitted changes and all |\n| Dev server & database | mocked, or absent | already running, right beside the agent |\n| Private networks & internal APIs | out of reach | reachable |\n| Toolchains, package caches | cold, reinstalled each time | warm, already installed |\n| GPUs / local inference | rented separately | the ones on your box |\n| Where your code sits | someone else's infrastructure | the machine you already trust |\n\nBivy lets you leave that environment running and reach it from anywhere.\n\n## What you can do\n\nEvery task in Bivy becomes a Session on a Machine you choose. Start it from the\nterminal, browser, phone, or an external trigger. Join it while it runs, or let\nit finish in the background.\n\n### Sessions\n\nStart an agent, watch it work, steer it, stop it, or approve a tool call. You can\nleave your desk and keep the Session open:\n\n```bash\nbivy run claude # or codex, pi, gemini, and a dozen more\nbivy open # continue the same session in the browser or PWA\nbivy resume # pick it back up in the terminal\nbivy run claude --no-follow # start it in the background instead of attaching\nbivy run claude --chat # start a chat session and open it in the browser\n```\n\n- Reconnect to the same Session from a phone, browser, or terminal.\n- Upload files and images from your phone, or download files the agent creates.\n- Import existing Claude Code and Codex Sessions.\n- Fork or move a Session to another agent, model, or Machine.\n- Connect several Machines, such as a workstation, server, or GPU box.\n\n### Runs\n\nA Run is a Session started as a background job. Start one yourself or trigger it\nfrom another service; Bivy queues it and returns immediately:\n\n```bash\nbivy runs start \"...\" # queue a one-off unattended Run, then `bivy runs wait <id>`\nbivy automation init # define jobs in .bivy/automations.yaml\n```\n\n- Trigger Runs from GitHub, Linear, Slack, a schedule, CI, or a signed webhook.\n- Choose the Machine, agent, model, sandbox, approval mode, and retry limit.\n- Review the changed files, checks, and final result in a Receipt.\n\nSee the [capability recipes](docs/capability-recipes.md) for examples and the\n[runtime support matrix](docs/runtime-support-matrix.md) for per-agent support.\n\n## Bring your own agents and models\n\nUse your existing agent login, an API key in Bivy's vault, or a local\nOpenAI-compatible server. Claude Code, Codex, Pi, and OpenCode have release-tested\nintegrations. Other agents run through ACP or a headless process adapter. Add\nyour own with:\n\n```bash\nbivy agent add # register an existing ACP or process agent\n```\n\n## Install\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash\n```\n\nBivy supports macOS and Linux and requires Node.js 20 or newer. The installer\nadds the [`@bivy/bivy`](https://www.npmjs.com/package/@bivy/bivy) package and\n`bivy` command, then runs `bivy setup`. Setup asks which agent to use, installs\nit if needed, configures remote access, and starts a launchd or systemd service.\n\nIf an agent is already installed, Bivy uses its existing command, login, and\nconfiguration. Re-running the installer updates Bivy and restarts the service.\n\n**Local and remote use.** `bivy run`, `bivy resume`, and `bivy sessions` work\nwithout an account or server. During setup, choose **local only for now** to skip\nremote access. The browser and phone apps need a control plane: use\n[app.bivy.sh](https://app.bivy.sh) or\n[self-host one](docs/self-host-quickstart.md). You can switch later with\n`bivy relay:setup`.\n\nSelf-hosted Bivy Core is open source and has no usage limits. Bivy Cloud offers\na managed app, relay, and hosted Machines; see\n[bivy.sh#pricing](https://bivy.sh#pricing) for details.\n\nPrefer to inspect the installer first?\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh -o install.sh\nless install.sh\nbash install.sh\n```\n\n**When the installer uses sudo:**\n\n- Debian/Ubuntu without a suitable Node.js: `sudo apt-get install curl\n ca-certificates`, then NodeSource's Node 22 setup script via `sudo`.\n- Other Linux, or macOS, without a suitable Node.js: downloads the official\n Node 22 tarball from nodejs.org (sha256-checked) and installs it under\n `/usr/local` with `sudo`.\n- If npm's global prefix isn't writable it falls back to `~/.local` — it never\n runs `npm install` under `sudo`.\n- It appends a marked PATH block to `~/.bashrc` or `~/.zshrc`\n (`BIVY_NO_RC_UPDATE=1` to opt out).\n\nWant no sudo at all? Bring your own Node.js 20+ and skip the script:\n\n```bash\nnpm install -g @bivy/bivy && bivy setup # install globally\nnpx @bivy/bivy setup # or try it once, no install\n```\n\nReleases are published from CI with provenance attestations; verify a build's\norigin with `npm audit signatures`. See [`docs/releasing.md`](docs/releasing.md).\n\n### Your first session\n\nAfter setup, start Bivy inside an existing repo:\n\n```bash\ncd your-repo\nbivy run claude # start an agent as a durable session in the current repo\n# Try: \"Explain this repo and suggest one small, safe improvement.\"\nbivy open # open that same session in the web app (needs relay setup)\nbivy resume # or pick it back up here in the terminal\n```\n\nFrom here the [quickstart](docs/quickstart.md) walks through Runs, multiple\nMachines, and automations.\n\n### Install options\n\nEnvironment variables passed to the one-line installer change what it does:\n\n| Goal | Variable |\n|---|---|\n| Track the dev channel (new build on every merge to `main`) | `BIVY_CHANNEL=staging` |\n| Pin an exact version | `BIVY_VERSION=0.1.0` |\n| Install the npm package into a user-owned prefix | `BIVY_NPM_PREFIX=~/.local` |\n| Preinstall every known upstream agent | `BIVY_INSTALL_ALL_AGENTS=1` |\n| Install optional Bivy bridges/native terminal dependency up front | `BIVY_INSTALL_OPTIONAL_DEPS=1` |\n| Don't touch `~/.bashrc` / `~/.zshrc`; print the PATH line instead | `BIVY_NO_RC_UPDATE=1` |\n\nFor example: `BIVY_CHANNEL=staging curl -fsSL https://bivy.sh/install.sh | bash`.\n\nWorking from a checkout of this repository instead:\n\n```bash\npnpm install\npnpm run setup\n```\n\nSee [`docs/install.md`](docs/install.md) for where data lives, service\nmanagement, and uninstall.\n\n## Updating\n\n```bash\nbivy update\n```\n\n`bivy update` uses the same install method you used originally. It waits for an\nactive turn to finish, updates Bivy, and restarts the background service:\n\n| Install kind | What `bivy update` does |\n|---|---|\n| npm global (`npm i -g`) | `npm install -g @bivy/bivy@<channel>`, then restart the service |\n| installer / packaged | re-runs `install.sh` (migrating to npm if needed), then restart |\n| git checkout | `git pull --ff-only` + `pnpm install --frozen-lockfile`, then restart |\n| `npx` run | nothing to update — each run already fetches the latest |\n\nUpdates follow the release **channel** recorded at install time — `latest`\n(production) by default, or `staging` if you installed with\n`BIVY_CHANNEL=staging`. Switch channels (the choice is remembered for next\ntime), or skip the wait for a busy session:\n\n```bash\nbivy update --staging # move to the dev channel\nbivy update --stable # move back to production (latest)\nbivy update --force # don't wait for an in-flight turn to finish\n```\n\nThe daemon checks for new releases and posts an update notice in the Session.\n\n## Architecture\n\nBivy has three parts. For normal interactive Sessions, code, credentials, and\ntranscripts stay on the node.\n\n```text\n your machine hosted or self-hosted\n\n ┌──────────────┐ ┌─────────┐ ┌───────────────┐\n │ node daemon │ ──dials──▶ │ relay │ ◀────▶ │ control plane │\n │ agents, keys │ outbound │ opaque │ │ accounts, web │\n │ repo, tools │ │ frames │ │ app, metadata │\n └──────────────┘ └─────────┘ └───────────────┘\n ▲ ▲\n └────────── end-to-end encrypted session ───────────┘\n phone · browser · another terminal\n```\n\n- **Node** — a daemon on your machine. Owns the workspace, credentials, and agent\n processes. Serves an API and WebSocket on `http://localhost:4317` plus a\n `/healthz` probe. **It hosts no web UI.**\n- **Relay** — forwards encrypted frames between your node and your devices. Your\n node dials out, so no inbound port is opened. The relay cannot read the frames.\n- **Control plane** — holds your account, node registry, and session index, and\n serves the web/PWA client. Use the hosted one or run your own.\n\nThe node has no web UI. The browser and phone apps come from `app.bivy.sh` or\nyour own control plane; the terminal CLI needs neither. Session traffic is\nend-to-end encrypted between the node and paired devices, so the relay cannot\nread it.\n\nQR pairing with `bivy link` lets the node authorize the device directly. Hosted\naccount pairing trusts the control plane to authorize devices and serve the web\napp that holds the keys. Read the\n[known limitations](docs/security-model.md#known-limitations-for-0x) before using\nBivy with sensitive work.\n\nSee [`docs/remote-access.md`](docs/remote-access.md) and\n[`docs/security-model.md`](docs/security-model.md).\n\n## Supported agents\n\n**Claude Code, Codex, Pi, and OpenCode are the release-tested paths.** The other\nadapters are maintained, but their features vary. Check the\n[runtime support matrix](docs/runtime-support-matrix.md) for resume, models,\napprovals, sandboxing, and test status.\n\n| Agent | Command | Notes |\n|---|---|---|\n| Claude Code | `bivy run claude` | Uses the operator-installed `claude` command through an SDK bridge |\n| Codex | `bivy run codex` | Installs `@openai/codex` |\n| Pi | `bivy run pi` | Uses the operator-installed `pi` command and Pi auth/config |\n| OpenCode | `bivy run opencode` | Installs `opencode-ai` |\n| Gemini CLI | `bivy run gemini` | Installs `@google/gemini-cli` |\n| Qwen Code | `bivy run qwen` | Installs `@qwen-code/qwen-code` |\n| Goose | `bivy run goose` | Requires `goose` on PATH |\n| Aider | `bivy run aider` | No session resume (upstream gap) |\n| Cline | `bivy run cline` | Installs `cline` |\n| Crush | `bivy run crush` | No session resume (upstream gap) |\n| Cursor | `bivy run cursor` | ACP-capable |\n| GitHub Copilot | `bivy run copilot` | ACP-capable |\n| Grok | `bivy run grok` | Model selection |\n| Amp | `bivy run amp` | Native thread resume |\n| Auggie | `bivy run auggie` | Headless CLI |\n| Droid | `bivy run droid` | Model selection |\n| Continue | `bivy run continue` | Headless CLI |\n| Kilo Code | `bivy run kilocode` | ACP-capable |\n| Rovo Dev | `bivy run rovodev` | Installed out of band |\n\nCodebuff, Hermes, and OpenClaw are experimental and hidden from the picker.\nRun them with `BIVY_RUNTIME=<id>`.\n\nRun any command with `bivy run -- ./your-agent --flags`. For a reusable entry in\nthe CLI and web picker, use `bivy agent add`. You can also create an experimental\n`v1alpha1` [plugin manifest](docs/plugins.md) with `bivy plugin init`.\n\nSee the [runtime support matrix](docs/runtime-support-matrix.md) for details.\n\n## Common commands\n\n```bash\nbivy # show the command overview\nbivy run claude # launch Claude Code as a durable session\nbivy run codex # run a different agent\nbivy sessions # list live and saved sessions\nbivy resume # resume the most recent session\nbivy open # open the web app (requires relay setup)\nbivy automation init # create .bivy/automations.yaml\nbivy agent add # connect an existing ACP or process agent\nbivy plugin list # installed declarative integration packages\nbivy status # config summary and node reachability\nbivy doctor # health check\nbivy logs -f # tail node logs\nbivy update # update Bivy and restart the service\n```\n\nFull command list, flags, and examples: [`docs/cli-reference.md`](docs/cli-reference.md).\n\n## Configuration\n\nThe common knobs:\n\n```bash\nBIVY_WORKSPACE=/path/to/repo # default workspace\nBIVY_SANDBOX=read-only # read-only | workspace-write (default) | danger-full-access\nBIVY_APPROVAL_MODE=risky # never | risky | always | autonomous (default)\n```\n\nManage node settings or add repo-specific checks and safety rules:\n\n```bash\nbivy config init\nbivy config set defaults.agent codex\nbivy config explain defaults.sandbox\nbivy config init --project # .bivy/policy.yaml\n```\n\nSee [`docs/config-as-code.md`](docs/config-as-code.md). Every environment\nvariable and precedence rule lives in\n[`docs/configuration.md`](docs/configuration.md).\n\n## Approvals and sandboxing\n\nThe default approval mode is **`autonomous`**, so most actions do not prompt.\nProtection depends on the agent. Some agents enforce Bivy's sandbox setting;\nothers expose tool calls that Bivy can approve or deny. A process agent that\nBivy cannot intercept runs with your user permissions. The picker shows which\ncase applies and asks for confirmation on unprotected paths.\n\nFor tool calls it can see, Bivy blocks destructive system commands and writes\noutside the workspace. It asks before force pushes, publishing, deployments,\nand `sudo`. These checks help prevent accidents. **They are not a security\nsandbox.**\n\nTo see more prompts, change the approval mode:\n\n```bash\nBIVY_APPROVAL_MODE=risky # prompt on risky shell commands and file edits\nBIVY_APPROVAL_MODE=always # prompt on all shell commands and file edits\nBIVY_APPROVAL_MODE=never # no prompts; structured-tool heuristic blocks still apply where available\n```\n\nApprove from the terminal, browser, or phone.\n\nCodex, Claude Code, Gemini CLI, and Qwen Code enforce the `read-only`,\n`workspace-write`, and `danger-full-access` tiers themselves. Other agents may\nrun with your full user permissions even when Bivy can inspect some tool calls.\nCheck the Protection label in the picker. **Bivy does not provide an OS-level\nsandbox.**\n\n## Credentials\n\nInteractive prompts, transcripts, and workspace files stay encrypted across the\nrelay. Credentials can remain on a Machine or in a vault you control:\n\n```bash\nbivy secrets list\nbivy secrets set github.repo-token\nbivy secrets ref github.repo-token op://Bivy/GitHub/repo-token\nbivy secrets doctor\n```\n\n`secret://`, `env://`, and `op://` (1Password) references are resolved only when\nan agent needs them, so the raw values do not appear in config files.\n\nHosted unattended provisioning is different from normal interactive Sessions.\nIf you enable it, Bivy Cloud may hold encrypted cloud, repository, model, or\nkey-escrow data that the service can access. See the\n[security model](docs/security-model.md#what-the-control-plane-sees) and\n[key-management guide](docs/key-management.md).\n\n## Automations as code\n\nDefine jobs in `.bivy/automations.yaml`, validate them, and test trigger events\nlocally:\n\n```bash\nbivy automation init\nbivy automation validate\nbivy automation test --event .bivy/events/failed-ci.yaml\nbivy automation apply\n```\n\nBivy encrypts instructions on the node before upload. Each job records its\nsandbox, approval mode, and maximum number of attempts. See\n[`docs/automations-as-code.md`](docs/automations-as-code.md).\n\n## GitHub Runs\n\nLabel an issue `bivy` (or `bivy/<machine>` to target a Machine), or mention the\nBivy GitHub App in a comment. Bivy creates a Run on the selected Machine, uses an\nisolated worktree, runs the configured checks, and posts the result.\n\nCore has no usage limits. Hosted pricing is managed in the separate Cloud\nrepository.\n\nA private GitHub App only installs on the account that owns it, so connect one\napp per GitHub account — one for your personal repos, one per organization\n(`bivy github:app-create --org <org>`). A node can serve several at once, each\nwith its own key and `@`-mention handle.\n\nSee [`docs/github-work-queue.md`](docs/github-work-queue.md).\n\n## Linear Runs\n\nApply `bivy` or `bivy/<machine>` to a Linear issue to create a Run on the selected\nMachine. The Machine fetches issue content directly from Linear, works in an\nisolated GitHub worktree, and asks the agent to open a pull request. See\n[`docs/linear-work-queue.md`](docs/linear-work-queue.md).\n\n## Development\n\n```bash\npnpm install\npnpm run dev # node daemon on http://localhost:4317\npnpm run dev:web # web client dev server (proxies /api and /ws to the node)\n```\n\nChecks — all of these run in CI:\n\n```bash\npnpm run typecheck\npnpm run typecheck:web\npnpm run lint\npnpm run test:unit\npnpm run test:core\npnpm run check:licenses\npnpm run check:secrets\n```\n\nRepository layout:\n\n- `src/` — node daemon, runtime adapters, approvals, secrets, sessions\n- `bin/` — the `bivy` CLI\n- `packages/core` — shared protocol, pairing, wire format\n- `packages/web` — the React/Vite PWA client (`@bivy/web`)\n- `services/relay` — self-hostable relay\n- `services/control-plane` — self-hostable control plane\n- `deploy/` — self-host deployment examples\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md).\n\n## Self-hosting\n\nNode, relay, and control plane are all in this repository. Point a node at your\nown deployment by passing URLs to `bivy relay:setup` — re-running it switches an\nexisting node over to the new endpoints:\n\n```bash\nbivy relay:setup \\\n --control-plane https://bivy.example.com \\\n --relay wss://relay.example.com\n```\n\nEach URL has a flag and an environment-variable equivalent (the flag wins):\n\n| Flag | Environment variable | Points at | Default |\n|---|---|---|---|\n| `--control-plane <url>` | `BIVY_CONTROL_PLANE_URL` | accounts, node registry, and the web-app API | hosted (`app.bivy.sh`) |\n| `--relay <wss-url>` | `BIVY_RELAY_URL` | the encrypted-frame relay your node dials out to | hosted |\n| `--client <url>` | `BIVY_CLIENT_BASE_URL` | base URL used when building app/PWA links | the `--control-plane` URL |\n\nSign-in defaults to GitHub device login (`--github`); pass\n`--email you@example.com` for an email magic-link, or `--session-token <token>`\nto skip interactive sign-in. `relay:setup` checks the control plane is reachable,\nenrolls this node, and writes the endpoints to `.bivy/relay.json`, so `bivy open`,\n`bivy link`, and `bivy update` all keep using your deployment afterwards.\n\n**Self-hosting is community-supported** — no SLA, best-effort help via GitHub\nissues. You own TLS, backups, upgrades, and hardening. Start with the\none-command VPS path in\n[`docs/self-host-quickstart.md`](docs/self-host-quickstart.md); the ops\nreference (backups, rotation, security boundary) is\n[`docs/self-host.md`](docs/self-host.md).\n\n## Security\n\nReport vulnerabilities through [GitHub private vulnerability reporting](https://github.com/bivysh/bivy/security/advisories/new).\nPlease don't open a public issue. See [`SECURITY.md`](SECURITY.md) for scope,\nresponse times, and safe harbour, and [`docs/security-model.md`](docs/security-model.md)\nfor the trust model and known limitations.\n\n## License\n\nBivy Core is free and open-source software under the GNU Affero General Public\nLicense, version 3.0 only (AGPL-3.0-only). You may use, study, modify, and\nself-host it under that license. If you modify Bivy and let users interact with\nit over a network, section 13 requires you to offer them the corresponding\nsource code. See [`LICENSE`](LICENSE).\n\n**Where the open-core line is.** Everything in this repository — node, CLI,\nrelay, control plane, and the web/PWA client — is AGPL Core, with no usage\nlimits. **Bivy Cloud** is the hosted operation of that stack plus billing and\nplans, and lives in a separate private repository. Contributions are accepted\nunder the [DCO](CONTRIBUTING.md#certificate-of-origin); there is no CLA.\n",
69
+ "readme": "# Bivy\n\n[![npm](https://img.shields.io/npm/v/@bivy/bivy?color=2b6cb0&label=%40bivy%2Fbivy)](https://www.npmjs.com/package/@bivy/bivy)\n[![license: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-2b6cb0)](LICENSE)\n[![node](https://img.shields.io/badge/node-%E2%89%A520-2b6cb0)](https://nodejs.org)\n\n**Run coding agents on your machines and use them from anywhere — from a phone,\nbrowser, terminal, GitHub issue, Slack message, schedule, or webhook.**\n\nStart Claude Code on your workstation, next to the repo, dev server, and\ndatabase you already use. Walk away. From your phone, you can see what it did,\nanswer a question, or approve a migration. CI or a webhook can start the next\njob on the right Machine without waiting for you to return.\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash # install + guided setup\ncd your-repo\nbivy run claude # start an agent in this repo\nbivy open # open it in a browser or on your phone\n```\n\nBivy does not replace Claude Code, Codex, or the other agents you use. It keeps\ntheir Sessions running, routes work to the right Machine, and gives you one place\nto start, join, approve, and review work.\n\nFirst thing to try: ask the agent to explain the repository, make one small safe\nchange, then open the same Session in the web app or on your phone while it runs.\n\n**[Quickstart](docs/quickstart.md)** ·\n**[Docs](docs/README.md)** ·\n**[Why Bivy](docs/why-bivy.md)** ·\n**[Security model](docs/security-model.md)** ·\n**[bivy.sh](https://bivy.sh)**\n\n> **Bivy is 0.x software.** Claude Code, Codex, Pi, and OpenCode are the\n> release-tested paths. Support for other agents varies; check the\n> [runtime support matrix](docs/runtime-support-matrix.md) before relying on a\n> specific feature.\n\n## Why not just a cloud sandbox?\n\nA hosted sandbox clones your repo into a clean environment. Bivy runs in the\nenvironment you already use: the current working tree, running services, and\nwarm caches.\n\n| | Cloud sandbox | Bivy Machine |\n|---|---|---|\n| Your repository | a cloned copy | the real working tree, uncommitted changes and all |\n| Dev server & database | mocked, or absent | already running, right beside the agent |\n| Private networks & internal APIs | out of reach | reachable |\n| Toolchains, package caches | cold, reinstalled each time | warm, already installed |\n| GPUs / local inference | rented separately | the ones on your box |\n| Where your code sits | someone else's infrastructure | the machine you already trust |\n\nBivy lets you leave that environment running and reach it from anywhere.\n\n## What you can do\n\nEvery task in Bivy becomes a Session on a Machine you choose. Start it from the\nterminal, browser, phone, or an external trigger. Join it while it runs, or let\nit finish in the background.\n\n### Sessions\n\nStart an agent, watch it work, steer it, stop it, or approve a tool call. You can\nleave your desk and keep the Session open:\n\n```bash\nbivy run claude # or codex, pi, gemini, and a dozen more\nbivy open # continue the same session in the browser or PWA\nbivy resume # pick it back up in the terminal\nbivy run claude --no-follow # start it in the background instead of attaching\nbivy run claude --chat # start a chat session and open it in the browser\n```\n\n- Reconnect to the same Session from a phone, browser, or terminal.\n- Upload files and images from your phone, or download files the agent creates.\n- Import existing Claude Code and Codex Sessions.\n- Fork or move a Session to another agent, model, or Machine.\n- Connect several Machines, such as a workstation, server, or GPU box.\n\n### Runs\n\nA Run is a Session started as a background job. Start one yourself or trigger it\nfrom another service; Bivy queues it and returns immediately:\n\n```bash\nbivy runs start \"...\" # queue a one-off unattended Run, then `bivy runs wait <id>`\nbivy automation init # define jobs in .bivy/automations.yaml\n```\n\n- Trigger Runs from GitHub, Linear, Slack, a schedule, CI, or a signed webhook.\n- Choose the Machine, agent, model, sandbox, approval mode, and retry limit.\n- Review the changed files, checks, and final result in a Receipt.\n\nSee the [capability recipes](docs/capability-recipes.md) for examples and the\n[runtime support matrix](docs/runtime-support-matrix.md) for per-agent support.\n\n## Bring your own agents and models\n\nUse your existing agent login, an API key in Bivy's vault, or a local\nOpenAI-compatible server. Claude Code, Codex, Pi, and OpenCode have release-tested\nintegrations. Other agents run through ACP or a headless process adapter. Add\nyour own with:\n\n```bash\nbivy agent add # register an existing ACP or process agent\n```\n\n## Install\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash\n```\n\nBivy supports macOS and Linux and requires Node.js 20 or newer. The installer\nadds the [`@bivy/bivy`](https://www.npmjs.com/package/@bivy/bivy) package and\n`bivy` command, then runs `bivy setup`. Setup asks which agent to use, installs\nit if needed, configures remote access, and starts a launchd or systemd service.\n\nIf an agent is already installed, Bivy uses its existing command, login, and\nconfiguration. Re-running the installer updates Bivy and restarts the service.\n\n**Local and remote use.** `bivy run`, `bivy resume`, and `bivy sessions` work\nwithout an account or server. During setup, choose **local only for now** to skip\nremote access. The browser and phone apps need a control plane: use\n[app.bivy.sh](https://app.bivy.sh) or\n[self-host one](docs/self-host-quickstart.md). You can sign in later with\n`bivy login` (or use `bivy relay:setup` for self-hosted endpoint options).\n\nSelf-hosted Bivy Core is open source and has no usage limits. Bivy Cloud offers\na managed app, relay, and hosted Machines; see\n[bivy.sh#pricing](https://bivy.sh#pricing) for details.\n\nPrefer to inspect the installer first?\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh -o install.sh\nless install.sh\nbash install.sh\n```\n\n**When the installer uses sudo:**\n\n- Debian/Ubuntu without a suitable Node.js: `sudo apt-get install curl\n ca-certificates`, then NodeSource's Node 22 setup script via `sudo`.\n- Other Linux, or macOS, without a suitable Node.js: downloads the official\n Node 22 tarball from nodejs.org (sha256-checked) and installs it under\n `/usr/local` with `sudo`.\n- If npm's global prefix isn't writable it falls back to `~/.local` — it never\n runs `npm install` under `sudo`.\n- It appends a marked PATH block to `~/.bashrc` or `~/.zshrc`\n (`BIVY_NO_RC_UPDATE=1` to opt out).\n\nWant no sudo at all? Bring your own Node.js 20+ and skip the script:\n\n```bash\nnpm install -g @bivy/bivy && bivy setup # install globally\nnpx @bivy/bivy setup # or try it once, no install\n```\n\nReleases are published from CI with provenance attestations; verify a build's\norigin with `npm audit signatures`. See [`docs/releasing.md`](docs/releasing.md).\n\n### Your first session\n\nAfter setup, start Bivy inside an existing repo:\n\n```bash\ncd your-repo\nbivy run claude # start an agent as a durable session in the current repo\n# Try: \"Explain this repo and suggest one small, safe improvement.\"\nbivy open # open that same session in the web app (needs relay setup)\nbivy resume # or pick it back up here in the terminal\n```\n\nFrom here the [quickstart](docs/quickstart.md) walks through Runs, multiple\nMachines, and automations.\n\n### Install options\n\nEnvironment variables passed to the one-line installer change what it does:\n\n| Goal | Variable |\n|---|---|\n| Track the dev channel (new build on every merge to `main`) | `BIVY_CHANNEL=staging` |\n| Pin an exact version | `BIVY_VERSION=0.1.0` |\n| Install the npm package into a user-owned prefix | `BIVY_NPM_PREFIX=~/.local` |\n| Preinstall every known upstream agent | `BIVY_INSTALL_ALL_AGENTS=1` |\n| Install optional Bivy bridges/native terminal dependency up front | `BIVY_INSTALL_OPTIONAL_DEPS=1` |\n| Don't touch `~/.bashrc` / `~/.zshrc`; print the PATH line instead | `BIVY_NO_RC_UPDATE=1` |\n\nFor example: `BIVY_CHANNEL=staging curl -fsSL https://bivy.sh/install.sh | bash`.\n\nWorking from a checkout of this repository instead:\n\n```bash\npnpm install\npnpm run setup\n```\n\nSee [`docs/install.md`](docs/install.md) for where data lives, service\nmanagement, and uninstall.\n\n## Updating\n\n```bash\nbivy update\n```\n\n`bivy update` uses the same install method you used originally. It waits for an\nactive turn to finish, updates Bivy, and restarts the background service:\n\n| Install kind | What `bivy update` does |\n|---|---|\n| npm global (`npm i -g`) | `npm install -g @bivy/bivy@<channel>`, then restart the service |\n| installer / packaged | re-runs `install.sh` (migrating to npm if needed), then restart |\n| git checkout | `git pull --ff-only` + `pnpm install --frozen-lockfile`, then restart |\n| `npx` run | nothing to update — each run already fetches the latest |\n\nUpdates follow the release **channel** recorded at install time — `latest`\n(production) by default, or `staging` if you installed with\n`BIVY_CHANNEL=staging`. Switch channels (the choice is remembered for next\ntime), or skip the wait for a busy session:\n\n```bash\nbivy update --staging # move to the dev channel\nbivy update --stable # move back to production (latest)\nbivy update --force # don't wait for an in-flight turn to finish\n```\n\nThe daemon checks for new releases and posts an update notice in the Session.\n\n## Architecture\n\nBivy has three parts. For normal interactive Sessions, code, credentials, and\ntranscripts stay on the node.\n\n```text\n your machine hosted or self-hosted\n\n ┌──────────────┐ ┌─────────┐ ┌───────────────┐\n │ node daemon │ ──dials──▶ │ relay │ ◀────▶ │ control plane │\n │ agents, keys │ outbound │ opaque │ │ accounts, web │\n │ repo, tools │ │ frames │ │ app, metadata │\n └──────────────┘ └─────────┘ └───────────────┘\n ▲ ▲\n └────────── end-to-end encrypted session ───────────┘\n phone · browser · another terminal\n```\n\n- **Node** — a daemon on your machine. Owns the workspace, credentials, and agent\n processes. Serves an API and WebSocket on `http://localhost:4317` plus a\n `/healthz` probe. **It hosts no web UI.**\n- **Relay** — forwards encrypted frames between your node and your devices. Your\n node dials out, so no inbound port is opened. The relay cannot read the frames.\n- **Control plane** — holds your account, node registry, and session index, and\n serves the web/PWA client. Use the hosted one or run your own.\n\nThe node has no web UI. The browser and phone apps come from `app.bivy.sh` or\nyour own control plane; the terminal CLI needs neither. Session traffic is\nend-to-end encrypted between the node and paired devices, so the relay cannot\nread it.\n\nQR pairing with `bivy link` lets the node authorize the device directly. Hosted\naccount pairing trusts the control plane to authorize devices and serve the web\napp that holds the keys. Read the\n[known limitations](docs/security-model.md#known-limitations-for-0x) before using\nBivy with sensitive work.\n\nSee [`docs/remote-access.md`](docs/remote-access.md) and\n[`docs/security-model.md`](docs/security-model.md).\n\n## Supported agents\n\n**Claude Code, Codex, Pi, and OpenCode are the release-tested paths.** The other\nadapters are maintained, but their features vary. Check the\n[runtime support matrix](docs/runtime-support-matrix.md) for resume, models,\napprovals, sandboxing, and test status.\n\n| Agent | Command | Notes |\n|---|---|---|\n| Claude Code | `bivy run claude` | Uses the operator-installed `claude` command through an SDK bridge |\n| Codex | `bivy run codex` | Installs `@openai/codex` |\n| Pi | `bivy run pi` | Uses the operator-installed `pi` command and Pi auth/config |\n| OpenCode | `bivy run opencode` | Installs `opencode-ai` |\n| Gemini CLI | `bivy run gemini` | Installs `@google/gemini-cli` |\n| Qwen Code | `bivy run qwen` | Installs `@qwen-code/qwen-code` |\n| Goose | `bivy run goose` | Requires `goose` on PATH |\n| Aider | `bivy run aider` | No session resume (upstream gap) |\n| Cline | `bivy run cline` | Installs `cline` |\n| Crush | `bivy run crush` | No session resume (upstream gap) |\n| Cursor | `bivy run cursor` | ACP-capable |\n| GitHub Copilot | `bivy run copilot` | ACP-capable |\n| Grok | `bivy run grok` | Model selection |\n| Amp | `bivy run amp` | Native thread resume |\n| Auggie | `bivy run auggie` | Headless CLI |\n| Droid | `bivy run droid` | Model selection |\n| Continue | `bivy run continue` | Headless CLI |\n| Kilo Code | `bivy run kilocode` | ACP-capable |\n| Rovo Dev | `bivy run rovodev` | Installed out of band |\n\nCodebuff, Hermes, and OpenClaw are experimental and hidden from the picker.\nRun them with `BIVY_RUNTIME=<id>`.\n\nRun any command with `bivy run -- ./your-agent --flags`. For a reusable entry in\nthe CLI and web picker, use `bivy agent add`. You can also create an experimental\n`v1alpha1` [plugin manifest](docs/plugins.md) with `bivy plugin init`.\n\nSee the [runtime support matrix](docs/runtime-support-matrix.md) for details.\n\n## Common commands\n\n```bash\nbivy # show the command overview\nbivy run claude # launch Claude Code as a durable session\nbivy run codex # run a different agent\nbivy sessions # list live and saved sessions\nbivy resume # resume the most recent session\nbivy open # open the web app (requires relay setup)\nbivy automation init # create .bivy/automations.yaml\nbivy agent add # connect an existing ACP or process agent\nbivy plugin list # installed declarative integration packages\nbivy status # config summary and node reachability\nbivy doctor # health check\nbivy logs -f # tail node logs\nbivy update # update Bivy and restart the service\n```\n\nFull command list, flags, and examples: [`docs/cli-reference.md`](docs/cli-reference.md).\n\n## Configuration\n\nThe common knobs:\n\n```bash\nBIVY_WORKSPACE=/path/to/repo # default workspace\nBIVY_SANDBOX=read-only # read-only | workspace-write (default) | danger-full-access\nBIVY_APPROVAL_MODE=risky # never | risky | always | autonomous (default)\n```\n\nManage node settings or add repo-specific checks and safety rules:\n\n```bash\nbivy config init\nbivy config set defaults.agent codex\nbivy config explain defaults.sandbox\nbivy config init --project # .bivy/policy.yaml\n```\n\nSee [`docs/config-as-code.md`](docs/config-as-code.md). Every environment\nvariable and precedence rule lives in\n[`docs/configuration.md`](docs/configuration.md).\n\n## Approvals and sandboxing\n\nThe default approval mode is **`autonomous`**, so most actions do not prompt.\nProtection depends on the agent. Some agents enforce Bivy's sandbox setting;\nothers expose tool calls that Bivy can approve or deny. A process agent that\nBivy cannot intercept runs with your user permissions. The picker shows which\ncase applies and asks for confirmation on unprotected paths.\n\nFor tool calls it can see, Bivy blocks destructive system commands and writes\noutside the workspace. It asks before force pushes, publishing, deployments,\nand `sudo`. These checks help prevent accidents. **They are not a security\nsandbox.**\n\nTo see more prompts, change the approval mode:\n\n```bash\nBIVY_APPROVAL_MODE=risky # prompt on risky shell commands and file edits\nBIVY_APPROVAL_MODE=always # prompt on all shell commands and file edits\nBIVY_APPROVAL_MODE=never # no prompts; structured-tool heuristic blocks still apply where available\n```\n\nApprove from the terminal, browser, or phone.\n\nCodex, Claude Code, Gemini CLI, and Qwen Code enforce the `read-only`,\n`workspace-write`, and `danger-full-access` tiers themselves. Other agents may\nrun with your full user permissions even when Bivy can inspect some tool calls.\nCheck the Protection label in the picker. **Bivy does not provide an OS-level\nsandbox.**\n\n## Credentials\n\nInteractive prompts, transcripts, and workspace files stay encrypted across the\nrelay. Credentials can remain on a Machine or in a vault you control:\n\n```bash\nbivy secrets list\nbivy secrets set github.repo-token\nbivy secrets ref github.repo-token op://Bivy/GitHub/repo-token\nbivy secrets doctor\n```\n\n`secret://`, `env://`, and `op://` (1Password) references are resolved only when\nan agent needs them, so the raw values do not appear in config files.\n\nHosted unattended provisioning is different from normal interactive Sessions.\nIf you enable it, Bivy Cloud may hold encrypted cloud, repository, model, or\nkey-escrow data that the service can access. See the\n[security model](docs/security-model.md#what-the-control-plane-sees) and\n[key-management guide](docs/key-management.md).\n\n## Automations as code\n\nDefine jobs in `.bivy/automations.yaml`, validate them, and test trigger events\nlocally:\n\n```bash\nbivy automation init\nbivy automation validate\nbivy automation test --event .bivy/events/failed-ci.yaml\nbivy automation apply\n```\n\nBivy encrypts instructions on the node before upload. Each job records its\nsandbox, approval mode, and maximum number of attempts. See\n[`docs/automations-as-code.md`](docs/automations-as-code.md).\n\n## GitHub Runs\n\nLabel an issue `bivy` (or `bivy/<machine>` to target a Machine), or mention the\nBivy GitHub App in a comment. Bivy creates a Run on the selected Machine, uses an\nisolated worktree, runs the configured checks, and posts the result.\n\nCore has no usage limits. Hosted pricing is managed in the separate Cloud\nrepository.\n\nA private GitHub App only installs on the account that owns it, so connect one\napp per GitHub account — one for your personal repos, one per organization\n(`bivy github:app-create --org <org>`). A node can serve several at once, each\nwith its own key and `@`-mention handle.\n\nSee [`docs/github-work-queue.md`](docs/github-work-queue.md).\n\n## Linear Runs\n\nApply `bivy` or `bivy/<machine>` to a Linear issue to create a Run on the selected\nMachine. The Machine fetches issue content directly from Linear, works in an\nisolated GitHub worktree, and asks the agent to open a pull request. See\n[`docs/linear-work-queue.md`](docs/linear-work-queue.md).\n\n## Development\n\n```bash\npnpm install\npnpm run dev # node daemon on http://localhost:4317\npnpm run dev:web # web client dev server (proxies /api and /ws to the node)\n```\n\nChecks — all of these run in CI:\n\n```bash\npnpm run typecheck\npnpm run typecheck:web\npnpm run lint\npnpm run test:unit\npnpm run test:core\npnpm run check:licenses\npnpm run check:secrets\n```\n\nRepository layout:\n\n- `src/` — node daemon, runtime adapters, approvals, secrets, sessions\n- `bin/` — the `bivy` CLI\n- `packages/core` — shared protocol, pairing, wire format\n- `packages/web` — the React/Vite PWA client (`@bivy/web`)\n- `services/relay` — self-hostable relay\n- `services/control-plane` — self-hostable control plane\n- `deploy/` — self-host deployment examples\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md).\n\n## Self-hosting\n\nNode, relay, and control plane are all in this repository. Point a node at your\nown deployment by passing URLs to `bivy relay:setup` — re-running it switches an\nexisting node over to the new endpoints:\n\n```bash\nbivy relay:setup \\\n --control-plane https://bivy.example.com \\\n --relay wss://relay.example.com\n```\n\nEach URL has a flag and an environment-variable equivalent (the flag wins):\n\n| Flag | Environment variable | Points at | Default |\n|---|---|---|---|\n| `--control-plane <url>` | `BIVY_CONTROL_PLANE_URL` | accounts, node registry, and the web-app API | hosted (`app.bivy.sh`) |\n| `--relay <wss-url>` | `BIVY_RELAY_URL` | the encrypted-frame relay your node dials out to | hosted |\n| `--client <url>` | `BIVY_CLIENT_BASE_URL` | base URL used when building app/PWA links | the `--control-plane` URL |\n\nSign-in defaults to GitHub device login (`--github`); pass\n`--email you@example.com` for an email magic-link, or `--session-token <token>`\nto skip interactive sign-in. `relay:setup` checks the control plane is reachable,\nenrolls this node, and writes the endpoints to `.bivy/relay.json`, so `bivy open`,\n`bivy link`, and `bivy update` all keep using your deployment afterwards.\n\n**Self-hosting is community-supported** — no SLA, best-effort help via GitHub\nissues. You own TLS, backups, upgrades, and hardening. Start with the\none-command VPS path in\n[`docs/self-host-quickstart.md`](docs/self-host-quickstart.md); the ops\nreference (backups, rotation, security boundary) is\n[`docs/self-host.md`](docs/self-host.md).\n\n## Security\n\nReport vulnerabilities through [GitHub private vulnerability reporting](https://github.com/bivysh/bivy/security/advisories/new).\nPlease don't open a public issue. See [`SECURITY.md`](SECURITY.md) for scope,\nresponse times, and safe harbour, and [`docs/security-model.md`](docs/security-model.md)\nfor the trust model and known limitations.\n\n## License\n\nBivy Core is free and open-source software under the GNU Affero General Public\nLicense, version 3.0 only (AGPL-3.0-only). You may use, study, modify, and\nself-host it under that license. If you modify Bivy and let users interact with\nit over a network, section 13 requires you to offer them the corresponding\nsource code. See [`LICENSE`](LICENSE).\n\n**Where the open-core line is.** Everything in this repository — node, CLI,\nrelay, control plane, and the web/PWA client — is AGPL Core, with no usage\nlimits. **Bivy Cloud** is the hosted operation of that stack plus billing and\nplans, and lives in a separate private repository. Contributions are accepted\nunder the [DCO](CONTRIBUTING.md#certificate-of-origin); there is no CLA.\n",
70
70
  "readmeFilename": "README.md"
71
71
  }