{"id":47617875,"url":"https://github.com/nasimstg/xenvsync","last_synced_at":"2026-04-01T21:44:11.288Z","repository":{"id":345923604,"uuid":"1187736753","full_name":"nasimstg/xenvsync","owner":"nasimstg","description":"Encrypt, commit, and inject .env secrets with AES-256-GCM. No cloud required.","archived":false,"fork":false,"pushed_at":"2026-03-29T17:16:17.000Z","size":96,"stargazers_count":2,"open_issues_count":15,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-29T18:58:36.976Z","etag":null,"topics":["aes-256","cli","cross-platform","cross-platform-development","devtools","dotenv","encryption","env","env-management","env-manager","env-shared","env-sync","env-syncer","environment-variables","golang","secret-manager","secrets","security","syncer"],"latest_commit_sha":null,"homepage":"https://xenvsync.softexforge.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nasimstg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["nasimstg"],"patreon":"nasimstg","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":["https://store.softexforge.io/checkout/buy/58064976-217d-4b8d-b0eb-9e96f1ca6707?media=0\u0026discount=0"]}},"created_at":"2026-03-21T04:56:44.000Z","updated_at":"2026-03-29T17:16:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nasimstg/xenvsync","commit_stats":null,"previous_names":["nasimstg/xenvsync"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/nasimstg/xenvsync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasimstg%2Fxenvsync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasimstg%2Fxenvsync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasimstg%2Fxenvsync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasimstg%2Fxenvsync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nasimstg","download_url":"https://codeload.github.com/nasimstg/xenvsync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nasimstg%2Fxenvsync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31292501,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aes-256","cli","cross-platform","cross-platform-development","devtools","dotenv","encryption","env","env-management","env-manager","env-shared","env-sync","env-syncer","environment-variables","golang","secret-manager","secrets","security","syncer"],"created_at":"2026-04-01T21:44:07.618Z","updated_at":"2026-04-01T21:44:11.270Z","avatar_url":"https://github.com/nasimstg.png","language":"TypeScript","funding_links":["https://github.com/sponsors/nasimstg","https://patreon.com/nasimstg","https://store.softexforge.io/checkout/buy/58064976-217d-4b8d-b0eb-9e96f1ca6707?media=0\u0026discount=0"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003exenvsync\u003c/h1\u003e\n  \u003cp align=\"center\"\u003e\n    Encrypt, commit, and inject \u003ccode\u003e.env\u003c/code\u003e secrets — no cloud required.\n  \u003c/p\u003e\n  \u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/nasimstg/xenvsync/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/nasimstg/xenvsync/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/nasimstg/xenvsync\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/nasimstg/xenvsync\" alt=\"Go Report Card\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://pkg.go.dev/github.com/nasimstg/xenvsync\"\u003e\u003cimg src=\"https://pkg.go.dev/badge/github.com/nasimstg/xenvsync.svg\" alt=\"Go Reference\"\u003e\u003c/a\u003e\n    \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/nasimstg/xenvsync\" alt=\"License\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/nasimstg/xenvsync/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/nasimstg/xenvsync?include_prereleases\" alt=\"Release\"\u003e\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/p\u003e\n\n---\n\n**xenvsync** encrypts your `.env` files into a `.env.vault` using **AES-256-GCM** so you can safely commit secrets to Git — while the decryption key never leaves your machine.\n\n```\n  .env (plaintext)                    .env.vault (encrypted)\n  ┌──────────────────┐    push ──►   ┌──────────────────────┐\n  │ DB_HOST=localhost │               │ #/---xenvsync vault--│\n  │ API_KEY=sk-secret │   ◄── pull   │ aGVsbG8gd29ybGQ...   │\n  └──────────────────┘               │ #/---end xenvsync----│\n         │                            └──────────────────────┘\n         │ run                                  │\n         ▼                                      │\n  ┌──────────────────┐               safe to commit to git\n  │ child process    │\n  │ (secrets in RAM) │\n  └──────────────────┘\n```\n\n## Install\n\n```bash\n# Homebrew (macOS / Linux)\nbrew install nasimstg/tap/xenvsync\n\n# Scoop (Windows)\nscoop bucket add nasimstg https://github.com/nasimstg/scoop-bucket\nscoop install xenvsync\n\n# npm\nnpm install -g @nasimstg/xenvsync\n\n# or run without installing\nnpx @nasimstg/xenvsync\n\n# Go 1.22+\ngo install github.com/nasimstg/xenvsync@latest\n\n# Nix\nnix run github:nasimstg/xenvsync\n```\n\nOr download a prebuilt binary from [Releases](https://github.com/nasimstg/xenvsync/releases).\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild from source\u003c/summary\u003e\n\n```bash\ngit clone https://github.com/nasimstg/xenvsync.git\ncd xenvsync\nmake build\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eArch Linux (AUR)\u003c/summary\u003e\n\n```bash\ngit clone https://aur.archlinux.org/xenvsync.git\ncd xenvsync\nmakepkg -si\n```\n\u003c/details\u003e\n\n## Quick Start\n\n```bash\nxenvsync init                    # generate key + update .gitignore\nxenvsync push                    # encrypt .env → .env.vault\ngit add .env.vault \u0026\u0026 git commit # safe to commit\n\nxenvsync pull                    # decrypt .env.vault → .env\nxenvsync run -- npm start        # inject secrets into process (in-memory, no .env written)\n```\n\n## Why xenvsync?\n\n| Problem | xenvsync |\n|---------|----------|\n| Cloud secret managers are overkill for local dev | Works 100% offline. Key stays on your machine. |\n| `.env` files leak into Git history | Encrypt first, commit the vault. |\n| Secrets written to disk in CI/scripts | `xenvsync run` injects in-memory. Plaintext never touches disk. |\n| Sharing secrets = Slack DMs and sticky notes | Commit `.env.vault`, share the key out-of-band. |\n\n## How It Compares\n\n| Feature | xenvsync | dotenv-vault | git-crypt | sops |\n|---------|:--------:|:------------:|:---------:|:----:|\n| No cloud account required | Yes | No | Yes | Yes |\n| Encrypts only `.env` (not all files) | Yes | Yes | No | Yes |\n| In-memory secret injection (`run`) | Yes | No | No | No |\n| Single binary, zero dependencies | Yes | No | No | No |\n| Diff / status preview | Yes | No | No | Yes |\n| Standard crypto (AES-256-GCM) | Yes | Yes | AES-256 | Various |\n\n## Commands\n\n| Command | Description |\n|---------|-------------|\n| `xenvsync init [--force] [--passphrase]` | Generate a 256-bit key, add it to `.gitignore` |\n| `xenvsync push [--env NAME]` | Encrypt `.env` → `.env.vault` |\n| `xenvsync pull [--env NAME]` | Decrypt `.env.vault` → `.env` |\n| `xenvsync run [--env NAME] -- \u003ccmd\u003e` | Decrypt in-memory and inject into a child process |\n| `xenvsync diff [--env NAME] [--show-values]` | Preview changes between `.env` and the vault |\n| `xenvsync status [--env NAME]` | Show file presence, timestamps, and sync direction |\n| `xenvsync keygen [--force]` | Generate an X25519 keypair for team vault encryption |\n| `xenvsync whoami` | Display your public key and identity path |\n| `xenvsync team add \u003cname\u003e \u003ckey\u003e` | Register a team member's public key |\n| `xenvsync team remove \u003cname\u003e` | Remove a team member from the roster |\n| `xenvsync team list` | List all team members and their public keys |\n| `xenvsync rotate [--env NAME] [--revoke NAME]` | Rotate encryption key and re-encrypt the vault |\n| `xenvsync doctor [--env NAME]` | Audit local setup for security issues |\n| `xenvsync verify [--env NAME]` | Verify vault integrity, detect duplicate keys |\n| `xenvsync log [--env NAME] [-n N]` | Show vault change history from Git commits |\n| `xenvsync envs` | List all discovered environments and their sync status |\n| `xenvsync export [--format FMT]` | Decrypt vault and output as JSON, YAML, shell, tfvars, or dotenv |\n| `xenvsync completion [SHELL]` | Generate shell completions (bash/zsh/fish/powershell) |\n| `xenvsync version` | Print version, commit, and build date |\n\n\u003e **Aliases**: `push` = `encrypt`, `pull` = `decrypt`\n\n## Multi-Environment Support\n\nUse `--env` to work with named environments. File paths follow the convention `.env.\u003cname\u003e` / `.env.\u003cname\u003e.vault`:\n\n```bash\nxenvsync push --env staging     # .env.staging → .env.staging.vault\nxenvsync pull --env production  # .env.production.vault → .env.production\nxenvsync run --env staging -- npm start\nxenvsync diff --env staging\nxenvsync envs                   # list all discovered environments\n```\n\nYou can also set `XENVSYNC_ENV` to avoid passing `--env` every time.\n\n### Fallback Merging\n\nWhen pushing, xenvsync automatically merges variables from fallback files if they exist:\n\n```\n.env.shared  →  base variables (lowest priority)\n.env.staging →  environment-specific overrides\n.env.local   →  developer-local overrides (highest priority)\n```\n\nUse `--no-fallback` to disable merging and encrypt only the primary file.\n\n## Team Sharing (V2 Vault)\n\nWith V2, each team member has their own X25519 keypair. Vaults are encrypted per-member — no shared symmetric key needed.\n\n```bash\n# Each member generates their identity (once)\nxenvsync keygen\n\n# Share your public key with the team\nxenvsync whoami\n\n# Add team members to the project roster\nxenvsync team add alice \u003calice-public-key\u003e\nxenvsync team add bob \u003cbob-public-key\u003e\nxenvsync team list\n\n# Push now auto-encrypts for all team members (V2 format)\nxenvsync push    # creates V2 vault with per-member key slots\n\n# Each member decrypts with their own private key\nxenvsync pull    # uses ~/.xenvsync/identity automatically\n```\n\nV1 vaults (created without a team roster) are still fully readable.\n\n## Key Rotation\n\nRotate encryption keys and re-encrypt the vault in one atomic step:\n\n```bash\n# V1 (symmetric key): generates new .xenvsync.key and re-encrypts\nxenvsync rotate\n\n# V2 (team mode): re-encrypts with fresh ephemeral keys for all members\nxenvsync rotate\n\n# Revoke a team member and rotate in one step\nxenvsync rotate --revoke exmember\n\n# Rotate a specific environment\nxenvsync rotate --env staging\n```\n\nWhen revoking a member, the member is removed from the roster and the vault is re-encrypted so they can no longer decrypt it — even if they have a copy of the old vault file.\n\n## Security Model\n\n| Property | Detail |\n|----------|--------|\n| Algorithm | AES-256-GCM (authenticated encryption) |\n| Key | 32 random bytes via `crypto/rand`, hex-encoded, file mode `0600` |\n| Nonce | Fresh 12-byte random nonce per encryption — same plaintext always produces different ciphertext |\n| Vault layout | `[nonce ‖ ciphertext ‖ GCM tag]`, base64-wrapped with header/footer |\n| Key isolation | Never embedded in vault output. Auto-added to `.gitignore` on `init` |\n| Identity | X25519 keypair at `~/.xenvsync/identity` (mode 0600) for asymmetric team sharing |\n| Passphrase | Optional key-encryption-key via scrypt (N=32768, r=8, p=1) + AES-256-GCM |\n| Memory zeroing | Key material overwritten with zeros after use |\n| Permission check | Warns at runtime if key file is readable by group/others |\n\n## Project Structure\n\n```\nxenvsync/\n├── main.go                        # Entry point + version wiring\n├── cmd/                           # CLI commands (Cobra)\n│   ├── root.go                    # Root command\n│   ├── init.go                    # init (--force)\n│   ├── push.go / pull.go          # encrypt / decrypt\n│   ├── run.go                     # in-memory injection\n│   ├── diff.go / status.go        # preview \u0026 sync state\n│   ├── keygen.go / whoami.go       # X25519 identity management\n│   ├── version.go                 # version info\n│   └── keyutil.go                 # shared key loading + permission check\n├── internal/\n│   ├── crypto/                    # AES-256-GCM + X25519 key exchange\n│   ├── env/                       # .env parser (multiline, quotes, export)\n│   └── vault/                     # vault file format\n├── examples/\n│   ├── docker/                    # Dockerfile, compose, entrypoint\n│   ├── ci/                        # GitHub Actions, GitLab, CircleCI, Bitbucket\n│   └── hooks/                     # Git pre-commit hook\n├── packaging/\n│   └── aur/                       # Arch Linux AUR PKGBUILD\n├── flake.nix                      # Nix flake for NixOS / nix users\n├── Makefile                       # build, test, lint, install\n├── .goreleaser.yml                # cross-platform release builds\n└── .github/workflows/ci.yml       # CI: test matrix, lint, release\n```\n\n## Contributing\n\nContributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\nIf you find xenvsync useful, consider giving it a star — it helps others discover the project.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnasimstg%2Fxenvsync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnasimstg%2Fxenvsync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnasimstg%2Fxenvsync/lists"}