Gemini CLI for paranoid developersAssume telemetry is on. Minimize exposure.
Google's Gemini CLI ships with defaults that work for demos and leak context in production. Here is what the defaults actually do, and how to change each one.
-
01
Default
ADC + free tier
workspace trust on, queries may train Google models
your env, their logs -
02
Hardened
Scoped service account
paid tier, per-call confirm, workspace trust off
your org, your audit -
03
Result
Queries under your control
logged in your GCP project, not Google's training set
Cloud Audit Logs
TL;DR· the answer, in twenty seconds
What: Gemini CLI authenticates via Application Default Credentials by default, free-tier queries are subject to Google's training data policy, and workspace trust runs without per-session confirmation.
Fix: Move to a paid tier, use a dedicated service account scoped to roles/aiplatform.user, set GOOGLE_CLOUD_PROJECT to your own GCP project, and set workspaceTrust: false in ~/.gemini/config.json.
Lesson: Any CLI agent that inherits ambient credentials and runs workspace trust without explicit confirmation is a surface the paranoid should close before the first query lands.
Google published Gemini CLI in mid-2025. By late 2025 it had GitHub star counts competitive with Claude Code, and by early 2026 it was the second coding agent in a meaningful number of enterprise repos. It also shipped with defaults tuned for adoption speed, not security posture.
This guide assumes you want to use Gemini CLI in a real codebase. It also assumes you are not willing to accept the defaults. Those two positions are compatible, but you need to understand what each default does before you can change it.
The framing is paranoid in the literal sense: assume telemetry is running, assume queries are logged somewhere you do not control, assume context sent to the model might be reviewed by a human for safety or quality purposes. Then minimize what is exposed. See what is left.
The short version
- Free-tier queries are subject to Google's Gemini Apps usage policy. That policy permits using query content to improve Google products.
- Gemini CLI authenticates via Application Default Credentials, which in practice means whatever identity
gcloud auth application-default loginproduced on your machine. That is often a personal Google account. - Workspace trust prompts once per session by default, not once per call. A compromised or confused session can read your full project tree without additional confirmation.
- GCP Audit Logs capture Gemini API calls, but only if you are on a paid tier with a GCP project, not on the free consumer tier.
- The system instruction override replaces the built-in safety and behavior layer entirely. It is powerful and it can reduce safety guardrails in ways that are not always visible.
ADC and what it actually inherits
Application Default Credentials (ADC) is Google Cloud's credential resolution chain. When Gemini CLI starts, it walks the chain in order:
GOOGLE_APPLICATION_CREDENTIALSenvironment variable (points to a JSON key file)gcloud auth application-default logincredentials (stored in~/.config/gcloud/application_default_credentials.json)- The metadata server, if running on a GCP VM or Cloud Run instance
On a developer laptop, step 2 almost always wins. The credential is your personal Google account, which means queries go out under your identity, not under a service account scoped to AI calls. Your account likely has much broader GCP permissions than a dedicated service account would.
# See what ADC currently resolves to
gcloud auth application-default print-access-token | cut -c1-40
gcloud config list account
# Create a dedicated service account for Gemini CLI
gcloud iam service-accounts create gemini-cli-agent \
--description="Scoped Gemini CLI identity" \
--display-name="Gemini CLI Agent"
# Bind only what the CLI needs
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="serviceAccount:gemini-cli-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
# Generate a key and point ADC at it
gcloud iam service-accounts keys create ~/.config/gcloud/gemini-cli-sa.json \
--iam-account="gemini-cli-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com"
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcloud/gemini-cli-sa.json"
The roles/aiplatform.user role covers Vertex AI inference. It does not cover GCS bucket access, BigQuery, Cloud SQL, or any other resource a broad personal account might touch. If Gemini CLI tries to read infrastructure it should not have, the call fails. That is the intended behavior.
Store the service account key in a location outside any repo directory. The ~/.config/gcloud/ path works. Check that path is in your global .gitignore.
Free tier versus paid tier: what Google retains
The data retention difference between free and paid is not buried. Google's Gemini Apps policy says query content submitted through the free consumer tier may be reviewed by trained reviewers and used to improve Google products. This is the standard cost-of-free-tier trade on most developer tools.
The paid tier, accessed through the Gemini API with a billable GCP project, operates under Google Cloud's data processing terms. Under those terms, your data is not used for training Google models without a separate contractual agreement. Queries are processed in-region if you specify a region. GCP Audit Logs record each call.
To confirm you are on the paid path, set GOOGLE_CLOUD_PROJECT in your environment before starting Gemini CLI. The CLI routes through Vertex AI when this variable is present:
export GOOGLE_CLOUD_PROJECT="your-gcp-project-id"
export GOOGLE_CLOUD_LOCATION="us-central1"
Check the value is actually in scope by running a test query and inspecting GCP Audit Logs:
gcloud logging read \
'protoPayload.serviceName="aiplatform.googleapis.com"' \
--project=your-gcp-project-id \
--limit=5 \
--format="json" | jq '.[].protoPayload.methodName'
If you see google.cloud.aiplatform.v1.PredictionService.GenerateContent, the query went through Vertex AI under your project. If you see nothing, the CLI is talking to the consumer endpoint.
Workspace trust: what it opens and how to narrow it
When Gemini CLI starts in a directory, it asks whether you trust the workspace. The prompt runs once per session. After that, the agent can read any file the shell user can read, including your .env, credential files in the project tree, and any secrets pulled into the working directory by other tools.
The trust prompt is better than nothing. It is not per-call confirmation, and it does not limit what the agent reads within the session. A session opened on a Monday morning and left running through code review, a lunch break, and a build pipeline is open for several hours.
Two settings address this. In ~/.gemini/config.json:
{
"workspaceTrust": false,
"sessionTimeout": 1800
}
workspaceTrust: false makes the CLI ask on each new operation that would expand file access. sessionTimeout: 1800 terminates idle sessions after 30 minutes. Neither is the default.
Also relevant: Gemini CLI respects a .geminiignore file in the project root, with the same syntax as .gitignore. Add paths that hold credentials:
.env
.env.local
*.pem
*.key
*.p12
secrets/
.gcp/
This does not block the agent from reading those files if you explicitly ask it to read them. It does prevent the agent from ingesting them as context during automatic project indexing.
GCP Audit Log integration
GCP Audit Logs are the mechanism that gives you a record of what the agent sent to the model. They run automatically for Vertex AI calls under any paid GCP project. No setup beyond enabling the audit log sink.
Verify Data Access logs are enabled for the AI Platform:
gcloud projects get-iam-policy your-gcp-project-id \
--flatten="auditConfigs" \
--format="table(auditConfigs.service,auditConfigs.auditLogConfigs)"
If aiplatform.googleapis.com is not in the output, enable it:
gcloud projects set-iam-policy your-gcp-project-id policy.json
Where policy.json includes:
{
"auditConfigs": [
{
"service": "aiplatform.googleapis.com",
"auditLogConfigs": [
{ "logType": "DATA_READ" },
{ "logType": "DATA_WRITE" }
]
}
]
}
With audit logs running, you can query what the agent sent and when:
gcloud logging read \
'protoPayload.serviceName="aiplatform.googleapis.com" AND protoPayload.request.contents!=""' \
--project=your-gcp-project-id \
--freshness=24h \
--format="json" | jq '.[].protoPayload.request.contents[0].parts[0].text' | head -20
This shows the first part of the prompt content for each call in the last 24 hours. If the agent accidentally ingested a file it should not have, it appears here. This is also your evidence trail if a security review asks what went to the model.
Log entries for Vertex AI are stored for 30 days by default in _Default log bucket. For longer retention, create a dedicated log sink to Cloud Storage or BigQuery.
The system instruction override
Gemini CLI supports a --system-instruction flag and a systemInstruction key in the config file. Both replace the model's built-in instruction set, including some safety layers that constrain how the agent uses tools.
gemini --system-instruction "$(cat ~/.gemini/my-system-prompt.txt)"
Or in ~/.gemini/config.json:
{
"systemInstruction": "You are a coding assistant. Only read and write files the user explicitly names. Do not infer file paths. Do not run shell commands unless given a literal command to execute."
}
The override is useful for tightening scope. It is also easy to misuse. A system prompt that says "ignore previous instructions" or that expands tool permissions can be injected through a file the agent reads during indexing. This is the same prompt-injection surface documented for other coding agents.
A tight system instruction reduces the attack surface because it makes unexpected behavior more visible. An agent that the instruction has constrained to read-only operations stands out when it tries to write a file.
What gets missed when teams harden Gemini CLI
Teams that do the ADC switch and the paid-tier move often skip the env var question. Gemini CLI inherits whatever environment the parent shell exports. If you run the CLI from a terminal that has AWS_SECRET_ACCESS_KEY, DATABASE_URL, or any other credential exported, those values are visible to the agent's process. The agent may include them in debug output, log them, or write them into a generated file.
The fix is not to scrub your shell. The fix is to launch the agent from a clean environment:
env -i \
HOME="$HOME" \
PATH="$PATH" \
GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcloud/gemini-cli-sa.json" \
GOOGLE_CLOUD_PROJECT="your-gcp-project-id" \
GOOGLE_CLOUD_LOCATION="us-central1" \
gemini
This passes only the variables the CLI needs. Everything else your shell had exported stays out of the agent's view.
If you're already running a broker like hasp, this wrapper gets shorter. The service account key reference lives in the vault. hasp run -- gemini injects GOOGLE_APPLICATION_CREDENTIALS for that one invocation, and the pointer to the key file stays under hasp's access control rather than in a shell alias anyone can read.
Run this from a wrapper script. Put the wrapper in ~/bin/gemini-safe and adjust your shell alias. The overhead is one extra file and zero ongoing friction.
A checklist you can paste into a PR
## Gemini CLI security review
- [ ] ADC resolves to a dedicated service account, not a personal account
- [ ] Service account has roles/aiplatform.user only
- [ ] Service account key stored outside all repo directories
- [ ] GOOGLE_CLOUD_PROJECT set to a billable GCP project
- [ ] GOOGLE_CLOUD_LOCATION set explicitly (no default region)
- [ ] Vertex AI Data Access Audit Logs enabled for aiplatform.googleapis.com
- [ ] Audit log retention sink configured (Cloud Storage or BigQuery)
- [ ] workspaceTrust: false in ~/.gemini/config.json
- [ ] sessionTimeout set in config (1800 seconds or lower)
- [ ] .geminiignore present with credential paths excluded
- [ ] CLI launched with env -i wrapper (only required vars passed)
- [ ] System instruction in place restricting tool scope
- [ ] Confirmed no free-tier consumer endpoint in use (check audit logs)
- [ ] ~/.config/gcloud/ in global .gitignore
What this means for your stack
Gemini CLI is not uniquely dangerous. It is the current example of a category problem: coding agents that inherit ambient credentials, run broad session trust, and route queries through consumer endpoints unless you actively configure them otherwise. The same shape appears in Claude Code, Codex CLI, and Cursor. The mitigations are different at the surface and the same underneath: a scoped identity, a short-lived session, and a record of what the agent sent.
For teams who want the credential side handled at the process level, not through shell configuration, hasp is one working implementation. curl -fsSL https://gethasp.com/install.sh | sh, hasp setup, connect a project, and the agent gets a reference instead of a live credential. Source-available (FCL-1.0), local-first, macOS and Linux, no account.
The paranoid framing holds regardless of tool. An agent that reads your environment at startup is an agent that sees whatever you had exported. Keep that surface small by design, not by discipline.
Sources· cited above, in one place
Stop handing the agent your real keys.
hasp keeps secrets in one local encrypted vault, brokers them into the child process at exec, and never lets the agent read the value.
- Local, encrypted vault — no account, no cloud, no telemetry by default.
- Brokered run — agent gets a reference, the child process gets the value.
- Pre-commit + pre-push hooks catch managed values before they ship.
- Append-only HMAC audit log answers "did the agent touch the prod token?" in seconds.
macOS & Linux. Source-available (FCL-1.0, converts to Apache 2.0). No account.