Skip to main content
← All posts

Feature Spotlight: How secrets management keeps your credentials out of agent state

Modulo Team

feature-spotlightsecretssecurity

Your agents need API keys, database passwords, and OAuth tokens. Modulo encrypts those credentials at rest with Fernet symmetric encryption, decrypts them once at run-start into a transient context object, and never writes them to LangGraph state, checkpoint blobs, logs, or OpenTelemetry spans. A connector receives the decrypted value in-process, uses it for the call, and discards it; the credential is never serialized, logged, or returned in an API response.

In short

  • Credentials are encrypted at rest with Fernet, keyed from FERNET_KEY, a separate key from the JWT-signing SECRET_KEY.
  • Decrypted credentials live only in a transient, run-scoped context object; they never enter LangGraph state, checkpoint blobs, OpenTelemetry span attributes, or logs.
  • Sensitive values render as dots by default; a reveal action calls the server, which verifies authentication and returns the value for a 30-second display window.
  • A pluggable SecretsBackend interface is planned for v2, enabling Vault or AWS Secrets Manager as the backing store.

How it works

Modulo's secrets management has five layers, each answering a different failure mode.

Fernet encryption keyed from FERNET_KEY

Every connector credential and model backend API key is encrypted at rest with Fernet symmetric encryption, keyed from the FERNET_KEY environment variable. Modulo refuses to start if FERNET_KEY is absent or shorter than 32 bytes, so a misconfigured deployment never silently stores plaintext. Fernet is authenticated encryption: a tampered ciphertext fails to decrypt rather than yielding a corrupted credential.

Separate FERNET_KEY and SECRET_KEY

FERNET_KEY handles credential encryption; SECRET_KEY handles JWT signing. The separation lets you rotate connector credentials without invalidating active sessions, and rotate the JWT signing key without re-encrypting stored credentials. Mixing them into one key couples the two, so rotating one forces rotating the other - a coupling that has caused outages.

The credential-in-state rule

Decrypted connector credentials and model backend API keys never enter LangGraph state, checkpoint blobs, OpenTelemetry span attributes, or log output. When a run starts, Modulo decrypts them once into a transient, run-scoped context object; connectors receive the value through that context, use it for the API call, and do not serialize it. Because the context is not persisted, a checkpoint taken after the call, an exported span, or a log line contains no credential. The same rule holds at the API boundary: listing connectors returns the name, type, and configuration metadata, and listing model backends omits the API key. A Semgrep rule, credential_in_state, enforces it in CI.

DOM sensitive data rule with 30-second reveal window

Sensitive field values - API key secrets, connector credentials, webhook secrets, model backend API keys - never exist in the DOM in plaintext unless the user has completed a server-authenticated reveal. The frontend renders them as ●●●●●●● by default. Clicking reveal calls the server, which verifies authentication and returns the value in the response body for the frontend to inject into the DOM; the default display window is 30 seconds, after which the value is removed, the element reverts to dots, and it is never stored in a JavaScript variable beyond the window.

Pluggable SecretsBackend

A pluggable SecretsBackend interface is planned for v2, allowing Modulo to use external secret stores - HashiCorp Vault, AWS Secrets Manager, or any backend that implements the interface - instead of the built-in Fernet-at-rest store. The credential-in-state rule, the DOM rule, and the logging rule apply regardless of backend: the backend controls storage and retrieval, and the security model controls what happens after retrieval.

Journey example: before and after

Before

Priya, a platform engineer, builds a pipeline that pulls data from a SaaS API and posts summaries to Slack, passing the API key through pipeline state so every node can read it. The checkpoint blob stores the key, and OpenTelemetry tracing puts it in a span attribute on every API call. A colleague spots it in a Jaeger trace and opens a security ticket; the key is rotated, the trace data is purged, and the pipeline is rewritten to keep the key out of state - a day of work, and the team loses confidence in its security model. The blast radius differs by leak (the checkpoint persists across restarts, the trace goes to an observability vendor, the log to an aggregator), but the root cause is the same: the credential existed somewhere it should not.

After

Priya configures the SaaS connector with the API key, encrypted at rest with Fernet. When the pipeline runs, the key is decrypted once into a transient context; the connector reads it, makes the API call, and discards it. The checkpoint blob, the OpenTelemetry trace, and the log contain no key. Priya checks the traces after the first run and sees the API call span with no credential data; the security ticket is never filed.

When to use it vs. when not to

Use secrets management whenever an agent or connector needs a credential: API keys, database passwords, OAuth tokens, webhook secrets, or model backend keys. If a value would cause damage if leaked, it belongs here, including credentials that seem harmless - a read-only API key can still expose data, and a webhook secret can still forge events.

Do not store non-secret configuration (endpoint URLs, timeouts, retry counts) as secrets; it adds noise to the secrets list and makes it harder to audit which values are actually sensitive.

Where it fits in a pipeline

Secrets management is a cross-cutting concern that touches every node that uses a connector or model backend. It works alongside HITL gates, which control human review at high-stakes steps, and the principles of Modulo, which treat autonomy as earned. Secrets are one part of the security model that makes autonomous pipelines safe to run.

Go deeper

Read the secrets docs for the full encryption flow, or the related docs on connectors and model backends. Take the product tour to see secrets management in action, or use the contact page to talk to us about securing credentials in your own pipeline.