Skip to main content
SecurityBest Practice

Environment Variables and Agent Readiness: Why Your API Keys Shouldn't Be in Your Agent Card

Your agent-card.json tells AI agents what your business can do. It should never tell them your secrets. Yet 8% of businesses we scan have API keys, tokens, or credentials in publicly accessible files. That is a critical security failure — and a direct hit to your Agent Readiness Score.

AH
AgentHermes Research
April 15, 202612 min read

The Rule: Capabilities Are Public, Credentials Are Private

Agent readiness requires two things to be true simultaneously. First, your business must be discoverable — agents need to find your capabilities, endpoints, and schemas in publicly accessible files like agent-card.json, llms.txt, and OpenAPI specs. Second, your secrets must be invisible — API keys, database credentials, webhook signing secrets, and tokens must never appear in any file an agent or crawler can access.

This sounds obvious, but the agent economy introduces a new category of public files that did not exist before. Businesses now create agent-card.json, AGENTS.md, llms.txt, and MCP server manifests. Each one is a new surface where secrets can accidentally leak. Developers who would never put an API key in their HTML sometimes put one in their agent-card.json because they confuse “the agent needs to authenticate” with “the agent card needs to contain the key.”

The agent card declares HOW to authenticate (OAuth2, API key in header, bearer token). The actual credentials live in environment variables on the server, never in the card.

8%
of scanned businesses expose secrets
-15
point penalty per exposed key
0.12
D7 Security weight
39
max score without TLS

What Goes in agent-card.json vs What Stays on the Server

The line is clear: structure and schemas go in the card, secrets stay on the server.

PUT in agent-card.json
NEVER put in agent-card.json
Endpoint URLs (https://api.yourbusiness.com/v1)
API keys (sk_live_abc123...)
Supported HTTP methods (GET, POST)
Database connection strings
Authentication TYPE (OAuth2, API Key header)
Actual OAuth client secrets
Rate limit info (100 req/min)
Internal service account tokens
Input/output schemas (JSON Schema)
Webhook signing secrets
Capability descriptions (natural language)
Admin passwords or master keys

A well-structured agent-card.json gives an AI agent everything it needs to understand your capabilities and initiate authentication — without exposing any secret material. The agent reads that your API requires an Authorization: Bearer header and that it can obtain a token via OAuth2 at /oauth/token. The agent's own credentials are configured in its runtime environment, not pulled from your public files.

Where Secrets Belong: Three Approaches by Maturity Level

From basic environment variables to full vault infrastructure, each approach increases security and contributes to your D7 Security score.

Environment Variables

The baseline. Store API keys, database URLs, and tokens in server-side environment variables. Never committed to git, never sent to the client, rotatable without code changes.

Example: process.env.STRIPE_SECRET_KEY or os.environ["OPENAI_API_KEY"]

Best for: Single-server deployments, Vercel/Netlify projects, Docker containers

Vault Services

Centralized secret management with access control, audit logging, automatic rotation, and encryption at rest. AWS Secrets Manager, HashiCorp Vault, Doppler, 1Password Secrets Automation.

Example: await vault.getSecret("mcp-server/stripe-key") with IAM role-based access

Best for: Multi-service architectures, teams, compliance requirements (SOC2, HIPAA)

OAuth Client Credentials Flow

The agent authenticates with a client ID and secret to get a short-lived access token. The token is used for API calls. No long-lived keys in transit. Token expires automatically.

Example: POST /oauth/token { grant_type: "client_credentials", client_id, client_secret } → { access_token, expires_in: 3600 }

Best for: Agent-to-agent communication, MCP servers calling third-party APIs, production agent networks

Most businesses starting their agent readiness journey should begin with environment variables — they are supported by every hosting platform (Vercel, Netlify, AWS, Railway, Fly.io) and require zero additional infrastructure. As you scale to multiple services or need compliance certification, graduate to a vault service. For production agent networks, OAuth client credentials flow is the gold standard.

What AgentHermes D7 Security Checks For

The D7 Security dimension (0.12 weight) evaluates six security factors. Exposed secrets are the most damaging, but other checks also affect your score.

Check
Impact
Severity
Details
API keys in public files
-15 points
Critical
Scans agent-card.json, llms.txt, AGENTS.md, and HTML source for patterns matching API key formats (sk_, pk_, key_, bearer tokens).
Secrets in JavaScript bundles
-12 points
Critical
Checks client-side JS for hardcoded tokens, connection strings, and credentials that should be server-side only.
Missing TLS (HTTP-only)
-10 points
High
All agent communication must be encrypted. HTTP-only endpoints cap the total score at 39/100.
No security.txt
-3 points
Medium
The /.well-known/security.txt file tells agents and researchers how to report vulnerabilities.
Exposed error details
-5 points
High
Stack traces, internal paths, or database error messages in API responses leak infrastructure details.
No CORS headers
-2 points
Low
Missing or overly permissive CORS headers affect how agents from different origins interact with your API.

Real example from our scans: A SaaS company scored 52/100 on their first scan — solid Bronze, close to Silver. But their agent-card.json contained a live Stripe publishable key and their AGENTS.md had a hardcoded webhook URL with the signing secret in a query parameter. After the -15 and -12 penalties on D7, their score dropped to 31/100. Removing the secrets and re-scanning lifted them back to 52 — a 21-point swing from two lines of configuration.

The Correct Pattern: Declare, Don't Expose

The correct pattern separates discovery from authentication. Your public files (agent-card.json, llms.txt, OpenAPI spec) declare what you offer and how agents should authenticate. Your server-side configuration (environment variables, vault, OAuth) handles the actual credentials.

Here is what a secure agent-card.json authentication section looks like:

agent-card.json (public — safe)
{
  "authentication": {
    "type": "oauth2",
    "flows": {
      "clientCredentials": {
        "tokenUrl": "https://api.yourbusiness.com/oauth/token",
        "scopes": {
          "read:products": "Read product catalog",
          "write:orders": "Create and manage orders"
        }
      }
    }
  }
}
Server environment (private — never public)
# .env (never committed, never in agent-card.json)
OAUTH_CLIENT_SECRET=sk_live_abc123xyz789
STRIPE_SECRET_KEY=sk_live_...
DATABASE_URL=postgresql://user:pass@host:5432/db
WEBHOOK_SIGNING_SECRET=whsec_...

The agent reads the public card, learns it needs to use OAuth2 client credentials, and requests a token from the declared token URL using its own configured credentials. At no point does the agent need secrets from your public files. The authentication handshake happens server-to-server using credentials both parties already have in their own environments.

For more on implementing security best practices for agent readiness, including CORS, rate limiting, and input validation, see our full security guide.

Five Common Mistakes That Leak Secrets

Hardcoded keys in agent-card.json

Developers add an "apiKey" field to their agent card thinking the agent needs it to connect. The agent does not. The agent's operator configures credentials in the agent's own environment.

Secrets in AGENTS.md examples

Documentation files include curl examples with real API keys instead of placeholder values. Agents and crawlers index these files. Use "YOUR_API_KEY" as the placeholder.

Client-side JavaScript bundles

API keys set in Next.js without the NEXT_PUBLIC_ prefix are server-only. But keys WITH the prefix are in the client bundle. Stripe publishable keys are fine; secret keys in client bundles are a critical leak.

Git history exposure

A key was in the code, got removed, but still exists in git history. If the repo is public, the key is public. Rotate any key that ever touched a git commit, even if you removed it.

Webhook URLs with embedded secrets

Webhook URLs like /webhook?secret=abc123 expose the signing secret in server logs, analytics tools, and any file that references the URL. Use header-based authentication for webhooks.

Frequently Asked Questions

What exactly goes in agent-card.json?

Your agent-card.json should contain: your business name, description, endpoint URLs, supported tools/methods, authentication type (not credentials), input/output schemas, rate limits, and capability descriptions. It tells agents WHAT you can do and HOW to authenticate — but never includes the actual secrets needed to authenticate.

How does AgentHermes detect exposed secrets?

The D7 Security dimension (weighted 0.12) scans all publicly accessible files for patterns that match known API key formats. This includes prefix patterns (sk_live_, pk_test_, AKIA for AWS), Base64-encoded strings that decode to credential formats, bearer tokens in HTML meta tags, and connection strings in JavaScript bundles. Finding any exposed secret triggers a critical penalty of -12 to -15 points.

I use environment variables — is that enough for agent readiness?

Environment variables are the minimum viable approach and work well for single-service deployments. For higher agent readiness scores, consider adding: secret rotation (change keys without downtime), OAuth client credentials flow (agents get short-lived tokens instead of long-lived keys), and a security.txt file. These improvements can lift D7 Security from 6/12 to 10/12.

What is OAuth client credentials flow and why does it matter for agents?

OAuth client credentials flow is how server-to-server authentication works without user involvement. An AI agent presents its client_id and client_secret to your /oauth/token endpoint and receives a short-lived access token (typically 1 hour). The agent uses this token for API calls. When it expires, the agent requests a new one. This is safer than static API keys because tokens auto-expire, can be revoked instantly, and create an audit trail.

Do agent-ready businesses really get penalized for exposed secrets?

Yes. AgentHermes scans find API keys in public files in approximately 8% of businesses scanned. Each instance triggers a -12 to -15 point penalty on D7 Security, which is weighted at 0.12 of the total score. A business that would otherwise score 55 (Silver) can drop to 40 (Bronze) from a single exposed secret. More importantly, exposed secrets are a real security vulnerability — not just a scoring issue.


Are your secrets exposed?

Run a free Agent Readiness Scan. The D7 Security dimension checks for exposed API keys, missing TLS, and other vulnerabilities — in 60 seconds.


Share this article: