docs/quickstart.md
Rama main · réplica de lectura del repositorio original.
# Quickstart
In about five minutes: install toolpass, ask it a question, put it in front of an agent tool, then
run the same thing as a server. You need Python 3.10 or later; step 3 also uses Docker (or
nothing more than the same `pip install`) and, for the Node client, Node 18.17 or later. Nothing
here touches a real system; step 4 connects one.
## 1. Install and ask
```sh
python3 -m venv .venv && . .venv/bin/activate
pip install toolpass
curl -sO https://raw.githubusercontent.com/roee-hersh/toolpass/main/examples/toolpass.yaml
```
The example config has a `demo` connection that talks to nothing. In it, `admin@example.com` may
write and `dana@example.com` may not. The engine runs in your process:
```sh
python3 -c '
from toolpass import Toolpass
tp = Toolpass.from_config("toolpass.yaml")
print(tp.check("dana@example.com", "demo", "thing.write", "thing:1"))
'
```
```text
Decision(decision='deny', reason='denied: dana@example.com is not an admin', status=200, identity='dana@example.com')
```
Change the user to `admin@example.com` and the answer is `allow`. Try `nobody@example.com` and it is
`deny` with `user_not_found`. Every answer is also a JSON line on stderr: that is the
[decision log](guides/operating.md#decision-log), set by `decision_log` in the file.
The command line asks the same question without any code:
```sh
toolpass check -config toolpass.yaml -connection demo \
-user dana@example.com -action thing.write -resource thing:1
```
## 2. Guard a tool
Wrap the function your agent calls, so it runs only after toolpass said `allow`. The user comes from
your application, never from the model. Save as `quickstart.py`:
```python
from contextvars import ContextVar
from toolpass import Toolpass, PermissionDenied, guarded
tp = Toolpass.from_config("toolpass.yaml")
current_user: ContextVar[str] = ContextVar("current_user")
@guarded(tp, "demo", "thing.write", "thing:{thing_id}", user=current_user)
def write_thing(thing_id: str) -> str:
return f"wrote thing {thing_id}" # your real action goes here
for who in ["admin@example.com", "dana@example.com"]:
current_user.set(who) # in a real agent: the signed-in user, never the model
try:
print(who, "->", write_thing(thing_id="1"))
except PermissionDenied as e:
print(who, "-> refused:", e.decision.reason)
```
```sh
python3 quickstart.py 2>/dev/null # stderr carries the decision log
```
```text
admin@example.com -> wrote thing 1
dana@example.com -> refused: denied: dana@example.com is not an admin
```
In a real agent, use your framework's adapter instead, configured once on the agent
(`toolpass.strands`, `toolpass.langchain`, ...), or put the framework's tool
decorator on top of `guarded`. The [agent guide](guides/agent-tools.md) covers each framework and
where the user comes from.
## 3. Run it as a server
In-process, the agent's process holds toolpass's lookup credentials. To keep them out of it, run
the same engine as a service and point the agent at it:
```sh
docker run --rm -p 8080:8080 -e TOOLPASS_API_KEY=change-me \
-v "$PWD/toolpass.yaml:/etc/toolpass/toolpass.yaml:ro" ghcr.io/roee-hersh/toolpass
```
No Docker? The package you installed is the server too:
`TOOLPASS_API_KEY=change-me toolpass serve -config toolpass.yaml`.
In a second terminal:
```sh
curl -X POST localhost:8080/check -H 'Authorization: Bearer change-me' \
-d '{"user":"dana@example.com","connection":"demo","action":"thing.write","resource":"thing:1"}'
```
```json
{"decision":"deny","reason":"denied: dana@example.com is not an admin"}
```
In `quickstart.py`, change one line and run it again; the output is the same:
```python
tp = Toolpass.remote("http://localhost:8080", "change-me") # or TOOLPASS_URL and TOOLPASS_API_KEY
```
**Node** talks to the server with `toolpass-client`. Save as `quickstart.mjs` (the `.mjs`
extension lets it use `import`):
```sh
npm install toolpass-client
```
```js
import { AsyncLocalStorage } from "node:async_hooks";
import { Toolpass, PermissionDenied, guarded } from "toolpass-client";
const tp = new Toolpass(); // reads TOOLPASS_URL and TOOLPASS_API_KEY
const currentUser = new AsyncLocalStorage();
const writeThing = guarded(tp, "demo", "thing.write", "thing:{thingId}", { user: currentUser })(
async ({ thingId }) => `wrote thing ${thingId}`, // your real action goes here
);
for (const who of ["admin@example.com", "dana@example.com"]) {
try {
console.log(who, "->", await currentUser.run(who, () => writeThing({ thingId: "1" })));
} catch (e) {
if (!(e instanceof PermissionDenied)) throw e;
console.log(who, "-> refused:", e.decision.reason);
}
}
```
```sh
TOOLPASS_URL=http://localhost:8080 TOOLPASS_API_KEY=change-me node quickstart.mjs
```
## 4. Connect a real system
Pick the system your agent acts in, follow its [integration page](integrations/README.md) to create
a read-only credential, and add a connection to `toolpass.yaml`. For example, Jira:
```yaml
connections:
- id: jira-main
integration: jira
url: https://acme.atlassian.net
username: toolpass-bot@acme.com
credential: env:JIRA_TOKEN
```
Then check the file and the credential before you rely on it:
```sh
export JIRA_TOKEN=... # the token from the integration page
export TOOLPASS_API_KEY=change-me # validate reads api_key when the file has one
toolpass validate -config toolpass.yaml
toolpass probe -config toolpass.yaml -connection jira-main
toolpass check -config toolpass.yaml -connection jira-main \
-user you@acme.com -action DELETE_ISSUES -resource issue:PAY-123
```
`toolpass catalog jira` lists the actions you can ask about. The integrations that sign with a
private key (a GitHub App, Google service accounts, Snowflake key pairs, Salesforce JWT, Microsoft
365 certificates) need `pip install "toolpass[crypto]"`; the Docker image has it.
## Next
- [Architecture](concepts/architecture.md): how a check flows, what is cached, and the trust boundaries.
- [Deploy](guides/deploy.md): in-process or as a server, with Docker, pip or Helm, and how to reach it over TLS.
- [Add toolpass to your agent](guides/agent-tools.md): the adapters for Strands, LangChain and
LangGraph, MCP, the OpenAI Agents SDK, the Claude Agent SDK, Google ADK, CrewAI, Pydantic AI,
LlamaIndex, and the TypeScript client.