envctl 2.3.3__tar.gz → 2.4.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.
- envctl-2.4.0/PKG-INFO +359 -0
- envctl-2.4.0/README.md +317 -0
- {envctl-2.3.3 → envctl-2.4.0}/pyproject.toml +20 -12
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/adapters/git.py +42 -6
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/app.py +26 -5
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/callbacks.py +1 -1
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/check/command.py +5 -2
- envctl-2.4.0/src/envctl/cli/commands/export/command.py +44 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/inspect/command.py +5 -2
- envctl-2.4.0/src/envctl/cli/commands/run/command.py +31 -0
- envctl-2.4.0/src/envctl/cli/commands/sync/command.py +35 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/vault/app.py +6 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/vault/commands/__init__.py +6 -0
- envctl-2.4.0/src/envctl/cli/commands/vault/commands/audit.py +27 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/vault/commands/check.py +8 -1
- envctl-2.4.0/src/envctl/cli/commands/vault/commands/decrypt.py +26 -0
- envctl-2.4.0/src/envctl/cli/commands/vault/commands/encrypt.py +26 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/vault/commands/show.py +2 -0
- envctl-2.4.0/src/envctl/cli/decorators.py +133 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/__init__.py +32 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/common.py +0 -10
- envctl-2.4.0/src/envctl/cli/presenters/config_error_presenter.py +46 -0
- envctl-2.4.0/src/envctl/cli/presenters/contract_error_presenter.py +34 -0
- envctl-2.4.0/src/envctl/cli/presenters/projection_error_presenter.py +33 -0
- envctl-2.4.0/src/envctl/cli/presenters/repository_error_presenter.py +71 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/resolution_presenter.py +38 -28
- envctl-2.4.0/src/envctl/cli/presenters/run_presenter.py +11 -0
- envctl-2.4.0/src/envctl/cli/presenters/state_error_presenter.py +26 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/vault_presenter.py +80 -10
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/runtime.py +8 -1
- envctl-2.4.0/src/envctl/cli/serializers/__init__.py +37 -0
- envctl-2.4.0/src/envctl/cli/serializers/common.py +20 -0
- envctl-2.4.0/src/envctl/cli/serializers/context.py +26 -0
- envctl-2.4.0/src/envctl/cli/serializers/diagnostics.py +124 -0
- envctl-2.4.0/src/envctl/cli/serializers/doctor.py +26 -0
- envctl-2.4.0/src/envctl/cli/serializers/errors.py +28 -0
- envctl-2.4.0/src/envctl/cli/serializers/resolution.py +47 -0
- envctl-2.4.0/src/envctl/cli/serializers/status.py +23 -0
- envctl-2.4.0/src/envctl/config/loader.py +305 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/config/writer.py +10 -1
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/constants.py +3 -4
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/app_config.py +2 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/contract.py +5 -1
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/expansion.py +9 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/operations.py +52 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/project.py +6 -1
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/errors.py +17 -4
- envctl-2.4.0/src/envctl/repository/contract_repository.py +231 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/repository/profile_repository.py +71 -5
- envctl-2.4.0/src/envctl/repository/project_context/__init__.py +15 -0
- envctl-2.4.0/src/envctl/repository/project_context/bindings.py +67 -0
- envctl-2.4.0/src/envctl/repository/project_context/builders.py +43 -0
- envctl-2.4.0/src/envctl/repository/project_context/context.py +146 -0
- envctl-2.4.0/src/envctl/repository/project_context/discovery.py +34 -0
- envctl-2.4.0/src/envctl/repository/project_context/identity.py +13 -0
- envctl-2.4.0/src/envctl/repository/project_context/recovery.py +107 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/repository/state_repository.py +82 -11
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/add_service.py +4 -2
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/check_service.py +4 -1
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/context_service.py +27 -0
- envctl-2.4.0/src/envctl/services/error_diagnostics.py +139 -0
- envctl-2.4.0/src/envctl/services/export_service.py +49 -0
- envctl-2.4.0/src/envctl/services/group_selection_service.py +65 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/inspect_service.py +4 -1
- envctl-2.4.0/src/envctl/services/projection_validation.py +73 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/rebind_service.py +38 -3
- envctl-2.4.0/src/envctl/services/resolution_service/__init__.py +11 -0
- envctl-2.4.0/src/envctl/services/resolution_service/builders.py +41 -0
- envctl-2.4.0/src/envctl/services/resolution_service/expansion.py +256 -0
- envctl-2.4.0/src/envctl/services/resolution_service/resolution.py +74 -0
- envctl-2.4.0/src/envctl/services/resolution_service/selection.py +40 -0
- envctl-2.4.0/src/envctl/services/resolution_service/types.py +13 -0
- envctl-2.4.0/src/envctl/services/resolution_service/validation.py +113 -0
- envctl-2.4.0/src/envctl/services/run_service.py +120 -0
- envctl-2.4.0/src/envctl/services/sync_service.py +50 -0
- envctl-2.4.0/src/envctl/services/vault_service.py +551 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/output.py +0 -6
- envctl-2.4.0/src/envctl/utils/projection_rendering.py +77 -0
- envctl-2.4.0/src/envctl/vault_crypto.py +241 -0
- envctl-2.4.0/src/envctl.egg-info/PKG-INFO +359 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl.egg-info/SOURCES.txt +36 -4
- envctl-2.4.0/src/envctl.egg-info/requires.txt +16 -0
- envctl-2.3.3/PKG-INFO +0 -397
- envctl-2.3.3/README.md +0 -358
- envctl-2.3.3/src/envctl/adapters/process_environment.py +0 -17
- envctl-2.3.3/src/envctl/cli/commands/export/command.py +0 -16
- envctl-2.3.3/src/envctl/cli/commands/run/command.py +0 -22
- envctl-2.3.3/src/envctl/cli/commands/sync/command.py +0 -16
- envctl-2.3.3/src/envctl/cli/decorators.py +0 -74
- envctl-2.3.3/src/envctl/cli/serializers.py +0 -132
- envctl-2.3.3/src/envctl/config/loader.py +0 -130
- envctl-2.3.3/src/envctl/repository/contract_repository.py +0 -120
- envctl-2.3.3/src/envctl/repository/project_context.py +0 -327
- envctl-2.3.3/src/envctl/services/export_service.py +0 -35
- envctl-2.3.3/src/envctl/services/resolution_service.py +0 -506
- envctl-2.3.3/src/envctl/services/run_service.py +0 -56
- envctl-2.3.3/src/envctl/services/sync_service.py +0 -41
- envctl-2.3.3/src/envctl/services/vault_service.py +0 -201
- envctl-2.3.3/src/envctl.egg-info/PKG-INFO +0 -397
- envctl-2.3.3/src/envctl.egg-info/requires.txt +0 -13
- {envctl-2.3.3 → envctl-2.4.0}/LICENSE +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/setup.cfg +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/__main__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/adapters/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/adapters/dotenv.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/adapters/editor.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/adapters/input.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/add/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/add/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/check/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/config/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/config/app.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/doctor/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/doctor/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/explain/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/explain/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/export/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/fill/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/fill/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/init/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/init/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/inspect/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/profile/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/profile/app.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/profile/commands/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/profile/commands/copy.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/profile/commands/create.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/profile/commands/list.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/profile/commands/path.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/profile/commands/remove.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/project/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/project/app.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/project/commands/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/project/commands/bind.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/project/commands/rebind.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/project/commands/repair.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/project/commands/unbind.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/remove/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/remove/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/run/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/set/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/set/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/status/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/status/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/sync/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/unset/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/unset/command.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/vault/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/vault/commands/edit.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/vault/commands/path.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/commands/vault/commands/prune.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/action_presenter.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/doctor_presenter.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/profile_presenter.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/project_presenter.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/presenters/status_presenter.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/prompts/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/cli/prompts/confirmation_prompts.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/config/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/config/defaults.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/config/profile_resolution.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/contract_inference.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/doctor.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/resolution.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/runtime.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/domain/status.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/repository/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/bind_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/config_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/doctor_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/explain_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/fill_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/init_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/profile_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/remove_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/repair_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/set_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/status_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/unbind_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/services/unset_service.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/__init__.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/atomic.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/filesystem.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/masking.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/project_ids.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/project_names.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/project_paths.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/shells.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl/utils/tilde.py +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl.egg-info/dependency_links.txt +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl.egg-info/entry_points.txt +0 -0
- {envctl-2.3.3 → envctl-2.4.0}/src/envctl.egg-info/top_level.txt +0 -0
envctl-2.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: envctl
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: Local environment control plane for contract-driven development workflows
|
|
5
|
+
Author-email: Alessandro Barbagallo <alessbarb@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/labrynx/envctl
|
|
8
|
+
Project-URL: Repository, https://github.com/labrynx/envctl
|
|
9
|
+
Project-URL: Issues, https://github.com/labrynx/envctl/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/labrynx/envctl/releases
|
|
11
|
+
Keywords: env,environment,dotenv,cli,developer-tools,configuration,secrets,contract-first,encryption
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: typer<1.0,>=0.12
|
|
27
|
+
Requires-Dist: PyYAML<7.0,>=6.0
|
|
28
|
+
Requires-Dist: pydantic<3.0,>=2.7
|
|
29
|
+
Requires-Dist: cryptography<45,>=41
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest==8.2.2; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov==7.1.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff==0.11.8; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy==1.11.2; extra == "dev"
|
|
35
|
+
Requires-Dist: types-PyYAML==6.0.12.20240311; extra == "dev"
|
|
36
|
+
Requires-Dist: types-cryptography; extra == "dev"
|
|
37
|
+
Requires-Dist: build==1.2.2; extra == "dev"
|
|
38
|
+
Requires-Dist: twine==6.2.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pkginfo==1.12.0; extra == "dev"
|
|
40
|
+
Requires-Dist: vulture==2.16; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# envctl
|
|
44
|
+
|
|
45
|
+
**Your `.env.local` files drift between machines, hide missing variables, and break when you least expect it. envctl fixes that.**
|
|
46
|
+
|
|
47
|
+
[](https://github.com/labrynx/envctl/actions/workflows/ci.yml)
|
|
48
|
+
[](https://www.python.org/downloads/)
|
|
49
|
+
[](https://github.com/labrynx/envctl/blob/main/LICENSE)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Why this exists
|
|
54
|
+
|
|
55
|
+
Most projects handle environment variables in a messy way:
|
|
56
|
+
|
|
57
|
+
- `.env.local` files are undocumented
|
|
58
|
+
- values get copied between machines
|
|
59
|
+
- something works locally until it suddenly does not
|
|
60
|
+
- CI and local setups behave differently
|
|
61
|
+
- nobody is fully sure which variables are required
|
|
62
|
+
|
|
63
|
+
It works — until it breaks.
|
|
64
|
+
|
|
65
|
+
`envctl` brings structure to this without turning environment setup into a second project.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## What is envctl?
|
|
70
|
+
|
|
71
|
+
`envctl` is a local-first environment control plane built around a **contract-first model**.
|
|
72
|
+
|
|
73
|
+
It separates three things that usually get mixed together:
|
|
74
|
+
|
|
75
|
+
- **what the project needs** → defined in `.envctl.schema.yaml` and committed to the repository
|
|
76
|
+
- **what you have locally** → stored in a private local vault, outside git
|
|
77
|
+
- **what actually runs** → a validated environment resolved when needed
|
|
78
|
+
|
|
79
|
+
That gives you:
|
|
80
|
+
|
|
81
|
+
- clear, documented variables
|
|
82
|
+
- no secrets in git
|
|
83
|
+
- fewer setup mistakes
|
|
84
|
+
- more predictable local and team workflows
|
|
85
|
+
- explicit validation before execution
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Install
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install envctl
|
|
93
|
+
````
|
|
94
|
+
|
|
95
|
+
Or from source:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/labrynx/envctl
|
|
99
|
+
cd envctl
|
|
100
|
+
pip install -e .
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Quickstart
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
envctl config init
|
|
109
|
+
envctl init
|
|
110
|
+
envctl fill
|
|
111
|
+
envctl check
|
|
112
|
+
envctl run -- python app.py
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
What this does:
|
|
116
|
+
|
|
117
|
+
* `config init` creates your local envctl config
|
|
118
|
+
* `init` connects the current repository to envctl
|
|
119
|
+
* `fill` asks only for missing values
|
|
120
|
+
* `check` validates the environment before you run anything
|
|
121
|
+
* `run` injects a clean resolved environment into the child process
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Why not just `.env.local`?
|
|
126
|
+
|
|
127
|
+
Because it does not scale cleanly.
|
|
128
|
+
|
|
129
|
+
| | `.env.local` | direnv | Doppler / Infisical | **envctl** |
|
|
130
|
+
| ------------------------------ | ------------ | ------------ | ------------------- | ---------- |
|
|
131
|
+
| Documents variables | ❌ | ❌ | Partial | ✅ |
|
|
132
|
+
| Validates values | ❌ | ❌ | ❌ | ✅ |
|
|
133
|
+
| Keeps secrets out of git | ⚠️ | ✅ | ✅ cloud | ✅ local |
|
|
134
|
+
| Supports multiple environments | manual files | manual files | ✅ | ✅ profiles |
|
|
135
|
+
| Works without cloud | ✅ | ✅ | ❌ | ✅ |
|
|
136
|
+
|
|
137
|
+
`envctl` is **not** a cloud secrets manager.
|
|
138
|
+
|
|
139
|
+
It is a way to make environment handling explicit, predictable, and local-first.
|
|
140
|
+
|
|
141
|
+
> your repository defines what is needed, your machine provides the values, and envctl resolves the final environment.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## A typical workflow
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# one developer adds a new requirement
|
|
149
|
+
envctl add API_KEY sk-example
|
|
150
|
+
git add .envctl.schema.yaml
|
|
151
|
+
git commit -m "require API_KEY"
|
|
152
|
+
|
|
153
|
+
# another developer pulls the change
|
|
154
|
+
envctl check
|
|
155
|
+
envctl fill
|
|
156
|
+
envctl run -- python app.py
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The contract is shared in git.
|
|
160
|
+
The values stay local.
|
|
161
|
+
The runtime environment is rebuilt consistently when needed.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## How it works
|
|
166
|
+
|
|
167
|
+
At a high level:
|
|
168
|
+
|
|
169
|
+
* **contract** → defines which variables exist and how they should look
|
|
170
|
+
* **vault** → stores the real local values
|
|
171
|
+
* **profile** → selects which local value set to use (`local`, `dev`, `staging`, ...)
|
|
172
|
+
* **resolution** → builds the final validated environment
|
|
173
|
+
* **projection** → applies it through `run`, `sync`, or `export`
|
|
174
|
+
|
|
175
|
+
Think of it like this:
|
|
176
|
+
|
|
177
|
+
> the repository defines the rules, your machine provides the values, and envctl builds the environment you actually run.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Profiles
|
|
182
|
+
|
|
183
|
+
Instead of juggling multiple `.env` files:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
envctl --profile dev fill
|
|
187
|
+
envctl --profile staging check
|
|
188
|
+
envctl --profile staging run -- python app.py
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Each profile is explicit and independent.
|
|
192
|
+
No hidden inheritance, no magic fallback between profiles.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Docker note
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
envctl run -- docker run ...
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`envctl` injects variables into the **Docker client process**.
|
|
203
|
+
|
|
204
|
+
To pass them into the container, you still need one of these:
|
|
205
|
+
|
|
206
|
+
* `-e`
|
|
207
|
+
* `--env`
|
|
208
|
+
* `--env-file`
|
|
209
|
+
|
|
210
|
+
A common pattern is:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
docker run --env-file <(envctl export --format dotenv) ...
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## CI mode
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
ENVCTL_RUNTIME_MODE=ci envctl check
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
In CI:
|
|
225
|
+
|
|
226
|
+
* validation still works
|
|
227
|
+
* mutating commands are blocked
|
|
228
|
+
|
|
229
|
+
That keeps automation predictable and avoids accidental local-style writes in CI environments.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Common commands
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
envctl check
|
|
237
|
+
envctl inspect
|
|
238
|
+
envctl explain DATABASE_URL
|
|
239
|
+
envctl status
|
|
240
|
+
envctl doctor
|
|
241
|
+
|
|
242
|
+
envctl add DATABASE_URL <value>
|
|
243
|
+
envctl set PORT 4000
|
|
244
|
+
envctl unset PORT
|
|
245
|
+
|
|
246
|
+
envctl run -- <command>
|
|
247
|
+
envctl sync
|
|
248
|
+
envctl export
|
|
249
|
+
|
|
250
|
+
envctl profile list
|
|
251
|
+
envctl profile create staging
|
|
252
|
+
|
|
253
|
+
envctl vault check
|
|
254
|
+
envctl vault show
|
|
255
|
+
envctl vault encrypt
|
|
256
|
+
envctl vault decrypt
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## When envctl is a good fit
|
|
262
|
+
|
|
263
|
+
envctl is a strong fit if:
|
|
264
|
+
|
|
265
|
+
* `.env.local` files drift between machines
|
|
266
|
+
* onboarding is fragile
|
|
267
|
+
* CI and local environments do not behave the same way
|
|
268
|
+
* you work with multiple environments
|
|
269
|
+
* you want a local-first workflow without depending on a hosted service
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## When envctl is not the right tool
|
|
274
|
+
|
|
275
|
+
envctl may be unnecessary if:
|
|
276
|
+
|
|
277
|
+
* you only have one static `.env` file
|
|
278
|
+
* the project is very small and has no real setup complexity
|
|
279
|
+
* you already rely fully on a centralized secrets platform and do not want local-first handling
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Security model
|
|
284
|
+
|
|
285
|
+
* the contract contains **no secrets**
|
|
286
|
+
* secrets stay on your machine
|
|
287
|
+
* sensitive values are masked in normal output
|
|
288
|
+
* vault files use restrictive permissions
|
|
289
|
+
* optional encryption at rest is available for vault files
|
|
290
|
+
|
|
291
|
+
### Vault encryption at rest
|
|
292
|
+
|
|
293
|
+
If you enable encryption, envctl stores vault files in an encrypted, self-identifying format instead of plaintext.
|
|
294
|
+
|
|
295
|
+
Enable it in your config:
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"encryption": {
|
|
300
|
+
"enabled": true
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Then migrate existing vault files once:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
envctl vault encrypt
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
This creates a local key file at:
|
|
312
|
+
|
|
313
|
+
```text
|
|
314
|
+
~/.envctl/vault/master.key
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
That key is stored with restrictive permissions.
|
|
318
|
+
|
|
319
|
+
After encryption is enabled:
|
|
320
|
+
|
|
321
|
+
* `vault edit` works transparently
|
|
322
|
+
* `vault check` reports whether the file is plaintext, encrypted, using the wrong key, or corrupted
|
|
323
|
+
* decrypt failures are explicit instead of looking like generic parse errors
|
|
324
|
+
|
|
325
|
+
To migrate back to plaintext:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
envctl vault decrypt
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Then disable encryption in config.
|
|
332
|
+
|
|
333
|
+
### Important limitation
|
|
334
|
+
|
|
335
|
+
Encryption at rest helps protect vault files on disk.
|
|
336
|
+
|
|
337
|
+
It does **not** protect against a fully compromised machine or a compromised user session.
|
|
338
|
+
|
|
339
|
+
> envctl assumes a trusted machine.
|
|
340
|
+
> If your machine is compromised, your secrets are compromised too.
|
|
341
|
+
|
|
342
|
+
Back up your `master.key` carefully.
|
|
343
|
+
If you lose it, encrypted vault data cannot be recovered.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Documentation
|
|
348
|
+
|
|
349
|
+
* [Quickstart](https://github.com/labrynx/envctl/blob/main/docs/getting-started/quickstart.md)
|
|
350
|
+
* [Mental model](https://github.com/labrynx/envctl/blob/main/docs/getting-started/mental-model.md)
|
|
351
|
+
* [Commands reference](https://github.com/labrynx/envctl/blob/main/docs/reference/commands.md)
|
|
352
|
+
* [Profiles reference](https://github.com/labrynx/envctl/blob/main/docs/reference/profiles.md)
|
|
353
|
+
* [Vault reference](https://github.com/labrynx/envctl/blob/main/docs/reference/vault.md)
|
|
354
|
+
* [Encryption reference](https://github.com/labrynx/envctl/blob/main/docs/reference/encryption.md)
|
|
355
|
+
* [Config reference](https://github.com/labrynx/envctl/blob/main/docs/reference/config.md)
|
|
356
|
+
* [CI workflow](https://github.com/labrynx/envctl/blob/main/docs/workflows/ci.md)
|
|
357
|
+
* [Team workflow](https://github.com/labrynx/envctl/blob/main/docs/workflows/team.md)
|
|
358
|
+
* [Security](https://github.com/labrynx/envctl/blob/main/docs/reference/security.md)
|
|
359
|
+
* [Internal architecture](https://github.com/labrynx/envctl/blob/main/docs/internals/architecture.md)
|
envctl-2.4.0/README.md
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# envctl
|
|
2
|
+
|
|
3
|
+
**Your `.env.local` files drift between machines, hide missing variables, and break when you least expect it. envctl fixes that.**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/labrynx/envctl/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
[](https://github.com/labrynx/envctl/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Why this exists
|
|
12
|
+
|
|
13
|
+
Most projects handle environment variables in a messy way:
|
|
14
|
+
|
|
15
|
+
- `.env.local` files are undocumented
|
|
16
|
+
- values get copied between machines
|
|
17
|
+
- something works locally until it suddenly does not
|
|
18
|
+
- CI and local setups behave differently
|
|
19
|
+
- nobody is fully sure which variables are required
|
|
20
|
+
|
|
21
|
+
It works — until it breaks.
|
|
22
|
+
|
|
23
|
+
`envctl` brings structure to this without turning environment setup into a second project.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## What is envctl?
|
|
28
|
+
|
|
29
|
+
`envctl` is a local-first environment control plane built around a **contract-first model**.
|
|
30
|
+
|
|
31
|
+
It separates three things that usually get mixed together:
|
|
32
|
+
|
|
33
|
+
- **what the project needs** → defined in `.envctl.schema.yaml` and committed to the repository
|
|
34
|
+
- **what you have locally** → stored in a private local vault, outside git
|
|
35
|
+
- **what actually runs** → a validated environment resolved when needed
|
|
36
|
+
|
|
37
|
+
That gives you:
|
|
38
|
+
|
|
39
|
+
- clear, documented variables
|
|
40
|
+
- no secrets in git
|
|
41
|
+
- fewer setup mistakes
|
|
42
|
+
- more predictable local and team workflows
|
|
43
|
+
- explicit validation before execution
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install envctl
|
|
51
|
+
````
|
|
52
|
+
|
|
53
|
+
Or from source:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/labrynx/envctl
|
|
57
|
+
cd envctl
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quickstart
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
envctl config init
|
|
67
|
+
envctl init
|
|
68
|
+
envctl fill
|
|
69
|
+
envctl check
|
|
70
|
+
envctl run -- python app.py
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
What this does:
|
|
74
|
+
|
|
75
|
+
* `config init` creates your local envctl config
|
|
76
|
+
* `init` connects the current repository to envctl
|
|
77
|
+
* `fill` asks only for missing values
|
|
78
|
+
* `check` validates the environment before you run anything
|
|
79
|
+
* `run` injects a clean resolved environment into the child process
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Why not just `.env.local`?
|
|
84
|
+
|
|
85
|
+
Because it does not scale cleanly.
|
|
86
|
+
|
|
87
|
+
| | `.env.local` | direnv | Doppler / Infisical | **envctl** |
|
|
88
|
+
| ------------------------------ | ------------ | ------------ | ------------------- | ---------- |
|
|
89
|
+
| Documents variables | ❌ | ❌ | Partial | ✅ |
|
|
90
|
+
| Validates values | ❌ | ❌ | ❌ | ✅ |
|
|
91
|
+
| Keeps secrets out of git | ⚠️ | ✅ | ✅ cloud | ✅ local |
|
|
92
|
+
| Supports multiple environments | manual files | manual files | ✅ | ✅ profiles |
|
|
93
|
+
| Works without cloud | ✅ | ✅ | ❌ | ✅ |
|
|
94
|
+
|
|
95
|
+
`envctl` is **not** a cloud secrets manager.
|
|
96
|
+
|
|
97
|
+
It is a way to make environment handling explicit, predictable, and local-first.
|
|
98
|
+
|
|
99
|
+
> your repository defines what is needed, your machine provides the values, and envctl resolves the final environment.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## A typical workflow
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# one developer adds a new requirement
|
|
107
|
+
envctl add API_KEY sk-example
|
|
108
|
+
git add .envctl.schema.yaml
|
|
109
|
+
git commit -m "require API_KEY"
|
|
110
|
+
|
|
111
|
+
# another developer pulls the change
|
|
112
|
+
envctl check
|
|
113
|
+
envctl fill
|
|
114
|
+
envctl run -- python app.py
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The contract is shared in git.
|
|
118
|
+
The values stay local.
|
|
119
|
+
The runtime environment is rebuilt consistently when needed.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## How it works
|
|
124
|
+
|
|
125
|
+
At a high level:
|
|
126
|
+
|
|
127
|
+
* **contract** → defines which variables exist and how they should look
|
|
128
|
+
* **vault** → stores the real local values
|
|
129
|
+
* **profile** → selects which local value set to use (`local`, `dev`, `staging`, ...)
|
|
130
|
+
* **resolution** → builds the final validated environment
|
|
131
|
+
* **projection** → applies it through `run`, `sync`, or `export`
|
|
132
|
+
|
|
133
|
+
Think of it like this:
|
|
134
|
+
|
|
135
|
+
> the repository defines the rules, your machine provides the values, and envctl builds the environment you actually run.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Profiles
|
|
140
|
+
|
|
141
|
+
Instead of juggling multiple `.env` files:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
envctl --profile dev fill
|
|
145
|
+
envctl --profile staging check
|
|
146
|
+
envctl --profile staging run -- python app.py
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Each profile is explicit and independent.
|
|
150
|
+
No hidden inheritance, no magic fallback between profiles.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Docker note
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
envctl run -- docker run ...
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`envctl` injects variables into the **Docker client process**.
|
|
161
|
+
|
|
162
|
+
To pass them into the container, you still need one of these:
|
|
163
|
+
|
|
164
|
+
* `-e`
|
|
165
|
+
* `--env`
|
|
166
|
+
* `--env-file`
|
|
167
|
+
|
|
168
|
+
A common pattern is:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
docker run --env-file <(envctl export --format dotenv) ...
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## CI mode
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
ENVCTL_RUNTIME_MODE=ci envctl check
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
In CI:
|
|
183
|
+
|
|
184
|
+
* validation still works
|
|
185
|
+
* mutating commands are blocked
|
|
186
|
+
|
|
187
|
+
That keeps automation predictable and avoids accidental local-style writes in CI environments.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Common commands
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
envctl check
|
|
195
|
+
envctl inspect
|
|
196
|
+
envctl explain DATABASE_URL
|
|
197
|
+
envctl status
|
|
198
|
+
envctl doctor
|
|
199
|
+
|
|
200
|
+
envctl add DATABASE_URL <value>
|
|
201
|
+
envctl set PORT 4000
|
|
202
|
+
envctl unset PORT
|
|
203
|
+
|
|
204
|
+
envctl run -- <command>
|
|
205
|
+
envctl sync
|
|
206
|
+
envctl export
|
|
207
|
+
|
|
208
|
+
envctl profile list
|
|
209
|
+
envctl profile create staging
|
|
210
|
+
|
|
211
|
+
envctl vault check
|
|
212
|
+
envctl vault show
|
|
213
|
+
envctl vault encrypt
|
|
214
|
+
envctl vault decrypt
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## When envctl is a good fit
|
|
220
|
+
|
|
221
|
+
envctl is a strong fit if:
|
|
222
|
+
|
|
223
|
+
* `.env.local` files drift between machines
|
|
224
|
+
* onboarding is fragile
|
|
225
|
+
* CI and local environments do not behave the same way
|
|
226
|
+
* you work with multiple environments
|
|
227
|
+
* you want a local-first workflow without depending on a hosted service
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## When envctl is not the right tool
|
|
232
|
+
|
|
233
|
+
envctl may be unnecessary if:
|
|
234
|
+
|
|
235
|
+
* you only have one static `.env` file
|
|
236
|
+
* the project is very small and has no real setup complexity
|
|
237
|
+
* you already rely fully on a centralized secrets platform and do not want local-first handling
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Security model
|
|
242
|
+
|
|
243
|
+
* the contract contains **no secrets**
|
|
244
|
+
* secrets stay on your machine
|
|
245
|
+
* sensitive values are masked in normal output
|
|
246
|
+
* vault files use restrictive permissions
|
|
247
|
+
* optional encryption at rest is available for vault files
|
|
248
|
+
|
|
249
|
+
### Vault encryption at rest
|
|
250
|
+
|
|
251
|
+
If you enable encryption, envctl stores vault files in an encrypted, self-identifying format instead of plaintext.
|
|
252
|
+
|
|
253
|
+
Enable it in your config:
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"encryption": {
|
|
258
|
+
"enabled": true
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Then migrate existing vault files once:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
envctl vault encrypt
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
This creates a local key file at:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
~/.envctl/vault/master.key
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
That key is stored with restrictive permissions.
|
|
276
|
+
|
|
277
|
+
After encryption is enabled:
|
|
278
|
+
|
|
279
|
+
* `vault edit` works transparently
|
|
280
|
+
* `vault check` reports whether the file is plaintext, encrypted, using the wrong key, or corrupted
|
|
281
|
+
* decrypt failures are explicit instead of looking like generic parse errors
|
|
282
|
+
|
|
283
|
+
To migrate back to plaintext:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
envctl vault decrypt
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Then disable encryption in config.
|
|
290
|
+
|
|
291
|
+
### Important limitation
|
|
292
|
+
|
|
293
|
+
Encryption at rest helps protect vault files on disk.
|
|
294
|
+
|
|
295
|
+
It does **not** protect against a fully compromised machine or a compromised user session.
|
|
296
|
+
|
|
297
|
+
> envctl assumes a trusted machine.
|
|
298
|
+
> If your machine is compromised, your secrets are compromised too.
|
|
299
|
+
|
|
300
|
+
Back up your `master.key` carefully.
|
|
301
|
+
If you lose it, encrypted vault data cannot be recovered.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Documentation
|
|
306
|
+
|
|
307
|
+
* [Quickstart](https://github.com/labrynx/envctl/blob/main/docs/getting-started/quickstart.md)
|
|
308
|
+
* [Mental model](https://github.com/labrynx/envctl/blob/main/docs/getting-started/mental-model.md)
|
|
309
|
+
* [Commands reference](https://github.com/labrynx/envctl/blob/main/docs/reference/commands.md)
|
|
310
|
+
* [Profiles reference](https://github.com/labrynx/envctl/blob/main/docs/reference/profiles.md)
|
|
311
|
+
* [Vault reference](https://github.com/labrynx/envctl/blob/main/docs/reference/vault.md)
|
|
312
|
+
* [Encryption reference](https://github.com/labrynx/envctl/blob/main/docs/reference/encryption.md)
|
|
313
|
+
* [Config reference](https://github.com/labrynx/envctl/blob/main/docs/reference/config.md)
|
|
314
|
+
* [CI workflow](https://github.com/labrynx/envctl/blob/main/docs/workflows/ci.md)
|
|
315
|
+
* [Team workflow](https://github.com/labrynx/envctl/blob/main/docs/workflows/team.md)
|
|
316
|
+
* [Security](https://github.com/labrynx/envctl/blob/main/docs/reference/security.md)
|
|
317
|
+
* [Internal architecture](https://github.com/labrynx/envctl/blob/main/docs/internals/architecture.md)
|