N AgentNavaKit
Security model · One paragraph

You store a secret in your workspace once. The value is encrypted at rest with the workspace master key. Your agent's spec references it by name (e.g. {{secrets.STRIPE_KEY}} in an HTTP tool's Authorization header). When the agent invokes that tool, the runtime pre-resolves the placeholder server-side — the LLM only ever sees the tool's response, never the substituted credential. Secrets are never logged, never echoed to the model, never returned by the SDK (only metadata).

Scope: workspace vs agent

Secrets live in one of two scopes:

  • Workspace — visible to every agent in the workspace. Use for keys you share across multiple agents (a single Stripe key, a single OpenAI key for an internal team).
  • Agent — visible only to one agent. Use when one agent needs a different value than the workspace default (a per-agent CRM token, a sandbox key for a dev agent).

When the same name exists at both scopes, the agent-scope row wins. So STRIPE_KEY set workspace-wide gets overridden for one specific agent by creating another STRIPE_KEY with that agent's ID.

Developer SDK (an.secrets)

Manage secrets programmatically — useful in CI, multi-tenant onboarding, and scripted backfills.

an.secrets.list ({ agentId? }?) → Secret[]

No-argument call returns workspace-scope secrets. Pass { agentId } for the resolved view (workspace + that agent's overrides). Returns metadata only — no plaintext values.

const wsSecrets = await an.secrets.list();
const forAgent  = await an.secrets.list({ agentId: 'agt_…' });
an.secrets.create ({ name, value, description?, agentId? }) → Secret

Create a secret. Name regex: [A-Z][A-Z0-9_]{0,63}. Value max 32 KB. 409 if a same-name secret already exists in that scope.

await an.secrets.create({
  name: 'STRIPE_KEY',
  value: 'sk_live_…',
  description: 'Stripe live key for billing agent',
  // omit agentId for workspace-scope; pass an agentId to scope to one agent
});
an.secrets.update (id, value) → void

Replace the value (re-encrypts with a fresh IV). Name and scope stay the same.

await an.secrets.update('sec_…', 'sk_live_NEW_VALUE');
an.secrets.delete (id) → void

Remove. Any agent referencing this name will start failing at the next tool call with an unresolved placeholder error.

Referencing secrets in your spec

Use the {{secrets.NAME}} placeholder anywhere in an HTTP tool's URL, headers, or body — and in MCP server header values. Declare the names the tool needs so the runtime knows what to pre-resolve.

// Spec — HTTP tool with a secret-backed header
{
  name: 'stripe_charge',
  description: 'Create a charge in Stripe',
  method: 'POST',
  urlTemplate: 'https://api.stripe.com/v1/charges',
  headers: {
    Authorization: 'Bearer {{secrets.STRIPE_KEY}}',
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  secrets: ['STRIPE_KEY'],   // declare so runtime pre-resolves
  params: {
    amount:   { type: 'number', required: true },
    currency: { type: 'string', required: true },
  },
}

For an MCP server with auth-bearing headers:

// Spec — MCP server pulling auth from a secret
{
  name: 'linear',
  type: 'http',
  url: 'https://mcp.linear.app/sse',
  headers: {
    Authorization: 'Bearer {{secrets.LINEAR_OAUTH_TOKEN}}',
  },
}

Security & limits

  • Encryption: AES-256-GCM with a workspace master key (12-byte random IV per record, stored as base64(IV || ciphertext+authTag)).
  • Max value: 32 KB per secret.
  • Name format: uppercase, digits, underscore — must start with a letter. Max 64 chars.
  • Server-side substitution: placeholders are resolved on the runtime, never on the LLM. The model sees the tool's response, not the secret value.
  • Never logged: secrets are stripped from error messages and dispatcher logs.
  • Cross-workspace isolation: queries are filtered by workspace_id; secrets never cross workspace boundaries.
  • last_used_at: stamped each time the runtime resolves a secret — visible in the console for audit.

End-to-end example

A runnable script at packages/kit/examples/secrets-grounded.ts creates a secret, configures an agent with an HTTP tool that references it, runs a session, then cleans up. Run with:

AGENTNAVA_API_KEY=…  bun run examples/secrets-grounded.ts