# 共享开发者 Mac：控制智能体工具访问

共享开发者 Mac 可以支持团队协作，但如果每个人和每个本地进程都继承同一堆凭据，就不能安全地运行自主智能体。问题通常始于方便：shell 文件里放了项目令牌，SSH 密钥曾经加载过一次，从旧终端启动了编辑器，然后让智能体“直接部署吧”。很快，没人说得清是谁授权了操作，也没人知道哪些进程还保留着访问权限。

把身份、凭据保管、授权和证据记录视为四项不同的工作。macOS 登录信息只能告诉你哪个用户拥有某个进程。它无法证明云令牌属于谁、智能体是否应该使用它，也无法说明一个午餐后仍在运行的进程是否应该继续保留权限。把这些工作混在一起，权限就会在不知不觉中扩大。

## 共享机器需要独立身份

只有当每个人、每个凭据和每次智能体运行都有清晰、可检查、可撤销的身份时，共享工作站才真正可用。机器登录只是第一道边界，不是完整设计。

不要因为维护起来似乎更简单，就给整个团队发放一个“dev”账户。这个账户会让文件所有权、shell 历史、浏览器会话、钥匙串条目和运行中的进程变成共同资源。部署出错后，审计记录只能显示是共享账户执行了操作。这不是责任追踪，而是死胡同。

为每名开发者创建独立的 macOS 账户。对于确实需要无人值守执行的工作，再使用独立的服务身份。服务身份应有负责人、明确用途、范围有限的凭据和停用计划。它不应变成一个伪装的团队账户，让人们在自己的环境配置不方便时改用它。

智能体场景尤其能体现这种区别。编程智能体可以启动子进程、读取工作目录、检查继承到的环境，并调用获准的工具。如果两个人通过同一个本地账户运行智能体，一个人的旧环境变量或辅助进程就可能成为另一个人的意外权限来源。

Apple Platform Security 介绍了 macOS 围绕独立用户数据和系统服务提供的账户保护。这些保护很重要，但它们不会判断复制到代码仓库、共享目录或进程环境中的令牌是否适合发现它的进程。文件权限只能限制一类错误，不能证明使用意图。

为每个可以修改生产系统、源代码管理、软件包发布系统或客户数据的凭据建立简单的归属记录：

- 指定一名负责人和一名备用联系人。
- 说明服务、允许的操作和环境。
- 记录秘密的保存位置以及轮换方式。
- 设置复查日期和停用条件。

不要把实际秘密写进记录。记录的目的是让归属清晰，而不是再复制一份凭据。

指定负责人并不意味着每次操作都要由这个人亲自执行，而是要有人能直接回答：这个凭据为什么存在，谁可以授权使用，现在撤销它会造成什么影响？如果没人能回答，就在安全的时间窗口撤销它，然后按更清晰的用途重新建立访问权限。

## Unix 账户不会自动隔离被复制的秘密

macOS 账户边界只有在凭据始终留在账户边界内，并且没有通过其他路径提供出去时，才能保护数据。团队往往保护了主目录，却把同一个令牌留在项目文件、终端历史、编辑器设置、CI 导出变量或长期运行的代理进程中。

先从开发者真正会使用的地方开始盘点。要获得许可并谨慎操作：把正在使用的秘密打印到共享终端的滚动记录中，会制造你本来想查找的问题。

```sh
# Search filenames and likely configuration references, not secret values.
find "$HOME" -maxdepth 3 \( -name '.env' -o -name '.netrc' -o -name 'credentials*' \) -print

# Show environment variable names for the current shell only.
env | cut -d= -f1 | grep -E 'TOKEN|SECRET|KEY|PASSWORD' || true

# Show loaded SSH identities without printing private key material.
ssh-add -l
```

最后一个命令通常会为每个身份打印一行，包括指纹、算法和注释。如果它输出 `The agent has no identities.`，这是一个有用的结果。如果输出了你无法解释的指纹，就不要再把这台机器当作干净环境，先查清楚是哪种工作流加载了它。

不要在智能体记录中运行 `env`，再把输出粘贴到问题单里。智能体、终端、编辑器日志和支持工单都不适合存放秘密。先搜索变量名和文件位置。如果有理由认为凭据值进入过记录或代码仓库历史，就轮换该凭据。

一个常见但失败的建议是：“把秘密放在本地 .env 文件里，并将它排除在 Git 之外。”它很受欢迎，因为五分钟就能配置好。但这样一来，从该目录启动的每个程序都可能读取凭据。`.gitignore` 可以防止提交，却无法阻止本地智能体读取文件、归档工具收集文件，或开发者把它复制进下一个项目。

`.env` 文件只适合低影响的本地设置，或有明确清理日期的临时迁移工作。对于能够修改共享系统的凭据，应将值放在受保护的本地存储中，并向智能体提供操作，而不是原始值。

对进程继承也要保持同样的警惕。终端导出 `DEPLOY_TOKEN`，编辑器从这个终端启动，扩展启动语言服务器，智能体再通过编辑器调用工具。最初的开发者可能几小时前就忘了这个令牌，但每个子进程仍然可以读取它。macOS 权限确实完成了被要求的工作，问题在于团队把秘密交给了太多进程。

## 凭据保管和操作权限是两回事

凭据回答的是“谁可以完成身份验证？”授权回答的是“这个进程现在是否可以执行这项操作？”团队经常把拥有前者当成拥有后者，尤其是在使用 API 令牌时。

不要因为智能体需要调用一次 API，就把 Bearer 令牌交给它。Bearer 令牌的持有者可以使用令牌所包含的范围。一旦令牌出现在智能体上下文、工具参数、子进程环境或调试输出中，就必须假设任何能读取这些内容的系统都可能重复使用它。

应改为定义操作契约。智能体使用结构化输入请求一个命名操作。受信任的本地组件保管凭据，验证目标和请求格式是否获准，执行调用，然后返回结果。智能体完全不会获得凭据，也不会收到一个之后可以从其他来源填充的空占位符。

例如，发布智能体可能需要创建部署记录。它可以发送这样的请求：

```json
{
  "action": "create_deployment",
  "environment": "staging",
  "revision": "7c31f4a",
  "summary": "Fix request timeout handling"
}
```

受信任的组件可以将 `create_deployment` 映射到一个获准的端点和一个存储的凭据。它应拒绝试图提供主机、路径、授权请求头或任意请求正文的输入。如果操作契约允许任意 URL 和请求头，那就只是用更多形式重新创建了通用网络访问。

将凭据范围限制在足够小的程度，让错误的影响有明确上限。将读权限和写权限分开，将预发布环境和生产环境分开。如果上游服务支持，优先使用短期凭据，但不要把短生命周期当成广泛权限的解决办法。一个短时间有效的令牌仍然可以在第一秒发起不可逆操作。

SSH 也适用同样的区别。SSH 私钥证明对某个主体的控制权，却不能说明本地智能体为什么此刻应该在某台主机上运行命令。应将命令和主机选择纳入授权决定。不要给智能体一个通用 shell 通道，再希望代码仓库里的说明能约束它。

## 本地进程可能借用超出预期的权限

本地进程可以通过环境变量、打开的文件描述符、Unix 套接字、浏览器会话和辅助服务继承权限。危险进程往往并非恶意，只是过期、配置错误，或启动它的人没有意识到父进程已经授予了哪些权限。

终止可疑进程前先检查它。在 macOS 上，下面的命令可以提供有用线索，同时不会暴露凭据值：

```sh
ps -axo pid,ppid,user,command | grep -i '[a]gent\|[c]laude\|[n]ode\|[p]ython'
lsof -nP -p <PID> | grep -E 'cwd|unix|TCP|IPv4|IPv6'
```

第一个命令显示进程 ID、父进程 ID、账户和启动命令。第二个通常会显示当前工作目录、Unix 套接字路径和开放的网络连接。位于旧项目中的工作目录，以及来自上一场会话的父终端，比复杂恶意软件更能解释许多事故。

不要因为窗口消失就以为进程结束了。编辑器会继续运行语言服务器，终端复用器会保留 shell，构建工具会启动监视进程。本地 MCP 客户端也可能在开发者不再关注后很久仍保持连接。

让访问结束成为一个明确动作。退出智能体客户端，停止启动它的终端会话，并移除本次运行临时授予的授权。如果辅助程序必须持续运行，就记录其用途，并让批准访问的人可以看到它的进程身份。

一个实际的交接测试可以尽早发现问题。开发者 A 运行一个能够执行预发布操作的智能体，然后关闭智能体并退出登录。开发者 B 登录自己的账户，发起一个无害请求。B 不应发现 A 的项目目录、环境变量、SSH 套接字、浏览器会话或活动授权。如果 B 可以接触到其中任何一项，就说明机器上存在需要清理的共享状态。

不要通过让开发者成为管理员来解决问题。管理员权限对系统管理可能有必要，但会扩大粗心安装程序或本地脚本的影响范围。日常智能体工作应使用标准账户，除非具体任务需要提升权限；需要时只为该任务提升，完成后恢复正常使用。

## SSH 代理需要像私钥一样受到审查

SSH 代理通过本地套接字在私钥背后保存签名权限，因此能够访问该套接字的进程可以请求签名，而不必读取私钥文件。这比把私钥散落在磁盘上更好，但并不意味着工作站上的所有进程都可以无条件使用它。

OpenSSH 将 `SSH_AUTH_SOCK` 定义为代理套接字的路径。把这个环境变量视为敏感的路由信息。如果智能体继承了它，就可能请求 SSH 代理中当前加载的身份进行签名。私钥仍然没有暴露，但实际操作风险依然存在。

对远程系统使用自动化前，先运行：

```sh
printf '%s\n' "${SSH_AUTH_SOCK:-SSH_AUTH_SOCK is unset}"
ssh-add -l
ssh -G deploy@staging.example.internal | grep -E '^(hostname|user|identityfile|forwardagent) '
```

`ssh -G` 会打印生效的 OpenSSH 客户端配置。输出通常包括 `user deploy`、`identityfile ...` 和 `forwardagent no` 等行。仔细检查最后一行。SSH 代理转发会让远程主机通过转发连接使用本地代理。除非你能说明确切的远程跳转路径，以及它为什么需要代理，否则应保持关闭。

OpenSSH 的 `ssh_config` 手册介绍了 `ForwardAgent`，并警告说，转发可能让远程主机上拥有足够权限的用户接触本地代理。团队仍然经常广泛启用它，因为这样无需复制密钥，跳板机也更方便。这种便利确实存在，但同样存在这样的事实：在转发会话持续期间，遭到入侵或权限过宽的远程环境可能请求签名。

为不同的访问类别使用不同身份。生产部署身份不应仅仅因为开发时两者都用得上，就和个人源代码管理身份一起放在同一个代理中。任务完成后，在可行时移除身份：

```sh
ssh-add -d ~/.ssh/id_staging_deploy
# Or clear every identity after a short, dedicated session.
ssh-add -D
```

清除所有身份可能会中断其他合法工作，因此应在专用会话结束时执行，而不要在别人依赖的共享 shell 标签页中执行。更好的做法是完全避免共享 shell。

不要把私钥路径、主机别名和宽松的转发规则放进每个开发者都会盲目采用的代码仓库配置中。代码仓库可以说明预期的主机和账户，但每名开发者都应自行决定哪个本地身份可以访问它。

## 审批必须指明进程和操作

只有当人能够识别调用者、理解请求的操作，并在工作结束后撤销权限时，审批提示才有用。写着“允许访问”的提示只会训练人们批准噪音。

按会话审批适合普通开发工作。新启动的智能体进程第一次调用时，应显示调用者身份，由用户决定这次运行是否可以使用某个操作通道。进程退出时授权也应结束，而不应作为看不见的偏好继续存在。

按调用审批适合后果不对称的操作，例如删除生产数据、发布软件包、修改支付设置，或在敏感主机上运行命令。它有意增加摩擦。不要为了制造安慰性的仪式，对只读状态调用也采用这种方式，否则人们会不看内容就批准。

审批卡片应使用通俗语言回答以下问题：

- 哪个本地进程请求访问？如果可以，应包括其签名主体。
- 它将使用哪个存储凭据或操作类别？
- 请求将发送到哪个目标、主机或环境？
- 将执行什么操作？哪些输入会实质改变结果？
- 审批只对本次调用有效，还是持续到该进程退出？

不要接受只将调用者标记为“终端”或“智能体”的审批系统。本地机器可以运行多个终端和多个智能体进程。批准者需要足够的信息，才能区分刚刚启动的工作运行和碰巧仍然存活的旧进程。

Sallyport 使用固定的决策阶梯：锁定的保险库拒绝所有操作，新智能体进程默认需要会话授权，选定的凭据可以要求每次使用都经过审批。对于开发者笔记本来说，这种有限模型优于复杂的本地策略语言，因为晦涩的规则会逐渐失效，没人能确信最终是哪条规则生效。

审批不能替代范围限制。人可能批准错误的事情。应先限制目标、凭据和操作形式，再在人工判断确实有价值时要求审批。

## 日志必须能在事后回答归属问题

有用的审计记录应让工程师重建谁执行了什么、哪个本地进程获得了权限、发生了什么外部操作，以及访问何时被撤销。终端滚动记录达不到这个标准，因为用户可以修改它，shell 会轮换它，不同进程也会消失在同一个历史文件中。

保留同一事件流的两个视图。一个视图记录会话：智能体进程身份、开始和结束时间、审批以及撤销。另一个视图记录操作：时间戳、目标、方法或命令、凭据引用、结果和错误。通过会话 ID 将两者关联起来，但即使不进行复杂的数据库考古，也应能理解这些信息。

防篡改证据会改变事故后的讨论质量。使用写入后不可修改的序列并连接哈希，可以发现静默编辑。这不能证明每条记录的操作都明智，也无法找回你从未收集的记录，但能让“有人清理过日志”更容易被质疑。

独立验证审计链。Sallyport 会根据加密哈希链审计日志生成会话和活动日志，`sp audit verify` 可以在离线状态下检查哈希链，无需访问保险库密钥。这种分离很重要，因为验证命令不应依赖正在调查的同一个秘密存储。

日志也必须保护秘密。记录凭据引用、操作名称、目标和结果。不要记录 Bearer 令牌、请求授权头、私钥材料，或包含密码的命令参数。一份能还原所有秘密的完整日志，就是一个访问控制更差的第二保险库。

审查失败的操作，而不只是成功的操作。反复遭到拒绝，可能说明智能体正在尝试过时的操作契约、开发者使用了错误账户，或后台进程在任务结束后仍然运行。一次无法解释的成功操作值得关注，但失败更能显示控制措施与实际工作之间的边界。

## 团队 Mac 上可执行的工作模式

只要让个人身份和明确的操作权限比共享硬件更重要，团队就可以让本地智能体支持严肃工作。工作模式需要日常习惯，而不是一份只在入职时出现的安全文档。

在共享办公机器上添加智能体工作流时，可以按以下顺序操作：

1. 为每个凭据指定负责人、允许的服务、环境和停用条件。
2. 为智能体建立专用操作契约，不要暴露通用令牌或 shell 会话。
3. 从指定开发者账户启动智能体，并让凭据环境保持为空或尽可能精简。
4. 检查进程身份和请求目标后，再批准新进程。
5. 结束运行，必要时撤销权限，并确认辅助进程和 SSH 身份都已消失。

这套流程有意没有把一个生产令牌放进大家都使用的 shell 配置文件中那么方便。那个捷径看起来高效，直到承包商使用这台机器、编辑器继承了环境变量，或旧进程一直运行到下一个人的班次。

在发生故障前定义紧急访问流程。团队需要指定一名可以批准紧急操作的人、一份范围有限的独立紧急凭据，以及一条说明使用原因的日志记录。不要把强大的永久令牌放在共享文件夹里“以备紧急情况”。它在那里，就会变成普通访问权限。

对于需要本地操作网关的工作，应将网关放在开发者自己的账户下，并让进程边界清晰可见。一台共享 Mac 可以先后服务多名用户，但不应承载一个无法区分的权限池。

## 在智能体仍运行时测试撤销

只有当撤销能够阻止一个已经获得权限的进程发起下一次操作时，撤销才值得信任。在退出所有客户端后才测试，几乎不能证明什么。

设置一个会返回已知响应的无害预发布操作。启动智能体进程，为它批准本次会话，并执行一次操作。然后在进程仍然运行时撤销会话，或锁定凭据存储。再次请求同一个操作。

预期结果应是与活动进程或锁定状态相关的拒绝。智能体不应退回使用环境变量中的令牌、已有的 SSH 套接字或缓存的浏览器会话。如果操作仍然成功，先检查它使用的路径，再增加更多提示或策略。

开发者退出登录、另一名开发者登录后，重复同样的测试。然后在机器睡眠和唤醒后再次测试，因为本地辅助连接和 SSH 代理有时会暴露只有经过长时间工作日才会出现的假设。简要记录结果，包括准确的进程身份和测试的操作。

最有用的第一步不是购买更大的秘密管理器，也不是增加更长的审批清单。先让一个范围过宽的凭据离开智能体的可达范围，用一条范围明确的操作路径替代它，然后证明撤销能够阻止仍在运行的进程。这项测试能告诉你，团队到底是在控制访问，还是只是在记录访问。
