baton-auth 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- baton_auth-0.1.3/.github/workflows/package.yml +107 -0
- baton_auth-0.1.3/.gitignore +9 -0
- baton_auth-0.1.3/PKG-INFO +78 -0
- baton_auth-0.1.3/README.md +52 -0
- baton_auth-0.1.3/assets/baton-banner.svg +56 -0
- baton_auth-0.1.3/docs/guide.md +124 -0
- baton_auth-0.1.3/docs/security.md +40 -0
- baton_auth-0.1.3/pyproject.toml +47 -0
- baton_auth-0.1.3/src/baton_auth/__init__.py +45 -0
- baton_auth-0.1.3/src/baton_auth/__main__.py +7 -0
- baton_auth-0.1.3/src/baton_auth/broker.py +342 -0
- baton_auth-0.1.3/src/baton_auth/catalog.py +268 -0
- baton_auth-0.1.3/src/baton_auth/cli.py +586 -0
- baton_auth-0.1.3/src/baton_auth/crypto.py +297 -0
- baton_auth-0.1.3/src/baton_auth/integration.py +191 -0
- baton_auth-0.1.3/src/baton_auth/materialize.py +215 -0
- baton_auth-0.1.3/src/baton_auth/mcp_server.py +202 -0
- baton_auth-0.1.3/src/baton_auth/models.py +160 -0
- baton_auth-0.1.3/src/baton_auth/py.typed +0 -0
- baton_auth-0.1.3/src/baton_auth/redact.py +98 -0
- baton_auth-0.1.3/src/baton_auth/refresh.py +242 -0
- baton_auth-0.1.3/src/baton_auth/runner.py +182 -0
- baton_auth-0.1.3/src/baton_auth/vault.py +747 -0
- baton_auth-0.1.3/tests/test_broker_ssrf.py +164 -0
- baton_auth-0.1.3/tests/test_cli.py +274 -0
- baton_auth-0.1.3/tests/test_core.py +253 -0
- baton_auth-0.1.3/tests/test_e2e.py +113 -0
- baton_auth-0.1.3/tests/test_hardening.py +171 -0
- baton_auth-0.1.3/tests/test_refresh.py +304 -0
- baton_auth-0.1.3/tests/test_surfaces.py +205 -0
- baton_auth-0.1.3/uv.lock +1701 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
name: Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test-and-build:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
python-version: ["3.10", "3.12", "3.14"]
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
|
|
25
|
+
with:
|
|
26
|
+
version: "0.13.5"
|
|
27
|
+
|
|
28
|
+
- name: Install package and test dependencies
|
|
29
|
+
run: uv sync --locked --extra dev --python ${{ matrix.python-version }}
|
|
30
|
+
|
|
31
|
+
- name: Test
|
|
32
|
+
run: uv run --no-sync pytest tests -q
|
|
33
|
+
|
|
34
|
+
- name: Build source archive and wheel
|
|
35
|
+
run: uv build
|
|
36
|
+
|
|
37
|
+
- name: Install and run the wheel in a clean environment
|
|
38
|
+
run: |
|
|
39
|
+
uv venv .package-venv --python ${{ matrix.python-version }}
|
|
40
|
+
uv pip install --python .package-venv/bin/python dist/*.whl
|
|
41
|
+
.package-venv/bin/python -m baton_auth --version
|
|
42
|
+
.package-venv/bin/baton-auth --version
|
|
43
|
+
.package-venv/bin/python -c 'import baton_auth, importlib.metadata; assert baton_auth.__version__ == importlib.metadata.version("baton-auth")'
|
|
44
|
+
.package-venv/bin/baton setup --state-dir "$RUNNER_TEMP/baton-smoke" > .package-venv/mcp-config.json
|
|
45
|
+
.package-venv/bin/python -c 'import json; from pathlib import Path; assert json.loads(Path(".package-venv/mcp-config.json").read_text())["mcpServers"]["baton"]["args"] == ["-m", "baton_auth.mcp_server"]'
|
|
46
|
+
.package-venv/bin/baton setup --client muse-code --output .package-venv/muse-settings.json --state-dir "$RUNNER_TEMP/baton-smoke"
|
|
47
|
+
.package-venv/bin/baton setup --client hermes-agent --output .package-venv/hermes-config.yaml --state-dir "$RUNNER_TEMP/baton-smoke"
|
|
48
|
+
.package-venv/bin/python -c 'import json; from pathlib import Path; from ruamel.yaml import YAML; assert json.loads(Path(".package-venv/muse-settings.json").read_text())["mcpServers"]["baton"]["type"] == "stdio"; assert YAML().load(Path(".package-venv/hermes-config.yaml").read_text())["mcp_servers"]["baton"]["command"]'
|
|
49
|
+
|
|
50
|
+
release:
|
|
51
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
52
|
+
needs: test-and-build
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
permissions:
|
|
55
|
+
contents: write
|
|
56
|
+
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
59
|
+
|
|
60
|
+
- uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
|
|
61
|
+
with:
|
|
62
|
+
version: "0.13.5"
|
|
63
|
+
|
|
64
|
+
- name: Check that the tag matches the package version
|
|
65
|
+
run: test "v$(uv version --short)" = "$GITHUB_REF_NAME"
|
|
66
|
+
|
|
67
|
+
- name: Build release files
|
|
68
|
+
run: |
|
|
69
|
+
uv build --no-sources
|
|
70
|
+
sha256sum dist/*.whl dist/*.tar.gz > dist/SHA256SUMS
|
|
71
|
+
|
|
72
|
+
- name: Publish GitHub release
|
|
73
|
+
env:
|
|
74
|
+
GH_TOKEN: ${{ github.token }}
|
|
75
|
+
run: gh release create "$GITHUB_REF_NAME" dist/*.whl dist/*.tar.gz dist/SHA256SUMS --verify-tag --generate-notes
|
|
76
|
+
|
|
77
|
+
- name: Save distributions for PyPI
|
|
78
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
79
|
+
with:
|
|
80
|
+
name: pypi-distributions
|
|
81
|
+
path: |
|
|
82
|
+
dist/*.whl
|
|
83
|
+
dist/*.tar.gz
|
|
84
|
+
if-no-files-found: error
|
|
85
|
+
|
|
86
|
+
publish-pypi:
|
|
87
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
88
|
+
needs: release
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
environment: pypi
|
|
91
|
+
permissions:
|
|
92
|
+
id-token: write
|
|
93
|
+
|
|
94
|
+
steps:
|
|
95
|
+
- uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
|
|
96
|
+
with:
|
|
97
|
+
version: "0.13.5"
|
|
98
|
+
enable-cache: false
|
|
99
|
+
|
|
100
|
+
- name: Download distributions
|
|
101
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
102
|
+
with:
|
|
103
|
+
name: pypi-distributions
|
|
104
|
+
path: dist/
|
|
105
|
+
|
|
106
|
+
- name: Publish to PyPI
|
|
107
|
+
run: uv publish --trusted-publishing always dist/*.whl dist/*.tar.gz
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: baton-auth
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Local authentication broker for AI agents and MCP tools
|
|
5
|
+
Project-URL: Homepage, https://github.com/DanDekker/baton
|
|
6
|
+
Project-URL: Documentation, https://github.com/DanDekker/baton/blob/main/docs/guide.md
|
|
7
|
+
Project-URL: Issues, https://github.com/DanDekker/baton/issues
|
|
8
|
+
Project-URL: Source, https://github.com/DanDekker/baton
|
|
9
|
+
Keywords: AI agents,MCP,authentication,credential management,secrets management
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Security
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: argon2-cffi>=25
|
|
17
|
+
Requires-Dist: blake3>=1.0
|
|
18
|
+
Requires-Dist: cryptography>=45
|
|
19
|
+
Requires-Dist: httpx>=0.27
|
|
20
|
+
Requires-Dist: keyring>=24
|
|
21
|
+
Requires-Dist: mcp>=2.0
|
|
22
|
+
Requires-Dist: ruamel-yaml>=0.18.16
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
<div align="center">
|
|
28
|
+
|
|
29
|
+
<img src="https://raw.githubusercontent.com/DanDekker/baton/main/assets/baton-banner.svg" alt="baton — Pass the baton, never the secret." width="100%">
|
|
30
|
+
|
|
31
|
+
<p><a href="#try-it">Try it</a> · <a href="https://github.com/DanDekker/baton/blob/main/docs/guide.md">User guide</a> · <a href="https://github.com/DanDekker/baton/blob/main/docs/security.md">Security</a></p>
|
|
32
|
+
|
|
33
|
+
<p><a href="https://github.com/DanDekker/baton/actions/workflows/package.yml"><img src="https://github.com/DanDekker/baton/actions/workflows/package.yml/badge.svg?branch=main" alt="Package CI"></a></p>
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
**baton is a local authentication broker for AI agents and MCP tools.** It lets an agent use your GitHub, cloud, or API credentials without putting the secret value into its chat or tool output.
|
|
40
|
+
|
|
41
|
+
Save a credential once, group it into a **context** such as `work`, then give an agent a **lease**: a temporary pass that can expire or be revoked. baton uses the credential for a command or HTTP request and hides known secrets in the result.
|
|
42
|
+
|
|
43
|
+
## Try it
|
|
44
|
+
|
|
45
|
+
Requires Python 3.10+ and [uv](https://docs.astral.sh/uv/). Install the package from GitHub:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv tool install git+https://github.com/DanDekker/baton.git
|
|
49
|
+
|
|
50
|
+
baton cred add gh-work --provider github --field token --context work
|
|
51
|
+
|
|
52
|
+
baton run work -- gh api /user
|
|
53
|
+
baton http work https://api.github.com/user
|
|
54
|
+
baton lease new work --ttl 600 --uses 20
|
|
55
|
+
baton setup # prints MCP config for an agent client
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The bare `--field token` prompts privately. Commands run with a temporary `HOME` containing that context's CLI settings.
|
|
59
|
+
|
|
60
|
+
`baton setup --client CLIENT` connects Codex, Claude Code, Cursor, VS Code, Muse Code, or Hermes Agent. `baton setup` prints portable MCP config for other clients. Agents with command access can use `baton run` or `baton http`.
|
|
61
|
+
|
|
62
|
+
## Cryptography at a glance
|
|
63
|
+
|
|
64
|
+
| Name | How baton uses it |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| AES-256-GCM-SIV; ChaCha20-Poly1305 fallback | Encrypts credentials in the local vault. |
|
|
67
|
+
| BLAKE3 | Derives separate keys, authenticates leases and vault records, and links audit entries. |
|
|
68
|
+
| Argon2id | Turns a passphrase into the vault key when passphrase mode is used. |
|
|
69
|
+
| HMAC-SHA-256 | Signs AWS requests using Signature Version 4. |
|
|
70
|
+
|
|
71
|
+
## Learn more
|
|
72
|
+
|
|
73
|
+
- [User guide](https://github.com/DanDekker/baton/blob/main/docs/guide.md) — contexts, leases, providers, refresh, and MCP tools.
|
|
74
|
+
- [Security and limits](https://github.com/DanDekker/baton/blob/main/docs/security.md) — what baton protects, how it stores secrets, and where its boundary ends.
|
|
75
|
+
|
|
76
|
+
The distribution is named `baton-auth` and also provides the `baton-auth` command. The Python import is `baton_auth`.
|
|
77
|
+
|
|
78
|
+
baton runs locally for one user on one machine. A temporary `HOME` keeps CLI configuration separate; it is not a full operating-system sandbox.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="https://raw.githubusercontent.com/DanDekker/baton/main/assets/baton-banner.svg" alt="baton — Pass the baton, never the secret." width="100%">
|
|
4
|
+
|
|
5
|
+
<p><a href="#try-it">Try it</a> · <a href="https://github.com/DanDekker/baton/blob/main/docs/guide.md">User guide</a> · <a href="https://github.com/DanDekker/baton/blob/main/docs/security.md">Security</a></p>
|
|
6
|
+
|
|
7
|
+
<p><a href="https://github.com/DanDekker/baton/actions/workflows/package.yml"><img src="https://github.com/DanDekker/baton/actions/workflows/package.yml/badge.svg?branch=main" alt="Package CI"></a></p>
|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
**baton is a local authentication broker for AI agents and MCP tools.** It lets an agent use your GitHub, cloud, or API credentials without putting the secret value into its chat or tool output.
|
|
14
|
+
|
|
15
|
+
Save a credential once, group it into a **context** such as `work`, then give an agent a **lease**: a temporary pass that can expire or be revoked. baton uses the credential for a command or HTTP request and hides known secrets in the result.
|
|
16
|
+
|
|
17
|
+
## Try it
|
|
18
|
+
|
|
19
|
+
Requires Python 3.10+ and [uv](https://docs.astral.sh/uv/). Install the package from GitHub:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv tool install git+https://github.com/DanDekker/baton.git
|
|
23
|
+
|
|
24
|
+
baton cred add gh-work --provider github --field token --context work
|
|
25
|
+
|
|
26
|
+
baton run work -- gh api /user
|
|
27
|
+
baton http work https://api.github.com/user
|
|
28
|
+
baton lease new work --ttl 600 --uses 20
|
|
29
|
+
baton setup # prints MCP config for an agent client
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The bare `--field token` prompts privately. Commands run with a temporary `HOME` containing that context's CLI settings.
|
|
33
|
+
|
|
34
|
+
`baton setup --client CLIENT` connects Codex, Claude Code, Cursor, VS Code, Muse Code, or Hermes Agent. `baton setup` prints portable MCP config for other clients. Agents with command access can use `baton run` or `baton http`.
|
|
35
|
+
|
|
36
|
+
## Cryptography at a glance
|
|
37
|
+
|
|
38
|
+
| Name | How baton uses it |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| AES-256-GCM-SIV; ChaCha20-Poly1305 fallback | Encrypts credentials in the local vault. |
|
|
41
|
+
| BLAKE3 | Derives separate keys, authenticates leases and vault records, and links audit entries. |
|
|
42
|
+
| Argon2id | Turns a passphrase into the vault key when passphrase mode is used. |
|
|
43
|
+
| HMAC-SHA-256 | Signs AWS requests using Signature Version 4. |
|
|
44
|
+
|
|
45
|
+
## Learn more
|
|
46
|
+
|
|
47
|
+
- [User guide](https://github.com/DanDekker/baton/blob/main/docs/guide.md) — contexts, leases, providers, refresh, and MCP tools.
|
|
48
|
+
- [Security and limits](https://github.com/DanDekker/baton/blob/main/docs/security.md) — what baton protects, how it stores secrets, and where its boundary ends.
|
|
49
|
+
|
|
50
|
+
The distribution is named `baton-auth` and also provides the `baton-auth` command. The Python import is `baton_auth`.
|
|
51
|
+
|
|
52
|
+
baton runs locally for one user on one machine. A temporary `HOME` keeps CLI configuration separate; it is not a full operating-system sandbox.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 300" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">baton</title>
|
|
3
|
+
<desc id="desc">Auth contexts for agents. Pass the baton, never the secret.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="background" x1="0" y1="0" x2="1" y2="1">
|
|
6
|
+
<stop offset="0" stop-color="#050505"/>
|
|
7
|
+
<stop offset="1" stop-color="#0d0d0d"/>
|
|
8
|
+
</linearGradient>
|
|
9
|
+
<radialGradient id="glow" cx="0" cy="0" r="1" gradientTransform="translate(1000 78) rotate(90) scale(260 350)">
|
|
10
|
+
<stop stop-color="#ffffff" stop-opacity=".04"/>
|
|
11
|
+
<stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
|
|
12
|
+
</radialGradient>
|
|
13
|
+
<linearGradient id="gold-body" x1="0" y1="0" x2="1" y2="0">
|
|
14
|
+
<stop offset="0" stop-color="#744414"/>
|
|
15
|
+
<stop offset=".18" stop-color="#c88b31"/>
|
|
16
|
+
<stop offset=".42" stop-color="#fff1ba"/>
|
|
17
|
+
<stop offset=".61" stop-color="#e5ad50"/>
|
|
18
|
+
<stop offset="1" stop-color="#8f581b"/>
|
|
19
|
+
</linearGradient>
|
|
20
|
+
<radialGradient id="gold-cap">
|
|
21
|
+
<stop stop-color="#fff6d1"/>
|
|
22
|
+
<stop offset=".65" stop-color="#dfaa4e"/>
|
|
23
|
+
<stop offset="1" stop-color="#8f5518"/>
|
|
24
|
+
</radialGradient>
|
|
25
|
+
<filter id="baton-shadow" x="-70%" y="-30%" width="240%" height="170%">
|
|
26
|
+
<feGaussianBlur stdDeviation="5"/>
|
|
27
|
+
</filter>
|
|
28
|
+
<clipPath id="frame">
|
|
29
|
+
<rect width="1200" height="300" rx="22"/>
|
|
30
|
+
</clipPath>
|
|
31
|
+
</defs>
|
|
32
|
+
|
|
33
|
+
<g clip-path="url(#frame)">
|
|
34
|
+
<rect width="1200" height="300" fill="url(#background)"/>
|
|
35
|
+
<rect width="1200" height="300" fill="url(#glow)"/>
|
|
36
|
+
<path d="M785 0v300M865 0v300M945 0v300M1025 0v300M1105 0v300" stroke="#ffffff" stroke-opacity=".06"/>
|
|
37
|
+
<path d="M740 72h398M740 150h398M740 228h398" stroke="#ffffff" stroke-opacity=".06"/>
|
|
38
|
+
|
|
39
|
+
<rect x="67" y="76" width="144" height="144" rx="34" fill="#252525" stroke="#484848" stroke-width="2"/>
|
|
40
|
+
<g transform="translate(139 148) rotate(45)">
|
|
41
|
+
<rect x="-16" y="-59" width="37" height="119" rx="16" fill="#000000" opacity=".5" filter="url(#baton-shadow)"/>
|
|
42
|
+
<rect x="-15" y="-58" width="30" height="116" rx="14" fill="url(#gold-body)" stroke="#6c3f11" stroke-width="1.5"/>
|
|
43
|
+
<path d="M-7 -43v86" stroke="#fff6d6" stroke-opacity=".65" stroke-width="2" stroke-linecap="round"/>
|
|
44
|
+
<path d="M9 -44v88" stroke="#5d340d" stroke-opacity=".35" stroke-width="3" stroke-linecap="round"/>
|
|
45
|
+
<ellipse cx="0" cy="-55" rx="14" ry="6" fill="url(#gold-cap)"/>
|
|
46
|
+
<ellipse cx="0" cy="55" rx="14" ry="6" fill="url(#gold-cap)"/>
|
|
47
|
+
</g>
|
|
48
|
+
|
|
49
|
+
<text x="252" y="94" fill="#b9b9b9" font-family="-apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" font-size="16" font-weight="700" letter-spacing="3.5">AUTH CONTEXTS FOR AGENTS</text>
|
|
50
|
+
<text x="246" y="184" fill="#f6f6f6" font-family="-apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" font-size="82" font-weight="750" letter-spacing="-3">baton</text>
|
|
51
|
+
<text x="251" y="229" fill="#c8c8c8" font-family="-apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" font-size="25">Pass the baton, never the secret.</text>
|
|
52
|
+
|
|
53
|
+
<path d="M849 121l47-47m-47 126l125-126m-47 126l125-126m-47 126l125-126" stroke="#ffffff" stroke-opacity=".07" stroke-width="16" stroke-linecap="round"/>
|
|
54
|
+
<path d="M0 298h1200" stroke="#ffffff" stroke-opacity=".14" stroke-width="4"/>
|
|
55
|
+
</g>
|
|
56
|
+
</svg>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Using baton
|
|
2
|
+
|
|
3
|
+
[Back to overview](../README.md) · [Security and limits](security.md)
|
|
4
|
+
|
|
5
|
+
baton keeps credentials on your machine and uses them on an agent's behalf. Three terms are enough to get started:
|
|
6
|
+
|
|
7
|
+
| Term | What it means |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| **Credential** | A token, password, key, or config file you store in baton. |
|
|
10
|
+
| **Context** | A named group of credentials, such as `work` or `staging`. |
|
|
11
|
+
| **Lease** | A temporary pass to use a context. It can expire, run out of uses, or be revoked. |
|
|
12
|
+
|
|
13
|
+
## Set up a context
|
|
14
|
+
|
|
15
|
+
Install with Python 3.10+ and [uv](https://docs.astral.sh/uv/), then add a GitHub token. A bare `--field token` prompts without showing the value or putting it in shell history.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv tool install git+https://github.com/DanDekker/baton.git
|
|
19
|
+
|
|
20
|
+
baton cred add gh-work --provider github --field token --context work
|
|
21
|
+
baton ctx list
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run a command or send an HTTP request with the `work` context:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
baton run work -- gh api /user
|
|
28
|
+
baton http work https://api.github.com/user
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For `baton run`, put baton options before the context name and the child command after `--`. The child gets a temporary `HOME` with this context's CLI configuration. Output is checked for known secrets before it is returned.
|
|
32
|
+
|
|
33
|
+
## Give an agent temporary access
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
baton lease new work --ttl 600 --uses 20
|
|
37
|
+
baton lease list
|
|
38
|
+
baton lease revoke LEASE_ID
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The first command prints a lease token. Give that token only to the agent that needs it: anyone holding a live lease can use its allowed context. `--ttl` is in seconds; `--uses` sets a use limit. Add `--service github` to restrict a lease to that service.
|
|
42
|
+
|
|
43
|
+
## Connect an MCP client
|
|
44
|
+
|
|
45
|
+
Connect a local agent with one command:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
baton setup --client codex
|
|
49
|
+
baton setup --client claude-code
|
|
50
|
+
baton setup --client cursor
|
|
51
|
+
baton setup --client vscode
|
|
52
|
+
baton setup --client muse-code
|
|
53
|
+
baton setup --client hermes-agent
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Codex and Claude Code use their own setup commands. Cursor gets a personal `~/.cursor/mcp.json`; VS Code gets `.vscode/mcp.json` in the current project. For another MCP client, run `baton setup` to print JSON, or `baton setup --output PATH` to merge into its JSON config. Add `--root servers` if that client uses a `servers` key. baton uses its installed Python environment and includes the vault path, so the client opens the same vault. Setup leaves other configured servers alone.
|
|
57
|
+
|
|
58
|
+
Muse Code gets `~/.config/muse/settings.json` (or `$XDG_CONFIG_HOME/muse/settings.json`); Hermes Agent gets `~/.hermes/config.yaml` (or `$HERMES_HOME/config.yaml`). Both keep their existing settings and other MCP servers. Use `--output PATH` to target another config file.
|
|
59
|
+
|
|
60
|
+
Client formats follow the current [Codex](https://learn.chatgpt.com/docs/extend/mcp), [Claude Code](https://code.claude.com/docs/en/mcp), [Cursor](https://cursor.com/docs/mcp), [VS Code](https://code.visualstudio.com/docs/agents/reference/mcp-configuration), [Muse Code](https://meta-models.github.io/muse-code-sdk/next/guides/extend/mcp-servers/), and [Hermes Agent](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/reference/mcp-config-reference.md) MCP guides.
|
|
61
|
+
|
|
62
|
+
If you use `BATON_MASTER_KEY` or `BATON_PASSPHRASE`, supply it to the client through its environment; setup never writes a key into the config. Agents without MCP can call `baton run` or `baton http` through a command tool. A local MCP server must be installed on the machine where the agent runs.
|
|
63
|
+
|
|
64
|
+
| What an agent needs to do | MCP tools |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| See available contexts and metadata | `baton_contexts`, `baton_credentials`, `baton_info` |
|
|
67
|
+
| Get or manage a pass | `baton_lease`, `baton_leases`, `baton_revoke` |
|
|
68
|
+
| Use a context | `baton_run`, `baton_http` |
|
|
69
|
+
| Refresh or inspect history | `baton_refresh`, `baton_audit` |
|
|
70
|
+
|
|
71
|
+
These tools do not return stored credential values. The server uses MCP Python SDK 2.x.
|
|
72
|
+
|
|
73
|
+
## Use other services
|
|
74
|
+
|
|
75
|
+
Run `baton info` to see the fields expected by each preset. Built-in presets are `aws`, `gcp`, `github`, `gitlab`, `npm`, `docker`, `kube`, `ssh`, and `generic`. Any other provider name uses bearer token auth by default.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
baton cred add kube-prod --provider kube --field-file kubeconfig=~/.kube/config
|
|
79
|
+
baton cred add registry --provider docker \
|
|
80
|
+
--field username --field password --field registry=ghcr.io
|
|
81
|
+
baton cred add my-api --provider my-service --field api_key \
|
|
82
|
+
--auth header:x-api-key --auth-field api_key \
|
|
83
|
+
--env 'MY_SERVICE_KEY={api_key}' --context work
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Presets decide how baton sets environment variables, writes CLI config files, or attaches HTTP authentication. For a custom provider, `--auth` also accepts `bearer`, `query:NAME`, `basic` (with `username` and `password` fields), and `none` for CLI-only use. Use `--field-file NAME=PATH` for a file or longer value. Avoid `--field NAME=VALUE` for secrets because the value can appear in shell history and process listings.
|
|
87
|
+
|
|
88
|
+
## Refresh expiring tokens
|
|
89
|
+
|
|
90
|
+
baton can refresh an expiring credential before use. OAuth 2.0 refresh tokens, external commands, and custom Python handlers are supported. For OAuth, keep the client secret and refresh token in prompted fields:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
baton cred add api --provider generic \
|
|
94
|
+
--field token --field refresh_token --field client_secret \
|
|
95
|
+
--refresh-provider oauth2 \
|
|
96
|
+
--refresh-token-url https://auth.example.com/token \
|
|
97
|
+
--refresh-client-id my-client-id
|
|
98
|
+
|
|
99
|
+
baton cred refresh api --force
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
OAuth refresh can rotate the refresh token and update the expiry from `expires_in`. Python integrations can register other handlers with `register_refresh_provider(name, handler)`.
|
|
103
|
+
|
|
104
|
+
## Useful commands
|
|
105
|
+
|
|
106
|
+
| Command | Purpose |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `baton status` | Show vault status and counts. |
|
|
109
|
+
| `baton cred list` / `baton ctx list` | See stored names without revealing values. |
|
|
110
|
+
| `baton env work` | See which environment variables a context sets. |
|
|
111
|
+
| `baton audit` | Check the audit chain and recent activity. |
|
|
112
|
+
| `baton --help` | See all commands and examples. |
|
|
113
|
+
|
|
114
|
+
Use `--state-dir PATH` to choose another vault directory. `baton env --reveal` prints values for a human operator on explicit request; never use it in an agent transcript.
|
|
115
|
+
|
|
116
|
+
## Work on the package
|
|
117
|
+
|
|
118
|
+
The distribution name is `baton-auth` because `baton` is an unrelated PyPI project. The import name is `baton_auth`. This repository installs the `baton`, `baton-auth`, and `baton-mcp` commands. It is installable from GitHub; a PyPI release has not been published yet.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv sync --extra dev
|
|
122
|
+
.venv/bin/python -m pytest tests -q
|
|
123
|
+
uv build
|
|
124
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Security and limits
|
|
2
|
+
|
|
3
|
+
[Back to overview](../README.md) · [User guide](guide.md)
|
|
4
|
+
|
|
5
|
+
baton keeps stored credential values away from MCP tools. Agents can see context names and use a temporary lease, while baton applies the real credential to a command or HTTP request. The returned output is checked for known secret values and recognized token shapes.
|
|
6
|
+
|
|
7
|
+
## What baton protects
|
|
8
|
+
|
|
9
|
+
| Area | How it works |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Vault | Credential records are encrypted and tied to their record identity. The database file alone cannot decrypt them. |
|
|
12
|
+
| Leases | Handles expire, can be revoked or limited by service and use count, and are stored only as token digests. |
|
|
13
|
+
| CLI commands | Each run gets a temporary `0700` home with its context's auth files. Common inherited code injection variables are removed. |
|
|
14
|
+
| HTTP requests | baton attaches bearer, header, basic, query, or AWS SigV4 auth. It rejects disallowed URL schemes, metadata endpoints, link-local addresses, and private networks by default. Redirects are returned without being followed. |
|
|
15
|
+
| Results | Known secret values and common provider token patterns are redacted before a broker result is returned. |
|
|
16
|
+
| Audit | Actions are recorded in a BLAKE3 hash chain. SQLite uses WAL and atomic lease use updates. |
|
|
17
|
+
|
|
18
|
+
The master seed is resolved in this order: `BATON_MASTER_KEY` → `BATON_PASSPHRASE` with Argon2id → OS keyring → a `0600` keyfile. Mutable key buffers are wiped when the vault closes.
|
|
19
|
+
|
|
20
|
+
## Where the boundary ends
|
|
21
|
+
|
|
22
|
+
- baton is local to one user on one machine. It does not sync vaults between hosts.
|
|
23
|
+
- A temporary `HOME` separates CLI settings; it is not an operating-system sandbox. Child processes keep their normal filesystem permissions.
|
|
24
|
+
- A lease is a bearer token. Share it only with the intended agent and revoke it when it is no longer needed.
|
|
25
|
+
- Redaction is based on known values and token patterns. It cannot undo a leak that happened through another tool or guarantee that every possible encoding is caught.
|
|
26
|
+
- Hostnames are checked before HTTP connections. DNS can change between validation and connection; use network-level egress controls for untrusted destinations.
|
|
27
|
+
- `baton env --reveal` prints values when an operator explicitly asks for them. MCP has no reveal tool.
|
|
28
|
+
|
|
29
|
+
## Cryptographic construction
|
|
30
|
+
|
|
31
|
+
| Purpose | Primitive |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| Record encryption | AES-256-GCM-SIV (RFC 8452) when available; ChaCha20-Poly1305 fallback |
|
|
34
|
+
| Envelope commitment | BLAKE3 keyed tag over header, associated data, and ciphertext |
|
|
35
|
+
| Subkeys | BLAKE3 `derive_key`, separated by purpose |
|
|
36
|
+
| Lease MACs and audit chain | BLAKE3 keyed mode |
|
|
37
|
+
| Passphrase derivation | Argon2id, 64 MiB memory, 3 iterations, parallelism 4 |
|
|
38
|
+
| Record identity | Associated data `cred:<id>:<name>` |
|
|
39
|
+
|
|
40
|
+
AWS SigV4 uses SHA-256 as required by that protocol. The implementation lives in [`src/baton_auth/crypto.py`](../src/baton_auth/crypto.py), [`src/baton_auth/vault.py`](../src/baton_auth/vault.py), and [`src/baton_auth/broker.py`](../src/baton_auth/broker.py).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "baton-auth"
|
|
7
|
+
description = "Local authentication broker for AI agents and MCP tools"
|
|
8
|
+
version = "0.1.3"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
keywords = ["AI agents", "authentication", "MCP", "credential management", "secrets management"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Environment :: Console",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Topic :: Security",
|
|
17
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"cryptography>=45",
|
|
21
|
+
"blake3>=1.0",
|
|
22
|
+
"argon2-cffi>=25",
|
|
23
|
+
"keyring>=24",
|
|
24
|
+
"mcp>=2.0",
|
|
25
|
+
"httpx>=0.27",
|
|
26
|
+
"ruamel.yaml>=0.18.16",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/DanDekker/baton"
|
|
31
|
+
Documentation = "https://github.com/DanDekker/baton/blob/main/docs/guide.md"
|
|
32
|
+
Issues = "https://github.com/DanDekker/baton/issues"
|
|
33
|
+
Source = "https://github.com/DanDekker/baton"
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
baton = "baton_auth.cli:main"
|
|
37
|
+
baton-auth = "baton_auth.cli:main"
|
|
38
|
+
baton-mcp = "baton_auth.mcp_server:main"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
dev = ["pytest>=8"]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["src/baton_auth"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""baton: pass auth context between agents as leases, never as secrets."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
from .crypto import CryptoError, zeroize
|
|
6
|
+
from .models import (
|
|
7
|
+
BatonError,
|
|
8
|
+
Context,
|
|
9
|
+
Credential,
|
|
10
|
+
Lease,
|
|
11
|
+
LeaseError,
|
|
12
|
+
RefreshError,
|
|
13
|
+
UnknownContext,
|
|
14
|
+
UnknownCredential,
|
|
15
|
+
VaultLocked,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
__version__ = version("baton-auth")
|
|
20
|
+
except PackageNotFoundError: # running from an unpackaged source tree
|
|
21
|
+
__version__ = "0+local"
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"BatonError",
|
|
25
|
+
"Context",
|
|
26
|
+
"Credential",
|
|
27
|
+
"CryptoError",
|
|
28
|
+
"Lease",
|
|
29
|
+
"LeaseError",
|
|
30
|
+
"RefreshError",
|
|
31
|
+
"UnknownContext",
|
|
32
|
+
"UnknownCredential",
|
|
33
|
+
"VaultLocked",
|
|
34
|
+
"Vault",
|
|
35
|
+
"zeroize",
|
|
36
|
+
"__version__",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def __getattr__(name: str):
|
|
41
|
+
if name == "Vault":
|
|
42
|
+
from .vault import Vault
|
|
43
|
+
|
|
44
|
+
return Vault
|
|
45
|
+
raise AttributeError(name)
|