Skip to content

link-foundation/sync-auth

Repository files navigation

sync-auth

Bidirectional auth credential sync for dev tools via Git repositories.

CI/CD Pipeline Rust Version License: Unlicense

Sync authentication credentials for developer tools (GitHub CLI, GitLab CLI, Claude Code, Codex, Gemini CLI, and more) through a Git repository. Works in containers, CI runners, and across machines.

Problem

Developers using AI coding tools and platform CLIs need to re-authenticate in every new container, CI runner, or machine. There's no universal way to sync these credentials.

Features

  • Bidirectional sync -- local credentials to/from a Git repository
  • 7 built-in providers: gh, glab, claude, codex, gemini, opencode, qwen-coder
  • Extensible -- add custom providers by implementing the AuthProvider trait
  • Conflict resolution -- skips expired/dead tokens, prefers fresher credentials
  • Shallow clone -- fast initial setup with --depth 1
  • Watch mode -- continuous monitoring and periodic sync
  • Daemon support -- start/stop/restart as background process or systemd service
  • CI/CD ready -- usable in GitHub Actions, GitLab CI, Docker containers
  • Config file + env vars -- TOML config with CLI/env override support

Supported Providers

Provider Tool Credential paths
gh GitHub CLI ~/.config/gh/
glab GitLab CLI ~/.config/glab-cli/
claude Claude Code ~/.claude/, ~/.claude.json
codex OpenAI Codex CLI ~/.codex/ (auth.json, config.toml)
gemini Gemini CLI ~/.gemini/ (.env, oauth_creds.json)
opencode Opencode ~/.local/share/opencode/, ~/.config/opencode/
qwen-coder Qwen Code ~/.qwen/ (oauth_creds.json, settings.json)

Quick Start

Install

cargo install sync-auth

Initialize config

sync-auth init
# Edit ~/.config/sync-auth/config.toml with your repo URL

Basic usage

# Pull credentials from remote repo
sync-auth --repo https://github.com/USER/credentials.git pull

# Push local credentials to remote repo
sync-auth --repo https://github.com/USER/credentials.git push

# Bidirectional sync
sync-auth --repo https://github.com/USER/credentials.git sync

# Sync only specific providers
sync-auth --repo https://github.com/USER/credentials.git -p gh,claude sync

# Watch mode (sync every 60 seconds)
sync-auth --repo https://github.com/USER/credentials.git watch --interval 60

# Check status
sync-auth --repo https://github.com/USER/credentials.git status

# List available providers
sync-auth providers

Environment variables

All CLI options can be set via environment variables:

export SYNC_AUTH_REPO="https://github.com/USER/credentials.git"
export SYNC_AUTH_PROVIDERS="gh,claude"
export SYNC_AUTH_BRANCH="main"

sync-auth sync

Daemon mode

# Start as background daemon
sync-auth --repo https://github.com/USER/credentials.git daemon start --interval 60

# Stop daemon
sync-auth daemon stop

# Restart
sync-auth daemon restart

# Print systemd service unit for permanent installation
sync-auth daemon setup

Library Usage

use sync_auth::{SyncEngine, SyncConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = SyncConfig {
        repo_url: "https://github.com/user/my-credentials.git".to_string(),
        providers: vec!["gh".to_string(), "claude".to_string()],
        ..Default::default()
    };

    let engine = SyncEngine::new(config)?;

    // Pull credentials from repo to local filesystem
    let report = engine.pull().await?;
    println!("Pulled {} credentials", report.pulled.len());

    // Push local credentials to repo
    let report = engine.push().await?;
    println!("Pushed {} credentials", report.pushed.len());

    Ok(())
}

Custom provider

use sync_auth::{AuthProvider, CredentialFile, ValidationResult};

#[derive(Debug)]
struct MyToolProvider;

#[async_trait::async_trait]
impl AuthProvider for MyToolProvider {
    fn name(&self) -> &str { "my-tool" }
    fn display_name(&self) -> &str { "My Custom Tool" }

    fn credential_files(&self) -> Vec<CredentialFile> {
        vec![CredentialFile {
            relative_path: "my-tool/config".to_string(),
            local_path: dirs::home_dir().unwrap().join(".my-tool"),
            is_dir: true,
        }]
    }

    async fn validate(&self) -> ValidationResult {
        ValidationResult::Unknown
    }
}

Configuration

Config file location: ~/.config/sync-auth/config.toml

# Git repository URL (required)
repo_url = "https://github.com/USER/credentials.git"

# Providers to sync (empty = all)
providers = ["gh", "claude", "glab"]

# Git branch
branch = "main"

# Use shallow clone for initial setup
shallow_clone = true

# Watch mode interval (seconds)
watch_interval_secs = 60

Docker / CI Usage

GitHub Actions

- name: Sync credentials
  run: |
    cargo install sync-auth
    sync-auth --repo ${{ secrets.CREDENTIALS_REPO }} pull

Docker (with link-foundation/sandbox)

# On host: push credentials
sync-auth --repo https://github.com/USER/credentials.git push

# In container: pull credentials
docker exec my-sandbox sync-auth --repo https://github.com/USER/credentials.git pull

Architecture

sync-auth
├── Library crate (sync_auth)
│   ├── AuthProvider trait     -- extensible provider system
│   ├── GitBackend trait       -- pluggable storage backend
│   ├── SyncEngine             -- orchestrates sync operations
│   ├── SyncConfig             -- TOML-based configuration
│   └── providers/             -- built-in providers (gh, claude, etc.)
└── CLI binary (sync-auth)
    └── Thin wrapper over the library with clap-based CLI

Prior Art

Development

cargo build           # Build
cargo test            # Run all tests
cargo run -- --help   # Run CLI
cargo clippy          # Lint
cargo fmt             # Format

Contributing

See CONTRIBUTING.md for guidelines.

License

Unlicense -- Public Domain

About

A CLI tool to sync authentication for CLI tools

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors