feat: credential injection and offload for agent tool calls (#2481)#2534
Merged
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
This PR is quite large. Consider breaking it into smaller PRs for easier review. |
🤖 AI Agent: security-scanner — View detailsNo security issues found. |
🤖 AI Agent: docs-sync-checker — Docs SyncDocs Sync
|
🤖 AI Agent: test-generator — `agent-governance-python/agent-os/src/agent_os/credential_vault.py`
|
🤖 AI Agent: breaking-change-detector — API CompatibilityAPI Compatibility
|
🤖 AI Agent: code-reviewer — Action Items:TL;DR: 0 blockers, 2 warnings. The PR is well-implemented and meets the issue's requirements, but there are minor follow-up improvements suggested.
Action Items:
Warnings:
|
|
🟡 Contributor Check: MEDIUM
Automated check by AGT Contributor Check. |
PR Review Summary
Verdict: |
f5890ec to
925f19b
Compare
13 tasks
Add CredentialVault, CredentialProfile, and CredentialInjector primitives so AGT can hold secrets on behalf of agents and inject them at the point of use. Agents only ever see opaque {{cred:NAME}} placeholders; resolved values stay inside the trust boundary.
Key properties (addresses original AC + kayalopez review):
- Encrypted-at-rest persistence via Fernet (cryptography). Fails closed (no plaintext on disk) when cryptography is unavailable.
- Per-agent profiles bind credential handles to action capabilities (e.g. github:read_issues vs github:push_code), not just agent identities.
- Workflow policy callback runs BEFORE any vault read, so prompt-injected tool args cannot smuggle credential references past policy.
- Placeholder allowlist per call: handles not pre-authorized by the workflow are rejected. MCP server metadata and tool descriptions cannot inject credential references.
- Deterministic DenyReceipt for missing / out-of-scope / policy-denied handles.
- Audit events record agent DID, handle name, target service, action class, decision, policy version. Never the resolved value.
- Rotation rebinds value in place; saved prompts/plans/MCP descriptions keep working.
- HMAC-SHA256 audit_digest helper for tamper-evident logs.
Surfaces:
- agent_os.credential_vault module + public exports on agent_os.
- agt cred {genkey,add,list,rotate,remove} CLI; never prints values.
- examples/credential_vault_example.py end-to-end example.
Also: add credential_vault.py to no-custom-crypto allowlist, extend cspell repo terms, and lower entropy of vault rotation test fixture.
Tests: 28 unit tests + 5 CLI tests, all green. ruff clean.
Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
2370b3a to
cbe17f3
Compare
This was referenced May 24, 2026
Ricky-G
added a commit
that referenced
this pull request
May 25, 2026
…2535) Port the Python credential vault primitive (#2481 / #2534) to the Go SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in package agentmesh: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, CredentialAuditDigest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via stdlib crypto/aes + crypto/cipher with 12-byte random nonce prefixed to ciphertext. Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in #2535. Tests: 19 Go test cases. Local 'go test' could not be executed (no Go toolchain on this dev box); will be verified by CI on PR open. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
Ricky-G
added a commit
that referenced
this pull request
May 25, 2026
) Port the Python credential vault primitive (#2481 / #2534) to the Rust SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in agentmesh::credential_vault module: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, audit_digest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via the aes-gcm crate with 12-byte random nonce prefixed to ciphertext. Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in #2535. Dependency: pin aes-gcm =0.10.3 in workspace + agentmesh crate. Tests: 23 integration cases. cargo build clean. cargo clippy --lib --tests -- -D warnings clean. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
Ricky-G
added a commit
that referenced
this pull request
May 25, 2026
…ls (#2535) Port the Python credential vault primitive (#2481 / #2534) to the TypeScript SDK with full API parity. Library-only: agt cred remains the canonical CLI. Surfaces: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, auditDigest. - Public exports on @microsoft/agent-governance-sdk. - examples/credential-vault-example.ts end-to-end usage. Security properties match the Python reference: opaque handles, action-class scoping (not just agent), workflow policy runs before any vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via @noble/ciphers with 12-byte random nonce prefixed to ciphertext. Wire-format note: not yet interoperable with the Python SDK's Fernet format -- cross-language spec tracked in #2535. Also: add credential-vault.ts to no-custom-crypto allowlist. Tests: 27 jest cases. tsc clean. eslint clean. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
Ricky-G
added a commit
that referenced
this pull request
May 25, 2026
…2535) Port the Python credential vault primitive (#2481 / #2534) to the .NET SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in AgentGovernance.Security namespace: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, CredentialAudit.Digest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via System.Security.Cryptography.AesGcm with 12-byte random nonce prefixed to ciphertext (and 16-byte tag appended). Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in #2535. Tests: 27 xUnit cases. dotnet build clean. dotnet test passes. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
imran-siddique
pushed a commit
that referenced
this pull request
May 25, 2026
* feat(typescript): credential injection and offload for agent tool calls (#2535) Port the Python credential vault primitive (#2481 / #2534) to the TypeScript SDK with full API parity. Library-only: agt cred remains the canonical CLI. Surfaces: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, auditDigest. - Public exports on @microsoft/agent-governance-sdk. - examples/credential-vault-example.ts end-to-end usage. Security properties match the Python reference: opaque handles, action-class scoping (not just agent), workflow policy runs before any vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via @noble/ciphers with 12-byte random nonce prefixed to ciphertext. Wire-format note: not yet interoperable with the Python SDK's Fernet format -- cross-language spec tracked in #2535. Also: add credential-vault.ts to no-custom-crypto allowlist. Tests: 27 jest cases. tsc clean. eslint clean. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> * test(typescript): fix key-prefix private key typing Use exportJSON() in key-prefix tests where privateKey is intentionally exercised. Also add credential-vault spell-check terms for the TypeScript PR. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> --------- Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
imran-siddique
added a commit
that referenced
this pull request
May 25, 2026
* feat(dotnet): credential injection and offload for agent tool calls (#2535) Port the Python credential vault primitive (#2481 / #2534) to the .NET SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in AgentGovernance.Security namespace: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, CredentialAudit.Digest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via System.Security.Cryptography.AesGcm with 12-byte random nonce prefixed to ciphertext (and 16-byte tag appended). Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in #2535. Tests: 27 xUnit cases. dotnet build clean. dotnet test passes. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> * review(dotnet): address credential vault quality findings Dispose MemoryStream in audit digest, use LINQ projections requested by code quality, simplify Put record construction with a conditional expression, switch temp path construction to Path.Join, and add dotnet credential vault spell-check terms. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> --------- Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> Co-authored-by: Ricky Gummadi <ricky.gummadi@outlook.com>
imran-siddique
pushed a commit
to imran-siddique/agent-governance-toolkit
that referenced
this pull request
May 25, 2026
…crosoft#2535) Port the Python credential vault primitive (microsoft#2481 / microsoft#2534) to the Rust SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in agentmesh::credential_vault module: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, audit_digest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via the aes-gcm crate with 12-byte random nonce prefixed to ciphertext. Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in microsoft#2535. Dependency: pin aes-gcm =0.10.3 in workspace + agentmesh crate. Tests: 23 integration cases. cargo build clean. cargo clippy --lib --tests -- -D warnings clean. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
imran-siddique
pushed a commit
to imran-siddique/agent-governance-toolkit
that referenced
this pull request
May 25, 2026
…icrosoft#2535) Port the Python credential vault primitive (microsoft#2481 / microsoft#2534) to the Go SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in package agentmesh: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, CredentialAuditDigest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via stdlib crypto/aes + crypto/cipher with 12-byte random nonce prefixed to ciphertext. Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in microsoft#2535. Tests: 19 Go test cases. Local 'go test' could not be executed (no Go toolchain on this dev box); will be verified by CI on PR open. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
imran-siddique
added a commit
that referenced
this pull request
May 25, 2026
* feat(rust): credential injection and offload for agent tool calls (#2535) Port the Python credential vault primitive (#2481 / #2534) to the Rust SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in agentmesh::credential_vault module: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, audit_digest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via the aes-gcm crate with 12-byte random nonce prefixed to ciphertext. Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in #2535. Dependency: pin aes-gcm =0.10.3 in workspace + agentmesh crate. Tests: 23 integration cases. cargo build clean. cargo clippy --lib --tests -- -D warnings clean. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> * docs(rust): add credential vault dependency audit Add dependency audit note for the new pinned aes-gcm Rust dependency and register credential-vault spell-check terms. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> * docs(rust): add clippy spell-check term Register clippy in the repository spell-check terms for the Rust credential vault dependency audit note. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> --------- Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> Co-authored-by: Ricky Gummadi <ricky.gummadi@outlook.com>
imran-siddique
pushed a commit
to imran-siddique/agent-governance-toolkit
that referenced
this pull request
May 25, 2026
…icrosoft#2535) Port the Python credential vault primitive (microsoft#2481 / microsoft#2534) to the Go SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in package agentmesh: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, CredentialAuditDigest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via stdlib crypto/aes + crypto/cipher with 12-byte random nonce prefixed to ciphertext. Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in microsoft#2535. Tests: 19 Go test cases. Local 'go test' could not be executed (no Go toolchain on this dev box); will be verified by CI on PR open. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com>
imran-siddique
added a commit
that referenced
this pull request
May 25, 2026
* feat(golang): credential injection and offload for agent tool calls (#2535) Port the Python credential vault primitive (#2481 / #2534) to the Go SDK with full API parity. Library-only; agt cred remains the canonical CLI. Surfaces in package agentmesh: - CredentialVault, CredentialInjector, CredentialProfile, CredentialHandle, DenyReceipt, VaultAuditEvent, CredentialAuditDigest. Security properties match the Python reference: opaque handles, action-class scoping, workflow policy runs before vault read, allowlist-gated placeholders (MCP metadata untrusted), deterministic DenyReceipt, value-free audit events, rotation preserves handle name. Encryption: AES-256-GCM via stdlib crypto/aes + crypto/cipher with 12-byte random nonce prefixed to ciphertext. Not currently interoperable with the Python SDK's Fernet format -- cross-language spec tracked in #2535. Tests: 19 Go test cases. Local 'go test' could not be executed (no Go toolchain on this dev box); will be verified by CI on PR open. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> * docs(golang): add credential vault spell-check terms Register Go identifier and credential-vault terms flagged by cspell for the Go SDK port. Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> --------- Signed-off-by: Ricky Gummadi <ricky.gummadi@outlook.com> Co-authored-by: Ricky Gummadi <ricky.gummadi@outlook.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements runtime credential offload and injection for agent tool calls so agents reference secrets by opaque {{cred:NAME}} placeholders and never hold raw credential values. Closes #2481.
Problem
Agents currently hold their own API keys, database passwords, and service tokens. A prompt-injected, context-manipulated, or exfiltrating agent leaks every credential it holds. AGT needed a kernel-level surface that holds secrets on behalf of agents and resolves them only inside the trust boundary, after policy evaluation, with a deterministic deny shape and a value-free audit trail.
Design
Agents only ever see CredentialHandle names and {{cred:NAME}} placeholders. The CredentialInjector is the only component that ever holds resolved values, and only long enough to render an outbound payload (HTTP headers, MCP tool args, subprocess env). Resolution requires:
Any deny -- missing handle, out-of-scope handle, or policy refusal -- returns the same DenyReceipt, so agents cannot probe vault contents through error shape. Audit events record agent DID, handle name, target service, action class, decision, and policy version; they never contain the resolved value.
Encrypted-at-rest persistence uses Fernet from cryptography. If cryptography is unavailable the vault refuses to persist (fails closed) rather than writing plaintext.
Maps to issue acceptance criteria + @kayalopez review
Changes
Testing
uff check --select E,F,W --ignore E501 on all changed files -- clean
Scope and follow-ups
This PR is Python-only by design. Credentials are a wire-protocol-adjacent primitive and the other AGT SDKs (TypeScript, Rust, .NET, Go) should mirror this contract. I intentionally landed Python first so the placeholder syntax, profile binding shape, audit event schema, and DenyReceipt can absorb review feedback before being ported, rather than maintaining five parallel WIPs.
Planned follow-up issues (will file once this lands):
Prior art
Design draws on the original issue #2481 proposal by @imran-siddique and incorporates the seven review points from @kayalopez. Concept is established in secret-management systems generally (HashiCorp Vault response-wrapping, AWS STS GetSessionToken with caller-side caching, OAuth token brokers). No code was borrowed from any external project.