GEMINI LABJP
API — The Interactions API reaches general availability as the default API for Gemini models and agentsAGENT — Managed Agents enter public preview, running autonomous agents in Google-hosted isolated Linux sandboxesSECURITY — From June 19, requests from unrestricted API keys are rejected, so keys now need restrictionsCLI — Gemini CLI reaches end-of-life on June 18, replaced by the Agentic 2.0 Antigravity CLIMODEL — Gemini 3.5 Flash is generally available for sustained frontier performance on agentic and coding tasksUPDATE — Older image preview models such as gemini-3.1-flash-image-preview were shut down on June 25API — The Interactions API reaches general availability as the default API for Gemini models and agentsAGENT — Managed Agents enter public preview, running autonomous agents in Google-hosted isolated Linux sandboxesSECURITY — From June 19, requests from unrestricted API keys are rejected, so keys now need restrictionsCLI — Gemini CLI reaches end-of-life on June 18, replaced by the Agentic 2.0 Antigravity CLIMODEL — Gemini 3.5 Flash is generally available for sustained frontier performance on agentic and coding tasksUPDATE — Older image preview models such as gemini-3.1-flash-image-preview were shut down on June 25
Articles/Advanced
Advanced/2026-07-01Advanced

Getting Artifacts Out of a Managed Agents Sandbox Safely — Scoped Credentials and Egress Design

Gemini API Managed Agents run in a Google-hosted isolated sandbox. Here is the short-lived, least-privilege credential and egress-boundary design I use to return generated artifacts to my own repository safely.

Gemini API159Managed Agents3Security4Agents7Indie Development7

Premium Article

The first thing I tried when Managed Agents hit public preview was whether I could move part of my usual article-generation pipeline into that sandbox. Having an agent plan, run code, and touch files entirely inside a Google-hosted isolated Linux environment is genuinely appealing when you are otherwise running your own containers.

But the moment I ran it, I hit the least glamorous and most dangerous problem right away: how do you get the artifacts you produced inside the sandbox back into something you control — a GitHub repository or object storage? Build this carelessly and you end up placing a key that can touch your entire infrastructure inside a sandbox that Google hosts. The whole point of isolation flips on its head.

This article focuses on that single concern — egress — and shares the design I actually adopted for the Dolice Labs automation. It also ties in neatly with the 6/19 change that started rejecting requests from unrestricted API keys.

Why design "getting artifacts out" as its own problem

A Managed Agents sandbox is best treated as a stateless environment that spins up per run and disappears when done. Planning, reasoning, and code execution all complete inside it. That is convenient, but it also means your artifacts — generated MDX, built JSON, images — vanish along with the sandbox. So you have to push them somewhere.

The naive path looks like this: you ask the agent to "push to GitHub" and hand it a full GITHUB_TOKEN as an environment variable. It works. But at that instant, the code running inside the sandbox — including code the model wrote, which you do not fully control — can reach every repository that token can touch. In my case, running several sites under one account as an indie developer, one over-broad token leaking puts all four sites in range.

There is one principle here. Give the sandbox nothing more than the ability to write this run's artifact to one predetermined place. No read, no reach into other repositories, no delete. Narrow egress to this granularity and even if the sandbox code goes rogue, the damage stops at "this run's output location gets dirty."

The bad way and the good way to hand over credentials

First, the shape to avoid.

# ❌ Bad: pass a long-lived, broad token straight into the sandbox
agent = client.agents.create(
    model="gemini-flash-latest",
    environment={
        "env_vars": {
            # This PAT can read/write every repo, and effectively never expires
            "GITHUB_TOKEN": "ghp_LONG_LIVED_BROAD_SCOPE_TOKEN",
            # And it even carries the deploy key along
            "CF_API_TOKEN": "cloudflare_account_wide_token",
        }
    },
)

The problem is that scope, lifetime, and reach are all "wide." The token is long-lived, can write to every repo, and touches deploys on top. Since you cannot verify the code running inside the sandbox line by line, you should trim the key down to exactly what that code needs to do.

The good shape is to not hand over a general-purpose token at all. On the caller side (your own controlled server), you issue a short-lived signed upload URL valid for exactly one object this run, and hand only that to the sandbox.

# ✅ Good: caller issues a write-only URL scoped to this run's single artifact
import datetime
from google.cloud import storage
 
def issue_upload_url(site: str, run_id: str) -> str:
    bucket = storage.Client().bucket("dolice-agent-artifacts")
    # Pin the target to a per-run prefix (cannot touch other runs' space)
    blob = bucket.blob(f"incoming/{site}/{run_id}/output.tar.gz")
    return blob.generate_signed_url(
        version="v4",
        expiration=datetime.timedelta(minutes=15),  # expires in 15 minutes
        method="PUT",                               # PUT only. no GET, no DELETE
        content_type="application/gzip",
    )
 
signed_url = issue_upload_url("gemilab", run_id="20260701-01")
 
agent = client.agents.create(
    model="gemini-flash-latest",
    environment={
        # All the sandbox receives is "write this one object, for 15 minutes"
        "env_vars": {"ARTIFACT_UPLOAD_URL": signed_url},
    },
)

The difference is stark. In the bad example the key's reach was "the whole account." In the good example it is "one object at incoming/gemilab/20260701-01/output.tar.gz, via PUT, for 15 minutes." Even if this signed URL ends up in a log, all a thief can do is overwrite this run's output — and it becomes invalid 15 minutes later.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
An egress implementation that hands the sandbox only a 15-minute signed URL — never a long-lived GitHub PAT
Locking the egress target to one bucket prefix with a write-only role so blast radius stays contained
Turning the 6/19 unrestricted-key rejection into a discipline for scoping the agent's Gemini API key
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Advanced2026-06-13
A Minimal Autonomous Agent with Gemini — Tool-Loop Design Lessons
Building an autonomous agent from a minimal setup with the google-genai SDK's automatic function calling — plus the step limits, tool allowlists, and retry decisions learned from automating real blog operations.
Advanced2026-03-19
Gemini 3 Multi-Tool Agents: Function Calling + Built-in Tools + Context Circulation in Production
A deep dive into Gemini 3's advanced tooling capabilities: combining Built-in Tools with Function Calling, mastering Context Circulation, and building production-ready multi-tool agents.
API / SDK2026-06-16
Wiring Gemini Managed Agents Into Your Automation: Keeping Conversation State and Environment State Apart
Managed Agents spin up a Linux sandbox, run an agent loop, and return a result in a single API call. The first thing that trips you up when moving off a hand-rolled loop is that conversation state and file state are two separate things. Here's that design, worked through live.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →