Skip to content

oci-sync 设计文档 ​

版本:0.1.0-dev | 更新时间:2026-05-11

1. 项目概述 ​

oci-sync 是一个 Go 语言命令行工具,将本地文件或目录以 OCI artifact 的形式同步到任意兼容 OCI Distribution Spec 的镜像仓库(Docker Hub、GHCR、Harbor、ACR 等),支持可选的 AES-256-GCM 加密,并记录所有操作到本地 activity cache。

使用场景 ​

  • 将配置文件、数据集、模型文件等任意内容存入 OCI 仓库进行版本管理
  • 跨机器、跨环境分发文件,借助镜像仓库的权限管理做访问控制
  • 敏感文件加密存储,密钥不离开本地
  • 通过自定义标签对 artifacts 进行分类和筛选管理
  • 使用 GUI 图形界面可视化管理快捷仓库和 artifacts

2. 整体架构 ​

┌──────────────────────────────────────────────────────────┐
│                         CLI 层                            │
│ cmd/root.go   cmd/push.go   cmd/pull.go   cmd/shortcut.go│
│ cmd/delete.go cmd/list.go   cmd/label.go  cmd/alias.go   │
│ cmd/recent.go cmd/gui.go                                 │
└──────────────┬───────────────┬──────────────┬────────────┘
               │              │
      ┌────────▼───┐    ┌─────▼────────┐
      │  archive   │    │     oci      │
      │  tar.gz    │    │  oras-go v2  │
      │  打包/解包  │    │  push/pull   │
      └────────────┘    └─────┬────────┘
                              │
      ┌─────────────┐   ┌─────▼────────────────┐
      │   crypto    │   │  Docker Credential    │
      │ AES-256-GCM │   │  Store (~/.docker/    │
      │ + scrypt    │   │  config.json)         │
      └─────────────┘   └──────────────────────┘
                              │
                     ┌────────▼────────────────┐
                     │       cache            │
                     │  ~/.cache/oci-sync/    │
                     │   activity.json       │
                     └───────────────────────┘

数据流(push)

本地路径 → [archive.Pack] → tar.gz bytes
         → [crypto.Encrypt](可选)→ 加密 bytes
         → [oci.Push] → OCI manifest + layer → Registry

数据流(pull)

Registry → [oci.IsEncrypted] → 检查加密状态(manifest 只读)
         → 校验 --passphrase 参数(若缺失则快速失败,无需下载)
         → [oci.Pull] → layer bytes + annotations
         → [crypto.Decrypt](若加密)→ tar.gz bytes
         → [archive.Unpack] → 本地路径

数据流(shortcut commands)

CLI 参数传入(--tag + shortcuts.<name>.repo 配置)
         → 拼装完整 remote ref
         → 复用标准 [push]/[pull]/[delete] 数据流

CLI 参数传入(shortcuts.<name>.repo 配置)
         → 解析 repository
         → 复用标准 [list] 数据流

数据流(delete)

CLI 参数传入 → [oci.Delete] 解析 descriptor → Registry 删除 API

数据流(list)

CLI 参数传入 → [oci.List] (支持 Registry/Repo 自动解析)
             → [reg.Repositories] (若为 Registry) → 遍历 repos
             → [repo.Tags] → 遍历 tags → [repo.Fetch] 获取 Manifest
             → 过滤 io.oci-sync.version 标记 → 返回 ArtifactInfo 列表
             → 支持 --label 筛选 → 格式化表格输出 (显示 REPO, TAG, DIGEST, LABELS 等)

数据流(label set/unset)

CLI 参数传入 → [oci.UpdateAnnotations] 获取 manifest
           → 修改 annotations (set 添加/更新, unset 删除指定 key)
           → Push 新 manifest → 更新 tag 指向新 digest

数据流(alias add/remove)

CLI 参数传入 → 读写配置文件 ~/.config/oci-sync/oci-sync.yaml
           → 修改 shortcuts 字段 → 保存

数据流(recent)

CLI 参数传入 → 读取 ~/.cache/oci-sync/activity.json
           → 支持 --limit 限制数量、--format 指定格式 (table/json/yaml)
           → 支持 --clear 清空历史记录

数据流(gui)

GUI 启动 → 加载所有配置的 shortcuts(左栏)
        → 点击 shortcut → [oci.List] 加载该仓库 artifacts(右栏)
        → Upload(本地路径 + Tag + 可选口令)→ [archive.Pack] → [crypto.Encrypt](可选)→ [oci.Push]
        → Download(目标路径 + 口令)→ [oci.IsEncrypted] → [oci.Pull] → [crypto.Decrypt](可选)→ [archive.Unpack]
        → Delete → [oci.Delete](确认弹窗)
        → 底部详情面板展示选中 artifact 的 Tag/Size/Version/加密状态

数据流(activity recording)

CLI push/pull/delete/label 操作成功
           → 写入 ~/.cache/oci-sync/activity.json
           → 记录类型、时间戳、远程引用、本地路径、标签、操作结果

3. 项目结构 ​

oci-sync/
├── main.go                        # 程序入口
├── go.mod / go.sum
├── FEATURE.md                     # 产品需求文档
├── README.md                      # 使用文档
├── docs/
│   └── design.md                  # 本设计文档
├── cmd/
│   ├── root.go                    # 根命令 & 全局配置(--quiet / -q)
│   ├── push.go                    # push 子命令
│   ├── pull.go                    # pull 子命令
│   ├── shortcut.go                # 动态 shortcut 子命令组
│   ├── delete.go                  # delete 子命令
│   ├── list.go                    # list 子命令
│   ├── label.go                   # label 子命令 (set/unset)
│   ├── alias.go                   # alias 子命令 (list/add/remove)
│   ├── recent.go                  # recent 子命令(查看活动历史)
│   ├── gui.go                     # gui 子命令(图形界面管理)
│   └── utils.go                   # 工具函数(formatBytes)
└── internal/
    ├── config/
    │   └── config.go              # 配置文件支持
    ├── archive/
    │   ├── archive.go             # tar.gz 打包/解包
    │   └── archive_test.go        # 单元测试
    ├── crypto/
    │   ├── crypto.go              # AES-256-GCM 加密/解密
    │   └── crypto_test.go         # 单元测试
    ├── oci/
    │   └── oci.go                 # OCI push/pull(oras-go v2)
    ├── cache/
    │   └── cache.go               # Activity cache 持久化
    ├── xdg/
    │   └── xdg.go                 # XDG 目录规范支持
    ├── gui/
    │   ├── gui.go                 # gogpu/ui 图形界面
    │   └── operations.go          # GUI 上传/下载/删除操作
    └── version/
    │   └── version.go             # 版本信息

4. 模块设计 ​

4.1 internal/archive — 打包/解包 ​

函数签名说明
Pack(srcPath string) ([]byte, error)将文件或目录打包为 tar.gz,返回字节
Unpack(data []byte, destPath string) error将 tar.gz 字节解包到指定目录

实现细节

  • 使用标准库 archive/tar + compress/gzip,无额外依赖
  • 目录打包保留完整的子目录结构,以 filepath.Walk 遍历
  • 解包时进行路径穿越检查(Path Traversal Security)

4.2 internal/crypto — 加密/解密 ​

函数签名说明
Encrypt(data []byte, passphrase string) ([]byte, error)加密数据
Decrypt(data []byte, passphrase string) ([]byte, error)解密数据

加密算法

  • KDF:scrypt(N=2^15=32768, r=8, p=1)→ 32 字节密钥 (OWASP 推荐最低)
  • 加密:AES-256-GCM(认证加密,同时提供保密性和完整性)
  • 存储格式:[salt(32B) | nonce(12B) | ciphertext+GCM-tag]
  • 每次加密生成新的随机 salt 和 nonce,保证相同明文加密结果不同
passphrase ──┐
             ├─► scrypt(N=32768) ──► 32B key ──► AES-256-GCM ──► ciphertext
random salt ─┘                                        │
random nonce ────────────────────────────────────────►│
                                                      ▼
                              [salt(32B)][nonce(12B)][ciphertext+tag]

4.3 internal/oci — OCI 操作 ​

函数签名说明
Push(ctx, data []byte, ref string, encrypted bool, labels map[string]string) error推送 artifact(支持 labels)
IsEncrypted(ctx, ref string) (bool, error)检查加密状态(仅拉取 manifest)
Pull(ctx, ref string) (*PullResult, error)拉取 artifact;返回 PullResult{Data, Encrypted}
Delete(ctx, ref string) error删除远程 artifact
List(ctx, ref string) ([]ArtifactInfo, error)列出远程仓库镜像记录(支持 Registry/Repo)
UpdateAnnotations(ctx, ref string, updates map[string]string, removeKeys []string) error更新 manifest annotations(set/unset labels)

OCI Artifact 结构

  • 使用标准 OCI Image Manifest 格式,schemaVersion: 2
  • Config mediaType:application/vnd.oci.image.config.v1+json(空 JSON {})
  • Layer mediaType:application/octet-stream(不定义自定义类型)
  • Manifest Annotations 携带元信息:
    • 系统保留 annotation:io.oci-sync.encrypted、io.oci-sync.version
    • 用户自定义 labels:存储于 annotations,可通过 --label 设置
Annotation Key值说明
io.oci-sync.encrypted"true" / "false"是否加密
io.oci-sync.version"0.1.0"工具版本
用户自定义任意字符串通过 --label key=value 设置

数据结构 ArtifactInfo

  • FullName: 镜像全名,格式为 <registry>/<repo>:<tag>(用于 JSON/YAML 输出)
  • Repo: 仓库名称
  • Tag: 镜像标签
  • Digest: 内容摘要
  • Encrypted: 是否加密(布尔值)
  • Version: 上传时的工具版本
  • Labels: 用户自定义标签映射(map[string]string)

认证

  • 支持两种认证方式(按顺序查找,找到即用):
    • 配置文件 (auths.<registry>.username/password):支持为每个仓库配置独立凭据
    • Docker credential store:通过 oras-go v2 内置的 credentials.NewStoreFromDocker() 加载
  • 自动支持 credsStore / credHelpers(macOS keychain、Windows Credential Manager、Linux secret service)
  • 用户只需提前执行 docker login <registry> 即可

4.4 internal/cache — Activity Cache ​

函数签名说明
InitCache() error初始化 cache 目录(~/.cache/oci-sync/)
AddActivity(Activity) error添加活动记录
GetRecentActivities(limit int) ([]Activity, error)获取最近活动
ClearActivities() error清空所有活动记录

Activity 数据结构

go
type Activity struct {
    Type      ActivityType  // push/pull/delete/label
    Timestamp time.Time     // 操作时间
    RemoteRef string        // 远程引用
    LocalPath string        // 本地路径(push/pull)
    Labels    []string      // 标签(label 操作)
    Success   bool          // 是否成功
    Error     string        // 错误信息
}

存储位置:遵循 XDG Base Directory Spec

  • 默认:~/.cache/oci-sync/activity.json
  • 支持 XDG_CACHE_HOME 环境变量覆盖

4.5 internal/xdg — XDG 目录规范 ​

函数签名说明
ConfigDir() string配置目录($XDG_CONFIG_HOME 或 ~/.config)
CacheDir() string缓存目录($XDG_CACHE_HOME 或 ~/.cache)
DataDir() string数据目录($XDG_DATA_HOME 或 ~/.local/share)

5. 依赖列表 ​

包版本用途
github.com/spf13/cobrav1.10.2CLI 框架
gopkg.in/yaml.v3v3.x配置文件解析(替代 viper)
oras.land/oras-go/v2v2.6.0OCI push/pull
github.com/opencontainers/image-specv1.1.1OCI 数据结构
github.com/opencontainers/go-digestv1.0.0内容摘要计算
charm.land/log/v2v2彩色日志输出
github.com/pterm/ptermv0.12.x表格/输出格式化(list、recent)
golang.org/x/cryptov0.49.0scrypt KDF
github.com/gogpu/uiv0.1.18图形界面 (GUI)
github.com/gogpu/gogpuv0.32.0GUI 窗口渲染后端(参考实现)
github.com/gogpu/ggv0.45.1GPU 绘制入口(GUI 渲染回退)

6. 安全考量 ​

风险缓解措施
密码暴力破解scrypt 高内存消耗(N=32768)增加破解成本
重放/篡改攻击AES-GCM 提供认证标签,解密失败立即报错
Nonce 重用每次加密随机生成 nonce,与 salt 一起存储
Path traversalUnpack 时检查解包路径必须在目标目录内
凭据泄露凭据通过系统 credential store 管理,不写入磁盘

7. CLI 接口 ​

push ​

bash
oci-sync push --local <local_path> --remote <remote_path> [--passphrase <passphrase>] [--label <key=value>]
# 或使用简写
oci-sync push -l <local_path> -r <remote_path> [--passphrase <passphrase>] [--label <key=value>]
参数必选说明
--local, -l✓本地文件或目录路径
--remote, -r✓目标仓库引用,格式:<registry>/<repo>:<tag>
--passphrase否加密口令,不提供则不加密
--label否设置标签,可重复使用(格式:key=value,value 可为空)
--quiet, -q否静默模式,全局生效

pull ​

bash
oci-sync pull --remote <remote_path> --local <local_path> [--passphrase <passphrase>]
# 或使用简写
oci-sync pull -r <remote_path> -l <local_path> [--passphrase <passphrase>]
参数必选说明
--remote, -r✓源仓库引用,格式:<registry>/<repo>:<tag>
--local, -l✓本地目标目录
--passphrase否解密口令(内容加密时必须提供)

<name> push ​

bash
oci-sync <name> push --local <local_path> --tag <tag> [--passphrase <passphrase>] [--label <key=value>]
参数必选说明
--local, -l✓本地文件或目录路径
--tag✓目标标签
--passphrase否加密口令,不提供则不加密
--label否设置标签,可重复使用(格式:key=value,value 可为空)

<name> pull ​

bash
oci-sync <name> pull --tag <tag> --local <local_path> [--passphrase <passphrase>]
参数必选说明
--tag✓源标签
--local, -l✓本地目标目录
--passphrase否解密口令(内容加密时必须提供)

<name> list ​

bash
oci-sync <name> list [--format table|json|yaml] [--label <key>=<value>]
参数必选说明
--format, -f否输出格式:table(默认)、json、yaml
--label否筛选标签(key=value 精确匹配,key 仅检查 key 存在),可重复

<name> delete ​

bash
oci-sync <name> delete --tag <tag>
参数必选说明
--tag✓要删除的目标标签

配置文件 ​

配置文件使用 YAML 格式,搜索路径顺序:

  1. 当前工作目录 ./oci-sync.yaml
  2. 用户配置目录 ~/.config/oci-sync/oci-sync.yaml

配置文件格式:

yaml
shortcuts:
  <name>:
    repo: <registry>/<repository>
  x:
    repo: <registry>/<repository>

auths:
  <registry1.example.com>:
    username: <username>
    password: <password>
  <registry2.example.com>:
    username: <username>
    password: <password>

配置说明:

配置项默认值说明
shortcuts.<name>.repo-动态命令的默认仓库地址
auths.<registry>.username-该仓库的认证用户名
auths.<registry>.password-该仓库的认证密码或令牌

认证优先级: 配置文件 auths > Docker credential store

delete ​

bash
oci-sync delete --remote <remote_path>
# 或使用简写
oci-sync delete -r <remote_path>
参数必选说明
--remote, -r✓目标仓库引用,格式:<registry>/<repo>:<tag>

list ​

bash
# 列出特定仓库的 tags
oci-sync list --remote <registry>/<repository> [--format table|json|yaml] [-q] [--label <key=value>]
# 列出整个注册表中的所有 oci-sync 镜像
oci-sync list -r <registry> [--format table|json|yaml] [-q] [--label <key=value>]
参数必选说明
--remote, -r✓目标仓库源或注册表,格式:<registry>/<repo> 或 <registry>
--format, -f否输出格式:table(默认)、json、yaml
--label否筛选标签(key=value 精确匹配,key 仅检查 key 存在),可重复
--quiet, -q否静默模式,全局生效

label ​

管理 OCI artifact 上的标签(存储于 manifest annotations)。

bash
# 设置标签
oci-sync label set --remote <remote_path> <key1=value1> [<key2=value2>...]

# 删除标签
oci-sync label unset --remote <remote_path> <key1> [<key2>...]
参数必选说明
--remote, -r✓目标仓库引用,格式:<registry>/<repo>:<tag>
key=value✓(set)设置标签,value 可为空字符串
key✓(unset)删除指定标签

alias ​

管理配置文件中的 shortcuts。

bash
# 列出所有 shortcuts
oci-sync alias list

# 添加 shortcut
oci-sync alias add <name> --repo <registry>/<repository>

# 删除 shortcut
oci-sync alias remove <name>
参数必选说明
--repo✓(add)shortcut 对应的仓库地址

注意:若配置文件不可写,会输出警告但不会报错。

recent ​

查看本地 activity cache 中记录的操作历史。

bash
# 查看最近 20 条活动(默认)
oci-sync recent

# 指定显示数量
oci-sync recent --limit 10

# 指定输出格式
oci-sync recent --format json
oci-sync recent --format yaml

# 清空所有活动记录
oci-sync recent --clear
参数必选说明
--limit, -n否最大显示条数,默认 20
--format, -f否输出格式:table(默认)、json、yaml
--clear否清空所有活动记录

存储位置:~/.cache/oci-sync/activity.json(支持 XDG_CACHE_HOME 环境变量)

gui ​

启动基于 gogpu/ui 的桌面图形界面(GUI),分栏管理 shortcuts 的 artifacts。

bash
# 启动 GUI 界面
oci-sync gui

界面分区:

  • Shortcuts(左栏):按名称排序的快捷键列表,展示 name 与 repo,点击加载对应仓库。
  • Artifacts(右栏):当前 shortcut 仓库的 artifacts 列表,每行显示加密状态 [E]/[ ]、Tag、Size 与 Version;点击选中。
  • 工具栏(右栏底部):Upload / Download / Delete / Refresh 四个按钮(Download/Delete 在未选中 artifact 时禁用)。
  • Details(底部详情面板):展示选中 artifact 的 Tag、Size、Version、加密状态等完整信息。

操作流程:

  • Upload:弹窗输入本地路径、Tag、可选口令(加密),后端打包 → 加密 → 推送。
  • Download:弹窗输入目标路径与口令(加密内容必填),后端检查加密状态 → 拉取 → 解密 → 解包。
  • Delete:删除所选 artifact。
  • Refresh:重新加载当前仓库的 artifacts 列表。

界面使用 Material 3 主题(基于 gogpu/ui),窗口默认尺寸 1000×700,支持鼠标点击选择与操作。


8. 后续扩展方向 ​

  • --insecure:支持 HTTP(非 TLS)仓库
  • --platform:多架构 manifest list 支持
  • 进度条:大文件上传/下载显示进度
  • 增量同步:对比 digest 跳过未变更内容
  • 多文件 layer:多个 layer 对应多个文件,支持细粒度更新