{"id":13510846,"url":"https://github.com/nir0s/ghost","last_synced_at":"2025-05-12T03:31:41.391Z","repository":{"id":57434230,"uuid":"67891539","full_name":"nir0s/ghost","owner":"nir0s","description":"A simple, server/less, single-api, multi-backend, ghostly secret-store/key-store for your passwords, ssh-keys and cloud credentials. Ghost isn't real, it's just in your head.","archived":false,"fork":false,"pushed_at":"2024-03-08T02:46:39.000Z","size":242,"stargazers_count":46,"open_issues_count":30,"forks_count":27,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-17T13:52:28.814Z","etag":null,"topics":["credentials","secret","secret-key","secret-storage","secret-store","serverless","ssh","ssh-key","ssh-keys","ssh-manager","vault"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nir0s.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-09-10T19:57:39.000Z","updated_at":"2025-01-13T17:30:21.000Z","dependencies_parsed_at":"2024-06-21T04:19:26.475Z","dependency_job_id":null,"html_url":"https://github.com/nir0s/ghost","commit_stats":{"total_commits":116,"total_committers":7,"mean_commits":"16.571428571428573","dds":"0.13793103448275867","last_synced_commit":"77da967a4577ca4cf100cfe34e87b39ad88bf21c"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nir0s%2Fghost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nir0s%2Fghost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nir0s%2Fghost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nir0s%2Fghost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nir0s","download_url":"https://codeload.github.com/nir0s/ghost/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253668056,"owners_count":21944969,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["credentials","secret","secret-key","secret-storage","secret-store","serverless","ssh","ssh-key","ssh-keys","ssh-manager","vault"],"created_at":"2024-08-01T02:01:56.365Z","updated_at":"2025-05-12T03:31:41.032Z","avatar_url":"https://github.com/nir0s.png","language":"Python","funding_links":[],"categories":["Python","serverless"],"sub_categories":[],"readme":"ghost (shhhhhh)\n===============\n\n[![Travis Build Status](https://travis-ci.org/nir0s/ghost.svg?branch=master)](https://travis-ci.org/nir0s/ghost)\n[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/kuf0x8j62kts1bpg/branch/master?svg=true)](https://ci.appveyor.com/project/nir0s/ghost)\n[![PyPI Version](http://img.shields.io/pypi/v/ghost.svg)](http://img.shields.io/pypi/v/ghost.svg)\n[![Supported Python Versions](https://img.shields.io/pypi/pyversions/ghost.svg)](https://img.shields.io/pypi/pyversions/ghost.svg)\n[![Requirements Status](https://requires.io/github/nir0s/ghost/requirements.svg?branch=master)](https://requires.io/github/nir0s/ghost/requirements/?branch=master)\n[![Code Coverage](https://codecov.io/github/nir0s/ghost/coverage.svg?branch=master)](https://codecov.io/github/nir0s/ghost?branch=master)\n[![Code Quality](https://landscape.io/github/nir0s/ghost/master/landscape.svg?style=flat)](https://landscape.io/github/nir0s/ghost)\n[![Is Wheel](https://img.shields.io/pypi/wheel/ghost.svg?style=flat)](https://pypi.python.org/pypi/ghost)\n\nghost aims to provide a secret-store with a single, simple-to-use API supporting multiple storage backends without requiring a server to run.\n\nTo that end, ghost supports file based backends like TinyDB and SQLite. Using other backends means, of course, that they need to be available to ghost, while ghost itself remains stateless.\n\nCurrently, ghost supports authenticating only via a passphrase. Authenticating via KMS, GitHub and the likes, might be supported in the future.\n\nNote that beginning with v0.6.1, Python 2.6 is no longer provided.\n\n## Alternatives\n\n* While [Vault](http://vaultproject.io) is truly spectacular and I've been using it for quite a while now, it requires a server running.\n* [Credstash](https://github.com/fugue/credstash) is only AWS KMS + DDB based. \n* [Keywhiz](https://github.com/square/keywhiz), like vault, also requires a server.. and let's face it, I ain't gonna run a JVM on my laptop just for that thank you.\n* [Unicreds](https://github.com/Versent/unicreds) is based on credstash and, again, only supports KMS + DDB.\n* [Sops](https://github.com/mozilla/sops) is complicated to use and also is KMS+DDB based. \n* There's a new project called [sstash](https://github.com/realcr/sstash), but it only supports file based encryption and is not intuitive enough as I see it. \n* Google developed something called [Keyczar](https://github.com/google/keyczar), but it doesn't seem to be under development.\n* Pinterest has a seemingly interesting project called [Knox](https://github.com/pinterest/knox). Knox required a server to be running and doesn't support multiple backends. It also seems more developer oriented than anything else.\n* Lyft has a really nice solution called [Confidant](https://lyft.github.io/confidant/) which also has a nice UI to go along with it. It authenticates via KMS and stores keys in DDB and requires and server to be running.\n\n\n## Installation\n\nGhost supports Linux, Windows and OSX on Python 2.7 and 3.3+\n\n```shell\npip install ghost\n```\n\nFor dev:\n\n```shell\npip install https://github.com/nir0s/ghost/archive/master.tar.gz\n```\n\n\n## Usage\n\n### CLI\n\n```shell\n$ ghost\nUsage: ghost [OPTIONS] COMMAND [ARGS]...\n\n  Ghost generates a secret-store in which you can keep your secrets\n  encrypted. Ghost isn't real. It's just in your head.\n\nOptions:\n  -h, --help  Show this message and exit.\n\nCommands:\n  delete   Delete a key\n  export   Export all keys to a file\n  get      Retrieve a key\n  init     Initialize a stash\n  list     List keys\n  load     Load keys from backup\n  lock     Lock a key to protect it\n  migrate  Migrate keys from source to destination stash\n  purge    Purge all keys\n  put      Insert a new key\n  ssh      Use a key to SSH-connect to a machine\n  unlock   Unlock a key\n\n\n# Initializing a stash\n$ ghost init\nInitializing stash...\nInitialized stash at: /home/nir0s/.ghost/stash.json\nYour passphrase can be found under the `passphrase.ghost` file in the current directory\nMake sure you save your passphrase somewhere safe. If lost, any access to your stash will be impossible.\n...\n\n$ export GHOST_PASSPHRASE=$(cat passphrase.ghost)\n\n$ ghost list\nListing all keys in /home/nir0s/.ghost/stash.json...\nThe stash is empty. Go on, put some keys in there...\n\n# Putting keys in the stash\n$ ghost put aws secret=my_secret access=my_access\nStashing key...\n$ ghost put gcp token=my_token --description \"GCP Token\" --meta Owner=Me --meta Exp=15.06.17\n...\n\n# Retrieving a key (alternatively, bash redirect to file - `ghost get aws` \u003e file)\n$ ghost get aws\nRetrieving key...\n\nDescription:   None\nUid:           08ee6102-5668-440f-b583-97a1c7a17e5a\nCreated_At:    2016-09-15 15:10:01\nMetadata:      None\nModified_At:   2016-09-15 15:10:01\nValue:         access=my_access;secret=my_secret;\nName:          aws\n\n# Retrieving a single value from the key\n$ ghost get aws secret\nmy_secret\n\n# Retrieving a key in machine readable json\n$ ghost get gcp -j\n{\n    \"description\": \"My GCP Token\", \n    \"uid\": \"b8552219-8761-4179-b20d-0a1544dd91a3\", \n    \"created_at\": \"2016-09-15 15:22:53\", \n    \"metadata\": {\n        \"Owner\": \"Me\", \n        \"ExpirationDate\": \"15.06.17\"\n    }, \n    \"modified_at\": \"2016-09-15 15:23:46\", \n    \"value\": {\n        \"token\": \"my_token\"\n    }, \n    \"name\": \"gcp\"\n}\n\n# Modifying an existing key\n# `--add` can be used to add to a key while modify overwrites it.\n$ ghost put gcp token=my_modified_token --modify\nStashing key...\n\n$ ghost get gcp\nRetrieving key...\n\nDescription:   My GCP Token\nUid:           789a3705-044c-4e34-b720-4bc43bfbae90\nCreated_At:    2016-09-15 15:56:04\nMetadata:      Owner=Me;ExpirationDate=15.06.17;\nModified_At:   2016-09-15 15:57:05\nValue:         token=my_modified_token;\nName:          gcp\n\n# Listing the existing keys\n$ ghost list\nListing all keys in /home/nir0s/.ghost/stash.json...\nAvailable Keys:\n  - aws\n  - gcp\n\n# Deleting a key\n$ ghost delete aws\nDeleting key...\n...\n\n# Deleting all keys\n$ ghost purge -f\nPurging stash /home/nir0s/.ghost/stash.json...\n\n$ ghost list\nListing all keys in /home/nir0s/.ghost/stash.json...\nThe stash is empty. Go on, put some keys in there...\n...\n```\n\nNOTE: The default backend for the CLI is TinyDB. If you want to use the SQLAlchemy backend, you must either provide the `--stash` and `--backend` flags with every command or set the `GHOST_STASH_PATH` and `GHOST_BACKEND` env vars after having initialized the stash. Not providing the stash path and the backend will result in ghost failing misrebly.\n\n### Directly from Python\n\n```python\nimport ghost\n\n# Initialize a new stash\nstorage = ghost.TinyDBStorage(\n    db_path='/home/nir0s/.ghost/stash.json',\n    stash_name='ghost')\n# Can also generate a passphrase via `ghost.generate_passphrase(size=20)`\nstash = ghost.Stash(storage, passphrase='P!3pimp5i31')\nstash.init()\n\n# Insert a key\nstash.put(name='aws', value={'secret': 'my_secret', 'access': 'my_access'})\n# Get the key\nkey = stash.get(key_name='aws')\nprint(key)\n...\n\n# List all keys in a stash\nstash.list()\n\n# Delete a key\nstash.delete('aws')\n```\n\n\n## Working with multiple stashes\n\nBy default, ghost generates a default stash named \"ghost\", regardless of the storage backend you're using. Each backend supports working with multiple stashes (or otherwise, \"tenants\"). This allows users to distinguish between environments, for example.\n\nTo initialize a named stash:\n\n```shell\n$ ghost init http://internal-es:9200[stash-name] --backend elasticsearch\n```\n\nYou can initialize as many stashes as you want, as long, of course, as each storage backend's endpoint has a unique name for each of its stashes.\n\nYou can then initialize another:\n\n```shell\n$ ghost init http://internal-es:9200[another-stash] --backend elasticsearch\n```\n\n## Locking and Unlocking keys\n\nSometimes, you might want to lock a key to make sure it isn't deleted or modified accidentally. \n\nNOTE: Purging a stash will also delete locked keys.\n\nTo that end, ghost allows you to lock a key:\n\n```shell\n$ ghost lock my_key\nLocking key...\n$ ghost delete my_key\nDeleting key...\nKey `my_key` is locked and therefore cannot be deleted Please unlock the key and try again\n...\n\n$ ghost unlock my_key\n...\n\n```\n\n## Listing containing matches or closest match\n\nWe can also list keys which contain a certain string or some close matches to that string.\n\nFor example, let's assume we have four keys: `aws`, `aws-2`, `abws-2` and `gcp`:\n\n```shell\n$ ghost list\nListing all keys...\nAvailable Keys:\n  - aws\n  - aws-2\n  - abws-2\n  - gcp\n\n$ ghost list aws\nListing all keys...\nAvailable Keys:\n  - aws\n  - aws-2\n\n$ ghost list ~aws\nListing all keys...\nAvailable Keys:\n  - aws\n  - aws-2\n  - abws-2\n\n```\n\n* Providing a `KEY_NAME` argument to `ghost list` will allow us to look for any keys containing `KEY_NAME`.\n* Providing a tilde infront of `KEY_NAME` allows us to look for closest matches. The cutoff weight can be passed using the `--cutoff` flag (or the `cutoff` argument in Python).\n* Note that this does not mean you can't provide key names starting with a tilde, as ~aws will always be a close match of aws unless the cutoff is high enough in which case it'll stop being reasonable to search for closest matches (around ~0.8 or so).\n\n## ssh-ing to a machine\n\nGhost allows you to store a key of type `ssh` and then use `ghost ssh` to connect to the machine.\n\nThis allows you to store secret information on your most used machines (you probably won't do that for 4000 application servers, unless you're crazy) and connect to them easily.\n\n```bash\n$ ghost put my-machine --type ssh conn=ubuntu@10.10.1.10 key_file_path=~/.ssh/key.pem\n\n$ ghost ssh my-machine\nWelcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-64-generic x86_64)\n\n * Documentation:  https://help.ubuntu.com\n * Management:     https://landscape.canonical.com\n * Support:        https://ubuntu.com/advantage\n\n  Get cloud support with Ubuntu Advantage Cloud Guest:\n    http://www.ubuntu.com/business/services/cloud\n\n17 packages can be updated.\n0 updates are security updates.\n\n\n*** System restart required ***\nLast login: Wed Mar 22 21:07:21 2017 from 46.120.240.223\nubuntu@10.10.1.10:~$\n...\n\n```\n\nAn added nicety is that you don't actually have to have key files stored on your file system, as ghost knows how to address (unlike the `ssh` executable) keys stored as strings. So instead of providing `ssh_key_path`, you could provide `ssh_key=...SSH_STRING...` and ghost will use that automatically.\n\nNote that ghost will force you to provide the `conn` and one of `ssh_key` or `ssh_key_path` values when using the `--type=ssh` key type.\n\n### SSH Proxying\n\nYou can also use a ProxyCommand based `ssh` method to connect to a machine through a proxy:\n\n```bash\n$ ghost put machine-through-proxy --type ssh conn=ubuntu@10.10.1.10 key_file_path=~/.ssh/key.pem proxy=ubuntu@172.16.31.8 proxy_key_path=~/.ssh/my_proxy_key\n\n$ ghost ssh my-machine\n...\n\n```\n\nYou can also use `proxy_key` to provide the string of the key instead of `ssh_key_path`.\n\nAdditionally, any string put under the `extend` value in the key will be concatenated to the resulting ssh command.\n\n### SSH Tunneling\n\nUsing the `tunnel` key, you can create an ssh tunnel to a server (through a proxy, or not):\n\n```bash\n$ ghost put machine-through-proxy --type ssh conn=ubuntu@10.10.1.10 key_file_path=~/.ssh/key.pem tunnel='LOCAL_PORT:localhost:REMOTE_PORT'\n\n$ ghost ssh my-machine \u003e/dev/null 2\u003e\u00261 \u0026\n...\n\n```\n\n## Purging a stash\n\nTo allow for extreme measures when necessary, ghost provides the `purge` API (and command). If you quickly need to delete all keys from a stash, you can use it. To purge a stash you'll have to provide a mandatory `force` flag as precautionary measure.\n\n\n## Passphrase file generation and discovery\n\nWhen initializing a stash, ghost generates a passphrase file containing either the passphrase you explicitly provide or an auto-generated one. The file is saved under `cwd/passphrase.ghost`. After having been generated, you can read the file into an environment variable to use it like so:\n\n```shell\n$ export GHOST_PASSPHRASE=$(cat passphrase.ghost)\n```\n\nTo simplify UX when using the CLI, ghost discovers the `passphrase.ghost` file generated when initializing the a stash and uses it unless told otherwise.\n\nunless the `--passphrase` flag or `GHOST_PASSPHRASE` env var are set, ghost will search for the `passphrase.ghost` file under:\n\n1. `cwd/passphrase.ghost`\n2. `~/.ghost/passphrase.ghost`\n3. (Only non-Windows) `/etc/ghost/passphrase.ghost`\n\nThe Python API requires passing the passphrase explicitly to the Stash class when generating its instance.\n\nIt is important to note that if you regularly use two storage backends, you might not want to use the auto-discovery mechanism at all as to not accidently try to use one key with a mismatching stash.\n\n\n## Backends\n\nNOTE: ghost includes dependencies required for TinyDB only as its installation should be light-weight by default. `\nYou can install extras for each specific backend. See below.\n\nNOTE: While true for the API, the CLI does not currently expose any advanced configuration for the backends such as setting certs, credentials or paths.\n\nUntil the API documentation is complete, please take a look at the Storage API's on how to use each storage.\n\n### [TinyDB](http://tinydb.readthedocs.io/en/latest/usage.html)\n\nThe TinyDB backend provides an easy to read, portable JSON file based stash. It is the default backend when using the CLI as it is the simplest to digest for new users.\n\n### [SQLAlchemy](http://www.sqlalchemy.org)\n\n(Initially tested on v1.0.15)\n\nNOTE: To use postgre, mysql and the likes, you must have the relevant package installed for SQLAlchemy to work. For instance, providing `postgresql://scott:tiger@localhost/mydatabase` as the path to the backend requires installing `psycopg2`. Failing to install the relevant package will result in SQLAlchemy raising an error which will state what's missing.\n\nTo enable, run `pip install ghost[sqlalchemy]`\n\nThe SQLAlchemy backend provides a way to use all well known SQL databases as backends including a local sqlite file. Functionally, the sqlite SQLAlchemy based backend resembles the TinyDB backend, but is not humanly readable.\n\nAll SQLAlchemy connection strings are allowed so Postgre, MySQL, MSSQL and the likes are easily accessible\n\n### [Elasticsearch](http://elastic.co)\n\n(Initially tested on v2.4.1 using elasticsearch-py 2.4.0)\n\nTo enable, run `pip install ghost[elasticsearch]`\n\nThe Elasticsearch backend resembles the TinyDB backend in that it simply stores JSON documents. An Index called `ghost` is created in the cluster (unless another index name is provided via the API) and used to store the keys.\n\n### [Consul](http://www.consul.io)\n\n(Initially tested on v0.7.0)\n\nTo enable, run `pip install ghost[consul]`\n\nNOTE: As per [consul's documentation], you cannot provide values larger\nthan 512kb.\n\nThe Consul backend allows to use Consul's distributed nature to distribute keys between servers. Consul's kv-store (v1) is used to store the keys. You must configure your Consul cluster prior to using it with Ghost as ghost will practically do zero-configuration on your cluster. As long as the kv-store's REST API is accessible to ghost, you're good. You may, of course, use a single Consul server as a stash, but to prevent dataloss, that is of course not recommended.\n\n### [Vault](http://www.vaultproject.io)\n\n(Initially tested on v0.6.1 using hvac 0.2.16)\n\nTo enable, run `pip install ghost[vault]`\n\nNOTE: You MUST provide your Vault token either via the API or via the `VAULT_TOKEN` env var to use the Vault backend.\n\nIronically maybe, you may use Vault as your stash. Since Vault itself encrypts and decrypts keys and requires a token, it would seem weird to use ghost as a front-end for it. I do not recommend using ghost with Vault unless you need to do cross-backend work - that is, use multiple backends at once or preserve a single API where Vault isn't always accessible. The main reason for using ghost and not Vault is mainly its no-server nature. If you already have Vault running, you may as well use its CLI/API and not use ghost to overcome unnecessary abstraction layers.\n\nAs such, much like with Consul, note that ghost does not provide any complicated configuration options for Vault using the CLI or otherwise. You need to have your Vault[Cluster] preconfigured after-which ghost will store all keys under the `secrets` path (can be overriden). You may provide a key named `aws/account_1`, for instance, in which case ghost will just pass the path along to Vault.\n\n\n### [S3](https://aws.amazon.com/s3/)\n\n\nTo enable, run `pip install ghost[s3]`. \n\nThe S3 backend saves keys as JSON encoded objects inside the provided bucket.\n\n#### Requirements\n\n\n- A Stash path must be provided - this is the bucket name to be used. Also, it is necessary to provide a bucket location. If you're using the CLI then you can use\n     ```\n     export GHOST_BUCKET_LOCATION=\"BUCKET_NAME\"\n     ```\n- Also AWS credentials and region name must be provided. If you're using the CLI then you can use \n    ```\n    export AWS_DEFAULT_REGION=\"***\"\n    export AWS_ACCESS_KEY_ID=\"***\"\n    export AWS_SECRET_ACCESS_KEY=\"***\"\n    OPTIONAL: \n    export AWS_SESSION_TOKEN=\"***\"\n    export AWS_PROFILE=\"***\"\n    ```\n\n\n## Encryption \u0026 Decryption\n\nEncryption is done using [cryptography](https://cryptography.io/en/latest/). It is done only on values and these are saved in hexa. Keys are left in plain text.\n\nValues are encrypted once provided and decrypted only upon request, meaning that they're only available in memory for a very short period of time.\n\nSee cryptography's [documentation](https://cryptography.io/en/latest/) for additional information.\n\n\n## Audit log\n\nNOTE: This is WIP. The audit log is currently kept on the machine where ghost is run. As such, it is hardly useful for auditing purposes when using a remote backend. As ghost evoles, it will offer remote auditing. \n\nAn audit log is saved under `~/.ghost/audit.log` containing a log of all primary actions (`put`, `get`, `delete`, `purge`, `list`) done on any stash. The path can be set using the `GHOST_AUDIT_LOG` env var.\n\nThe log file itself is not machine readable. Whether it will be remains to be seen.\n\nThe log should look somewhat like this:\n\n```\n2016-10-25 15:23:24,441 - [/home/nir0s/.ghost/stash.json] [LIST]\n2016-10-25 15:23:31,350 - [/home/nir0s/.ghost/stash.json] [PUT] - {\"key_name\": \"aws\", \"metadata\": \"null\", \"description\": null, \"value\": \"HIDDEN\", \"uid\": \"19fde800-89b9-4c25-a0af-b790e118bab7\"}\n2016-10-25 15:23:34,954 - [/home/nir0s/.ghost/stash.json] [LIST]\n2016-10-25 15:24:33,322 - [/home/nir0s/.ghost/stash.json] [GET] - {\"key_name\": \"aws\"}\n2016-10-25 15:24:33,323 - [/home/nir0s/.ghost/stash.json] [DELETE] - {\"key_name\": \"aws\"}\n2016-10-25 15:24:33,323 - [/home/nir0s/.ghost/stash.json] [DELETE] - {\"key_name\": \"aws\"}\n2016-10-25 15:24:49,890 - [/home/nir0s/.ghost/stash.json] [PUT] - {\"key_name\": \"aws\", \"metadata\": \"null\", \"description\": null, \"value\": \"HIDDEN\", \"uid\": \"ffa4fb66-e3c0-445c-bafc-a60f480dc45a\"}\n2016-10-25 15:24:52,230 - [/home/nir0s/.ghost/stash.json] [PUT] - {\"key_name\": \"gcp\", \"metadata\": \"null\", \"description\": null, \"value\": \"HIDDEN\", \"uid\": \"567f891a-d097-4575-a472-4409dc459a9a\"}\n2016-10-25 15:24:55,625 - [/home/nir0s/.ghost/stash.json] [PUT] - {\"key_name\": \"gfa\", \"metadata\": \"null\", \"description\": null, \"value\": \"HIDDEN\", \"uid\": \"434b197b-c82e-41b1-a4d2-eaeb7cd6cf72\"}\n2016-10-25 15:25:00,553 - [/home/nir0s/.ghost/stash.json] [LIST]\n2016-10-25 15:25:08,413 - [/home/nir0s/.ghost/stash.json] [GET] - {\"key_name\": \"aws\"}\n2016-10-25 15:25:08,414 - [/home/nir0s/.ghost/stash.json] [DELETE] - {\"key_name\": \"aws\"}\n2016-10-25 15:25:08,414 - [/home/nir0s/.ghost/stash.json] [DELETE] - {\"key_name\": \"aws\"}\n2016-10-25 15:25:16,416 - [/home/nir0s/.ghost/stash.json] [PURGE] - all keys\n```\n\n## Exporting and Importing\n\nYou can export and import all keys in a stash using the `ghost export` and `ghost load` commands (same methods in the Python API).\n\nThe `export` command allows you to generate a json file containing all keys (encrypted, of course) while the `load` command can then load that file into another stash using the same, or a different storage backend. \n\nSo, for instance, if you have a local implementation using sqlite, you could export all keys; create a new stash using the SQLAlchemy storage for postgre and load all keys into that storage for your server's implementation.\n\nThe `migrate` command will allow you to easily migrate all of your keys from one backend to another like so:\n\n```shell\nghost migrate my_stash.json postgresql://localhost/ghost \\\n  --source-passphrase 123 \\\n  --destination-passphrase 321 \\\n  --source-backend tinydb \\\n  --destination-backend sqlalchemy\n```\n\nNote that using the `migrate` command (or API) will result in keys being decrypted and reencrypted on the destination stash.\n\n\n## Secret key delegation\n\nSince ghost doesn't run as a distributed server, it doesn't provide a formal method for delegating keys to a server without explicitly passing them over in plain text post-decryption. You can work around that by retrieving a key without decrypting it (via the `--no-decrypt` flag in the CLI or the `decrypt` argument in the Python API) and sending it to the other server where the same passphrase is held and decrypting  it there.\n\nThis can be done somewhat like this:\n\n```python\n...\nencrypted_value = stash.get('my_key', decrypt=False)['value']\nsave_to_file(encrypted_value)\n\n# and on the server\n...\nstash = Stash(storage, passphrase='SAME_PASSPHRASE')\ndecrypted_value = stash._decrypt(encrypted_value_from_file)\n```\n\nNote that if you're using Consul as a backend, the distribution nature of its kv-store allows to delegate keys easily.\n\n## Testing\n\n```shell\ngit clone git@github.com:nir0s/ghost.git\ncd ghost\npip install tox\ntox\n```\n\n\n## Contributions..\n\nSee [CONTRIBUTIONS](https://github.com/nir0s/ghost/blob/master/CONTRIBUTING.md)\non how to contribute additional backends.\n\nPull requests are always welcome..\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnir0s%2Fghost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnir0s%2Fghost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnir0s%2Fghost/lists"}