Skip to content
Go back

同步 Codex auth.json:本地与多台服务器的登录状态管理

Published:  at  10:30 PM
| 约 4 分钟阅读,全文 1,436 字

我经常在本地和多台远程服务器之间切换使用 Codex。每台机器单独登录会带来重复认证和状态不一致的问题,所以我在本地 ~/.zshrc 里写了一个小函数:比较三处 auth.json 的刷新时间,选择最新的一份,再同步到其余机器。

一、背景:为什么需要同步 Codex 登录状态

Codex 的登录状态保存在本地用户目录下的 auth.json。在单机使用时这不是问题;但一旦进入多机器开发环境,就会出现几个重复场景:

我的同步目标可以抽象成:

位置路径说明
本地 Mac$HOME/.codex/auth.json日常主要入口
remote-a$HOME/.codex/auth.json远程服务器
remote-b$HOME/.codex/auth.json另一台远程环境

这个问题的核心不是文件复制,而是:哪一份文件才是最新、最应该作为同步源

如果只是无脑从本地覆盖远程,可能会把服务器上刚刷新的登录状态覆盖掉。如果只是从远程拉回本地,也可能反过来覆盖本地新状态。所以需要先比较时间,再决定同步方向。

二、方案思路:用 last_refresh 决定同步源

Codex 的 auth.json 里包含 last_refresh 字段。这个字段可以作为登录状态新旧的判断依据。

同步逻辑可以拆成四步:

  1. 读取本地 auth.jsonlast_refresh
  2. 通过 SSH 读取每台远程服务器上的 last_refresh
  3. 把这些时间转成 Unix timestamp,选出最大值。
  4. 把最新的那份 auth.json 同步到其余位置。

这样做的优点是方向自动化。无论最新登录状态出现在本地、remote-a 还是 remote-b,函数都能把它扩散到其他机器。

三、本地 zsh 函数

下面是我放在本地 ~/.zshrc 里的函数。这里使用脱敏后的主机名和路径写法;实际使用时只需要把 remote-aremote-b 替换成自己 ~/.ssh/config 里的 SSH alias。

# 同步 codex auth.json 文件
auth() {
  local L="$HOME/.codex/auth.json"
  local H1="remote-a"
  local H2="remote-b"

  lts(){ [[ -f "$1" ]] || return; jq -r '.last_refresh // empty' "$1" 2>/dev/null | sed -E 's/\.[0-9]+Z$/Z/' | xargs -I{} date -j -u -f "%Y-%m-%dT%H:%M:%SZ" "{}" "+%s" 2>/dev/null; }
  rts(){ ssh "$1" 'f="$HOME/.codex/auth.json"; [[ -f "$f" ]] || exit 0; v=$(jq -r ".last_refresh // empty" "$f" 2>/dev/null | sed -E "s/\.[0-9]+Z$/Z/"); [[ -n "$v" ]] && date -u -d "$v" +%s 2>/dev/null'; }
  pull_remote(){ mkdir -p "$HOME/.codex"; ssh "$1" 'cat "$HOME/.codex/auth.json"' > "$L"; }
  push_remote(){ ssh "$1" 'mkdir -p "$HOME/.codex"; cat > "$HOME/.codex/auth.json"' < "$L"; }
  relay_remote(){ ssh "$1" 'cat "$HOME/.codex/auth.json"' | ssh "$2" 'mkdir -p "$HOME/.codex"; cat > "$HOME/.codex/auth.json"'; }

  local tl="$(lts "$L")" t1="$(rts "$H1")" t2="$(rts "$H2")"
  [[ -z "$tl$t1$t2" ]] && { echo "[ERROR] no valid auth.json"; return 1; }

  local src="local" max="${tl:-0}"
  [[ "${t1:-0}" -gt "$max" ]] && src="$H1" max="$t1"
  [[ "${t2:-0}" -gt "$max" ]] && src="$H2" max="$t2"

  [[ "$src" != "local" && "$tl" != "$max" ]] && pull_remote "$src"
  [[ "$src" != "$H1"   && "$t1" != "$max" ]] && { [[ "$src" == "local" ]] && push_remote "$H1" || relay_remote "$src" "$H1"; }
  [[ "$src" != "$H2"   && "$t2" != "$max" ]] && { [[ "$src" == "local" ]] && push_remote "$H2" || relay_remote "$src" "$H2"; }

  echo "[INFO] synced from $src"
}

使用方式很简单:

source ~/.zshrc
auth

如果改名为 codex_auth,对应调用就是:

codex_auth

四、关键实现细节

4.1 本地时间解析:lts

lts 负责读取本地文件:

lts(){
  [[ -f "$1" ]] || return
  jq -r '.last_refresh // empty' "$1" 2>/dev/null |
    sed -E 's/\.[0-9]+Z$/Z/' |
    xargs -I{} date -j -u -f "%Y-%m-%dT%H:%M:%SZ" "{}" "+%s" 2>/dev/null
}

这里有两个处理点:

本地是 macOS,所以使用的是 BSD date

date -j -u -f "%Y-%m-%dT%H:%M:%SZ" "2026-05-01T12:00:00Z" "+%s"

其中 -j 表示只解析时间,不修改系统时间。

4.2 远程时间解析:rts

rts 通过 SSH 在远程服务器上执行解析:

rts(){
  ssh "$1" 'f="$HOME/.codex/auth.json"; [[ -f "$f" ]] || exit 0; v=$(jq -r ".last_refresh // empty" "$f" 2>/dev/null | sed -E "s/\.[0-9]+Z$/Z/"); [[ -n "$v" ]] && date -u -d "$v" +%s 2>/dev/null'
}

远程服务器通常是 Linux,所以这里使用 GNU date

date -u -d "2026-05-01T12:00:00Z" +%s

这也是为什么本地和远程分别写成 ltsrts。macOS 与 Linux 的 date 参数不兼容,如果强行复用同一条命令,很容易在某一端失效。

4.3 选择最新源

这一段负责比较三处时间:

local src="local" max="${tl:-0}"
[[ "${t1:-0}" -gt "$max" ]] && src="$H1" max="$t1"
[[ "${t2:-0}" -gt "$max" ]] && src="$H2" max="$t2"

默认先认为本地最新,然后依次比较 remote-aremote-b。如果远程时间更大,就把同步源切换到对应机器。

这里使用 ${tl:-0}${t1:-0}${t2:-0} 是为了处理文件不存在或字段无效的情况。无法解析的时间会被当作 0,不会成为同步源。

4.4 同步到其他机器

如果最新源在本地,就把本地文件通过 SSH 标准输入推送到远程:

ssh "$H1" 'mkdir -p "$HOME/.codex"; cat > "$HOME/.codex/auth.json"' < "$L"
ssh "$H2" 'mkdir -p "$HOME/.codex"; cat > "$HOME/.codex/auth.json"' < "$L"

如果最新源在远程,就分两种情况:

第二种写法避免了要求两台服务器之间能够互相 SSH。只要本地能分别连上两台机器,就可以通过本地这条管道完成中转。

五、依赖与前置条件

这个函数依赖四类能力:

依赖使用位置说明
zsh本地 shell函数写在 ~/.zshrc
jq本地和远程解析 auth.json
ssh本地到远程读取远程时间、远程间中转
SSH 标准输入输出本地与远程文件复制同步 auth.json

本地 macOS 可以用 Homebrew 安装 jq

brew install jq

远程 Ubuntu / Debian 可以用 apt 安装:

sudo apt update
sudo apt install -y jq

一般都有的 🥳

还需要保证 SSH alias 可用。例如本地 ~/.ssh/config 中需要有类似配置:

Host remote-a
    HostName <remote-a-host>
    User <remote-user>
    Port <ssh-port>

Host remote-b
    HostName <remote-b-host>
    User <remote-user>
    Port <ssh-port>

这里不建议把真实 IP、端口和私钥路径写进公开博客。文章里保留 alias 和目录结构即可,具体连接信息应留在本机 SSH 配置中。

六、验证方式

6.1 检查三处文件是否存在

先在本地检查:

test -f ~/.codex/auth.json && echo "local auth exists"

再检查远程:

ssh remote-a 'test -f ~/.codex/auth.json && echo "remote-a auth exists"'
ssh remote-b 'test -f ~/.codex/auth.json && echo "remote-b auth exists"'

6.2 检查 last_refresh

本地:

jq -r '.last_refresh // empty' ~/.codex/auth.json

远程:

ssh remote-a "jq -r '.last_refresh // empty' ~/.codex/auth.json"
ssh remote-b "jq -r '.last_refresh // empty' ~/.codex/auth.json"

6.3 执行同步

auth

正常情况下会输出:

[INFO] synced from local

或者:

[INFO] synced from remote-a

这里的 src 只表示本轮选择的最新源。它不是固定方向,每次都会根据三处 last_refresh 重新判断。

6.4 验证同步结果

同步完成后,再次查看三处 last_refresh

jq -r '.last_refresh // empty' ~/.codex/auth.json
ssh remote-a "jq -r '.last_refresh // empty' ~/.codex/auth.json"
ssh remote-b "jq -r '.last_refresh // empty' ~/.codex/auth.json"

三处输出应该一致,或者至少都指向同一轮最新刷新时间。


Suggest Changes
Share this post on:

Previous Post
Codex 记忆功能的本地实现:文件索引、摘要与检索
Next Post
Codex Full Course 2026:把 AI 编程工具当成 Agent 工作台使用