Skip to content

feat(store): each object store sets how long its transfer grants last, 12 hours by default - #32

Open
earakely-scale wants to merge 3 commits into
edgararakelyan/local-provider-transfer-grantsfrom
edgararakelyan/store-grant-lifetime
Open

earakely-scale wants to merge 3 commits into
edgararakelyan/local-provider-transfer-grantsfrom
edgararakelyan/store-grant-lifetime

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Each object store now sets how long its transfer grants last, instead of agent-env asking for a fixed hour. The default is 12 hours. Stacked on #13.

  • A setting on every built-in store. S3ObjectStore, GcsObjectStore and LocalFilesystemObjectStore take grant_lifetime_seconds in their config table (default 43200), and it applies to read and write grants. A caller can still pass expires_in. ObjectStore.grant_lifetime_seconds carries the default for other stores.

    [stores.object]
    impl = "agent_env.store.object_store:S3ObjectStore"
    config = { bucket = "<bucket>", grant_lifetime_seconds = 3600 }
  • agent-env no longer picks the lifetime. write_object, read_object and the validator's probe call issue_write_grant / issue_read_grant without expires_in, whose default is now None, meaning the store's setting. GRANT_LIFETIME_SECONDS is gone.

    • Under @local routing, each grant is issued, and timed, by the store that owns its object. expires_in is passed on only when the caller named one, so a custom store's own default is left alone.
    • A custom store that kept its own expires_in default still behaves exactly as before.
  • Checked when the store is made. The value must be a whole number of seconds and at least 15 minutes (MIN_GRANT_LIFETIME_SECONDS), which outlasts the 10 minutes agent-env waits for a transfer. It can be no more than the store can sign: 7 days for S3's SigV4, for GCS 12 hours when signing through IAM or 7 days with a key, and 7 days for the local store. 12 hours is exactly the GCS IAM maximum, so the default is valid on every store.

    • An S3 grant signed with temporary credentials still ends when they do, and its expires_at says so.
    • Local grants still end when the process that issued them exits.
  • Unchanged: the changelog upload policy. It keeps lasting the agent's TTL, which it must cover for the whole capture.

Longer grants mean a leaked grant URL stays valid longer. Deployments that want the old window can set grant_lifetime_seconds = 3600.

Test plan

  • pytest tst/unit packages/agentenv-protocol/tests -n auto: 5775 passed.
    • The 12-hour default; a configured lifetime on S3, GCS and the local store (the local case through its real grant server); a caller's explicit expires_in still honoured.
    • Invalid values refused at construction: non-integers, anything under 15 minutes, S3's and the local store's 7-day cap, and GCS's IAM and key caps. The floor stays above the transfer timeout.
    • @local routing leaves a custom store's own expires_in default alone.
    • agent-env's read and write grants following the issuing store; @local routing following the owning store; and the setting read from [stores.object] config.
  • pytest -m 'not int_test_slow' tst/integration --ignore=tst/integration/env/gateway/gateway_test.py: 141 passed.
  • Plugin API check against the base: no break.

🤖 Generated with Claude Code

RetriggerConfidence Score: 5/5

The PR appears safe to merge; none of the previous findings remains outstanding.

Summary

Object stores now set the default lifetime for read and write grants, with a configurable 12-hour default. Agent-env leaves the lifetime unset so the store that owns an object can choose its expiry.

  • Built-in stores check configured lifetimes against a 15-minute minimum and each store’s signing limit.
  • Under @local routing, each grant uses the default of the store that owns its object.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Agent transfer] --> B[Issue grant without a lifetime]
  B --> C{Local routing?}
  C -- Yes --> D[Store that owns the object]
  C -- No --> E[Configured store]
  D --> F[Store chooses grant lifetime]
  E --> F
Loading

Reviews (3) · Last reviewed commit: "Merge remote-tracking branch 'origin/edg..."

…, 12 hours by default

The built-in object stores (S3, GCS and the local store) take a grant_lifetime_seconds setting,
12 hours unless configured, and their read and write grants last that long when the caller names
no lifetime. agent-env no longer names one (it asked for a fixed hour), so a grant lasts what the
store that issues it says, and a local run's grants follow the store that owns each object.

A lifetime a store cannot sign is refused when the store is made: S3 caps SigV4 at 7 days, and GCS
at 12 hours when it signs through IAM or 7 days with a key. The changelog upload policy still lasts
the agent's TTL, which it must cover.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment thread src/agent_env/store/routing.py Outdated
Comment thread src/agent_env/store/object_store/object_store.py Outdated
Comment thread src/agent_env/store/object_store/local_object_store.py Outdated
earakely-scale and others added 2 commits September 30, 2026 12:34
… a lifetime must outlast a transfer

LocalRunObjectStore now passes expires_in on only when its caller named one, so a store whose grant
methods default it to a number of their own keeps that default instead of receiving None.

A store's grant_lifetime_seconds must be at least 15 minutes, longer than the 10 minutes agent-env
waits for an agent to move an object through a grant, and the local store's at most 7 days, as S3's,
so a value it could not turn into an expiry is refused when the store is made rather than at its
first grant.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ansfer-grants' into edgararakelyan/store-grant-lifetime

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant