Skip to content

feat(sessions): add secret: session state scope#5132

Draft
caohy1988 wants to merge 1 commit intogoogle:mainfrom
caohy1988:feat/secret-state-scope
Draft

feat(sessions): add secret: session state scope#5132
caohy1988 wants to merge 1 commit intogoogle:mainfrom
caohy1988:feat/secret-state-scope

Conversation

@caohy1988
Copy link
Copy Markdown

Summary

Implements Phase 1 of the RFC in #5112: a new secret: prefix for session state keys that keeps sensitive data (tokens, credentials, API keys) in process memory only — never persisted to any storage backend and never logged by BigQuery Agent Analytics.

  • State.SECRET_PREFIX ("secret:") added alongside existing app:, user:, temp: prefixes
  • extract_state_delta() now excludes secret: keys from all persistence buckets
  • Process-local cache on BaseSessionService with lifecycle helpers (_apply, _trim, _seed, _restore, _evict)
  • All 4 session services updated: InMemory, Database, Sqlite, VertexAI — seed on create, restore on get, evict on delete
  • BQAA redaction hardened: secret:* keys redacted; new _is_sensitive_json_string() detects credential-bearing JSON blobs
  • Instruction template injection accepts secret: as a valid state prefix
  • 32 new tests: unit tests for helpers + integration tests parametrized across InMemory/Database/Sqlite

Behavior

session = await session_service.create_session(
    app_name="app", user_id="user",
    state={"secret:api_key": "sk-xxx", "visible": "ok"},
)

# Available in process memory
session.state["secret:api_key"]  # → "sk-xxx"

# Survives get_session (restored from process cache)
restored = await session_service.get_session(...)
restored.state["secret:api_key"]  # → "sk-xxx"

# Never persisted — lost on process restart
# Never in event deltas — invisible to storage and analytics

Test plan

  • 32 new tests in tests/unittests/sessions/test_secret_state.py
  • 98 existing session service tests pass (no regressions)
  • 187 existing BQAA plugin tests pass (no regressions)
  • 14 existing instructions_utils tests pass (no regressions)
  • Reviewer: verify secret keys do not appear in DB/Sqlite storage after append_event
  • Reviewer: verify VertexAI session service strips secret keys before API call

Closes #5112

🤖 Generated with Claude Code

Introduce a new `secret:` prefix for session state keys that keeps
sensitive data (tokens, credentials) in process memory only — never
persisted to any storage backend and never logged by BQ Agent Analytics.

- Add `State.SECRET_PREFIX` constant and wire it through
  `extract_state_delta()` so secret keys are excluded from all
  persistence buckets.
- Add process-local cache and lifecycle helpers on
  `BaseSessionService` (_apply, _trim, _seed, _restore, _evict).
- Update all four session services (InMemory, Database, Sqlite,
  VertexAI) to seed/restore/evict secret state on create/get/delete.
- Harden BQ Agent Analytics redaction: redact `secret:*` keys and
  detect JSON-encoded blobs containing sensitive credential keys.
- Accept `secret:` as a valid prefix in instruction template injection.
- 32 new tests (unit + integration across all service types).

Closes google#5112 (Phase 1)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@google-cla
Copy link
Copy Markdown

google-cla bot commented Apr 4, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@adk-bot adk-bot added the services [Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc label Apr 4, 2026
@adk-bot
Copy link
Copy Markdown
Collaborator

adk-bot commented Apr 4, 2026

Automated notice: a potentially related change has been submitted. Please verify no merge conflicts exist with incoming PRs. (ref: triage cross-check)

@caohy1988 caohy1988 marked this pull request as draft April 4, 2026 04:40
caohy1988 added a commit to caohy1988/adk-python that referenced this pull request Apr 4, 2026
Migrate existing credential writers to use the `secret:` prefix so
that OAuth tokens and credentials are never persisted to session
storage backends.

- Change BIGQUERY_TOKEN_CACHE_KEY to "secret:bigquery_token_cache"
- Update SessionStateCredentialService.save_credential and
  load_credential to prefix credential_key with State.SECRET_PREFIX
- Update tests to expect secret-prefixed state keys

This is a breaking change for existing sessions: cached credentials
under the old unprefixed keys will not be found, requiring
re-authentication. This is intentional — the old behavior stored
credentials in plaintext in session backends.

Depends on google#5132 (Phase 1: secret: scope infrastructure)
Closes google#5112

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
caohy1988 added a commit to caohy1988/adk-python that referenced this pull request Apr 4, 2026
Migrate existing credential writers to use the `secret:` prefix so
that OAuth tokens and credentials are never persisted to session
storage backends.

- Change BIGQUERY_TOKEN_CACHE_KEY to "secret:bigquery_token_cache"
- Update SessionStateCredentialService.save_credential and
  load_credential to prefix credential_key with State.SECRET_PREFIX
- Add backward-compatible fallback: load paths try the secret-prefixed
  key first, then fall back to the legacy unprefixed key so existing
  sessions migrate without re-authentication
- Update and add tests for prefixed keys and legacy fallback

Depends on google#5132 (Phase 1: secret: scope infrastructure)
Closes google#5112

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
caohy1988 added a commit to caohy1988/adk-python that referenced this pull request Apr 4, 2026
Migrate existing credential writers to use the `secret:` prefix so
that OAuth tokens and credentials are never persisted to session
storage backends.

- Change BIGQUERY_TOKEN_CACHE_KEY to "secret:bigquery_token_cache"
- Update SessionStateCredentialService.save_credential and
  load_credential to prefix credential_key with State.SECRET_PREFIX
- Backward-compatible migration: load paths try the secret-prefixed
  key first, then fall back to the legacy unprefixed key. On fallback
  hit, the value is copied to the secret: key and the legacy key is
  set to None so it is cleared from persistent storage on the next
  state delta flush.
- Use key-presence check (not truthiness) so explicit None in the
  secret-scoped key is respected and does not revive stale legacy
  credentials.

Depends on google#5132 (Phase 1: secret: scope infrastructure)
Closes google#5112

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

services [Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Secret" session state scope

2 participants