Skip to content

feat(backend-gcp): v0.3 Phase 1 — Google Cloud Secret Manager backend#44

Merged
TechAlchemistX merged 1 commit intomainfrom
feat/v0.3-gcp-backend
Apr 19, 2026
Merged

feat(backend-gcp): v0.3 Phase 1 — Google Cloud Secret Manager backend#44
TechAlchemistX merged 1 commit intomainfrom
feat/v0.3-gcp-backend

Conversation

@TechAlchemistX
Copy link
Copy Markdown
Owner

Summary

  • First v0.3 backend — secretenv-backend-gcp wraps gcloud secrets versions access / add / delete + gcloud auth print-access-token. Strict-mode mocks from day one (32 tests in the crate + 1 setup test = +33 workspace tests, 365 → 398).
  • Canonical #version=<n> directive supports positive integers + latest; version 0 rejected locally. Fragment + secret-name validation happen BEFORE any gcloud call (v0.2.6 pattern).
  • CV-1 stdin discipline via --data-file=/dev/stdin; fragment on set URI rejected.
  • Token-leak hardening: gcloud auth print-access-token stdout (real OAuth2 bearer) is dropped without interpolation. Canary test check_level2_auth_ok_never_logs_token_body locks this with a sentinel substring.
  • Triple-parallel check() via tokio::join! (version + token + account).
  • CLI: secretenv setup --gcp-project <p> [--gcp-impersonate-service-account <sa>]; gcp(-*) accepted as a scheme; JSON registry wire-shape.
  • Workspace: 0.3.0-alpha.0 (no tag). Ships as part of the aggregate v0.3.0 publish alongside Azure (Phase 2).

Spec: kb/wiki/backends/gcp.md. Companion Azure spec: kb/wiki/backends/azure.md.

Test plan

  • cargo fmt --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo test --workspace — 398/398
  • cargo deny check
  • cargo audit
  • CI green
  • Integration smoke deferred to Phase 3 (after Azure lands); spec's matrix is fully mock-driven today

🤖 Generated with Claude Code

First v0.3 backend: `secretenv-backend-gcp` wraps the `gcloud` CLI. Strict-mode
mocks from day one — no v0.2.x retrofit needed. Implementation mirrors the
post-v0.2.6 aws-secrets template and uses the Phase 0 `factory_helpers` +
`tokio::join!`-in-`check()` patterns.

- **URI shape:** `gcp-<instance>:///<secret-name>[#version=<n>]`. Canonical
  `#version=<n>` directive accepts positive integers or `latest`; `latest`
  omits the flag so `gcloud` resolves to the newest enabled version.
- **`get`:** fragment + secret-name validation happen BEFORE any `gcloud`
  call (v0.2.6 pattern). Shorthand, unsupported directives, extras alongside
  `version`, non-integer version IDs, and off-charset secret names all
  reject locally with enumerated-offender errors.
- **`set`:** secret piped via child stdin via `--data-file=/dev/stdin` (CV-1
  discipline). Fragment on `set` URI explicitly rejected — `versions add`
  always creates a new latest version; `#version=N` on `set` is nonsensical.
- **`check()`:** Level 1 (`--version`) + Level 2 (`auth print-access-token`) +
  identity enrichment (`config get-value account`) run concurrently via a
  3-arm `tokio::join!`. The OAuth2 bearer token from `print-access-token`
  is read for exit status only; `output.stdout` is dropped without
  interpolation into logs / errors / identity strings. A canary test
  (`check_level2_auth_ok_never_logs_token_body`) locks this with a sentinel
  token substring.
- **Factory:** required `gcp_project`, optional `gcp_impersonate_service_account`
  (plausibility-validated as an SA email at factory time), optional
  `gcloud_bin` (test hook). All extraction goes through the shared
  `secretenv-core::factory_helpers`.
- **CLI wiring:** `secretenv setup` gains `--gcp-project` +
  `--gcp-impersonate-service-account` flags. `backend_type_from_scheme`
  accepts `gcp` + `gcp-*` suffixes. `serialize_registry` adds `gcp` to
  the JSON arm.
- **Tests:** +32 crate-level tests + 1 new setup test = +33 workspace tests
  (365 → 398). Full fmt / clippy (`-D warnings`) / test / deny / audit
  suite green.

Spec: kb/wiki/backends/gcp.md. Build-log: kb/wiki/build-log.md §v0.3 Phase 1.
Ships as part of the aggregate `v0.3.0` release alongside the forthcoming
Azure backend.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TechAlchemistX TechAlchemistX merged commit 618317c into main Apr 19, 2026
7 checks passed
@TechAlchemistX TechAlchemistX deleted the feat/v0.3-gcp-backend branch April 19, 2026 14:56
TechAlchemistX added a commit that referenced this pull request Apr 19, 2026
Second v0.3 backend: `secretenv-backend-azure` wraps the `az` CLI. Strict-mode
mocks from day one — 36 tests. Near-mirror of the Phase 1 gcp pattern with
three Azure-specific additions.

- **Vault URL regex** at factory time covers all four sovereign clouds
  (.vault.azure.net|.vault.azure.cn|.vault.usgovcloudapi.net|
  .vault.microsoftazure.de) with anchored `/?$` to block path traversal
  (`https://foo.vault.azure.net/evil/../etc` rejected) + hyphen-edge
  vault-name rejection. Regex discipline was the explicit call-out in
  the spec — `url::Url::parse` accepts traversal shapes.
- **URI shape:** `azure-<instance>:///<secret-name>[#version=<32-hex>]`.
  Canonical `#version=<id>` accepts 32-char lowercase hex OR `latest`;
  `latest` normalizes to omitting the `--version` flag (Azure CLI does
  NOT treat `latest` as a keyword here).
- **CV-1 + encoding lock:** `set` uses `--file /dev/stdin --encoding utf-8`.
  The `--encoding utf-8` flag is load-bearing — the default `base64`
  would interpret stdin as b64-encoded and corrupt stored secrets.
  Dedicated drift-catch test (`set_drift_catch_rejects_missing_encoding_utf8`)
  declares the buggy argv form so a regression dropping the flag fails
  the mock.
- **`--value`-leak POSITIVE drift-catch:** declared argv carries the
  BUGGY `--value <secret>` form. Post-fix code emits `--file /dev/stdin`
  instead — diverges, exit 97. Guards against future refactors
  "optimizing small values onto argv" (which would break CV-1).
- **Cert-bound-secret detection:** `kid != null` in the `az keyvault
  secret show` JSON response surfaces a distinct error — v0.3 targets
  text secrets only.
- **`check()`:** Level 1 (`az --version`, multi-line stripped) + Level 2
  (`az account show --output json`, JSON parsed) via `tokio::join!`.
  Identity `user=<name> tenant=<id> subscription=<name> vault=<short>`.
- **Soft-delete documented.** `delete` is soft (Azure default); we do
  NOT chain `secret purge`. Asymmetric with aws-secrets
  (--force-delete-without-recovery) and gcp (full delete) — platform
  reality.
- **CLI surface:** `secretenv setup` gains `--azure-vault-url` +
  `--azure-tenant` + `--azure-subscription`. `backend_type_from_scheme`
  accepts `azure(-*)`. `serialize_registry` adds azure to the JSON arm.
  `setup_rejects_unknown_scheme` now uses `totally-made-up://` — all
  in-spec schemes now route.
- Brings `regex = "1"` into the workspace as a new dep (first user).
- Workspace tests 398 → 435 (+37: 36 crate + 1 setup). Gates: fmt ✅
  clippy (-D warnings) ✅ test ✅ deny ✅ audit ✅.

Spec: kb/wiki/backends/azure.md. Build-log: kb/wiki/build-log.md
§v0.3 Phase 2. Ships as part of the aggregate `v0.3.0` release alongside
gcp (Phase 1, merged as #44).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TechAlchemistX added a commit that referenced this pull request Apr 19, 2026
* feat(backend-azure): v0.3 Phase 2 — Azure Key Vault backend

Second v0.3 backend: `secretenv-backend-azure` wraps the `az` CLI. Strict-mode
mocks from day one — 36 tests. Near-mirror of the Phase 1 gcp pattern with
three Azure-specific additions.

- **Vault URL regex** at factory time covers all four sovereign clouds
  (.vault.azure.net|.vault.azure.cn|.vault.usgovcloudapi.net|
  .vault.microsoftazure.de) with anchored `/?$` to block path traversal
  (`https://foo.vault.azure.net/evil/../etc` rejected) + hyphen-edge
  vault-name rejection. Regex discipline was the explicit call-out in
  the spec — `url::Url::parse` accepts traversal shapes.
- **URI shape:** `azure-<instance>:///<secret-name>[#version=<32-hex>]`.
  Canonical `#version=<id>` accepts 32-char lowercase hex OR `latest`;
  `latest` normalizes to omitting the `--version` flag (Azure CLI does
  NOT treat `latest` as a keyword here).
- **CV-1 + encoding lock:** `set` uses `--file /dev/stdin --encoding utf-8`.
  The `--encoding utf-8` flag is load-bearing — the default `base64`
  would interpret stdin as b64-encoded and corrupt stored secrets.
  Dedicated drift-catch test (`set_drift_catch_rejects_missing_encoding_utf8`)
  declares the buggy argv form so a regression dropping the flag fails
  the mock.
- **`--value`-leak POSITIVE drift-catch:** declared argv carries the
  BUGGY `--value <secret>` form. Post-fix code emits `--file /dev/stdin`
  instead — diverges, exit 97. Guards against future refactors
  "optimizing small values onto argv" (which would break CV-1).
- **Cert-bound-secret detection:** `kid != null` in the `az keyvault
  secret show` JSON response surfaces a distinct error — v0.3 targets
  text secrets only.
- **`check()`:** Level 1 (`az --version`, multi-line stripped) + Level 2
  (`az account show --output json`, JSON parsed) via `tokio::join!`.
  Identity `user=<name> tenant=<id> subscription=<name> vault=<short>`.
- **Soft-delete documented.** `delete` is soft (Azure default); we do
  NOT chain `secret purge`. Asymmetric with aws-secrets
  (--force-delete-without-recovery) and gcp (full delete) — platform
  reality.
- **CLI surface:** `secretenv setup` gains `--azure-vault-url` +
  `--azure-tenant` + `--azure-subscription`. `backend_type_from_scheme`
  accepts `azure(-*)`. `serialize_registry` adds azure to the JSON arm.
  `setup_rejects_unknown_scheme` now uses `totally-made-up://` — all
  in-spec schemes now route.
- Brings `regex = "1"` into the workspace as a new dep (first user).
- Workspace tests 398 → 435 (+37: 36 crate + 1 setup). Gates: fmt ✅
  clippy (-D warnings) ✅ test ✅ deny ✅ audit ✅.

Spec: kb/wiki/backends/azure.md. Build-log: kb/wiki/build-log.md
§v0.3 Phase 2. Ships as part of the aggregate `v0.3.0` release alongside
gcp (Phase 1, merged as #44).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(license): switch MIT → AGPL-3.0-only + add CLA

Pre-launch licensing change. v0.1 and v0.2.0 shipped under MIT (v0.2.0 is
the only release currently published on crates.io / Homebrew / GH Releases).
v0.3.0 and all subsequent releases ship under GNU Affero General Public
License v3.0 (AGPL-3.0-only). The published MIT releases remain available
under their original terms.

Rationale: v0.3.0 finalizes the big-3-cloud-providers story (AWS + GCP +
Azure) and the install base at the time of this change is effectively zero
(zero crates.io downloads for v0.2.0 in the last 7 days; no HN launch yet).
AGPLv3 closes the SaaS-wrapping loophole — §13 requires source availability
to network users of modified versions — while preserving user freedom for
direct installs. Paired with a Contributor License Agreement (license grant,
NOT copyright assignment) to enable dual-licensing if commercial terms are
ever needed.

Changes:

- LICENSE: full AGPLv3 text (GNU canonical, fetched via `gh api
  /licenses/agpl-3.0`).
- Cargo.toml `[workspace.package] license`: "MIT" → "AGPL-3.0-only". Every
  crate inherits via `license.workspace = true`; no per-crate edit needed.
- deny.toml: added "AGPL-3.0-only" to the licenses.allow list so
  `cargo deny check` accepts the workspace's own crates. The broader
  third-party allowlist (MIT / Apache-2.0 / BSD / ISC / Unicode / CC0 /
  Zlib / MPL-2.0) is unchanged — copy-left-strong upstreams (GPL-family)
  still require explicit review before adoption.
- README: license badge MIT → AGPL v3; expanded License section explains
  the switch + the §13 SaaS-source-availability requirement + links to CLA.
- CLA.md (new): the Individual Contributor License Agreement. Grants the
  project a perpetual, worldwide, non-exclusive, royalty-free copyright +
  patent license AND the right to relicense the Contribution under any
  terms (enables dual-licensing). Explicitly NOT a copyright assignment —
  contributors retain ownership. Signing via `git commit --signoff` on
  every commit + adding name to AUTHORS.md on first contribution. Corporate
  contributors contact the maintainer for a Corporate CLA.
- AUTHORS.md (new): ledger of CLA-signed contributors. Seeded with the
  project maintainer.
- CONTRIBUTING.md: new §License-and-CLA section documenting the signing
  mechanism + disambiguating `-s` (sign-off = CLA) from `-S` (cryptographic
  signature = commit-signing). Both are required.
- CHANGELOG.md: flagged as a pre-launch breaking change per the policy in
  the session memory (install base zero + CHANGELOG documents the
  exception honestly).

Gates: fmt ✅ clippy ✅ test ✅ (435/435) deny ✅ audit ✅.

Ships on PR #45 (v0.3 Phase 2 / Azure) as part of the aggregate v0.3.0
release gate.

Signed-off-by: Mandeep Patel <mandeep@techalchemist.io>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: TechAlchemistX <mandeep@techalchemist.io>

---------

Signed-off-by: Mandeep Patel <mandeep@techalchemist.io>
Signed-off-by: TechAlchemistX <mandeep@techalchemist.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant