# noctivault
[](https://pypi.org/project/noctivault/)
[](https://pypi.org/project/noctivault/)
[](LICENSE)
[](https://pypi.org/project/noctivault/)
Noctivault loads secrets from cloud Secret Managers into process memory without going through environment variables. Values are masked by default, and configuration is clearly separated from execution.
- Safe by default: in‑memory resolution, masked string representations
- Operational clarity: two files — references (refs) and mock values (mocks)
- Flexible: local encrypted store and remote (GCP) fetch supported
## Why Noctivault
- Avoids env‑vars; secrets are handled via explicit API calls
- Lighter than Vault/SOPS for Python apps; DX focused
- Two‑file split clarifies “what to use” (refs) vs “where values come from” (mocks)
- Commit `.yaml.enc`, distribute the key separately for safer, reproducible setups
## Support & Requirements
- Python: >= 3.10
- Local encrypted store: extra `local-enc` (`cryptography`, `argon2-cffi`)
- Remote (GCP): `google-cloud-secret-manager` (also available as extra `gcp`)
- Remote platform support: Google only for now (AWS/Azure on roadmap)
## Install
- pip:
- `pip install noctivault`
- Local encrypted store: `pip install 'noctivault[local-enc]'`
- GCP remote: `pip install google-cloud-secret-manager` (or `pip install 'noctivault[gcp]'`)
- Poetry:
- `poetry add noctivault`
- Local encrypted store: `poetry add "noctivault[local-enc]"`
- GCP remote: `poetry add google-cloud-secret-manager` (or `poetry add "noctivault[gcp]"`)
## Quickstart (Local, two‑file layout)
1) Mocks (plaintext or encrypted source): `noctivault.local-store.yaml`
```yaml
platform: google
gcp_project_id: demo
secret-mocks:
- name: db-password
value: s3cr3t
version: 2
- name: db-port
value: "5432"
version: 1
```
2) Refs (plaintext; do not encrypt): `noctivault.yaml`
```yaml
platform: google
gcp_project_id: demo
secret-refs:
- key: database
children:
- cast: password
ref: db-password
version: latest
type: str
- cast: port
ref: db-port
version: 1
type: int
```
3) Encrypt mocks (recommended)
- Generate key: `noctivault key gen` (default `~/.config/noctivault/local.key`, chmod 600)
- Create encrypted file: `noctivault local seal . --key-file ~/.config/noctivault/local.key --rm-plain`
- Commit `.yaml.enc`; keep plaintext `.yaml` and `local.key` out of VCS
4) Load (Python)
```python
from noctivault import NoctivaultSettings
import noctivault
nv = noctivault.noctivault(settings=NoctivaultSettings(source="local"))
secrets = nv.load(local_store_path="./") # prefers .enc; falls back to .yaml
print(secrets.database.password) # -> ***
print(secrets.database.password.get()) # -> "s3cr3t"
print(secrets.database.port.get()) # -> 5432
```
Restore plaintext from `.enc` when needed:
```bash
noctivault local unseal noctivault.local-store.yaml.enc --key-file ~/.config/noctivault/local.key > noctivault.local-store.yaml
```
## Quickstart (Remote, GCP)
Prereqs
- Dependency: `poetry add google-cloud-secret-manager` (or `poetry add "noctivault[gcp]"`)
- Auth (ADC)
- Service account JSON: `export GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json`
- Or: `gcloud auth application-default login`
- Permission: `roles/secretmanager.secretAccessor`, etc.
`noctivault.yaml` (refs only)
```yaml
platform: google
gcp_project_id: my-gcp-project
secret-refs:
- key: database
children:
- cast: password
ref: db-password
version: latest
type: str
```
Load (Python)
```python
from noctivault import NoctivaultSettings
import noctivault
nv = noctivault.noctivault(settings=NoctivaultSettings(source="remote"))
secrets = nv.load(local_store_path=".") # reads ./noctivault.yaml
print(secrets.database.password) # -> ***
print(secrets.database.password.get()) # -> UTF-8 string from GCP
```
Common errors and fixes
- MissingDependencyError: add `google-cloud-secret-manager`
- AuthorizationError: fix ADC/permissions
- MissingRemoteSecretError: check project/ref/version in `noctivault.yaml`
## CLI
- Key: `noctivault key gen [--out PATH]`
- Seal: `noctivault local seal <dir|file> (--key-file PATH | --passphrase PW | --prompt) [--out PATH] [--rm-plain] [--force]`
- Unseal: `noctivault local unseal <enc_file> (--key-file PATH | --passphrase PW | --prompt)`
- Verify: `noctivault local verify <enc_file> (--key-file PATH | --passphrase PW | --prompt)`
When a directory is given, `.yaml.enc` is preferred; otherwise falls back to `.yaml`. You can pass file paths directly as well.
## Settings & Precedence
- `NoctivaultSettings(source="local"|"remote")`
- Local encrypted store key material (in this order):
- `settings.local_enc.key_file_path`
- `NOCTIVAULT_LOCAL_KEY_FILE`
- `./local.key` next to `.enc`
- `~/.config/noctivault/local.key`
- Passphrase mode
- `settings.local_enc.passphrase` or `NOCTIVAULT_LOCAL_PASSPHRASE`, or CLI `--prompt`
## Schema Summary
- Mocks (`noctivault.local-store.yaml`)
- Top‑level `platform`, `gcp_project_id` are required; entries inherit if omitted
- `secret-mocks`: `name`, `value` (str), `version` (int)
- Refs (`noctivault.yaml`)
- Top‑level `platform`, `gcp_project_id` required
- `secret-refs`: entries or grouped by `key`/`children`
- `type`: `str|int` (default `str`), `version`: `latest|int`
See `docs/api.md` for full details.
## Security
- Masked by default (`repr/str` -> `***`); call `.get()` to reveal
- Encryption format (NVLE1)
- AES‑256‑GCM; Argon2id (for passphrase); KDF params stored in header
- Tamper detection via GCM tag; invalid tag -> decryption failure
- Operational guidance
- Keep plaintext `.yaml` and `local.key` out of VCS (use `.gitignore`)
- Key files should be mode 600; distribute via secure channels
## Troubleshooting
- CombinedConfigNotAllowedError: don’t mix mocks and refs in one file
- MissingLocalMockError / MissingRemoteSecretError: name/version/project mismatch
- TypeCastError: e.g., `type=int` for non‑numeric values
- InvalidEncHeaderError / DecryptError: bad header / wrong key
- RemoteUnavailableError: transient GCP issues; retry later
## FAQ
- Why two files?
- Separate intent (refs) from value sources (mocks); simpler reviews and switching between local/remote
- Which file is preferred?
- Local: `.yaml.enc` > `.yaml`; Remote uses `noctivault.yaml` only
- How to migrate from a single file?
- Move `secret-mocks` into `noctivault.local-store.yaml`, and `secret-refs` into `noctivault.yaml`
## Roadmap
- Remote drivers for AWS/Azure
- Cache/TTL/force‑refresh
- Pluggable providers; HMAC option for `display_hash`
## Contributing
- PRs welcome. Please keep Ruff/Mypy clean and maintain high test coverage
- For security reports, please use responsible disclosure
## License
MIT — see `LICENSE`.
Raw data
{
"_id": null,
"home_page": null,
"name": "noctivault",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "secret, secret-manager, vault, security",
"author": "ricky-rehivo",
"author_email": "ricky.rehivo@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/b7/c3/0e8e681c95d24f2f493b9b88e126e699d11ed3d68ee407c14ef0ed399931/noctivault-0.1.0.tar.gz",
"platform": null,
"description": "# noctivault\n\n[](https://pypi.org/project/noctivault/)\n[](https://pypi.org/project/noctivault/)\n[](LICENSE)\n[](https://pypi.org/project/noctivault/)\n\nNoctivault loads secrets from cloud Secret Managers into process memory without going through environment variables. Values are masked by default, and configuration is clearly separated from execution.\n\n- Safe by default: in\u2011memory resolution, masked string representations\n- Operational clarity: two files \u2014 references (refs) and mock values (mocks)\n- Flexible: local encrypted store and remote (GCP) fetch supported\n\n## Why Noctivault\n\n- Avoids env\u2011vars; secrets are handled via explicit API calls\n- Lighter than Vault/SOPS for Python apps; DX focused\n- Two\u2011file split clarifies \u201cwhat to use\u201d (refs) vs \u201cwhere values come from\u201d (mocks)\n- Commit `.yaml.enc`, distribute the key separately for safer, reproducible setups\n\n## Support & Requirements\n\n- Python: >= 3.10\n- Local encrypted store: extra `local-enc` (`cryptography`, `argon2-cffi`)\n- Remote (GCP): `google-cloud-secret-manager` (also available as extra `gcp`)\n- Remote platform support: Google only for now (AWS/Azure on roadmap)\n\n## Install\n\n- pip:\n - `pip install noctivault`\n - Local encrypted store: `pip install 'noctivault[local-enc]'`\n - GCP remote: `pip install google-cloud-secret-manager` (or `pip install 'noctivault[gcp]'`)\n- Poetry:\n - `poetry add noctivault`\n - Local encrypted store: `poetry add \"noctivault[local-enc]\"`\n - GCP remote: `poetry add google-cloud-secret-manager` (or `poetry add \"noctivault[gcp]\"`)\n\n## Quickstart (Local, two\u2011file layout)\n\n1) Mocks (plaintext or encrypted source): `noctivault.local-store.yaml`\n\n```yaml\nplatform: google\ngcp_project_id: demo\n\nsecret-mocks:\n - name: db-password\n value: s3cr3t\n version: 2\n - name: db-port\n value: \"5432\"\n version: 1\n```\n\n2) Refs (plaintext; do not encrypt): `noctivault.yaml`\n\n```yaml\nplatform: google\ngcp_project_id: demo\n\nsecret-refs:\n - key: database\n children:\n - cast: password\n ref: db-password\n version: latest\n type: str\n - cast: port\n ref: db-port\n version: 1\n type: int\n```\n\n3) Encrypt mocks (recommended)\n\n- Generate key: `noctivault key gen` (default `~/.config/noctivault/local.key`, chmod 600)\n- Create encrypted file: `noctivault local seal . --key-file ~/.config/noctivault/local.key --rm-plain`\n - Commit `.yaml.enc`; keep plaintext `.yaml` and `local.key` out of VCS\n\n4) Load (Python)\n\n```python\nfrom noctivault import NoctivaultSettings\nimport noctivault\n\nnv = noctivault.noctivault(settings=NoctivaultSettings(source=\"local\"))\nsecrets = nv.load(local_store_path=\"./\") # prefers .enc; falls back to .yaml\n\nprint(secrets.database.password) # -> ***\nprint(secrets.database.password.get()) # -> \"s3cr3t\"\nprint(secrets.database.port.get()) # -> 5432\n```\n\nRestore plaintext from `.enc` when needed:\n\n```bash\nnoctivault local unseal noctivault.local-store.yaml.enc --key-file ~/.config/noctivault/local.key > noctivault.local-store.yaml\n```\n\n## Quickstart (Remote, GCP)\n\nPrereqs\n\n- Dependency: `poetry add google-cloud-secret-manager` (or `poetry add \"noctivault[gcp]\"`)\n- Auth (ADC)\n - Service account JSON: `export GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json`\n - Or: `gcloud auth application-default login`\n- Permission: `roles/secretmanager.secretAccessor`, etc.\n\n`noctivault.yaml` (refs only)\n\n```yaml\nplatform: google\ngcp_project_id: my-gcp-project\n\nsecret-refs:\n - key: database\n children:\n - cast: password\n ref: db-password\n version: latest\n type: str\n```\n\nLoad (Python)\n\n```python\nfrom noctivault import NoctivaultSettings\nimport noctivault\n\nnv = noctivault.noctivault(settings=NoctivaultSettings(source=\"remote\"))\nsecrets = nv.load(local_store_path=\".\") # reads ./noctivault.yaml\nprint(secrets.database.password) # -> ***\nprint(secrets.database.password.get()) # -> UTF-8 string from GCP\n```\n\nCommon errors and fixes\n\n- MissingDependencyError: add `google-cloud-secret-manager`\n- AuthorizationError: fix ADC/permissions\n- MissingRemoteSecretError: check project/ref/version in `noctivault.yaml`\n\n## CLI\n\n- Key: `noctivault key gen [--out PATH]`\n- Seal: `noctivault local seal <dir|file> (--key-file PATH | --passphrase PW | --prompt) [--out PATH] [--rm-plain] [--force]`\n- Unseal: `noctivault local unseal <enc_file> (--key-file PATH | --passphrase PW | --prompt)`\n- Verify: `noctivault local verify <enc_file> (--key-file PATH | --passphrase PW | --prompt)`\n\nWhen a directory is given, `.yaml.enc` is preferred; otherwise falls back to `.yaml`. You can pass file paths directly as well.\n\n## Settings & Precedence\n\n- `NoctivaultSettings(source=\"local\"|\"remote\")`\n- Local encrypted store key material (in this order):\n - `settings.local_enc.key_file_path`\n - `NOCTIVAULT_LOCAL_KEY_FILE`\n - `./local.key` next to `.enc`\n - `~/.config/noctivault/local.key`\n- Passphrase mode\n - `settings.local_enc.passphrase` or `NOCTIVAULT_LOCAL_PASSPHRASE`, or CLI `--prompt`\n\n## Schema Summary\n\n- Mocks (`noctivault.local-store.yaml`)\n - Top\u2011level `platform`, `gcp_project_id` are required; entries inherit if omitted\n - `secret-mocks`: `name`, `value` (str), `version` (int)\n- Refs (`noctivault.yaml`)\n - Top\u2011level `platform`, `gcp_project_id` required\n - `secret-refs`: entries or grouped by `key`/`children`\n - `type`: `str|int` (default `str`), `version`: `latest|int`\n\nSee `docs/api.md` for full details.\n\n## Security\n\n- Masked by default (`repr/str` -> `***`); call `.get()` to reveal\n- Encryption format (NVLE1)\n - AES\u2011256\u2011GCM; Argon2id (for passphrase); KDF params stored in header\n - Tamper detection via GCM tag; invalid tag -> decryption failure\n- Operational guidance\n - Keep plaintext `.yaml` and `local.key` out of VCS (use `.gitignore`)\n - Key files should be mode 600; distribute via secure channels\n\n## Troubleshooting\n\n- CombinedConfigNotAllowedError: don\u2019t mix mocks and refs in one file\n- MissingLocalMockError / MissingRemoteSecretError: name/version/project mismatch\n- TypeCastError: e.g., `type=int` for non\u2011numeric values\n- InvalidEncHeaderError / DecryptError: bad header / wrong key\n- RemoteUnavailableError: transient GCP issues; retry later\n\n## FAQ\n\n- Why two files?\n - Separate intent (refs) from value sources (mocks); simpler reviews and switching between local/remote\n- Which file is preferred?\n - Local: `.yaml.enc` > `.yaml`; Remote uses `noctivault.yaml` only\n- How to migrate from a single file?\n - Move `secret-mocks` into `noctivault.local-store.yaml`, and `secret-refs` into `noctivault.yaml`\n\n## Roadmap\n\n- Remote drivers for AWS/Azure\n- Cache/TTL/force\u2011refresh\n- Pluggable providers; HMAC option for `display_hash`\n\n## Contributing\n\n- PRs welcome. Please keep Ruff/Mypy clean and maintain high test coverage\n- For security reports, please use responsible disclosure\n\n## License\n\nMIT \u2014 see `LICENSE`.\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Secure secret handler with cloud platform",
"version": "0.1.0",
"project_urls": {
"Changelog": "https://github.com/stellavoid-org/noctivault/releases",
"Issues": "https://github.com/stellavoid-org/noctivault/issues",
"Repository": "https://github.com/stellavoid-org/noctivault"
},
"split_keywords": [
"secret",
" secret-manager",
" vault",
" security"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "c770b2ebef5dd6c8b24b52a7ce1b75a64a56a6fe6b6967df8d29d2401e006686",
"md5": "726bb31aab0c1d0eedd84889d3618b0e",
"sha256": "7ac1378e6aed1f4571cc4a517cd3081463aa1626b5a9138e859921d1f143bc66"
},
"downloads": -1,
"filename": "noctivault-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "726bb31aab0c1d0eedd84889d3618b0e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 20349,
"upload_time": "2025-09-15T01:35:21",
"upload_time_iso_8601": "2025-09-15T01:35:21.723920Z",
"url": "https://files.pythonhosted.org/packages/c7/70/b2ebef5dd6c8b24b52a7ce1b75a64a56a6fe6b6967df8d29d2401e006686/noctivault-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "b7c30e8e681c95d24f2f493b9b88e126e699d11ed3d68ee407c14ef0ed399931",
"md5": "98980258e9b2139cdb34920e8e37be1e",
"sha256": "28d06321b439cad30729ed542db52854bb1308354d3f4ec647874d3a3be2aff5"
},
"downloads": -1,
"filename": "noctivault-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "98980258e9b2139cdb34920e8e37be1e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 18342,
"upload_time": "2025-09-15T01:35:23",
"upload_time_iso_8601": "2025-09-15T01:35:23.414206Z",
"url": "https://files.pythonhosted.org/packages/b7/c3/0e8e681c95d24f2f493b9b88e126e699d11ed3d68ee407c14ef0ed399931/noctivault-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-15 01:35:23",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "stellavoid-org",
"github_project": "noctivault",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "noctivault"
}