CrewAI
CrewAI reads OPENAI_API_KEY and OPENAI_BASE_URL from the environment, and its
LLM class takes api_key and base_url directly. All three shapes are short.
note
Start here if seekrit is new: three
commands put the keys in
an environment, mint a token bound to it, and export SEEKRIT_TOKEN. A token
reads everything in its
environment, so
there is no per-key or per-framework setup to do before any of the below.
1. Wrap the process
seekrit run -- python crew.py
seekrit run -- crewai run
from crewai import Agent, Task, Crew
researcher = Agent(role="Research Specialist", goal="Conduct comprehensive analysis")
search = Task(description="Research the topic: {topic}", expected_output="A report", agent=researcher)
crew = Crew(agents=[researcher], tasks=[search])
print(crew.kickoff(inputs={"topic": "AI safety"}))
CrewAI's own docs tell you never to commit API keys and to use secret
management; this is that, without the .env step in between.
2. Resolve in code
import seekrit
from crewai import LLM
secrets = seekrit.Client().resolve()
llm = LLM(model="openai/gpt-5.6-terra", api_key=secrets["OPENAI_API_KEY"])
researcher = Agent(role="Research Specialist", goal="Analyse", llm=llm)
3. Never hold the key
from crewai import LLM
llm = LLM(
model="openai/gpt-5.6-terra",
base_url="http://127.0.0.1:8080/openai/v1",
api_key="{{seekrit:OPENAI_API_KEY}}",
)
# seekrit-proxy.toml
listen = "127.0.0.1:8080"
[[route]]
prefix = "/openai"
upstream = "https://api.openai.com"
allow = ["OPENAI_API_KEY"]
methods = ["POST"]
paths = ["/v1/chat/completions"]
# The crew's tools, bounded to the operations they actually need.
[[route]]
prefix = "/github"
upstream = "https://api.github.com"
allow = ["GITHUB_TOKEN"]
methods = ["GET", "POST"]
paths = ["/repos/*/*/issues", "/repos/*/*/issues/*"]
Without running the proxy
CrewAI reaches the network through LiteLLM, whose only seam is a module global:
import httpx, litellm
from seekrit.transport import AsyncSeekritTransport, SeekritTransport
allow = {"api.openai.com": ["OPENAI_API_KEY"]}
litellm.client_session = httpx.Client(transport=SeekritTransport(allow=allow))
litellm.aclient_session = httpx.AsyncClient(transport=AsyncSeekritTransport(allow=allow))
It works, and being process-wide it cannot scope per request. In-process injection sets out the trade-off against the proxy.
Gotchas
- The tools are the reason to bother. A crew is several agents sharing one
process, so every agent effectively has every tool's credentials. The proxy's
methodsandpathsare how you stop a research agent from being able to perform a write the delegation graph never intended. kickoffis one long process. Values resolved at start hold for the whole run. For a crew that runs for hours, either restart on rotation or use shape 3, where the proxy re-resolves on its own interval.- Delegation crosses no security boundary.
allow_delegationmoves work between agents in the same process with the same environment. If two agents must have different reach, that is two processes, or the proxy's session tickets.