nexvault 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.4
2
+ Name: nexvault
3
+ Version: 1.0.0
4
+ Summary: An encrypted environment variable manager for developers and teams.
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: argon2-cffi>=25.1.0
8
+ Requires-Dist: cryptography>=48.0.1
9
+ Requires-Dist: pyperclip>=1.11.0
10
+ Requires-Dist: rich>=15.0.0
11
+ Requires-Dist: typer>=0.26.7
12
+
13
+ # NexVault
14
+
15
+ NexVault is a secure command-line tool for storing, managing, and exporting environment variables using password-based encryption.
16
+
17
+ Instead of keeping secrets in plaintext `.env` files, NexVault stores them in an encrypted vault that can be exported whenever needed.
18
+
19
+ ## Features
20
+
21
+ - Password-protected encrypted storage
22
+ - Store and retrieve secrets securely
23
+ - List stored keys
24
+ - Export secrets to `.env`
25
+ - Import existing `.env` files
26
+ - Clipboard support for secret retrieval
27
+ - Built with modern cryptography
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install nexvault
33
+ ````
34
+
35
+ ## Quick Start
36
+
37
+ Initialize a vault:
38
+
39
+ ```bash
40
+ nexvault init
41
+ ```
42
+
43
+ Store a secret:
44
+
45
+ ```bash
46
+ nexvault set API_KEY your-secret
47
+ ```
48
+
49
+ Retrieve it:
50
+
51
+ ```bash
52
+ nexvault get API_KEY
53
+ ```
54
+
55
+ Copy directly to clipboard:
56
+
57
+ ```bash
58
+ nexvault get API_KEY --copy
59
+ ```
60
+
61
+ Export to a `.env` file:
62
+
63
+ ```bash
64
+ nexvault toenv
65
+ ```
66
+
67
+ Import an existing `.env`:
68
+
69
+ ```bash
70
+ nexvault fromenv
71
+ ```
72
+
73
+ ## Security
74
+
75
+ NexVault uses password-derived encryption keys and authenticated encryption to protect stored secrets.
76
+
77
+ Your plaintext secrets are never stored unless you explicitly export them.
78
+
79
+ ## License
80
+
81
+ Apache License 2.0
82
+
@@ -0,0 +1,70 @@
1
+ # NexVault
2
+
3
+ NexVault is a secure command-line tool for storing, managing, and exporting environment variables using password-based encryption.
4
+
5
+ Instead of keeping secrets in plaintext `.env` files, NexVault stores them in an encrypted vault that can be exported whenever needed.
6
+
7
+ ## Features
8
+
9
+ - Password-protected encrypted storage
10
+ - Store and retrieve secrets securely
11
+ - List stored keys
12
+ - Export secrets to `.env`
13
+ - Import existing `.env` files
14
+ - Clipboard support for secret retrieval
15
+ - Built with modern cryptography
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install nexvault
21
+ ````
22
+
23
+ ## Quick Start
24
+
25
+ Initialize a vault:
26
+
27
+ ```bash
28
+ nexvault init
29
+ ```
30
+
31
+ Store a secret:
32
+
33
+ ```bash
34
+ nexvault set API_KEY your-secret
35
+ ```
36
+
37
+ Retrieve it:
38
+
39
+ ```bash
40
+ nexvault get API_KEY
41
+ ```
42
+
43
+ Copy directly to clipboard:
44
+
45
+ ```bash
46
+ nexvault get API_KEY --copy
47
+ ```
48
+
49
+ Export to a `.env` file:
50
+
51
+ ```bash
52
+ nexvault toenv
53
+ ```
54
+
55
+ Import an existing `.env`:
56
+
57
+ ```bash
58
+ nexvault fromenv
59
+ ```
60
+
61
+ ## Security
62
+
63
+ NexVault uses password-derived encryption keys and authenticated encryption to protect stored secrets.
64
+
65
+ Your plaintext secrets are never stored unless you explicitly export them.
66
+
67
+ ## License
68
+
69
+ Apache License 2.0
70
+
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "nexvault"
3
+ version = "1.0.0"
4
+ description = "An encrypted environment variable manager for developers and teams."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "argon2-cffi>=25.1.0",
9
+ "cryptography>=48.0.1",
10
+ "pyperclip>=1.11.0",
11
+ "rich>=15.0.0",
12
+ "typer>=0.26.7",
13
+ ]
14
+
15
+ [project.scripts]
16
+ nexvault = "nexvault.main:app"
17
+
18
+ [tool.uv]
19
+ package = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,90 @@
1
+ import os, json, shutil, sys, re
2
+ import pyperclip
3
+ from rich import print
4
+ from rich.prompt import Prompt, Confirm
5
+
6
+ import nexvault.helpers.passwords as passwords
7
+ import nexvault.helpers.json as jsonMethods
8
+ import nexvault.commands.set as setKey
9
+
10
+
11
+ def fromENV():
12
+ if not os.path.exists(".env"):
13
+ print("[bold red]\n.env file doesn't exists.[/bold red]\n")
14
+ sys.exit(1)
15
+
16
+ while True:
17
+ password = Prompt.ask("Enter Password ", password=True)
18
+ if password == "":
19
+ print("[bold red]Password can't be left blank.[/ bold red]")
20
+ continue
21
+
22
+ else:
23
+ verification = passwords.verify_password(password)
24
+ if verification == "Incorrect":
25
+ print("[bold red]Incorrect Password, try again.[/ bold red]")
26
+ continue
27
+ break
28
+
29
+ break
30
+ try:
31
+ with open(".nexvault/key.enc", "rb") as f:
32
+ data = f.read()
33
+ data = jsonMethods.decrypt_json(data, password)
34
+
35
+ with open(".env", "r") as f:
36
+ envs = f.readlines()
37
+
38
+ for el in envs:
39
+ el = el.strip()
40
+ match = re.match(r"^([^=\s]+)=(.*)$", el)
41
+ if (
42
+ not el
43
+ or el.startswith("#")
44
+ or el.startswith("=")
45
+ or "=" not in el
46
+ or not match
47
+ ):
48
+ print(f"\n[bold red]Ignored '{el[:7]}...' , corrupted ! [/bold red]")
49
+ continue
50
+
51
+ listkeyvalue = el.split("=")
52
+ if listkeyvalue[0] not in data:
53
+ data[listkeyvalue[0]] = listkeyvalue[1].strip()
54
+ jsonMethods.increaseKey()
55
+
56
+ else:
57
+ print(
58
+ f"[bold blue]The value for given '{listkeyvalue[0]}' already exist.[/bold blue]"
59
+ )
60
+ cmd = Confirm.ask("Do you want to update the value ")
61
+ if cmd:
62
+ data[listkeyvalue[0]] = listkeyvalue[1].strip()
63
+ jsonMethods.increaseKey()
64
+ else:
65
+ pass
66
+
67
+ with open(".nexvault/key.enc", "wb") as f:
68
+ data = jsonMethods.encrypt_json(data, password)
69
+ f.write(data)
70
+
71
+ print("[bold green]\nValues are added to nexvault.[/bold green]")
72
+ cmd = Confirm.ask("Do you wanna delete .env file (recommended) ")
73
+
74
+ if cmd:
75
+ os.remove(".env")
76
+ print("[bold green]\nOperation Successfull[/bold green]\n")
77
+ sys.exit(0)
78
+
79
+ else:
80
+ print("[bold green]\nOperation Successfull[/bold green]\n")
81
+ sys.exit(0)
82
+
83
+ except FileNotFoundError:
84
+ print("[bold red]File reading error.[/bold red]")
85
+ sys.exit(1)
86
+
87
+ except Exception as e:
88
+ print(e)
89
+ print("[bold red]An Error Ocuured.[/bold red]")
90
+ sys.exit(1)
@@ -0,0 +1,55 @@
1
+ import os, json, shutil, sys
2
+ import pyperclip
3
+ from rich import print
4
+ from rich.prompt import Prompt, Confirm
5
+
6
+ import nexvault.helpers.passwords as passwords
7
+ import nexvault.helpers.json as jsonMethods
8
+
9
+
10
+ def getKey(key, copy=False):
11
+ while True:
12
+ password = Prompt.ask("Enter Password ", password=True)
13
+ if password == "":
14
+ print("[bold red]Password can't be left blank.[/ bold red]")
15
+ continue
16
+
17
+ else:
18
+ verification = passwords.verify_password(password)
19
+ if verification == "Incorrect":
20
+ print("[bold red]Incorrect Password, try again.[/ bold red]")
21
+ continue
22
+ break
23
+
24
+ break
25
+ try:
26
+ with open(".nexvault/key.enc", "rb") as f:
27
+ data = f.read()
28
+ data = jsonMethods.decrypt_json(data, password)
29
+ if key not in data:
30
+ print(
31
+ "[bold blue]key is not added[/bold blue]\nAdd it using '[bold]set[/bold]' command.\n"
32
+ )
33
+ sys.exit(0)
34
+ if copy:
35
+ pyperclip.copy(data[key])
36
+ print("[bold green]\nValue copied to clipboard.\n[/bold green]")
37
+ else:
38
+ print(
39
+ "[bold yellow]Warn ! Your secret will be shown in terminal[/bold yellow]"
40
+ )
41
+ print(
42
+ "Always use '[bold]nexvault get <key> -c[/bold]' to direct copy to clipboard without showing in terminal."
43
+ )
44
+ print(
45
+ "Use 'clear' if in macOS or linux terminal or use 'cls' if in windows."
46
+ )
47
+ print(">> " + data[key] + "\n")
48
+
49
+ except FileNotFoundError:
50
+ print("[bold red]File reading error.[/bold red]")
51
+ sys.exit(1)
52
+
53
+ except Exception:
54
+ print("[bold red]An Error Ocuured.[/bold red]")
55
+ sys.exit(1)
@@ -0,0 +1,43 @@
1
+ import os, json, shutil, sys
2
+ import pyperclip
3
+ from rich import print
4
+ from rich.prompt import Prompt, Confirm
5
+
6
+ import nexvault.helpers.passwords as passwords
7
+ import nexvault.helpers.json as jsonMethods
8
+
9
+
10
+ def listKey():
11
+ while True:
12
+ password = Prompt.ask("Enter Password ", password=True)
13
+ if password == "":
14
+ print("[bold red]Password can't be left blank.[/ bold red]")
15
+ continue
16
+
17
+ else:
18
+ verification = passwords.verify_password(password)
19
+ if verification == "Incorrect":
20
+ print("[bold red]Incorrect Password, try again.[/ bold red]")
21
+ continue
22
+ break
23
+
24
+ break
25
+
26
+ try:
27
+ with open(".nexvault/key.enc", "rb") as f:
28
+ data = f.read()
29
+ data = jsonMethods.decrypt_json(data, password)
30
+
31
+ i = 1
32
+
33
+ for key in data:
34
+ print(f"{i}) {key}")
35
+ i += 1
36
+
37
+ except FileNotFoundError:
38
+ print("[bold red]File reading error.[/bold red]")
39
+ sys.exit(1)
40
+
41
+ except Exception:
42
+ print("[bold red]An Error Ocuured.[/bold red]")
43
+ sys.exit(1)
@@ -0,0 +1,44 @@
1
+ import os, subprocess
2
+
3
+ from rich import print
4
+ from rich.prompt import Prompt, Confirm
5
+
6
+ import nexvault.helpers.passwords as passwords
7
+ import nexvault.helpers.json as jsonMethods
8
+
9
+
10
+ def runEG(cmd):
11
+ while True:
12
+ password = Prompt.ask("Enter Password ", password=True)
13
+ if password == "":
14
+ print("[bold red]Password can't be left blank.[/ bold red]")
15
+ continue
16
+
17
+ else:
18
+ verification = passwords.verify_password(password)
19
+ if verification == "Incorrect":
20
+ print("[bold red]Incorrect Password, try again.[/ bold red]")
21
+ continue
22
+ break
23
+
24
+ break
25
+
26
+ try:
27
+ with open(".nexvault/key.enc", "rb") as f:
28
+ data = f.read()
29
+ data = jsonMethods.decrypt_json(data, password)
30
+
31
+ print("[bold green]\nCommand will execute.[/bold green]\n")
32
+
33
+ env = os.environ.copy()
34
+ env.update(data)
35
+
36
+ subprocess.run(cmd, env=env)
37
+
38
+ except FileNotFoundError:
39
+ print("[bold red]File reading error.[/bold red]")
40
+ sys.exit(1)
41
+
42
+ except Exception:
43
+ print("[bold red]An Error Ocuured.[/bold red]")
44
+ sys.exit(1)
@@ -0,0 +1,113 @@
1
+ import os, json, shutil, sys
2
+ from rich import print
3
+ from rich.prompt import Prompt, Confirm
4
+
5
+ import nexvault.helpers.passwords as passwords
6
+ import nexvault.helpers.json as jsonMethods
7
+
8
+
9
+ def setKey(key, value):
10
+ while True:
11
+ password = Prompt.ask("Enter Password ", password=True)
12
+ if password == "":
13
+ print("[bold red]Password can't be left blank.[/ bold red]")
14
+ continue
15
+
16
+ else:
17
+ verification = passwords.verify_password(password)
18
+ if verification == "Incorrect":
19
+ print("[bold red]Incorrect Password, try again.[/ bold red]")
20
+ continue
21
+ break
22
+
23
+ break
24
+
25
+ try:
26
+ with open(".nexvault/key.enc", "rb") as f:
27
+ data = f.read()
28
+ data = jsonMethods.decrypt_json(data, password)
29
+
30
+ if key not in data:
31
+ data[key] = value
32
+ jsonMethods.increaseKey()
33
+ pass
34
+
35
+ else:
36
+ print(f"[bold blue]The value for given '{key}' already exist.[/bold blue]")
37
+ cmd = Confirm.ask("Do you want to update the value ")
38
+ if cmd:
39
+ data[key] = value
40
+ jsonMethods.increaseKey()
41
+ pass
42
+ else:
43
+ print("[bold red]Operation cancelled by user[/bold red]")
44
+ sys.exit(0)
45
+
46
+ with open(".nexvault/key.enc", "wb") as f:
47
+ data = jsonMethods.encrypt_json(data, password)
48
+ f.write(data)
49
+
50
+ print("[bold green]\nKey and Value added.[/bold green]\n")
51
+
52
+ except FileNotFoundError:
53
+ print("[bold red]File reading error.[/bold red]")
54
+ sys.exit(1)
55
+
56
+ except Exception:
57
+ print("[bold red]An Error Ocuured.[/bold red]")
58
+ sys.exit(1)
59
+
60
+
61
+ def setKeys(keyvalues):
62
+ while True:
63
+ password = Prompt.ask("Enter Password ", password=True)
64
+ if password == "":
65
+ print("[bold red]Password can't be left blank.[/ bold red]")
66
+ continue
67
+
68
+ else:
69
+ verification = passwords.verify_password(password)
70
+ if verification == "Incorrect":
71
+ print("[bold red]Incorrect Password, try again.[/ bold red]")
72
+ continue
73
+ break
74
+
75
+ break
76
+
77
+ try:
78
+ with open(".nexvault/key.enc", "rb") as f:
79
+ data = f.read()
80
+ data = jsonMethods.decrypt_json(data, password)
81
+
82
+ listofkeyvalues = keyvalues.split("---")
83
+
84
+ for el in listofkeyvalues:
85
+ listkeyvalue = el.split("=")
86
+ if listkeyvalue[0] not in data:
87
+ data[listkeyvalue[0]] = listkeyvalue[1]
88
+ jsonMethods.increaseKey()
89
+
90
+ else:
91
+ print(
92
+ f"[bold blue]The value for given '{listkeyvalue[0]}' already exist.[/bold blue]"
93
+ )
94
+ cmd = Confirm.ask("Do you want to update the value ")
95
+ if cmd:
96
+ data[listkeyvalue[0]] = listkeyvalue[1]
97
+ jsonMethods.increaseKey()
98
+ else:
99
+ pass
100
+
101
+ with open(".nexvault/key.enc", "wb") as f:
102
+ data = jsonMethods.encrypt_json(data, password)
103
+ f.write(data)
104
+
105
+ print("[bold green]\nKeys and Values added.[/bold green]\n")
106
+
107
+ except FileNotFoundError:
108
+ print("[bold red]File reading error.[/bold red]")
109
+ sys.exit(1)
110
+
111
+ except Exception:
112
+ print("[bold red]An Error Ocuured.[/bold red]")
113
+ sys.exit(1)
@@ -0,0 +1,50 @@
1
+ import os, json, shutil, sys
2
+ import pyperclip
3
+ from rich import print
4
+ from rich.prompt import Prompt, Confirm
5
+
6
+ import nexvault.helpers.passwords as passwords
7
+ import nexvault.helpers.json as jsonMethods
8
+
9
+
10
+ def toENV(copy=False):
11
+ while True:
12
+ password = Prompt.ask("Enter Password ", password=True)
13
+ if password == "":
14
+ print("[bold red]Password can't be left blank.[/ bold red]")
15
+ continue
16
+
17
+ else:
18
+ verification = passwords.verify_password(password)
19
+ if verification == "Incorrect":
20
+ print("[bold red]Incorrect Password, try again.[/ bold red]")
21
+ continue
22
+ break
23
+
24
+ break
25
+ try:
26
+ with open(".nexvault/key.enc", "rb") as f:
27
+ data = f.read()
28
+ data = jsonMethods.decrypt_json(data, password)
29
+
30
+ env = ""
31
+ for key in data:
32
+ env += f"{key}={data[key]}\n"
33
+
34
+ if copy:
35
+ pyperclip.copy(env)
36
+ print("[bold green]\nAll env copied to clipboard.\n[/bold green]")
37
+
38
+ else:
39
+ with open(".env", "w") as f:
40
+ f.write(env)
41
+
42
+ print("[bold green]\n.env file is created in root folder.\n[/bold green]")
43
+
44
+ except FileNotFoundError:
45
+ print("[bold red]File reading error.[/bold red]")
46
+ sys.exit(1)
47
+
48
+ except Exception:
49
+ print("[bold red]An Error Ocuured.[/bold red]")
50
+ sys.exit(1)
@@ -0,0 +1,94 @@
1
+ import base64, os, json, sys
2
+
3
+ from cryptography.fernet import Fernet, InvalidToken
4
+ from cryptography.hazmat.primitives import hashes
5
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
6
+
7
+
8
+ def getSalt():
9
+ try:
10
+ with open(".nexvault/nexvault.lock", "r") as f:
11
+ data = json.load(f)
12
+
13
+ salt = data["salt"]
14
+ salt = bytes.fromhex(salt)
15
+ return salt
16
+
17
+ except FileNotFoundError:
18
+ print("[bold red]File reading error.[/bold red]")
19
+ sys.exit(1)
20
+
21
+ except Exception as e:
22
+ print("[bold red]An Error Ocurred.[/bold red]")
23
+ sys.exit(1)
24
+
25
+
26
+ def increaseKey(n=1):
27
+ try:
28
+ with open(".nexvault/nexvault.lock", "r") as f:
29
+ data = json.load(f)
30
+
31
+ data["keys"] += n
32
+
33
+ with open(".nexvault/nexvault.lock", "w") as f:
34
+ json.dump(data, f, indent=2)
35
+
36
+ except FileNotFoundError:
37
+ print("[bold red]File reading error.[/bold red]")
38
+ sys.exit(1)
39
+
40
+ except Exception:
41
+ print("[bold red]An Error Ocurred.[/bold red]")
42
+ sys.exit(1)
43
+
44
+
45
+ def encrypt_json(data, password):
46
+ try:
47
+ salt = getSalt()
48
+ kdf = PBKDF2HMAC(
49
+ algorithm=hashes.SHA256(), length=32, salt=salt, iterations=480_000
50
+ )
51
+ key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
52
+ return Fernet(key).encrypt(json.dumps(data).encode())
53
+
54
+ except TypeError:
55
+ print("[bold red]Invalid data provided for encryption.[/bold red]")
56
+ sys.exit(1)
57
+
58
+ except ValueError:
59
+ print("[bold red]Invalid encryption parameters.[/bold red]")
60
+ sys.exit(1)
61
+
62
+ except Exception:
63
+ print("[bold red]An Error Ocurred.[/bold red]")
64
+ sys.exit(1)
65
+
66
+
67
+ def decrypt_json(data, password):
68
+ try:
69
+ salt = getSalt()
70
+ kdf = PBKDF2HMAC(
71
+ algorithm=hashes.SHA256(), length=32, salt=salt, iterations=480_000
72
+ )
73
+ key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
74
+ return json.loads(Fernet(key).decrypt(data).decode())
75
+
76
+ except InvalidToken:
77
+ print("[bold red]Invalid password or corrupted encrypted data.[/bold red]")
78
+ sys.exit(1)
79
+
80
+ except json.JSONDecodeError:
81
+ print("[bold red]Failed to decode decrypted JSON data.[/bold red]")
82
+ sys.exit(1)
83
+
84
+ except TypeError:
85
+ print("[bold red]Invalid encrypted data provided.[/bold red]")
86
+ sys.exit(1)
87
+
88
+ except ValueError:
89
+ print("[bold red]Invalid decryption parameters.[/bold red]")
90
+ sys.exit(1)
91
+
92
+ except Exception:
93
+ print("[bold red]An Error Ocurred.[/bold red]")
94
+ sys.exit(1)
@@ -0,0 +1,38 @@
1
+ from argon2 import PasswordHasher
2
+ from argon2.exceptions import VerifyMismatchError
3
+ import sys
4
+
5
+ ph = PasswordHasher()
6
+
7
+
8
+ def hash_password(password, file=".nexvault/.enc"):
9
+ try:
10
+ with open(file, "w") as secret:
11
+ secret.write(ph.hash(password) + "\n")
12
+
13
+ except FileNotFoundError:
14
+ print("[bold red]File reading error.[/bold red]\n")
15
+ sys.exit(1)
16
+
17
+ except Exception:
18
+ print("[bold red]An Error Ocurred.[/ bold red]\n")
19
+ sys.exit(1)
20
+
21
+
22
+ def verify_password(password, file=".nexvault/.enc"):
23
+ try:
24
+ with open(file, "r") as secret:
25
+ secretdata = secret.read()
26
+ try:
27
+ ph.verify(secretdata.strip(), password)
28
+ return "Correct"
29
+ except VerifyMismatchError:
30
+ return "Incorrect"
31
+
32
+ except FileNotFoundError:
33
+ print("[bold red]File reading error.[/bold red]\n")
34
+ sys.exit(1)
35
+
36
+ except Exception:
37
+ print("[bold red]An Error Ocurred.[/bold red]\n")
38
+ sys.exit(1)
@@ -0,0 +1,105 @@
1
+ import os, json, shutil, sys
2
+ from rich import print
3
+ from rich.prompt import Prompt, Confirm
4
+
5
+ import nexvault.helpers.passwords as passwords
6
+ import nexvault.helpers.json as jsonMethods
7
+
8
+
9
+ def initialise():
10
+ try:
11
+ if os.path.exists(".nexvault"):
12
+ print("[bold blue]nexvault is already initialised.[/bold blue]")
13
+ print(
14
+ "[bold red]Recreation will remove all your previous data and secrets permanently[/bold red]."
15
+ )
16
+ cmd = Confirm.ask("Do you want to recreate ")
17
+ if cmd:
18
+ shutil.rmtree(".nexvault")
19
+ create()
20
+
21
+ else:
22
+ print("[bold red]Operation Cancelled by User.[/bold red]")
23
+ sys.exit()
24
+
25
+ else:
26
+ create()
27
+
28
+ except Exception as e:
29
+ print(e)
30
+ print("[bold red]An Error Ocuured.[/bold red]\n")
31
+ sys.exit(1)
32
+
33
+
34
+ def create():
35
+ os.mkdir(".nexvault")
36
+ updateGit()
37
+ open(".nexvault/.enc", "w").close()
38
+ open(".nexvault/nexvault.lock", "w").close()
39
+ open(".nexvault/key.enc", "w").close()
40
+ createConifg()
41
+ askPassword()
42
+ print(
43
+ f"\n[bold green]nexvault initialised for '{os.path.basename(os.getcwd())}'[/bold green]\n"
44
+ )
45
+ sys.exit(0)
46
+
47
+
48
+ def createConifg():
49
+ try:
50
+ vault_name = Prompt.ask(
51
+ "Enter your vault name ", default=os.path.basename(os.getcwd())
52
+ )
53
+ data = {
54
+ "vault": vault_name,
55
+ "version": "0.1.0",
56
+ "author": "",
57
+ "description": "",
58
+ "project": os.path.basename(os.getcwd()),
59
+ "salt": os.urandom(16).hex(),
60
+ "keys": 0,
61
+ }
62
+ with open(".nexvault/nexvault.lock", "w") as config:
63
+ json.dump(data, config, indent=2)
64
+
65
+ return True
66
+
67
+ except FileNotFoundError:
68
+ print("[bold red]File reading error.[/ bold red]\n")
69
+ sys.exit(1)
70
+
71
+ except Exception:
72
+ print("[bold red]An Error Ocuured.[/ bold red]\n")
73
+ sys.exit(1)
74
+
75
+
76
+ def askPassword():
77
+ while True:
78
+ password = Prompt.ask("Create a password ", password=True)
79
+ if password == "":
80
+ print("[bold red]Password can't be left blank.[/ bold red]")
81
+ continue
82
+ break
83
+
84
+ while True:
85
+ confirm = Prompt.ask("Confirm password ", password=True)
86
+ if confirm != password:
87
+ print("[bold red]Password doesn't match, try again.[/ bold red]")
88
+ continue
89
+ break
90
+
91
+ passwords.hash_password(password)
92
+ with open(".nexvault/key.enc", "wb") as f:
93
+ f.write(jsonMethods.encrypt_json({}, password))
94
+
95
+ return True
96
+
97
+
98
+ def updateGit():
99
+ if os.path.exists(".gitignore"):
100
+ with open(".gitignore", "a") as f:
101
+ f.write("\n#nexvault\n.nexvault/\n.nexvault\n*.env\n*.enc\n*.lock\n")
102
+
103
+ else:
104
+ with open(".gitignore", "w") as f:
105
+ f.write("\n#nexvault\n.nexvault/\n.nexvault\n*.env\n*.enc\n*.lock\n")
@@ -0,0 +1,85 @@
1
+ import typer
2
+ from typing import List
3
+
4
+ import nexvault.initEG as initEG
5
+ import nexvault.commands.get as getKey
6
+ import nexvault.commands.set as setKey
7
+ import nexvault.commands.list as listKey
8
+ import nexvault.commands.toEnv as changetoenv
9
+ import nexvault.commands.fromEnv as changefromenv
10
+ import nexvault.commands.run as runEG
11
+
12
+ app = typer.Typer(
13
+ help="NexVault is a secure command-line tool for storing, managing, and exporting encrypted environment variables."
14
+ )
15
+
16
+
17
+ @app.command(help="Initialize a new NexVault vault in the current project.")
18
+ def init():
19
+ initEG.initialise()
20
+
21
+
22
+ @app.command(help="Store a single key-value pair securely in the vault.")
23
+ def set(
24
+ key: str = typer.Argument(..., help="The environment variable name."),
25
+ value: str = typer.Argument(..., help="The value to associate with the key."),
26
+ ):
27
+ setKey.setKey(key, value)
28
+
29
+
30
+ @app.command(help="Store multiple key-value pairs in a single operation.")
31
+ def setmul(
32
+ keys: List[str] = typer.Argument(
33
+ ..., help="Multiple KEY=VALUE pairs to add to the vault."
34
+ ),
35
+ ):
36
+ string = "---".join(keys)
37
+ setKey.setKeys(string)
38
+
39
+
40
+ @app.command(help="Retrieve the value associated with a stored key.")
41
+ def get(
42
+ key: str = typer.Argument(..., help="The key to retrieve."),
43
+ copy: bool = typer.Option(
44
+ False,
45
+ "--copy",
46
+ "-c",
47
+ help="Copy the retrieved value to the clipboard instead of displaying it.",
48
+ ),
49
+ ):
50
+ getKey.getKey(key, copy)
51
+
52
+
53
+ @app.command(help="Display all keys currently stored in the vault.")
54
+ def list():
55
+ listKey.listKey()
56
+
57
+
58
+ @app.command(help="Export all stored secrets to a .env file.")
59
+ def toenv(
60
+ copy: bool = typer.Option(
61
+ False,
62
+ "--copy",
63
+ "-c",
64
+ help="Copy the generated .env content to the clipboard instead of creating a file.",
65
+ ),
66
+ ):
67
+ changetoenv.toENV(copy)
68
+
69
+
70
+ @app.command(help="Import environment variables from a .env file into the vault.")
71
+ def fromenv():
72
+ changefromenv.fromENV()
73
+
74
+
75
+ @app.command(help="Run a command with NexVault environment variables injected.")
76
+ def run(
77
+ cmd: List[str] = typer.Argument(
78
+ ..., help="The command and its arguments to execute."
79
+ ),
80
+ ):
81
+ runEG.runEG(cmd)
82
+
83
+
84
+ if __name__ == "__main__":
85
+ app()
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.4
2
+ Name: nexvault
3
+ Version: 1.0.0
4
+ Summary: An encrypted environment variable manager for developers and teams.
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: argon2-cffi>=25.1.0
8
+ Requires-Dist: cryptography>=48.0.1
9
+ Requires-Dist: pyperclip>=1.11.0
10
+ Requires-Dist: rich>=15.0.0
11
+ Requires-Dist: typer>=0.26.7
12
+
13
+ # NexVault
14
+
15
+ NexVault is a secure command-line tool for storing, managing, and exporting environment variables using password-based encryption.
16
+
17
+ Instead of keeping secrets in plaintext `.env` files, NexVault stores them in an encrypted vault that can be exported whenever needed.
18
+
19
+ ## Features
20
+
21
+ - Password-protected encrypted storage
22
+ - Store and retrieve secrets securely
23
+ - List stored keys
24
+ - Export secrets to `.env`
25
+ - Import existing `.env` files
26
+ - Clipboard support for secret retrieval
27
+ - Built with modern cryptography
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install nexvault
33
+ ````
34
+
35
+ ## Quick Start
36
+
37
+ Initialize a vault:
38
+
39
+ ```bash
40
+ nexvault init
41
+ ```
42
+
43
+ Store a secret:
44
+
45
+ ```bash
46
+ nexvault set API_KEY your-secret
47
+ ```
48
+
49
+ Retrieve it:
50
+
51
+ ```bash
52
+ nexvault get API_KEY
53
+ ```
54
+
55
+ Copy directly to clipboard:
56
+
57
+ ```bash
58
+ nexvault get API_KEY --copy
59
+ ```
60
+
61
+ Export to a `.env` file:
62
+
63
+ ```bash
64
+ nexvault toenv
65
+ ```
66
+
67
+ Import an existing `.env`:
68
+
69
+ ```bash
70
+ nexvault fromenv
71
+ ```
72
+
73
+ ## Security
74
+
75
+ NexVault uses password-derived encryption keys and authenticated encryption to protect stored secrets.
76
+
77
+ Your plaintext secrets are never stored unless you explicitly export them.
78
+
79
+ ## License
80
+
81
+ Apache License 2.0
82
+
@@ -0,0 +1,19 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/nexvault/__init__.py
4
+ src/nexvault/initEG.py
5
+ src/nexvault/main.py
6
+ src/nexvault.egg-info/PKG-INFO
7
+ src/nexvault.egg-info/SOURCES.txt
8
+ src/nexvault.egg-info/dependency_links.txt
9
+ src/nexvault.egg-info/entry_points.txt
10
+ src/nexvault.egg-info/requires.txt
11
+ src/nexvault.egg-info/top_level.txt
12
+ src/nexvault/commands/fromEnv.py
13
+ src/nexvault/commands/get.py
14
+ src/nexvault/commands/list.py
15
+ src/nexvault/commands/run.py
16
+ src/nexvault/commands/set.py
17
+ src/nexvault/commands/toEnv.py
18
+ src/nexvault/helpers/json.py
19
+ src/nexvault/helpers/passwords.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ nexvault = nexvault.main:app
@@ -0,0 +1,5 @@
1
+ argon2-cffi>=25.1.0
2
+ cryptography>=48.0.1
3
+ pyperclip>=1.11.0
4
+ rich>=15.0.0
5
+ typer>=0.26.7
@@ -0,0 +1 @@
1
+ nexvault