ignoresync

command module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 7, 2026 License: MIT Imports: 4 Imported by: 0

README

IGNORESYNC

Your shadow repository for ignored files

CI Go Reference Go Report Card LICENSE Ask DeepWiki

Overview

ignoresync is a CLI tool for securely syncing files ignored by Git across multiple machines. It maintains data confidentiality using a dual-trust encryption model that combines locally derived keys with AWS KMS. It uses the .gitignore file in the repository as a baseline and only manages files that match the specified patterns. In effect, it adds a "shadow repository" for confidential files that live inside the working tree but are intentionally excluded from normal Git tracking.

In addition, ignoresync includes a runner, allowing the app to run even if it depends on sensitive files that are not present in the local repository. This involves a simple workflow where those files are automatically fetched beforehand and deleted after execution.

[!IMPORTANT] ignoresync is still experimental. Please use it at your own risk. The author assumes no responsibility for any loss, damage, or other harm arising from the use of this tool.

Design Principles

  • End-to-End Encryption: Encrypt files locally before upload and decrypt them only after download, ensuring data is never exposed in transit or at rest.
  • Dual-Trust Encryption: Protect repository-scoped keys by combining a locally generated key with AWS KMS, requiring both local device authentication and AWS IAM authorization for decryption.
  • Minimal Shared Secret Surface: Limit shared secrets to the MasterKey. Other key material is either derived locally or protected by AWS-managed mechanisms.
  • Concealment: Keep ignoresync-specific configuration out of the repository by managing target patterns remotely, so that routine repository contents do not clearly advertise the tool's usage.
  • Constant-Memory-Oriented Streaming: Process large file payloads in a streaming, constant-memory-oriented manner, while handling small control data such as keys and patterns in memory for simplicity.

Architecture

ignoresync uses a hub-and-spoke model with a single central storage layer backed by AWS resources such as S3 and KMS, and each client connects to this environment for data synchronization. This remote environment acts as the Single Source of Truth (SSOT) for encrypted objects and metadata.

Each client is initialized using a shared master key. This key is stored in the OS keystore along with information used to uniquely identify the AWS resource. By maintaining this information as local state, there is no need to pass bucket names or key information as arguments for each operation.

During push and pull operations, the client merges local and AWS KMS-backed key material to restore the cryptographic context for secure processing. Data confidentiality remains absolute provided that neither the local key material nor the AWS operational permissions are compromised.

Key Concepts

ignoresync has several key concepts and terms:

Term Purpose
MasterKey Root shared secret
State A set of credentials and resource identifiers stored locally
Environment A set of deployed cloud resources
LocalKey Key derived from the MasterKey
CloudKey Key material obtained from AWS KMS
WrapKey Key derived from the LocalKey and CloudKey
RepoKey Repository-scoped key encryption key
DataKey Per-object key for body encryption

MasterKey

The master key is a random shared secret key generated during the bootstrap process. It serves as the root secret key for device activation and is used solely for deriving local keys. It is the only secret key that must be shared among devices participating in the same environment. When a rotation is performed, a new master key is registered on the local machine, and the latest key becomes active.

State

The state is a local data structure stored in the OS keystore that contains all the information a device needs to participate in encryption and decryption. It represents the device’s activation credentials and defines the AWS account, region, bucket, and encryption keys to be used.

Environment

The Environment is the set of cloud resources (S3 buckets, KMS keys) provisioned during bootstrap. It represents the remote infrastructure used to store and protect encrypted objects.

LocalKey

The LocalKey is derived from the MasterKey and acts as the local trust anchor. It is never uploaded and is required to derive the key that unwraps the repository-scoped key.

CloudKey

The CloudKey is obtained from AWS KMS as data key material and provides a cloud-managed cryptographic protection layer. Its wrapped form is stored remotely, while the plaintext is used only during key derivation.

WrapKey

The WrapKey is derived from the LocalKey and CloudKey. It is used to wrap and unwrap the RepoKey, combining local trust and AWS authorization into a unified initialization of the repository's cryptographic context.

RepoKey

The RepoKey is a random repository-scoped key stored remotely only in wrapped form. It is bound to the repository identity and is used to wrap the per-object DataKey for files and patterns.

DataKey

The DataKey is a random, per-object symmetric key generated for each upload. It is used exclusively for content encryption and is never stored in plaintext.

Commands

The CLI commands and their functions are as follows:

Command Description Scope
bootstrap Provisions a new Environment and generates the initial MasterKey and State. Global
check Checks the local State and the health of remote resources. Global
activate Add specidfied MasterKey to the local machine. Local
deactivate Removes specified MasderKey from the local machine. Local
list List the key IDs stored in the State. Local
rotate Generate new MasterKey and add it to the State. Local
leave Completely delete that state from the local machine. Local
push Archives, encrypts, and uploads files to S3. Repository
pull Downloads, decrypts, and restores files from S3. Repository
rm Deletes the files and patterns from S3. Repository
set Sets and uploads target patterns. Repository
preview Decrypts and previews files and patterns without writing to disk. Repository
rewrap Re-encrypt files and patterns using the latest MasterKey in the State. Repository
clean Clean up files in the local repossitory that matched target patterns. Repository
run Pull files, Run specified command depends on files, and clean up files. Repository

Bootstrap

The bootstrap command runs the bootstrap pipeline. Before it runs, it displays the account and region of the current profile and asks whether you want to use them for the environment. You can explicitly specify the account and region with the --profile and --region options. If they are not specified, the default profile is loaded. If no region is specified, it falls back to us-east-1.

ignoresync bootstrap --profile dev --region us-east-1

Check

The check command is a non-destructive validation tool. It concurrently performs the following checks and returns either OK or NG:

  • Field-level validation of the local state
  • CloudFormation stack status and stability
  • S3 bucket accessibility via HEAD, PUT, GET, and DELETE operations
  • KMS key health via round-trip encryption and decryption

An NG result may indicate that the bootstrap process was not completed correctly, resources were modified after bootstrap, or the current AWS profile is incorrect. Note that the check command is intended for functional validation and is not an exhaustive test suite.

ignoresync check --profile dev --region us-east-1

Activate

The activate command generates a state using the specified master key and saves it to the OS keystore, thereby joining the local machine to the existing environment. If an incorrect master key is entered, an incorrect local key will be generated, causing all encryption and decryption operations to fail. If a state already exists, this command attempts to register the next new master key. The most recent key becomes active.

ignoresync activate --profile dev

Deactivate

The deactivate command removes the specified master key from State. Attempting to remove incorrect key information will result in an error. You cannot remove a key that is currently active.

ignoresync deactivate --profile dev

List

The list command retrieves a list of key IDs from the key information stored in the State and returns it in JSON format. It also displays the currently active key.

ignoresync list --profile dev

Rotate

The rotate command generates a new master key, registers it with State, and activates it. Please note that this command is intended solely for rotating keys on the local machine. To continue encrypting and decrypting data, you must run the rewrap command. It is the user’s responsibility to securely share the latest master key and ensure that all users have the most recent and correct key.

ignoresync rotate --profile dev

Leave

The leave command completely removes State from your local machine. This disconnects you from the environment. Please note that all master keys registered with State will be deleted.

ignoresync leave

Push

The push command runs the push pipeline and securely uploads files that match the configured patterns to the repository. If the remote name of the repository is not "origin", you can specify it with the --remote option. If omitted, it defaults to "origin". This also applies to other commands. The repository identity includes the service domain, such as github.com/owner/repo. You can also enable the --dry-run option to check the target files without uploading them.

ignoresync push --profile dev --remote not-origin --dry-run

Pull

The pull command runs the pull pipeline and securely downloads files from the environment that match the configured patterns. If there are differences in file content or permissions between the local and remote versions, the user is prompted to confirm whether to overwrite the file. If the --overwrite option is enabled, the differences are ignored and the file is overwritten.

ignoresync pull --profile dev --overwrite

Rm

The rm command removes the files and the patterns from the environment. This command is allowed only when the current machine can successfully decrypt the RepoKey.

ignoresync rm --profile dev

Set

The set command takes a JSON array of target patterns for your repository and uploads them to your environment. This ensures that target patterns are centrally managed in your environment and leave no traces in your repository. You can also remove patterns entirely by sending an empty JSON array. Note that ignoresync only targets files that match both patterns ignored in .gitignore and patterns set with set. This means that if you don't run set, the files won't be included in the target. You also need to be careful not to accidentally include hierarchical structures such as node_modules. You can always simulate this with ignoresync push --dry-run.

ignoresync set '["**/.env*","**/*/bin"]' --profile dev

Preview

The preview command runs a pull pipeline and securely downloads files and patterns for preview purposes only. It lists the target files and patterns and discards them without writing them to disk.

ignoresync preview --profile dev

Rewrap

The rewrap command runs the rewrap pipeline to rotate RepoKey protection under a new MasterKey; updates wrapped keys in metadata. After running the rotate command, be sure to run rewrap on the local machine where you executed the command.

ignoresync rewrap --profile dev

Clean

The clean command retrieves the target patterns managed by the environment and deletes only the files in the local repository that match those patterns. You can restore them at any time by running pull.

ignoresync clean --profile dev

Run

The run command executes a specified command after the pull command has downloaded files matching the target pattern from the environment. Once the command completes or is interrupted, it calls clean to clean up the files downloaded by pull. This allows you to launch applications transparently even when there are no dependency files in the local repository. Please note that the pull command will forcefully overwrite any files that already exist locally.

ignoresync run --profile dev --region us-east-1 -- [command]

Pipelines

ignoresync executes a series of processing pipelines based on the issued commands.

Bootstrap Pipeline

The bootstrap pipeline activates machines and provisions environments. It is run only once per environment:

+----------------------------------------+
| Start Bootstrap                        |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Resolve AWS Account/Region using STS   |
| GetCallerIdentity API                  |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Ask user to confirm account and region |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Generate MasterKey                     |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Generate State:                        |
| Derive bucket and key identifiers      |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Deploy Environment:                    |
| Create and deploy CloudFormation stack |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Store State:                           |
| Persist state in the OS keystore       |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Display MasterKey once for secure      |
| sharing                                |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Finish Bootstrap                       |
|                                        |
+----------------------------------------+

Push Pipeline

The Push Pipeline securely uploads files and patterns:

+----------------------------------------+
| Start Push                             |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Load State from local keystore         |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Get WrappedCloudKey, WrappedRepoKey,   |
| and WrappedDataKey from S3 metadata    |
+----------------------------------------+
                    |
                    +--------------------------------------------+
                    |                                            |
            (metadata exists)                        (metadata does not exist)
                    |                                            |
                    v                                            v
+----------------------------------------+   +----------------------------------------+
| Decrypt WrappedCloudKey using KMS      |   | Get CloudKey and WrappedCloudKey using |
| Decrypt API                            |   | KMS GenerateDataKey API                |
+----------------------------------------+   +----------------------------------------+
                    |                                            |
                    v                                            v
+----------------------------------------+   +----------------------------------------+
| Derive LocalKey from MasterKey         |   | Derive LocalKey from MasterKey         |
|                                        |   |                                        |
+----------------------------------------+   +----------------------------------------+
                    |                                            |
                    v                                            v
+----------------------------------------+   +----------------------------------------+
| Derive WrapKey from LocalKey and       |   | Derive WrapKey from LocalKey and       |
| CloudKey                               |   | CloudKey                               |
+----------------------------------------+   +----------------------------------------+
                    |                                            |
                    v                                            v
+----------------------------------------+   +----------------------------------------+
| Decrypt WrappedRepoKey with WrapKey    |   | Generate RepoKey                       |
|                                        |   |                                        |
+----------------------------------------+   +----------------------------------------+
                    |                                            |
                    |                                            v
                    |                        +----------------------------------------+
                    |                        | Wrap RepoKey with WrapKey              |
                    |                        |                                        |
                    |                        +----------------------------------------+
                    |                                            |
                    |<-------------------------------------------+
                    |
                    v
+----------------------------------------+
| Compress files into archive            |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Generate DataKey                       |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Wrap DataKey with RepoKey              |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Encrypt archive with DataKey           |
| (AES-GCM chunked)                      |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Upload archive to S3 with metadata     |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Finish Push                            |
|                                        |
+----------------------------------------+

Pull Pipeline

The Pull Pipeline securely downloads files and patterns:

+----------------------------------------+
| Start Pull                             |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Load State from local keystore         |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Get WrappedCloudKey, WrappedRepoKey,   |
| and WrappedDataKey from S3 metadata    |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Decrypt WrappedCloudKey using KMS      |
| Decrypt API                            |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Derive LocalKey from MasterKey         |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Derive WrapKey from LocalKey and       |
| CloudKey                               |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Decrypt WrappedRepoKey with WrapKey    |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Download archive and metadata from S3  |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Get WrappedRepoKey from metadata and   |
| unwrap DataKey from metadata           |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Decrypt archive with DataKey           |
| (AES-GCM chunked)                      |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Restore files from archive             |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Finish Pull                            |
|                                        |
+----------------------------------------+

Rewrap Pipeline

+----------------------------------------+
| Start Rewrap                           |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Load State from local keystore         |
|                                        |
+----------------------------------------+
                    |
                    v
+----------------------------------------+
| Download archive and WrappedCloudKey,  |
| WrappedRepoKey, and WrappedDataKey     |
+----------------------------------------+
                    |
                    | Skip if match
                    +-----------------------------+
   Run if not match |                             |
                    v                             |
+----------------------------------------+        |
| Decrypt WrappedCloudKey using KMS      |        |
| Decrypt API                            |        |
+----------------------------------------+        |
                    |                             |
                    v                             |
+----------------------------------------+        |
| Derive current LocalKey from current   |        |
| MasterKey                              |        |
+----------------------------------------+        |
                    |                             |
                    v                             |
+----------------------------------------+        |
| Derive current WrapKey from current    |        |
| LocalKey and CloudKey                  |        |
+----------------------------------------+        |
                    |                             |
                    v                             |
+----------------------------------------+        |
| Decrypt WrappedRepoKey with WrapKey    |        |
|                                        |        |
+----------------------------------------+        |
                    |                             |
                    v                             |
+----------------------------------------+        |
| Derive new LocalKey from new MasterKey |        |
|                                        |        |
+----------------------------------------+        |
                    |                             |
                    v                             |
+----------------------------------------+        |
| Derive new WrapKey from new LocalKey   |        |
| and CloudKey                           |        |
+----------------------------------------+        |
                    |                             |
                    v                             |
+----------------------------------------+        |
| Wrap RepoKey with WrapKey              |        |
|                                        |        |
+----------------------------------------+        |
                    |                             |
                    v                             |
+----------------------------------------+        |
| Upload archive to S3 with metadata     |        |
|                                        |        |
+----------------------------------------+        |
                    |                             |
                    |<----------------------------+
                    |
                    v
+----------------------------------------+
| Finish Rewrap                          |
|                                        |
+----------------------------------------+

Usage for CI

ignoresync can also be used in CI. Before accessing the keystore, it checks whether the environment variable IGNORESYNC_CREDENTIAL is set. If a value is set, it validates its format and, if successful, derives the state directly from that key instead of loading it from the local keystore. Commands that would create, update, or delete local keystore state are skipped. We strongly recommend storing IGNORESYNC_CREDENTIAL as a Secret with your CI provider (e.g., GitHub Actions Secrets) to avoid exposing it in build logs.

The behavior of each subcommand in CI mode is as follows:

Command Behavior in CI mode
bootstrap Skipped
check Runs normally
activate Skipped
deactivate Skipped
list Skipped
rotate Skipped
leave Skipped
push Runs normally
pull The --overwrite flag is enforced
rm Runs normally
set Runs normally
preview Runs normally
rewrap Runs normally
clean Runs normally
run Runs normally

Advanced Usage

One effective way to use ignoresync is to prepare a dedicated repository that acts as an inventory of personal confidential files. In this style, the repository itself contains almost nothing except a .gitignore file and files that are ignored by Git. You then use ignoresync set to define which ignored files should actually be synchronized.

This approach works well when you want a single place to manage private artifacts such as local environment files, personal certificates, SSH-related materials, or other machine-specific secrets. The repository remains minimal, while the target patterns managed in the environment serve as the catalog of what should be shared across your machines.

Because ignoresync only targets files matched by both .gitignore and the configured target patterns, this usage keeps the repository clean and makes synchronization intent explicit without introducing ignoresync-specific configuration files into the repository.

Installation

Install with Homebrew

brew install nekrassov01/tap/ignoresync

Install with go

go install github.com/nekrassov01/ignoresync@latest

Or download the binary from releases

Shell completion

Supported Shells are as follows:

  • bash
  • zsh
  • fish
  • pwsh
ignoresync completion bash|zsh|fish|pwsh

Todo

  • Support CI mode
  • Support master key rotation
  • Use S3 transfermanager instead S3 manager
  • Support command runner
  • Add action.yml
  • Improve release workflows
  • Add E2E testing
  • Support launching UI locally

Author

nekrassov01

License

MIT

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
cmd
env
log

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL