pip install Is a Trust Decision
AI projects accumulate providers, SDKs, agents, vector stores, and secrets quickly. I started treating every new dependency as part of the security model.
I review code I wrote much more carefully than code I install.
That is a strange habit when you think about it.
I can spend half an hour reviewing a pull request, then run:
pip install some-package
and give code I have never read access to the same process environment as my application.
AI projects make that trust gap worse.
There are a lot of secrets in one process
A typical AI application can have access to several of these at once:
LLM provider keys
database credentials
vector database tokens
cloud credentials
GitHub tokens
monitoring credentials
internal service URLs
If a dependency can read environment variables, “it is only an LLM library” is not much of a security boundary.
The LiteLLM supply-chain incident was a useful reminder of that for me. The interesting part was not LiteLLM specifically. Any popular package can become an attractive target because compromising one publishing path can reach a large number of downstream environments.
That changed how I think about dependency updates.
Pinning is not security, but it removes surprises
For anything important, I prefer an exact version over an open-ended upgrade range.
Not because a pinned version is guaranteed safe. It is not.
It simply prevents tomorrow’s release from entering production because a build happened to run after the package was published.
I also do not feel a need to upgrade a critical dependency the minute a release appears. “Latest” and “safest” are different properties.
Smaller dependency trees are easier to reason about
This is also one reason I became more aggressive about optional dependency groups in my own projects.
If an API image does not need evaluation tooling, test frameworks, or ingestion packages, I do not want them in that image.
That decision helps image size, but it also reduces the amount of third-party code running with production credentials.
Before adding a package now, I ask a few boring questions:
Do I actually need it?
Is it maintained?
What does it pull in transitively?
Can I do this with something I already depend on?
None of those questions is sophisticated. They are useful.
Secrets need their own boundary too
I do not treat a private repository as a secret manager.
Local .env files stay out of Git. CI secrets live in the CI secret store. Kubernetes workloads get credentials through Kubernetes mechanisms rather than hard-coded Python constants.
And for agents, I try to keep permissions narrower than the application’s total capability.
If a tool only needs read access, I do not want the credential behind it to have write access “just in case.”
The same applies to high-impact actions. Sending email, deleting data, executing shell commands, or changing infrastructure should not become automatic just because an LLM can call a tool.
AI-assisted development makes it very easy to add another SDK, another provider, another integration, another agent.
That speed is useful.
It also means the security review cannot start after the architecture is already full of trust relationships.
For me, the smallest useful mental shift has been this:
pip install is not setup. It is permission.