autonomous-agents · · 10 min read

Moving Autonomous Agent Secrets Out of .env

For a long time, my autonomous agent found credentials the same way many applications do. Why I replaced a flat environment file with scoped Vaultwarden access, short-lived agent sessions, and a verified audit trail that now reaches Graylog and Wazuh.

Moving Autonomous Agent Secrets Out of .env
Autonomous agent secret checkout

For a long time, my autonomous agent found credentials the same way many applications do. A service needed an API key, username, password, or token, so the agent looked for a named variable in an .env file.

That worked. It was also a security design I would not recommend to anyone else.

The problem was not that environment files are always wrong. They are useful for configuration, especially when the values are non-secret. The problem was that I had allowed one file to become a flat credential store for unrelated systems. If the agent could read that file, it could potentially read far more than the one credential needed for the task in front of it. Rotation meant editing the file and finding every consumer. Access was difficult to separate by identity. Retrieval left no useful item-level audit record. A debugging command, an overly broad log statement, or a careless support request could expose several systems at once.

Convenience does not make security

Autonomous agents make that design more dangerous. They operate across tools, services, repositories, and infrastructure. They can take a credential from one context and use it in another before a human sees the intermediate step. Giving an agent broad access because it is convenient is the opposite of least privilege.

I wanted a better answer than "be careful with the .env file."

This work came out of practicing what I wrote

This was not an isolated vault project. It came out of the work I described in Practice at Home the Way You Preach at Work. That post was about holding my home lab to the same standard I expect from an enterprise environment: identify what is really there, treat assumptions as risks, make controlled changes, verify them with evidence, and update the record afterward.

Once I applied that standard beyond the network, the flat .env file stopped looking like a convenient implementation detail. It looked like a finding. The agent had broad access to unrelated credentials, the operator and automation boundaries were weak, rotation depended on tracing consumers by hand, and item-level retrieval was not visible in the SIEM.

Home labs also have a "bucket-list" problem. Ideas get added faster than they get finished. A vault deployment, centralized audit logging, a collector, better credential metadata, and removal of old secret copies can each become separate someday projects. They drift into the land of forgotten projects, where the first useful piece gets built but the controls around it never arrive.

By now, you should be seeing a trend. My home lab and environment rely heavily on open-source, free, and community-driven tooling. That comes with limitations, but it also creates opportunities to get creative and solve problems that might already be handled for you in a paid product.

Vaultwarden is a good example of that here. Instead of reaching for a commercial product just because it already provides the capability I need, I can use the tools I already run and trust and build the necessary controls around them. It takes a little more work, but that is part of the reason I have a lab in the first place. I want to understand the problem, figure out what I actually need, and build a solution that fits my environment or can be used as an example to provide to others.

I did not want Vaultwarden to become another one of those half-finished projects. Deploying the vault was only the first step. I had to separate identities, constrain collections, prove the client generated events, move those events into Graylog and Wazuh, test the detections, schedule the collector, update the runbooks, and keep the remaining .env migration visible instead of pretending the work ended when the login page appeared.

Designed around a SOC pipeline

That is the connection between the two posts. Practicing at home the way I preach at work means dealing with the old bucket list, closing the control gaps around what I already built, and recording what remains unfinished.

Separate the agent from the operator

I deployed Vaultwarden as a private, self-hosted vault behind HTTPS on my internal network. The web application is exposed only through Caddy. The Vaultwarden container has no directly published application port, the images are pinned, persistent data is root-owned, and public registration is disabled after account bootstrap.

The more important decision was identity separation.

Least privilege, full context

I have my own operator account. The autonomous agent has a different account with its own master password and its own audit identity. We do not share a login. The agent is a normal organization member, not an owner or administrator, and it can reach only the collection assigned to automation. My personal vault and unrelated collections are outside that boundary.

I retain access to the shared collection so I can add a credential, correct its metadata, rotate it, or remove it. The agent can use the credentials required for its work without inheriting my personal vault access.

That sounds basic because it is basic. Human and machine identities should be separate. The fact that the machine happens to be an AI agent does not change the access-control model.

Stop making the file the vault

The old pattern looked like this:

Agent task
    -> read named value from .env
    -> call service

The new pattern looks like this:

Agent task
    -> unlock dedicated vault session
    -> synchronize the authorized collection
    -> retrieve the required item
    -> use the value in memory or through standard input
    -> lock the vault session

The agent no longer needs a service password copied into its general .env file before it can act. It asks the vault for the item when the task requires it. The value does not go into chat, Git, Wiki.js, a shell argument, or command output.

This also changes how I document credentials. A useful secret record needs more than a name and a value. Each item now carries non-secret operational context: purpose, owning system, environment, endpoint, consumer, privilege, owner, recovery owner, source reference, validation date, rotation trigger, dependencies, outage impact, and rollback procedure.

That metadata matters when an agent is making decisions. A token called API_KEY tells the agent almost nothing. An item that says which service accepts it, what access it grants, who owns rotation, and what breaks when it changes gives the agent enough context to use it safely.

"Checkout" needs an honest definition

I use the word checkout because it describes the workflow, but Vaultwarden is not issuing a leased database credential that expires at the target system. It is returning an encrypted vault item to an authenticated client.

Once the automation account is unlocked, it can read the items available to that account. The real authorization boundary is the account's collection assignment. Locking the client ends the usable session, but it does not rotate the service credential or revoke a value that was already used.

There is still a bootstrap secret. The agent needs a protected way to unlock its vault account. I store that minimum bootstrap material in a mode 0600 file inside a mode 0700 directory, outside Git and outside general configuration. Moving secrets into Vaultwarden reduces the bootstrap problem to a narrow, controlled credential. It does not make the bootstrap problem disappear.

That is a better design than pretending the vault eliminated every local secret.

Log the retrieval, then prove the log exists

A vault should tell me when the automation identity touches an organization item. Vaultwarden supports organization event logging, so I enabled it with a 365-day retention period.

Bitwarden clients can report events such as item creation, changes, deletion, viewing, password display, password copy, exports, logins, and membership changes. The record includes the acting member, client type, source address, item identifier, and timestamp.

The CLI behavior matters here. A bw get item, bw get username, or bw get password operation records item-view event type 1107. The password-specific display event is not what the CLI sends for bw get password; the CLI records the item access before returning the selected field.

I did not stop after setting two environment variables and seeing the service come back up. I started with an empty event table, forced a full synchronization of the automation client, retrieved one authorized item, and queried the event table using a read-only aggregate. The result was one event, type 1107. No username, password, token, item content, source address, or session key was printed during the test.

That verification caught a real implementation issue. An ordinary client sync retained the old cached useEvents=false capability because enabling organization events did not change the user's vault revision. The server was configured correctly, but the client still believed event reporting was disabled. A forced full sync refreshed the capability, and the next retrieval produced the expected event.

This is why "the configuration saved successfully" is not an acceptance test.

Put the audit trail in the SIEM

Local event retention gave me proof on the Vaultwarden host. It did not give me centralized search, detection, or a useful way to correlate vault activity with the rest of the environment.

Audit the events

I added a fixed, root-owned exporter that opens the Vaultwarden event database in read-only mode. Its query is hard-coded. The collector cannot submit SQL, change the database path, or ask for decrypted item content. The exporter returns only audit metadata such as the event identifier and type, timestamp, source address, client type, actor, target, and affected object identifiers.

Passwords, item notes, attachments, encryption keys, API tokens, vault sessions, password hashes, and TOTP data are outside the query.

Every five minutes, a collector retrieves the event metadata and sends each new record to two independent consumers. Graylog keeps the full audit history for search and retention. Wazuh evaluates the same normalized JSON against rules for item access, authentication failures, high-rate retrieval, vault exports, destructive changes, membership changes, administrator password resets, and organization policy changes.

The collector tracks delivery separately for each side. If Graylog accepts an event and Wazuh is unavailable, the Wazuh copy remains pending without sending another Graylog copy. A seven-day source window gives the job room to recover from normal scheduler or network interruptions. Anything longer needs a controlled backfill.

I tested the live path with two real item-view events. The first collector run delivered both events to Graylog and Wazuh. The next run delivered zero. Graylog returned both from its dedicated Vaultwarden index, and Wazuh indexed both under the item-view detection rule. No secret-bearing field was present in the exported or indexed records.

This is a scheduled pull, so normal worst-case detection latency is five minutes. I accepted that tradeoff because the event volume is low and the source interface remains narrow. If I need immediate blocking or mandatory server-side issuance later, this collector is not the control that provides it.

The audit trail has limits

Vaultwarden's item-view event is client-reported. The official Bitwarden client sends it immediately for CLI retrieval, and Vaultwarden stores it, but the server does not independently mediate a one-time secret lease.

Vaultwarden audit trail

A modified client could suppress the event. A client working from previously synchronized local state may not produce the server-side evidence I expect. A failed event upload can also leave a gap. These logs are useful for accountability, troubleshooting, and incident review. I would not call them tamper-proof forensic evidence.

That limitation does not make the logging worthless. It tells me what control I built and what control I did not build. If I need high-assurance, server-enforced, leased credentials with independent access telemetry, I need a secrets platform designed around dynamic credentials and mandatory server-side issuance. Vaultwarden solves a different problem well: encrypted storage, scoped sharing, separate identities, operational metadata, and client-reported organization events.

Rotation is still the part that closes exposure

Putting an existing credential into a vault does not fix prior exposure. If a password or token appeared in chat, source control, logs, or an old environment file, I treat it as compromised.

The rotation sequence is deliberate. Validate the current credential. Create a replacement through an authorized path. Test the replacement. Update every consumer. Confirm recovery and rollback. Revoke the old value. Prove the old value no longer works where that test is safe. Remove temporary copies.

Rotate first and test later is how a security improvement becomes an outage.

I imported the existing infrastructure credentials into the shared collection with their operational metadata and validated them without printing their values. That gave me a controlled destination. The remaining migration is consumer by consumer: change each workflow to retrieve from the vault, prove it still works, then remove the redundant secret from the old .env source.

I am not declaring victory while legacy consumers still depend on the old file. The goal is to retire those service secrets safely, not delete a file for the satisfaction of saying it is gone.

Autonomous does not mean trusted

An autonomous agent should have an identity, a defined scope, an approved place to obtain credentials, and an audit trail. It should not inherit every secret available to the person who deployed it. Autonomy does not remove the need for least privilege. If anything, it makes least privilege more important because the agent can act without someone approving every individual step.

Trusted, constrained, observable

The practical improvement here is simple. The agent can retrieve the credential required for a task without using a broad environment file as its password vault. I can see the organization item access that the supported client reports in the same SIEM that monitors the rest of the environment. I can revoke the agent account or remove collection access without changing my own account. I can rotate a single credential with enough metadata to identify its consumers and recovery path. Each of those controls reduces the blast radius without taking away the agent's ability to do useful work.

This is also the kind of security model I discuss in Cybersecurity Architect's Handbook, Second Edition. The technologies may change, and AI and agentic systems introduce new ways for software to interact with an environment, but the underlying architectural principles remain familiar: establish identity, enforce least privilege, separate responsibilities, protect credentials, constrain access, and maintain enough visibility to understand what happened. The book goes deeper into AI and agentic security controls, least privilege, and the broader architectural practices needed to apply those principles across an enterprise.

That is how I want an agent operating in my environment. Capable enough to do useful work, constrained enough that one mistake does not become access to everything, and observable enough that I can reconstruct what it touched. Giving an agent autonomy should not mean giving it unrestricted authority.

The agent can work autonomously. The access model still answers to me.

For a deeper look at the AI, agentic security, least privilege, and cybersecurity architecture practices behind this approach, see my book, Cybersecurity Architect's Handbook, Second Edition, available on Amazon.

Further reading

Read next

The Terminal Is a Conversation
Linux · Featured

The Terminal Is a Conversation

My first Linux distribution no longer exists. I mention that because it lands two points at once: Linux is old enough to have history, and the skills transfer anyway...

The 45Drives Professional Pro15: Storage You Actually Own
45Drives · Featured

The 45Drives Professional Pro15: Storage You Actually Own

Most of the storage I have bought across my career came from a few of the usual vendors, and most of it worked. It also came with things I could not source elsewhere, firmware that refused drives and software layer I paid for every year whether I used it or not. The Pro15 is a different animal...