# tsafe-gcp
GCP Secret Manager integration for [tsafe](https://crates.io/crates/tsafe-cli).
## What this does
Synchronous HTTP client for pulling secrets from GCP Secret Manager into the
local tsafe vault, with explicit push helpers for operator-approved write-back.
The local vault remains the default working source of truth; no secret data is
written back to GCP unless a `tsafe gcp-push` workflow is invoked.
Used by the gated `tsafe gcp-pull` and `tsafe gcp-push` command surfaces.
## Direct use
Most users should install the CLI:
```
cargo install tsafe-cli
```
This crate is published separately for consumers who want to call the GCP
Secret Manager REST API from Rust without pulling in the full CLI surface.
```toml
[dependencies]
tsafe-gcp = "1"
```
## Auth
Authentication is resolved in this order:
1. **`GOOGLE_OAUTH_TOKEN`** — a pre-obtained OAuth2 bearer token (e.g. from
`gcloud auth print-access-token`). Useful in CI.
2. **GCE metadata server** — tried automatically when running on GCE, Cloud
Run, or GKE with a service account attached.
3. **Application Default Credentials file** — path from
`GOOGLE_APPLICATION_CREDENTIALS`, or the well-known location
`~/.config/gcloud/application_default_credentials.json`.
The project ID is resolved from `GOOGLE_CLOUD_PROJECT`, then `GCLOUD_PROJECT`,
then the GCE metadata server.
| `GOOGLE_CLOUD_PROJECT` | yes* | GCP project ID |
| `GCLOUD_PROJECT` | yes* | Alternate project ID env var |
| `GOOGLE_OAUTH_TOKEN` | auth† | Pre-obtained OAuth2 token |
| `GOOGLE_APPLICATION_CREDENTIALS` | auth† | Path to ADC JSON file |
\* Falls back to the GCE metadata server when neither env var is set.
† Authentication tries `GOOGLE_OAUTH_TOKEN` first, then GCE metadata, then ADC.
## Key normalisation
Secret names such as `my-db-password` are normalised to `MY_DB_PASSWORD`
(hyphens and dots replaced with underscores, uppercased) so they are
immediately usable as environment variable names.
## Example
```rust
use tsafe_gcp::{GcpConfig, acquire_token, pull_secrets};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = GcpConfig::from_env()?;
// acquire_token tries GOOGLE_OAUTH_TOKEN, then GCE metadata, then ADC.
let secrets = pull_secrets(&cfg, &|| acquire_token(&cfg), None)?;
for (key, value) in &secrets {
println!("{key}=<{} bytes>", value.len());
}
Ok(())
}
```
To pull only secrets whose names begin with a given prefix:
```rust
let secrets = pull_secrets(&cfg, &|| acquire_token(&cfg), Some("app-"))?;
```
The prefix filter is applied client-side after listing; the list call uses
`pageSize=100` and follows `nextPageToken` for pagination automatically.
## Secret payload encoding
GCP Secret Manager returns secret payloads as standard base64. The client
decodes them and returns plain UTF-8 strings. Binary secrets (non-UTF-8
payloads) are not supported and will return an error.
## Retry behaviour
The HTTP client retries on 429 (throttled) responses, honouring the
`Retry-After` header when present, and retries on transient transport errors
with exponential backoff and jitter. Both retry budgets are capped at 30
seconds per attempt.
## License
Same as the tsafe workspace — see the repository root.