跳到主要内容
版本:2.10

管理环境变量与密钥

Shine 可以保存预设模板变量,也可以使用 GPG 或 age 封存项目环境中的敏感值。封存后的 密钥既可以提供给本机子进程,也可以通过 SSH Secret Broker 由远端命令按需请求本机解密。 不要把真实密钥写进公开仓库或文档示例。

密钥操作统一位于 shine env secret 下;workspace 形式的 shine env run 和 env run --with 用于按需向子进程注入变量。若目标命令在 SSH 远端,先阅读下文“向远端命令提供变量与密钥”, 选择直接转发或按需解密。

查看和设置变量​

按需选择查看、设置或删除操作,不要将以下示例作为设置后立即删除的流程执行:

操作命令
查看变量及其来源shine env list
读取生效值shine env get HTTP_PROXY_PORT
检查优先级和来源shine env explain HTTP_PROXY_PORT
保存变量值shine env set HTTP_PROXY_PORT 6152
删除所选位置保存的值shine env delete HTTP_PROXY_PORT

PROXY_NO_PROXY 控制 setproxy 设置的 NO_PROXY 和 no_proxy,默认为 localhost,127.0.0.1,::1。修改它或其他代理变量后,shine update 会把已安装的 proxy shell 预设标记为可更新;运行 shine upgrade 应用新值。

内置图片命令默认使用 IMAGE_QUALITY=80、IMAGE_MAX_WIDTH=1920、 IMAGE_MAX_HEIGHT=1080。可用 --quality、--width、--height 只覆盖当次运行,也可用 shine env set 为当前机器保留不同默认值。

shine env list 默认隐藏敏感值;--reveal 可取消脱敏,应只在安全终端中使用。 输出按 builtin、overlay、全局配置、全局 env 文件、项目配置、项目 env 文件分组,显示实际路径 及当前项目根。env explain KEY 从低到高展示同一存储键的定义、胜出层和描述来源,默认隐藏 所有值;--reveal 不会自动解密。显式注入仍先查找 KEY_SECRET,再查找 KEY;来源说明和 写入输出会提示已有的密文键。 默认值只在运行时兜底,查询不会创建配置。保存显式值时,即使与内置值相同也会记录该项。

如果 Shine 报告 TOML 格式错误,请修正提示的文件或字段后重试。

全局 ~/.shine/config.toml 和项目 shine.config.toml 的 [env] 支持简写字符串,也支持 同时记录值和说明:

[env]
HTTP_PROXY_PORT = "6152"
MY_API_TOKEN = { value = "<令牌>", description = "内部 API 的访问令牌" }

value 的使用方式与简写字符串完全相同;description 会显示在 shine env list 中。 对已有详细条目执行 shine env set MY_API_TOKEN <新值> 时,Shine 会更新 value 并保留 说明。

写入目标由参数和当前目录确定,与有效值来自哪里无关:

参数目标
无范围参数当前项目 shine.config.toml 的 [env],无项目时为全局配置
--global全局 config.toml 的 [env]
--project最近项目的 [env],无项目时报错
--env-file所选或默认范围的 shine.env.toml
--overlay当前手动 overlay 的 shine.env.toml

范围参数互斥。--overlay --env-file 使用同一目标。脚本应同时指定范围和文件选项, 避免依赖执行目录。未传任何目标参数时,同键被更高层定义就拒绝写入,即使值相同;显式选择 目标允许维护被遮蔽项,只修改指定文件并分别报告保存位置、当前有效来源和值是否改变。

按需选择操作和写入位置:

操作命令
写入全局配置shine env set HTTP_PROXY_PORT 7890 --global
写入项目配置shine env set HTTP_PROXY_PORT 8080 --project
写入项目 env 文件shine env set HTTP_PROXY_PORT 8081 --project --env-file
写入手动 overlayshine env set HTTP_PROXY_PORT 6152 --overlay
从全局 env 文件删除shine env delete HTTP_PROXY_PORT --global --env-file
加密当前生效的 MY_TOKEN,将密文以 MY_TOKEN_SECRET 保存到项目 env 文件中shine env secret encrypt --from MY_TOKEN --set MY_TOKEN_SECRET --project --env-file

上述加密命令会保留原配置层中的明文 MY_TOKEN。

delete 只删除目标文件实际存在的项,不删除其他层或同时删除 KEY_SECRET;不存在则报错。 删除恢复继承,不表示禁用变量,空字符串仍是合法值。Git 托管 overlay 镜像禁止直接写入; 应修改上游、使用手动维护目录,或选择本地覆盖层。上述三个写入入口的旧 --force 已停用, 请求会失败且零写入,并提示准确的替代参数。

环境变量写入期间请勿同时编辑配置。若提示文件发生变化,请检查后重试。 若提示“文件已保存,但有效状态核对失败”,说明写入已发生;再次修改前,请重新查询该值。

写入会保留注释和无关条目,拒绝符号链接及特殊文件。Unix 文件仅允许所有者访问, 但未加密的值仍然是明文。

不带 [env] 表头的全局、overlay 和项目 shine.env.toml 覆盖文件也支持这两种格式:

HTTP_PROXY_PORT = "7890"
PROXY_HOST = { value = "127.0.0.1", description = "本地代理主机" }

字符串与仅含 value 的详细项继承低层说明;显式 description 才替换说明,空字符串清空 说明且不再使用 preset catalog 兜底。数字、数组、缺少 value 等无效条目会直接报错,不会被静默忽略。

修改用于模板渲染的值后,运行:

shine upgrade

Env 分层变更的人工升级与回退​

新顺序统一为:内置默认值 < overlay 基线 < 全局 [env] < 全局 env 文件 < 项目 [env] < 项目 env 文件。旧版 overlay 高于全局覆盖文件,所有独立 env 文件高于两层 [env]; 升级可能改变有效值,不提供旧顺序模式或自动迁移。

  1. 升级前记录实际 runtime root、重要项目及关键变量来源。对准备编辑的配置做私有备份, 不把秘密提交仓库或复制进公开日志。
  2. 更新旧 --force 脚本:全局 env 文件改用 --global --env-file,项目 env 文件改用 --project --env-file,手动 overlay 改用 --overlay。托管镜像改为上游维护或本地覆盖。
  3. 升级后在项目外和各重要项目内分别运行 env list、env explain KEY。项目可选择不同的 overlay,一个项目的结果不能代表其他项目。
  4. 历史项(包括过去自动补写、等于旧默认值的项)全部保留为显式值。确认不再需要时,用明确 目标的 env delete 逐项清理并再次检查来源;不能只凭“等于默认值”判断删除。只有没有显式 定义的键才跟随新版默认值。希望 overlay 胜出的用户须人工删除不再需要的本机或项目同键项。
  5. 核对 KEY_SECRET;调整或删除明文键不取消密文键优先。运行 shine update 检查安装产物, 审阅 shine upgrade 的 Plan 后再更新。修改配置或升级二进制不会自动重渲染、执行或批准预设。

回退二进制会恢复旧排序,但不能恢复手动删除的项或已更新的安装产物。按需核对并恢复私有备份, 避免覆盖期间的无关编辑;产物仍需通过既有 lifecycle 审阅处理。没有专用 env 迁移或恢复命令。 旧二进制还可能重新把缺失的内置默认值补写到全局配置中;这不能恢复已删除的自定义值。

使用 GPG 加密值​

请安装所选加密后端(gpg 或 age)及其所需的身份插件。

先确认本机的 gpg 可以使用对应公钥;私钥保存在 YubiKey 时,可参考 在 macOS 和 Windows 使用 YubiKey OpenPGP完成接入。然后在 ~/.shine/config.toml 中指定默认接收者(可同时加密给多把 GPG 公钥):

gpg_recipients = ["user@example.com", "team-backup@example.com"]

将已有明文变量加密并保存为另一个 key:

shine env secret encrypt --from MY_TOKEN --set MY_TOKEN_SECRET
shine env secret decrypt MY_TOKEN_SECRET

加密只需要接收者公钥;解密时才需要连接持有对应私钥的 YubiKey,并按提示输入 PIN 或触摸设备。

旧版的单值 gpg_key_id 已废弃。先用 shine state migrate --dry-run 查看,再运行 shine state migrate 将它转换为 gpg_recipients;工作区中旧的 [env.encryption].recipient 也会在 env run 或 env secret seal 需要使用时提示迁移。

需要导出到当前 shell 时:

按需选择保留原变量名或重命名:

export_code="$(shine env secret export MY_TOKEN)" && eval "$export_code"
export_code="$(shine env secret export MY_TOKEN --as API_TOKEN)" && eval "$export_code"

在 PowerShell 中,仅在导出成功后执行生成的代码:

$exportCode = shine env secret export MY_TOKEN --as API_TOKEN
if ($LASTEXITCODE -eq 0) { Invoke-Expression $exportCode }

PowerShell 导出会保留 Unicode 和多行值,包括原始换行符。 安装 utils shell 预设后,也可以使用 shine-env-export MY_TOKEN --as API_TOKEN。 环境变量无法表示含 NUL 的值。env secret export 会拒绝该值; env get 和 env secret decrypt 仍保留原始输出。

使用 age identity​

Shine 支持 age 作为第二种密钥后端。它适合把密文提交到团队仓库中,并加密给多个成员各自的 recipient。已有 GPG 密文不需要迁移:不带标签的旧密文继续按 GPG 解密,age 后端生成的新密文会带有 age: 标签。

先确认 age 1.3 或更高版本已安装并位于 PATH 中。生成一个普通软件 identity,并记录输出中的 recipient:

shine env secret identity init
shine env secret identity list

普通 identity 默认写入 ~/.shine/age/identity.txt。可用 --output 选择其他路径, 再按命令提示将其加入 age_identity 或 age_identities 配置。TOML 中使用的路径必须为 有效 UTF-8。identity list 显示已配置的身份路径和 recipient。

替换已有身份需要 --force。若仍需用旧身份解密历史数据,请先妥善备份。 Unix 身份文件仅允许所有者访问。

在 macOS 上使用 Touch ID​

如果希望每次解密都需要本机用户授权,macOS 上可以改用由 Secure Enclave 托管的 identity。 这种方式还需要安装 age-plugin-se;Homebrew 可以同时安装两个依赖:

brew install age age-plugin-se

不要再执行上面的普通 identity 初始化,而是运行 Touch ID 形式,然后记录它的 recipient:

shine env secret identity init --touch-id
shine env secret identity list

--touch-id 只适用于 macOS。解密时需要本机 Secure Enclave,并会触发 Touch ID 或系统密码 授权;即使 identity 文件被复制到另一台机器,通常也不能直接解密。看到意外的授权提示时, 应取消授权并检查触发它的命令。如果 AI Agent 可以在本机运行命令,请继续阅读 在 AI Agent 参与开发时保护环境密钥,了解这种方式的安全边界。

新建 Touch ID identity 会输出 age1tag... recipient。age 1.3 可以在 macOS、Linux 或 Windows 上直接向这个公开 recipient 加密,无需在加密端安装 age-plugin-se;只有解密端需要 插件和原来的 Mac。Tagged recipient 以可识别性换取这种跨平台能力:已经知道 recipient 的人 可以判断某份密文是否发给它。

如需在本机所有项目中使用同一默认后端和 recipient,将它们写入 ~/.shine/config.toml:

secret_backend = "age"
age_recipients = ["age1tag1qexample...", "age1qteammate..."]
age_identity = "~/.shine/age/identity.txt"
age_identities = ["C:/Users/<user>/AppData/Local/age-plugin-phone/identity-....txt"]

旧的 age_identity 单路径会与新增的 age_identities 路径列表按顺序合并并去重。这样普通 identity、Secure Enclave identity 和硬件 plugin stub 可以共存,不需要复制任何文件。

如果 recipient 是某个项目团队共享的名单,应将它写入项目根目录的 shine.workspace.toml 的 [env.encryption];这样可以随项目提交,而不会影响本机的其他 项目。该配置会优先于全局默认值,完整格式见下文“使用分层项目环境”。不要将私有 age_identity 提交到仓库。

也可以只在单次命令中选择后端和 recipient:

shine env secret encrypt --backend age -r age1tag1qexample... -r age1qteammate... --from MY_TOKEN
shine env secret seal --backend age -r age1tag1qexample... -r age1qteammate...

-r/--recipient 对 GPG 和 age 都可以重复使用。

旧版 Secure Enclave identity 可能使用 age1se... recipient,导致仅执行加密的电脑也必须 安装 age-plugin-se。在 workspace 所在项目中预览并应用公开 recipient 转换:

操作命令
预览旧状态迁移shine state migrate --dry-run
审阅预览后应用迁移shine state migrate

迁移将配置中的 age1se... 转为等价的 age1tag...,不会解密密钥。 迁移后请重新封存,以使用新 recipient。已有密文保持不变,仍可解密。

为已有 workspace 密钥添加接收者​

仅修改 age_recipients 不会更新已有密文。在 shine.workspace.toml 中保留原有接收者, 追加新成员的公开 recipient:

[env.encryption]
backend = "age"
age_recipients = ["age1existing...", "age1newmember...", "age1backup..."]

将所有占位符替换为实际 recipient,然后在本机 identity 仍能解密旧 payload 的设备上运行:

shine env secret seal

seal 会解密旧 payload,再按当前完整接收者名单重新加密。即使 [secret] 中所有项都是 true,也会保留原值并重新加密;保持这些项为 true 即可,无需导出明文或重新输入值。 新增成员只需提供公开 recipient,不需要提供私有 identity。解密旧 payload 时,仍可能 要求 Touch ID、手机授权或对应 identity 的其他确认。

不指定文件时,seal 会处理 workspace 在已配置的各个 mode(包括 default_mode)下 引用的已存在源文件。使用 shine env secret seal <FILE> 可只处理一个源文件。如果封存 因错误中止,部分文件可能已更新;应解决报告的错误并重新运行,再共享结果。

将更新后的 workspace 配置和重新封存的共享源文件一起提交。名单中的每个接收者都能 独立解密新密文;请新成员在自己的设备上验证访问。新接收者无法解密 Git 历史中的旧密文。 同样,移除接收者并重新封存也不会撤销其对历史密文的访问,或轮换上游服务中的真实凭据; 必要时应在上游服务中轮换已泄露的凭据。

在 Windows 和 macOS 上实验手机授权​

age-plugin-phone 已提供面向技术用户的有限 Beta,仍只能用于合成或可丢弃数据,不能保护真实或生产 secret。安装、桌面与手机制品匹配、 配对和恢复验证请参照项目的 Beta 发布说明 和 Windows Beta 快速入门。 始终保留独立验证过的恢复 recipient。

Windows 要求 Windows 11 x64、TPM 2.0、Microsoft Platform Crypto Provider,以及能力检查 合格的 Android StrongBox 手机。Windows 和 macOS 均支持通过 Cargo 从源码安装,构建前提 以插件文档为准。可选的 Windows ZIP 使用测试签名,不要将其私有签名根证书导入系统信任库。 macOS 没有安装器包。

Shine 也开放了实验性 macOS 配对入口。请按照插件的 macOS 源码快速入门 搭配匹配的 Android StrongBox 应用。插件要求在已登录用户会话中使用真正的 Secure Enclave; Intel/T2、其它硬件和系统版本尚未普遍验证。编译部署下限不代表已经验证的最低 macOS 支持版本。 iPhone 验证仅限现有开发设备,Beta 不提供可供外部用户安装的 iOS 应用。硬件检查由插件负责。

Beta 已验证的传输范围是 Android Developer USB 和记录中设备的前台 Wi-Fi;tagged recipient 的 QR 流程、BLE 和后台唤醒不在该范围内。macOS 的 auto 在没有 Wi-Fi listener 响应时可能 选择 QR,因此请先准备好手机 Wi-Fi listener,或在此流程中显式使用 Android ADB。 操作失败后,不要通过恢复旧的插件 replay 状态来修复;请遵循插件的恢复说明。

例如,在已授权 Android ADB 设备的 Mac 上运行:

shine env secret identity init --phone --label "Work Mac" --transport adb --adb-serial SERIAL

安装匹配的桌面 plugin 和 Android 应用后,可以通过 Shine 启动 plugin 自己的事务式配对:

shine env secret identity init --phone --label "NUC WiFi Pair" --transport auto

如果希望通过局域网完成配对,先在手机端打开显式的 Pair · Wi-Fi 操作,再运行上述 命令。Windows 和 macOS 上的 auto 会先执行一次有界的 Wi-Fi discovery:只有一个匹配且位于前台的手机 listener 响应时选择 Wi-Fi;没有 listener 响应时,会在创建 pairing offer 之前选择 Windows 的 Developer USB/ADB 或 macOS 的 QR。QR 需要受支持的摄像头,并按插件提示完成 扫码。多个响应或本机 discovery 错误会安全失败;协议处理开始后不会再切换 transport。auto 是默认值,因此省略 --transport auto 时策略不变。

Developer USB 的顺序相反:先启动桌面命令;plugin 选择 ADB 并开始等待手机连接后,再在手机端 点击 Pair · USB。使用 --transport adb 时,plugin 会在预检完成后直接选择 ADB。手机只会 立即尝试连接一次,因此如果在桌面建立 adb reverse 规则前点击 Pair · USB,手机会报告 usb_transport_failed。

配对标签默认使用 Windows 或 macOS 计算机名;无法取得有效名称时使用 Shine desktop。 显式标签不能为空白,且最多为 64 个 UTF-8 字节。也可以显式指定标签、固定使用 Developer USB 或 QR, 以及在存在多台 ADB 设备时指定序列号:

为此设备选择一种配对方式:

shine env secret identity init --phone --label "Work laptop"
shine env secret identity init --phone --transport adb
shine env secret identity init --phone --transport qr
shine env secret identity init --phone --adb-serial SERIAL

指纹确认成功后,Shine 会把身份路径加入当前用户的全局 age_identities。如果当前项目显式 覆盖了 age_identity 或 age_identities,命令会在配对前退出,避免创建一个随后被项目忽略 的 identity。对应的手工配置形式如下,请使用插件在当前平台上实际返回的绝对 identity 路径:

age_identities = ["/absolute/path/returned/by/plugin/identity.txt"]

如果仅在配置多个手机身份时解密失败,可设置 AGE_PLUGIN_PHONE_MESSAGES=1 查看诊断, 并检查是否需要升级插件。临时规避时只配置匹配的身份路径;不要删除配对或恢复状态。

这个快捷命令不会修改 secret_backend,也不会自动添加 recipient。plugin setup 如果中断, 必须按照其文档使用 age-plugin-phone setup --resume 或 age-plugin-phone setup --cleanup; 不要把重新发起一次配对当作恢复手段。

在可提交到仓库的项目 shine.workspace.toml 中,同时加入配对输出的 age1tag... recipient 和一个已经独立验证过的恢复 recipient:

[env.encryption]
backend = "age"
age_recipients = ["age1tag...", "age1..."]

Shine 默认请求 --recipient-type tag,配对前要求 age 1.3+,并且桌面插件和手机应用都必须 支持 tagged recipient。该能力从 0.1.0-beta.1 开始提供,旧 alpha.5 版本不包含它。 Shine 不会重新发起配对,也不会自动退回其它 recipient 类型。向 age1tag... 加密的电脑只需 age 1.3+,无需 phone 插件,也不会弹出手机授权。配对和手机解密仍需插件及匹配的手机应用。

如需保留插件的 phone recipient 格式,请显式选择:

shine env secret identity init --phone --recipient-type phone

每台向 age1phone... 加密的电脑仍需安装 age-plugin-phone。插件自身仍默认 phone, Shine 会显式传入所选类型。与 phone v2 的私有 recipient 选择不同,知道 tagged recipient 的人 可以判断密文是否发给它。

对于已有配对,先升级并验证桌面插件和手机应用的 tag 支持,再导出公开 tag recipient,无需重新配对:

age-plugin-phone recipients -i <IDENTITY_STUB> --recipient-type tag

替换全局或项目 age_recipients,或 workspace [env.encryption].age_recipients 中对应的 recipient,保留已经独立验证的恢复 recipient,再重新 seal。shine state migrate 不转换 phone recipient。导出不会改写 stub 或已有密文;identity list 仍显示 stub 中记录的 recipient。 重新封存已有 payload 需要先解密,因此可能要求手机授权;参见 为已有 workspace 密钥添加接收者。修改 recipient 不会撤销对历史密文的访问。

使用 auto 配对后,如果希望后续 解密也优先走 Wi-Fi,请开启 Wi-Fi auto-listen 并保持手机应用在前台。plugin 会在创建 unwrap request 前发现匹配的 listener;未发现时在 Windows 上选择 Developer USB/ADB,在 macOS 上选择 QR,不会并行竞速或 在请求开始后自动重试其它路径。解密由手机保护的 secret(包括通过 shine env run 使用它)时, 标准 age plugin 会为每次 file key 解包要求一次新的强生物验证。Developer USB 和 Wi-Fi 的 plugin 提示默认 静默;设置 AGE_PLUGIN_PHONE_MESSAGES=1 可显式开启。QR 请求必须由手机扫描,因此仍会显示在终端中。 对于需要保留的数据,绝不能只配置这个实验性手机 recipient;恢复路径不能依赖同一部手机的 StrongBox 密钥、同一台电脑的 TPM/Secure Enclave 密钥或该 plugin 的本地状态。

如果 AI Agent 会参与开发,先阅读在 AI Agent 参与开发时保护环境密钥,确认 identity 文件、Touch ID、手机授权提示和命令执行权限的安全边界。

只向一个命令提供变量​

不修改当前终端、也不创建 workspace 文件时,使用可重复的 --with:

按需选择一种注入方式:

操作命令
按原名称注入变量shine env run --with MY_TOKEN -- bun run build
为子进程重命名变量shine env run --with MY_TOKEN=API_TOKEN -- bun run build
注入多个变量shine env run --with TOKEN_A --with TOKEN_B=OTHER_TOKEN -- bun run build
跳过工作区加载shine env run --no-workspace --with MY_TOKEN -- bun run build

每个 KEY 都优先解密 <KEY>_SECRET,不存在时才读取明文 <KEY>。等号右侧是子进程中 的变量名。显式 --with 值优先于当前进程和 workspace 的同名变量。

--no-workspace 会完全跳过 shine.workspace.toml 查找,只合并当前进程环境和显式 --with;它不能与 --workspace 或 --mode 同时使用。这个模式也用于需要固定读取 Shine 配置环境、但不应受当前工作目录影响的受管 Bun 命令入口。

选择单次注入还是透明代理​

偶尔执行一次敏感操作时,优先使用单次注入。例如,Cargo 的 cargo:token credential provider 启用时,可以通过 CARGO_REGISTRY_TOKEN 读取 crates.io token,因此执行 yank 时 不必把 token 留在 Shell 中,也不必长期启用命令代理:

shine env run --no-workspace \
--with CARGO_REGISTRY_TOKEN \
-- cargo yank my-crate@1.2.3

--with CARGO_REGISTRY_TOKEN 会优先解密 CARGO_REGISTRY_TOKEN_SECRET,只在本次运行中 注入 Cargo。Cargo 及其启动的后代进程仍然可以读取该值。日常持久使用 Cargo 认证时,Cargo 官方更推荐操作系统 credential provider;只有明确希望把 token 加密保存在 Shine 中时,才选择 Shine 注入。详见 Cargo registry authentication 和 cargo yank。

为固定凭据变量安装透明代理​

如果一个 CLI 每次调用都需要同一个固定凭据变量,可以安装透明代理。有些 CLI(例如 GitHub CLI)不会接受从命令行传入的 token,而是读取 GH_TOKEN 这类环境变量:

shine env proxy install gh --with GH_TOKEN
gh pr list

Shine 会在 ~/.shine/bin/ 创建同名 shim,并记录当前 PATH 中找到的真实命令。运行 gh 时,shim 只在它的子进程中解析 GH_TOKEN_SECRET;若不存在密文,才读取明文 GH_TOKEN。 该值不会写回或导出到父 Shell。--with 可重复使用,也可写成 KEY=ALIAS,以不同的变量名 传给目标命令。

代理转发子进程的 stdout/stderr,并保留其数字退出码。在 Unix 上,子进程被信号终止时返回 128 + 信号编号(例如 SIGTERM 返回 143);禁用注入或没有匹配规则时也遵循这一规则。

shim 会保留安装时选定的绝对运行时目录,包括自定义 --config-dir。之后即使调用者的 SHINE_CONFIG_DIR 不同,仍会使用安装时的目录;项目规则仍按调用时的工作目录发现。 重新运行 env proxy install 可更新旧 shim,或将其安装到另一个运行时目录。 代理安装要求配置目录和程序路径为有效 UTF-8。

代理规则作用于整个命令,不能只匹配某个子命令。如果明确要代理 Cargo,应在不需要 token 时 停止注入:

shine env proxy install cargo --with CARGO_REGISTRY_TOKEN
shine env proxy disable cargo

# 之后只在需要凭据的操作前启用:
shine env proxy enable cargo
cargo yank my-crate@1.2.3
shine env proxy disable cargo

启用期间,每个 Cargo 子命令及其后代进程都可能继承 token。disable 会保留 shim,只停止 解密和注入,并直接转发到真实 Cargo。偶尔执行 yank 时,应优先采用上面的单次 env run。

只代理你信任的命令,名称只能包含 ASCII 字母、数字、-、_ 或 .。 请将 ~/.shine/bin/ 放在 PATH 的靠前位置。目标必须是 Shine 命令目录之外的可执行程序, 不能是另一个 Shine 代理或 Shine 自身。

Windows 上请使用 gh,不要使用 gh.exe:安装会拒绝 .exe、.com、.bat、.cmd 和 .ps1 后缀。此前安装的带后缀名称仍可用 shine env proxy uninstall <COMMAND> 移除。

安装或卸载时,Shine 会保留发生冲突的文件。重试前请检查提示的冲突。 操作失败或中断后,请检查代理和配置再继续使用,不要假定恢复已经完成。

默认规则保存在全局 ~/.shine/config.toml。在含有 shine.config.toml 的项目内加入 --project,可将该命令的规则限定到项目;同一命令的项目规则会覆盖全局规则。 离开项目后,shim 会使用已有的全局规则;若没有适用规则,则直接执行原命令,不注入变量:

shine env proxy install gh --with GH_TOKEN --project
shine env proxy list

需要临时保留 shim、但禁止任何解密或注入时,可禁用规则。禁用后命令会直接转发给真实程序:

按需选择需要的状态和范围:

操作命令
禁用代理shine env proxy disable gh
启用代理shine env proxy enable gh
禁用项目代理shine env proxy disable gh --project

不加 --project 时,启用/禁用只修改全局规则,项目规则仍可覆盖它。 如果提示规则已保存但无法确认生效状态,请检查配置并再次运行 shine env proxy list。

不再需要代理时,移除 Shine 管理的 shim 及其用户级规则:

shine env proxy uninstall gh

如果真实命令被升级、移动或删除,重新执行安装命令以记录新的目标路径。

向远端命令提供变量与密钥​

通过 shine ssh 运行远端命令时,根据远端需要看到明文的范围选择方式:

目标本机命令明文可见范围
转发普通变量shine ssh --with API_URL dev远端登录 shell 或指定命令
解密并直接转发一个密钥shine ssh --with-secret API_TOKEN dev远端登录 shell 或指定命令
由远端子命令按需请求本机解密shine ssh --secret-broker ... dev仅获准启动的远端子进程

直接转发密钥适合可信远端上的临时操作,但远端登录 Shell 和同账号进程可能读取明文。若解密私钥 必须保留在本机,而且只有指定远端子进程应接收明文,请使用 SSH Secret Broker;远端管理员或 同账号进程仍可能检查该子进程。

设置命令、本机策略、登记流程和完整安全边界见 SSH 会话:密钥代理与文件传输。

从 dotenv 初始化工作区​

已有 Vite 风格的 .env 文件时,可在项目根目录生成 Shine workspace 和对应的 TOML 环境源:

操作命令
预览导入 dotenv 文件shine env workspace init --from-dotenv --dry-run
执行已审阅的导入shine env workspace init --from-dotenv

它读取 .env、.env.local、.env.<mode> 与 .env.<mode>.local,自动发现 mode,并保持该覆盖顺序。原 dotenv 文件不会被修改;生成目标已存在时命令会拒绝覆盖,包括悬空符号链接和并发导入创建的文件,确认后才添加 --force。只导入指定 mode 时可重复使用 --mode:

shine env workspace init --from-dotenv --mode development --mode production

--dry-run 只预览导入,不创建或替换文件。

导入时可将明确知道的敏感键放进 [secret],之后配置 recipient 并封存。未标记的值会作为明文导入;不要把实际凭据误当作普通配置提交。 导入的 Secret 在封存前也仍是明文。Unix 上,生成的 workspace 与环境源文件使用仅所有者可读写的 0600 权限,使用 --force 替换时同样如此。

shine env workspace init --from-dotenv --secret DATABASE_URL
shine env secret seal

为避免改变 dotenv 语义,含 $(包括 $BASE_URL 和 ${BASE_URL})或带转义的双引号内容的值会被拒绝,即使它们位于引号内。先解析插值再导入;需要字面量美元符号时,请手动编写 Shine 环境源。单行反引号值会保留其中的 #、引号、反斜杠和空白;跨行引号值会被拒绝。未加引号的 # 表示注释开始;值本身含 # 时请加引号。即使没有选择 --secret,生成文件仍会保留带说明的空 [secret] 表。

将 workspace 导出为 dotenv​

当其他工具需要普通 dotenv 文件,或者准备停止使用 Shine env 时,可导出某个 mode 合并后的最终结果:

shine env workspace export \
--format dotenv \
--mode production \
--output .env.production.local

--format 为必填项,以便明确导出格式。命令按 workspace 声明的顺序合并环境源,但不会混入当前进程变量或 --with 值。默认只导出最终生效的 [plain] 项,不会解密 payload;若后层 secret 覆盖了前层 plain,同名旧明文也不会被导出。

只有目标确实需要完整可运行环境时,才显式包含已经封存的 secret:

shine env workspace export \
--format dotenv \
--mode production \
--output .env.production.local \
--include-secrets

这会将密钥以明文写入文件,请将输出排除在版本控制之外;Unix 上仅允许所有者读取。 已有目标默认保留,只有添加 --force 才允许替换。--dry-run 预览目标和非机密变量,不打印值、 不解密、不写文件,即使指定 --include-secrets 也一样。

导出文件不含 Shine 元数据,也不依赖 Shine 运行。若要停用 Shine env,请逐个导出并验证所需 mode,将含 secret 的输出加入 .gitignore,移除 shine env run 包装,最后再自行归档或删除 shine.workspace.toml 与对应的 *.shine.toml 环境源。导出命令不会删除这些源文件。

导出使用 dotenv 引号保留反斜杠、引号和换行原值。含 $、回车、NUL 字节或无法安全引用的值会在写入或替换输出前被拒绝。遇到这些值时,请继续使用 shine env run,或为目标工具单独准备输出文件。切换前务必用该工具验证导出结果。

使用分层项目环境​

在项目根目录创建 shine.workspace.toml,声明可用 mode、按顺序合并的文件和项目共享的 GPG recipient:

version = 2

[env]
modes = ["development", "production"]
default_mode = "development"
files = [
".env.shine.toml",
".env.local.shine.toml",
".env.{mode}.shine.toml",
".env.{mode}.local.shine.toml",
]

[env.encryption]
gpg_recipients = ["user@example.com", "team-backup@example.com"]
# 团队使用 age 时,取消以下两行注释,并填入每位成员的 recipient
# backend = "age"
# age_recipients = ["age1tag1qexample...", "age1qteammate..."]

后面的环境文件覆盖前面的文件。{mode} 会替换为 --mode 指定的值;省略 --mode 时使用 default_mode。环境源文件可以同时包含明文值和待封存的 secret:

version = 1

[plain]
VITE_APP_NAME = "Example App"

[secret]
DATABASE_URL = true
API_TOKEN = false
SENTRY_TOKEN = "<待封存的值>"

[payload]
data = "<由 Shine 管理的 GPG 密文>"
  • true 保留 payload 中已有的密文值。
  • false 会在下次 seal 时安全提示输入。
  • 字符串会在封存后替换为 true,避免明文继续留在文件中。

封存待处理的 secret,再用合并后的环境启动命令:

shine env secret seal
shine env run --mode production -- bun run build

seal 默认处理 workspace 引用的环境文件。可用 shine env secret seal <FILE> 只处理一个文件, 或通过 --workspace <FILE> 指定其他 workspace;-r/--recipient 可临时覆盖接收者。

默认情况下,当前进程已经存在的环境变量优先于 workspace。设置 env.override_process_env = true 后改由 workspace 值覆盖;显式 --with 始终具有最高 优先级。

个人覆盖文件应加入 .gitignore:

.env.local.shine.toml
.env.*.local.shine.toml

不要提交含有尚未封存字符串的环境文件。可在提交前搜索 [secret] 项并确认它们都已变为 true。

环境文件结构和覆盖顺序见配置参考。

让 GPG 与 age 成员共享一份 payload​

从 Shine 2.0.x 转换工作区前,先将所有读取端(包括 SSH broker 所在本机)升级到支持 hybrid 的版本。旧读取端无法读取 hybrid: 密文。封存需要 GnuPG 2.2–2.5 和 age 1.3+, 读取端只需所选后端。既有未标记 GPG 和 age: 密文继续 可读,普通读取不迁移文件。显式配置工作区,替换以下公钥占位符:

[env.encryption]
backend = "hybrid"
gpg_recipients = ["<full 40-hex primary fingerprint>"]
age_recipients = ["age1..."]

运行 shine env secret seal --workspace shine.workspace.toml。读取旧密文需要其对应 identity;重新封存需要两种加密工具及完整公钥名单。任意一组均可独立读取同一份数据密文。 混合 GPG 密钥封装排除本机选项文件与隐式额外收件人;group 和模糊用户 ID 会在解密已有 秘密前被拒绝。

Shine 可能以加密形式缓存合并后的环境,但不会保存解锁会话;每次读取仍须遵循后端授权要求。 解密失败或取消授权时,命令会停止。

工作区输入变化会使环境缓存失效。旧版本缓存会从源文件重新生成,升级后首次运行可能需要 逐个重新授权源文件;已有加密源文件无需重新封存。

混合密钥解密默认不显示后端诊断。手机 QR 提示和显式启用的手机消息仍会实时显示。 每次密钥解密的诊断输出上限为 64 KiB,包括这些交互消息;超限会停止操作,不会尝试 另一个后端。

可在选定的 shine.workspace.toml 旁创建个人配置 shine.config.local.toml, 使用 --workspace 时也以该工作区所在目录为准:

hybrid_decrypt_backend = "age"

将 /shine.config.local.toml 加入该项目的 .gitignore。本机 env run、 env secret seal 和 env workspace export 使用此偏好。文件或字段缺失时继承全局 config.toml;两层都未设置时沿用能力检查和交互选择。配置无效会明确报错。 此文件目前只接受 hybrid_decrypt_backend = "gpg" | "age"。它属于受信任的本机项目配置, 能够修改它的程序也能改变所选授权路径。SSH broker 请求不加载或传输此文件;实际解密 机器仍使用自己的配置并执行原有释放检查。

封存只接受普通源文件,拒绝符号链接;Unix 上也拒绝具有多个硬链接的文件。 封存期间,包括等待硬件授权时,请勿编辑工作区或源文件。失败时,之前的文件可能已更新, 待封存明文也可能仍在。重试或提交前请检查提示的文件。封存仍在进行时不要删除 .shine-seal.lock。

临时机密文件​

启用临时机密文件前,请将所有读取同一工作区的客户端升级到 Shine 2.5 或更高版本。

使用 workspace version 3,可将签名 keystore、密码文件和服务凭据加密后保存在 Git 中。 文件组是命名的映射集合,不是目录。每条映射包含密文 source 和明文 target;一个组可覆盖 多个目录,重复使用 --file-group 可选择多个组。应用代码读取普通文件,不需要 Shine API; 使用自动解密的构建入口才需要安装 Shine CLI。

将接收者占位符替换为实际公钥,解密身份保存在仓库外:

version = 3

[env]
default_mode = "staging"
modes = ["staging", "production"]

[env.encryption]
backend = "age"
age_recipients = ["<actual age recipient>"]

[[file_groups.android-signing.files]]
source = ".secrets/{mode}/release.jks.enc"
target = ".android-signing/release.jks"

[[file_groups.android-signing.files]]
source = ".secrets/{mode}/signing.properties.enc"
target = ".android-signing/signing.properties"

[[file_groups.service-account.files]]
source = ".secrets/{mode}/service-account.json.enc"
target = "app/.credentials/service-account.json"

两个路径均相对 workspace 所在目录解析,并支持 {mode}。--mode 优先于 env.default_mode;所选环境必须属于 env.modes 或 env.default_mode。缺失文件时失败, 不回退到其他环境。所有环境使用相同映射清单。Version 2 workspace 仍受支持,旧客户端会拒绝 version 3。Version 3 配置了文件组时可省略 env.files;此类 workspace 的 env run 必须选择文件组。

在 Git 仓库使用前,先忽略所有明文目标,例如 .android-signing/ 和 app/.credentials/。 已跟踪或未被忽略的目标会被拒绝,包括工作区文件已缺失的已跟踪路径的大小写或 Unicode 等价名称。 Shine 不修改 .gitignore。 检查以每个目标所属的仓库为准,包括嵌套仓库。Git 检查失败会在释放明文前终止操作。 文件组也可用于 Git 仓库之外的目录,包括位于独立文件系统上的目录。 准备好明文后执行:

shine env secret seal --mode production --file-group android-signing --file-group service-account --dry-run
shine env secret seal --mode production --file-group android-signing --file-group service-account --remove-plaintext
shine env run --mode production --file-group android-signing --file-group service-account -- pnpm android:build

seal --file-group 读取 target 并创建或更新 source,复用现有 GPG、age 和 hybrid 策略, 包括 CLI 覆盖与 hybrid 收件人约束。默认保留明文。--remove-plaintext 会先解密并逐字节验证 全部写入的密文,再开始删除明文,因此需要可用身份,并可能触发授权。删除前验证失败或取消时, 保留全部原始明文。多文件写入或删除中途失败时,之前的改动仍会保留;请检查报告的已完成项和剩余项。 首版不提供仅从密文更新收件人的专用流程。

如果希望长期保留解密后的普通文件,让工具直接使用而无需每次通过 Shine 启动,可将选中文件组 导出到配置中的 target:

shine env workspace export --format files --mode production --file-group android-signing --file-group service-account --dry-run
shine env workspace export --format files --mode production --file-group android-signing --file-group service-account

--mode 必填。此操作只导出选中的文件组,不导出环境变量。全部文件解密成功后才开始创建目标。 已有目标一律拒绝覆盖,包括并发写入时出现的目标;重试前应先检查并移走这些文件。 files 格式不接受 --force、--output 或 --include-secrets。Workspace 配置和密文保持不变, 路径、Git 忽略、大小限制和私有权限要求与临时解密相同。

成功导出的文件会持续保留,由你管理或删除。env run --file-group 会拒绝这些已有目标, 不会接管或清理它们。导出失败或受控中断时,Shine 尝试移除本次创建的文件和空目录,保留被替换 的对象,并报告清理失败及可能残留。导出中断后可能有部分文件残留,尤其是在强制杀死 Shine 或断电后。不要将明文提交到 Git。

env run --file-group 在全部选中文件解密并准备就绪后才启动命令,且不会覆盖已有目标。 命令结束或正常中断后,Shine 会尝试删除本次创建的文件;运行期间被替换的文件会保留并报告。 若出现清理警告,请检查是否仍有明文残留。

临时文件仅允许当前用户访问;Windows 要求文件系统支持私有用户权限。 命令输出仍可用于管道,失败会报错。不要启动需要在命令结束后继续使用这些文件的脱离后台任务; 剩余受管子进程会被终止。

--dry-run 只检查配置和元数据,不读取明文、不解密、不写文件、不启动命令,也无法验证密文 完整性或私钥可用性。--file-group 与 seal <FILE> 互斥;在 env run 中还与 --no-workspace、--secret-broker 互斥。持久文件导出支持 --secret-broker。 普通 seal 和 env run 不会隐式选择文件组。

路径必须位于 workspace 内;拒绝绝对路径、父目录穿越、符号链接/重解析点、硬链接和映射重叠。 映射不能与 workspace 定义文件重叠,包括仅大小写或 Unicode 规范化形式不同的名称。各平台均保守拒绝这些路径别名。 单个明文文件上限为 16 MiB,单次总量上限为 64 MiB。 编码后的密文单文件上限为 32 MiB、单次总量上限为 128 MiB。文件密文使用 Shine 现有带标签的存储编码,不是原生 .age 或 .gpg 文件。

子命令及拥有相应本机权限的其他进程可以读取临时明文。强制杀死 Shine 或断电可能留下文件: 重试前应人工检查已有目标。删除文件不代表安全擦除。构建不会自动把改动后的目标重新加密回仓库。 删除收件人也无法撤销其对 Git 历史密文的访问能力。

通过 SSH broker 导出​

为 env workspace export --format files 添加 --secret-broker,即可在启用了 shine ssh --secret-broker 的会话中请求本机授权和解密。该选项要求明确选择文件组, 并匹配文件策略,不适用于 dotenv 导出。未指定时,手动导出仍使用执行机器的后端。 参见 SSH 文件授权和 App 自动秘密文件安装。