概念与架构
Bromure Agentic Coding 在你自己的 Mac 上的硬件虚拟化 Linux 机器内运行 AI 编码代理,并在代理无法绕过的单一节点上强制执行每一项安全控制。本章定义了本手册其余部分所依赖的术语——宿主机与客户机、工作区与会话、传输边界、持久化存储与临时存储——并解释各部分如何协同工作。如果在快速开始之外你只读一章,那就读这一章。
宿主机与客户机
Bromure Agentic Coding 中的一切都处于一条明确界线的某一侧:
- 宿主机就是你的 Mac——运行 macOS 14 或更高版本的 Apple Silicon——以及在其上运行的 Bromure Agentic Coding 进程。宿主机拥有一切敏感内容:你的真实凭据(用 macOS Keychain 中的主密钥加密)、代表代理与互联网通信的 MITM 代理服务器、Bromure 根 CA 的私钥、追踪存储,以及应用的窗口、终端和仪表板。你输入所用的终端渲染在宿主机上,而不在虚拟机内部。
- 客户机是代理实际运行所在的 Linux 虚拟机——Claude Code、Codex CLI、Grok CLI 或普通的 shell 工具,连同它们安装和接触的一切:克隆的仓库、软件包缓存、Docker 容器、虚拟环境。客户机在设计上被视为不可信。它不持有任何真实机密,只有故意无效的伪造凭据,且其出站 HTTPS 通过宿主机的代理服务器汇集转发。
两侧通过两条狭窄通道通信:vsock(virtio 套接字,一种完全无需网络的宿主机到客户机传输)用于控制、终端和代理路径;以及一个私有虚拟网络(vmnet NAT)用于普通 IP 流量。两者均在下文详述。
正是这种切分让产品的安全主张变得具体:一个被提示注入或以其他方式行为异常的代理可以在客户机内部为所欲为,但它无法读取真实的 API 密钥、用真实的 AWS 机密进行签名,或提取 SSH 私钥——这些东西都不存在于其所在界线的那一侧。
架构一览
自下而上阅读这张图:代理在工作区虚拟机中工作;其 HTTPS 通过 vsock 离开发往宿主机代理服务器,那是伪造凭据变为真实凭据的唯一场所;会话窗口通过独立的 vsock 桥接连接到虚拟机的终端;一个可选的一次性浏览器虚拟机共享同一私有网络,从而能加载代理的开发服务器。
工作区、会话与虚拟机
三个词在本手册中始终承载着精确而独特的含义:
- 工作区是一份已存储的配置加上其持久化存储。它定义运行哪个代理(以及如何认证)、凭据、共享文件夹、环境、MCP 服务器、护栏、外观和虚拟机规格——即你在编辑工作区窗口中编辑的一切——并且它拥有
~/Library/Application Support/BromureAC/profiles/<uuid>/下的一个目录,保存其系统磁盘、home 镜像、SSH 密钥、检查点和已存储的状态。无论是否有东西在运行,工作区都存在。在内部(在诸如profile.json的文件名中以及在 CLI 标志中)工作区被称为配置文件(profile);这两个术语可以互换。典型做法是每个项目或每个凭据边界一个工作区。参见工作区。 - 虚拟机 (VM) 是从工作区的存储启动的正在运行的 Linux 实例。每个工作区精确映射到一个虚拟机——一个 Ubuntu 客户机,拥有自己的磁盘、确定性的 MAC 地址、IP 地址和按工作区的 MITM 代理监听器。两个工作区绝不共享一个虚拟机,一个工作区也绝不拥有两个虚拟机。
- 会话是工作区虚拟机的一次连续运行:它在你启动(或恢复)工作区时开始,在虚拟机关闭或挂起时结束。会话窗口可以从会话中分离而不结束会话——在后台运行关闭操作会让虚拟机在无界面下继续运行,你之后可以从侧栏重新连接。有些状态是刻意限定于会话范围的:凭据批准授予与拒绝仅存于内存中,在会话拆除时被撤销,而按每次启动的元数据共享在每次启动时重建。参见会话。
一个有用的简记:工作区是名词(它在磁盘上持久存在),虚拟机是机器(它在通电或挂起期间存在),会话是运行(它有开始和结束)。
工作区虚拟机
每个工作区虚拟机都是运行在 Apple 的 Virtualization.framework 之下的 Ubuntu 24.04 客户机——这与 macOS 自身提供的虚拟机监控程序技术相同,要求 Apple Silicon 且仅支持 ARM64 客户机。虚拟机以固定的 4 vCPUs 和工作区中设置的 RAM 量启动(默认按你的 Mac 规格设定:4、6 或 8 GB),从两个虚拟磁盘启动:
disk.img——系统磁盘。在工作区首次启动时,它被创建为共享的、已签名的 Ubuntu 基础镜像的 APFS 写时复制克隆(参见安装):该克隆瞬间出现,在客户机写入之前不占用任何新的磁盘空间。后续启动重用同一克隆,因此你apt install的软件包、你拉取的 Docker 镜像以及任何其他系统级更改都能跨会话存续。home.img——一个私有的稀疏 ext4 镜像,容纳/home/ubuntu,作为第二个 virtio-blk 设备挂载。它默认有 64 GiB 的表观大小,但按需惰性分配,并且随着客户机中文件被删除而在宿主机上收缩。home 即使经过重置磁盘也能存续,这就是为什么你的仓库、点文件和 shell 历史能比系统磁盘重置活得更久。(较旧的工作区可能仍使用旧式的共享文件夹 home;应用提供一次性升级——参见工作区。)
客户机还挂载少量按每次启动的 virtiofs 共享:一个只读的元共享,携带该次启动的配置(伪造令牌环境文件、代理设置、Bromure CA 证书、客户机代理、MCP 配置),以及一个可写的发件箱,客户机用它将事件发布回宿主机(其 IP 地址、tmux 标签页名册、代理状态)。两者都在每次启动时重建,且不携带任何持久状态。此外最多可将 8 个宿主机项目文件夹共享到客户机中,符号链接到 home 目录。
设计上的持久化——与 Bromure(Web)的对比
Bromure Agentic Coding 与其姊妹应用 Bromure——网页浏览器变体——出自同一代码库,两者刻意做出相反的生命周期选择:
| Bromure(Web) | Bromure Agentic Coding | |
|---|---|---|
| 客户机镜像 | Alpine + Chromium | Ubuntu 24.04 |
| 虚拟机生命周期 | 每个浏览会话一个一次性虚拟机,窗口关闭时销毁 | 每个工作区一个持久虚拟机,跨会话复用 |
| 关闭时的数据 | 全部销毁 | 系统磁盘和 home 被保留 |
| 受控对象 | 虚拟机的整个状态(临时性) | 虚拟机的机密面(伪造凭据、传输边界) |
编码工作需要持久状态——克隆的仓库、软件包缓存、虚拟环境、shell 历史——所以每次会话后销毁虚拟机会让代理毫无用处。Bromure Agentic Coding 不控制虚拟机的状态,而是控制虚拟机被允许持有的内容:没有真实机密,也没有通往互联网的未经中介的路径。临时性仍然作为逃生舱而非默认存在:重置磁盘从基础镜像重新克隆系统磁盘,磁盘和 home 检查点允许回滚,bromure-cli vm run --rm 创建 docker 风格的一次性工作区,在虚拟机停止时删除,而失陷流程在保留你的设置和密钥的同时擦除被污染的磁盘和 home。
浏览器辅助虚拟机
Bromure(Web)的一部分在 Bromure Agentic Coding 中得以延续:代理浏览器窗格。当你(或代理)打开它时,工作区获得第二个临时虚拟机——一个 Alpine + Chromium 客户机,克隆到临时目录中,在窗格拆除时删除。这个辅助虚拟机是印证规则的例外:浏览状态默认可丢弃(除非工作区启用了保持登录网站,它会将 Chromium 的配置文件保留在按工作区加密的磁盘上),而它旁边的工作区虚拟机则是持久的。两个虚拟机位于同一私有网络段上,因此浏览器可以通过虚拟机的 IP 加载代理刚刚启动的开发服务器。
虚拟机生命周期:关闭、挂起与运行
每个工作区始终恰好处于三种持久状态之一,以侧栏中工作区名称旁以及虚拟机仪表板上的胶囊标签显示。(在虚拟机启动过程中,你还会短暂看到过渡状态——侧栏中的**正在启动…**以及仪表板上的启动指示。)
选择工作区的名称会显示其任意状态的仪表板:运行时是实时的 CPU、内存、vCPU、磁盘和运行时间卡片,关闭或挂起时则是机器的规格和配置摘要——上面的截图显示了一个从未启动过的工作区,这就是为什么磁盘卡片仍然显示 0 MB(尚不存在克隆)。右上角的按钮对于关闭的工作区是启动,对于挂起的工作区是恢复。
| 状态 | 虚拟机… | 保留 | 丢失 |
|---|---|---|---|
| 关闭 | 完全不运行。没有内存,没有进程。 | 系统磁盘(disk.img)、home(home.img)、检查点、工作区配置、SSH 密钥、MAC/IP 绑定。 | 正在运行的进程、RAM 内容、终端标签页、任何已存储的 RAM 快照(干净关机会清除它,因此下次启动为冷启动)。 |
| 挂起 | 已冻结。其 RAM 已写入工作区目录中的 vm.state,标签页布局写入 tabs.json。 | 关闭所保留的一切,外加每个正在运行的进程、打开的文件和终端标签页——恢复会精确地从会话离开之处将其复原,几乎即时。 | 无,除非快照必须被丢弃(见下文)。 |
| 运行 | 活动中。会话窗口可能已连接,或虚拟机可能在后台无界面运行。 | 一切都是活动的。 | — |
有几条值得内化的生命周期规则:
- 挂起是 RAM 快照,不是存档文件。 恢复它要求虚拟机的配置完全相同,这就是为什么每个工作区都持久化一个机器标识符和一个确定性的 MAC 地址。如果你在快照存在时更改共享文件夹集合,应用会请求丢弃挂起状态——下次启动为冷启动,且不影响任何文件。
- 快照绝不会比其磁盘活得更久。 重置或擦除系统磁盘也会丢弃任何已存储的 RAM 快照和标签页状态,因为全新磁盘加上陈旧 RAM 会瞬间损坏。
- 关闭并不意味着已抹除。 一个关闭的工作区会无限期保留其系统磁盘和 home。实际的数据销毁总是显式的(重置磁盘、擦除 home、删除工作区),或由失陷流程强制执行。
关闭操作
关闭会话时会发生什么是每个工作区的选择(工作区编辑器中的关闭窗口时):在后台运行(分离窗口,保持虚拟机运行)、挂起、关机或询问——默认选项,每次都以全部三个选项提示。关闭最后一个终端标签页也走同一选择。完整流程参见会话。
传输边界
传输边界是产品的核心思想:来自客户机的所有出站 HTTPS 都经过宿主机侧的中间人(MITM)代理服务器,而每一项防护——机密保管、凭据作用域限定、供应链扫描、提示注入检测、追踪——都在那一个节点上强制执行。代理无法绕过它,因为在其所在界线的那一侧没有任何有用的东西。
端到端的流程:
- 客户机只持有伪造品。 在会话启动时,宿主机将故意无效的占位凭据写入虚拟机——诸如
ANTHROPIC_API_KEY的环境变量,以及诸如~/.git-credentials、~/.docker/config.json和~/.kube/config的配置文件。伪造品保持结构(sk-ant-api03-brm-…、ghp_…形状、brm_…),因此客户端验证器会接受它们,并且每次安装都是确定性的,因此工具绝不会看到密钥在会话之间"轮换"。匹配的真实值被加载到宿主机上代理服务器的内存交换映射中,每个都以它所属的目标主机为键。 - 客户机的 TLS 在代理服务器处终止。 客户机中的每个 shell 都导出
HTTPS_PROXY=http://127.0.0.1:8080,这是一个虚拟机内端点,通过 vsock(端口 8443)隧道到宿主机代理服务器。代理服务器出示一个由 Bromure 根 CA 签名的、按主机伪造的叶证书,检查请求,然后向真实上游重新加密。 - 伪造品仅在出站途中变为真实。 代理服务器将每个伪造品替换为其真实值——API 密钥和令牌的头部替换、OAuth 刷新流程的正文替换、AWS 的完整 SigV4 重新签名(客户机用伪造机密签名;宿主机剥离该签名并用真实机密重新签名)、SSH 的通过 vsock 的 ssh-agent 签名。替换的作用域被限定为精确匹配或子域到已注册的主机,绝不做子串匹配,因此为
api.anthropic.com注册的凭据绝不会被注入到一个相似的仿冒域名。
Bromure 根 CA
代理服务器之所以能终止客户机的 TLS,仅仅是因为客户机信任它这么做。在首次启动时,应用生成一个按安装的证书颁发机构——Bromure Agentic Coding 根 CA——其公开证书在启动时通过元共享挂载到每个虚拟机的信任存储中。私钥绝不离开宿主机(它位于 ~/Library/Application Support/BromureAC/ca/ 下,仅所有者可读)。你的 Bromure 虚拟机之外没有任何东西信任此 CA:它不能被用来拦截你 Mac 自身的流量,而删除 ca/ 目录只会在下次启动时铸造一个全新的 CA。
设计上的故障关闭
该设计是故障关闭的:如果客户机流量曾经躲过代理服务器——一个忽略代理变量的工具、一个原始套接字、一次刻意的绕过尝试——它唯一能出示的凭据是一个没有任何上游会接受的伪造品。AWS 返回 InvalidSignatureException;API 提供方拒绝占位密钥。绕过边界对攻击者毫无所获,因为边界不是检查机密的地方——它是机密存在的唯一场所。
失陷检测器
边界还监视相反方向的滥用:外泄。代理服务器扫描每个出站请求,查找该工作区任何已注册的伪造令牌。一个伪造品恰好只有一个合法目的地——它被铸造时所针对的主机——所以观察到一个伪造品前往其他任何地方就是代理试图泄露凭据的标志。代理服务器在不转发任何一个字节的情况下拒绝该请求,暂停虚拟机,并向你告警;工作区被标记为已失陷,并在其(推定已被污染的)磁盘和 home 被擦除之前拒绝再次启动。你的设置、令牌和 SSH 密钥被保留——而且由于泄露的只有伪造品,真实凭据从不需要轮换。完整的凭据模型,包括按凭据的批准提示和受 TTL 限制的授予,在凭据与传输边界中介绍。
什么会持久化,什么不会
Bromure Agentic Coding 对生命周期是明确的。一个工作区所拥有的一切都位于 ~/Library/Application Support/BromureAC/profiles/<uuid>/ 下;下表是关于什么能在什么之后存续的权威映射。
持久化——在关机和应用重启后存续:
| 项目 | 位置 | 备注 |
|---|---|---|
| 系统磁盘 | disk.img | 基础镜像的 APFS CoW 克隆。在关机后存续;由重置磁盘、删除工作区或失陷擦除移除。 |
| home 目录 | home.img(旧式:home/) | 容纳 /home/ubuntu。在关机以及重置磁盘后存续。 |
| 回滚检查点 | checkpoints/、checkpoints/home/ | 磁盘和 home 的经启动验证的快照,带分层保留。 |
| 工作区配置 | profile.json | 非机密设置;真实凭据单独存储,在宿主机上加密。 |
| SSH 密钥 | ssh/ | 工作区的密钥对(仅通过签名、经 vsock 提供给客户机)。 |
| 机器标识 | machine-identifier.bin + profile-macs.json 中的 MAC 绑定 | 跨启动保持虚拟机的身份(通常还有其 IP)稳定;恢复挂起快照所必需。 |
| 挂起快照 | vm.state + tabs.json | 仅在工作区挂起期间;由干净关机清除。 |
| 浏览器配置文件(选择加入) | browser-profiles/<uuid>/image/ | 仅当启用保持登录网站时;按工作区加密。 |
临时——自动重建或销毁:
| 项目 | 生命周期 |
|---|---|
元共享内容(meta-share/:伪造令牌环境文件、代理配置、CA 证书、客户机代理) | 每次启动时重建。 |
发件箱事件(outbox/) | 每次启动。 |
| 浏览器辅助虚拟机磁盘(临时目录中的 CoW 克隆) | 浏览器窗格拆除时删除。 |
| 凭据批准授予与拒绝 | 仅存于内存;在会话拆除时撤销。 |
| 客户机 RAM、进程、终端标签页 | 除非挂起,否则在关机时丢失。 |
从这张映射中得出两个推论。第一,"关闭窗口"本身绝不具破坏性——具破坏性的动词都是显式的且经过确认的。第二,当你想要可丢弃性时,你有分级的选项:回滚某个检查点、重置磁盘(home 存续)、擦除 home、删除工作区,或者一开始就用 bromure-cli vm run --rm 以一次性方式启动。
注意: 共享宿主机文件夹是你 Mac 上的项目目录,位于 Bromure 的存储之外。它们绝不会被任何 Bromure 操作擦除——包括失陷擦除——擦除提示会明确说明这一点。
宿主机与客户机桥接(vsock)
Mac 与客户机之间的交互式集成走 virtio 套接字——点对点的宿主机到客户机通道,独立于虚拟机的网络而存在。你从不配置这些,但知道它们的存在有助于阅读追踪或故障排除章节。每个桥接在一个编号的 vsock 端口上监听:
工作区虚拟机桥接:
| 端口 | 桥接 | 承载内容 |
|---|---|---|
| 8443 | MITM 代理服务器 | 所有客户机 HTTPS——传输边界本身。 |
| 8444 | ssh-agent 桥接 | 来自客户机 SSH_AUTH_SOCK 的签名请求;私钥字节绝不跨越。 |
| 8445 | AWS 凭据助手 | credential_process 馈送(真实访问密钥 ID、伪造机密)。 |
| 8446 | Claude 令牌代理 / 本地推理 | Claude 的订阅令牌注入,以及客户机到宿主机的本地推理桥接(该端口由两者共享)。 |
| 8447 | Codex 令牌代理 | Codex 的订阅令牌注入。 |
| 5800 | Shell 执行代理 | 终端连接路径、bromure-cli exec、文件浏览器窗口、文件资源管理器窗格以及图像粘贴上传。 |
| 5010 | OAuth 回调中继 | 回环 OAuth 重定向(用于 gh、gcloud 及类似登录)送回虚拟机内的 CLI。 |
| 5830 | 浏览器 MCP 垫片 | 将代理的浏览器自动化 MCP 服务器连接到宿主机。 |
浏览器辅助虚拟机桥接: 配置(5000)、文件传输(5100)、Chrome DevTools 协议(5200)、链接中继(5300)、网络摄像头(5400)、原生标签栏(5810)和网络追踪(5900)。
剪贴板
工作区终端没有单独的剪贴板守护进程——剪贴板搭载终端协议本身。在客户机内复制(tmux 选区,或任何发出 OSC 52 的程序)会自动落到 macOS 剪贴板;⌘C 复制宿主机侧的终端选区;⌘V 使用括号粘贴向客户机粘贴。在浏览器窗格中,一个客户机剪贴板代理加上捕获的 ⌘C/⌘V 在 Mac 与 Chromium 之间提供复制和粘贴。
文件传输
文件在 Mac 与工作区之间以三种方式移动,全部在会话中介绍:共享文件夹(virtiofs,项目文件的常规路径)、类 Finder 的文件浏览器窗口(通过端口 5800 上的 vsock 文件服务拖入和拖出)以及图像粘贴(⌘V 加一张图像会将其上传到客户机并粘贴其路径)。端口 5100 上的专用文件传输协议属于浏览器辅助虚拟机。
网络
NAT 模式(默认)
NAT 模式下的所有工作区虚拟机都接入一个进程范围的软件 L2 交换机,多路复用到一个 vmnet 共享/NAT 接口上。其后果:
- 一个私有子网——默认
192.168.64.0/24(网关.1,地址从.2到.254租用 24 小时)。如果你 Mac 自身的 LAN 已经使用该范围,Bromure 会自动选取另一个192.168.x.0/24。 - Bromure 在该交换机上运行自己的 DHCP 服务器(Apple 内置的那个每个接口只能跟踪单个租约),租约持久化在宿主机上的
dhcp-leases.sqlite中。 - 稳定寻址——每个工作区获得一个持久化在
profile-macs.json中的确定性、本地管理的 MAC 地址,结合持久租约,一个工作区在应用重启后通常保持相同的 IP(尽力而为:只要该地址保持空闲)。当前 IP 始终显示在虚拟机仪表板头部和工具栏胶囊标签中。 - 虚拟机之间可以相互到达。 NAT 模式下的每个虚拟机——工作区虚拟机和浏览器辅助虚拟机一样——在设计上都位于同一 L2 段上,因此浏览器窗格可以加载运行在工作区虚拟机中的开发服务器,两个工作区也可以相互访问对方的服务。仪表板的监听端口卡片将每个可从外部到达的客户机套接字列为
<VM-IP>:<port>端点,也就是你实际会从 Mac 连接的地址。 - 与外部隔离。 NAT 意味着虚拟机可从你的 Mac 到达,但不会暴露在你的物理 LAN 上,而且来自别处的入站连接不可能实现,除非你显式发布服务(按服务的 Cloudflare 快速隧道,从监听端口卡片)。
客户机网卡的 MTU 默认被钳制到 1280——一个能在 VPN 和企业路径 MTU 环境中存活的保守值——并且可以用 defaults write io.bromure.agentic-coding vm.mtu -int <value> 提高。
请记住这个网络上的普通 IP 流量不是凭据流动的方式:客户机 HTTPS 被引导到虚拟机内的代理端点并经 vsock 到达传输边界。NAT 网络承载其他一切——而任何绕开代理服务器的东西都只携带伪造凭据,这正是上文描述的故障关闭特性。
桥接模式(按工作区)
工作区可以改为加入你的物理 LAN:桥接模式通过 vmnet 桥接将虚拟机附加到选定的宿主机接口,使其显示为本地网络上的一台设备(当其他机器必须直接到达虚拟机时很有用)。如果选定的接口在启动时不可用,虚拟机会回退到 NAT。网络模式在工作区编辑器中按工作区设置;参见工作区。
即时启动与预热
两种不同的机制让会话感觉即时,而知道哪种适用于何处是有价值的:
- 工作区虚拟机不做池化。 每个工作区直接启动自己的持久虚拟机。首次启动很快,因为系统磁盘是即时的 CoW 克隆而非复制的镜像;后续的冷启动是普通的 Linux 启动(由动画启动覆盖层覆盖);而一个挂起的工作区完全跳过启动——其 RAM 快照被恢复,会话原地续接,终端及一切,几乎即时。
- 浏览器辅助虚拟机使用热池。 引擎在后台保持一个预启动的浏览器虚拟机就绪,因此打开代理浏览器窗格用时不到 1 秒。当那个虚拟机被认领时,一个替补开始预热;空闲时,热虚拟机的内存气球被充气(客户机保留大约 512 MB),并且虚拟机在 30 秒后被挂起以保持低成本,然后在认领时被恢复且气球放气(完整内存复原给客户机)。
这个区别直接源于生命周期哲学:池化的虚拟机只有在每个实例都可互换时才有意义,这对一次性浏览器虚拟机为真,对持久的、按工作区的机器为假。