内容
# 托管
**一种供应代理,为 AI 编码代理提供能力,而不是凭证,基于限时的租期,租期到期后自动销毁。**
[](https://github.com/AleBrito124356/bailment/actions/workflows/ci.yml)
[](LICENSE)
[](pyproject.toml)
---
## 问题
目前,许多公司为 AI 编码代理提供数据库的方式如下:
```bash
export DATABASE_URL=postgresql://admin:...@prod-db.internal:5432/main
claude --dangerously-skip-permissions
```
一个长期有效的凭证,一个终端,和希望。凭证不会过期。它存在于模型的上下文窗口中,这意味着它存在于某个日志中,并且在被抓取的网页或中毒的依赖README中,凭证泄露成为可能。代理使用它创建的内容,没有人跟踪;星期二下午创建的分支数据库仍然在三月份计费。
平台团队目前只有一个答案:“不要这样做”。这个答案在已经使用代理的团队面前是站不住脚的。
托管是另一种答案。代理请求一个*能力*——“一个带有预备数据的 Postgres 数据库,持续两个小时”——通过一个输入的MCP工具。策略引擎进行决策。如果允许,工作器会提供实际资源,将凭证密封在加密信封中,并将引用交给代理:`bailment://binding/3f2a...`。代理永远不会看到凭证。当它需要对数据库运行某些操作时,它运行`bailment exec <lease> -- pytest`,并且值被解密到该子进程的环境中,而其他地方都没有。两个小时后,租期到期,数据库被删除。然后,协调器寻找任何幸存的东西。
托管的名称来源于法律术语。托管是指你将财产交给某人用于有限目的和有限时间,他们有义务将其归还。
---
## 快速开始,无需云账户
以下所有操作都针对内置的`memory`提供程序运行。无需 API 密钥,无需信用卡,无需注册。大约需要一分钟加上五分钟的时钟运行时间。
```bash
git clone https://github.com/AleBrito124356/bailment
cd bailment
docker compose up
```
或者不使用 Docker,使用 [uv](https://docs.astral.sh/uv/):
```bash
uv sync
export BAILMENT_ENCRYPTION_KEY=$(uv run bailment keygen)
export BAILMENT_API_TOKENS=agent:demo-agent-token
export BAILMENT_ADMIN_TOKENS=alice:demo-operator-token
uv run bailment db upgrade
uv run bailment serve
```
无论哪种方式,您现在都在 `http://localhost:8080` 上拥有一个代理。在第二个终端中,请求一个沙盒资源:
```bash
curl -s -X POST http://localhost:8080/api/v1/leases \
-H 'Authorization: Bearer demo-agent-token' \
-H 'Content-Type: application/json' \
-d '{"golden_path":"sandbox","inputs":{"name":"hello"},"ttl":"5m"}'
```
```json
{
"lease": {
"id": "b63df949-480b-49f6-99f4-6e09753f5d0e",
"golden_path": "sandbox",
"provider": "memory",
"state": "pending",
"requester": "agent",
"ttl_seconds": 300,
"max_ttl_seconds": 3600,
"policy_effect": "allow",
"policy_reason": "The sandbox provider creates nothing outside this process, so there is nothing to gate. Take one.",
"external_name": "bailment-sandbox-b63df949",
"binding_reference": null,
"usable": false
},
"replayed": false,
"ttl_clamped": false,
"notices": []
}
```
注意在仍为`pending`的租期中`external_name`。资源将要承担的名称在调用任何提供程序之前已提交到数据库。这个细节使得预先检测成为可能;参见[预先命名](#write-ahead-naming)。
几秒钟后,工作器已配置它:
```console
$ bailment lease list
┌──────────┬────────┬─────────────┬──────────┬───────────┬────────────┬─────────┬─────┐
│ id │ state │ golden path │ provider │ requester │ expires in │ created │ $/h │
├──────────┼────────┼─────────────┼──────────┼───────────┼────────────┼─────────┼─────┤
│ b63df949 │ active │ sandbox │ memory │ agent │ 4m36s │ 24s ago │ - │
└──────────┴────────┴─────────────┴──────────┴───────────┴────────────┴─────────┴─────┘
```
```console
$ bailment lease get b63df949
┌─ lease ─────────────────────────────────────────────────────────────────────────┐
│ id b63df949-480b-49f6-99f4-6e09753f5d0e │
│ state active │
│ golden path sandbox @ memory │
│ requester agent │
│ inputs {"name": "hello", "simulate": "ok"} │
│ created 2026-07-25T18:52:49+00:00 (25s ago) │
│ activated 2026-07-25T18:52:49+00:00 (25s ago) │
│ expires 2026-07-25T18:57:49+00:00 4m34s │
│ ttl 5m00s granted, 1h00m ceiling │
│ renewals 0/2 │
│ external name bailment-sandbox-b63df949 │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─ binding ───────────────────────────────────────────────────────────────────────┐
│ reference bailment://binding/c0ef5a88-5e23-401a-85bd-6e5fe1ed5bcb │
│ sealed SANDBOX_URL │
│ published SANDBOX_ID = bailment-sandbox-b63df949 │
│ use bailment exec b63df949 -- <command> │
└─────────────────────────────────────────────────────────────────────────────────┘
```
两者都来自同一个租期。`SANDBOX_ID`在 golden path 中声明为`secret: false`,因此会打印出来。`SANDBOX_URL`不是,因此您得到的是它的名称。没有标志可以将第二行变成第一行;要使用密封值,请将托管交付给需要它的命令:
```console
$ bailment exec b63df949 -- python -c "import os; print(os.environ['SANDBOX_URL'][:24] + '...')"
memory://bailment-sandbo...
```
这是整个系统中唯一一个将引用恢复为值的代码路径。它在操作员的机器上运行,将纯文本写入一个子进程的环境,并在审计日志中记录访问权限,然后子进程启动。如果您想在不解密任何内容的情况下查看将注入的内容,`bailment exec --dry-run`会打印变量名称并停止。
现在等待。在截止日期前一分钟,租期进入`EXPIRING`并发出通知。在零时,它过期,一个工作器调用提供者的销毁路径,资源消失:
```
[info] lease notice kind=expiring lease_id=88efb73b… seconds_remaining=29
message='lease 88efb73b… (sandbox) expires in 29s. Renew it if you still need it;
when it expires the resource is destroyed.'
[info] lease tick expired=['88efb73b…'] queued_for_teardown=['88efb73b…']
[info] lease released external_name=bailment-sandbox-88efb73b lease_id=88efb73b…
```
```console
$ bailment lease get 88efb73b
│ state released │
│ expires 2026-07-25T18:54:49+00:00 -2m13s │
│ released 2026-07-25T18:54:55+00:00 │
```
距截止日期六秒后,供应商确认资源已被销毁。这个间隔是`BAILMENT_LEASE_TICK_SECONDS`的分辨率,它是在给定部署中“租期过期”值得的诚实衡量标准。
最后,询问现实是否与数据库一致:
```console
$ bailment reconcile
┌────────────┬──────────────────────────────┬───────────┬────────┬─────────┬───────┬────────┐
│ provider │ checked │ resources │ leases │ orphans │ drift │ errors │
├────────────┼──────────────────────────────┼───────────┼────────┼─────────┼───────┼────────┤
│ cloudflare │ provider is not configured, │ 0 │ 0 │ 0 │ 0 │ 0 │
│ │ so it cannot be asked what │ │ │ │ │ │
│ │ exists │ │ │ │ │ │
│ memory │ yes │ 0 │ 0 │ 0 │ 0 │ 0 │
│ neon │ provider is not configured… │ 0 │ 0 │ 0 │ 0 │ 0 │
│ upstash │ provider is not configured… │ 0 │ 0 │ 0 │ 0 │ 0 │
└────────────┴──────────────────────────────┴───────────┴────────┴─────────┴───────┴────────┘
warning: not every provider could be swept, so this run says nothing about cloudflare, neon, upstash
```
在`orphans`列之前读取`checked`列。无法询问的提供者报告为零个孤儿,零不是答案。大多数漂移仪表盘不区分这一点,而一个干净的面板因为什么都没运行而看起来干净是这种表格旨在防止的故障模式。
尝试拒绝也是如此,您可以看到代理实际得到的结果:
```bash
curl -s -X POST http://localhost:8080/api/v1/leases \
-H 'Authorization: Bearer demo-agent-token' -H 'Content-Type: application/json' \
-d '{"golden_path":"dns-record","inputs":{"subdomain":"admin","target":"1.2.3.4","record_type":"A"}}'
```
```
"state": "rejected",
"policy_effect": "deny",
"policy_reason": "That subdomain is reserved. Names like www, api and admin are where this
organisation's real services live … Pick a name that says what your change is instead, such
as pr-1234, alice-checkout-fix or demo-2026-07: anything that is not on the reserved list is
self-service and takes about a second."
```
拒绝命名规则,解释原因,并说明请求替换内容。这不是礼貌。被告知“拒绝”的模型会尝试相同的请求并带有不同的参数,然后是不同的路径,然后是执行相同操作的外壳命令。关闭问题的拒绝是不会被绕过的拒绝。
---
## 它是如何工作的
```
agent (MCP) ──┐
human (web) ──┼──▶ golden path ──▶ policy ──▶ lease row ──▶ worker ──▶ provider
CI (OSB /v2) ──┘ (one YAML) (no eval) (named first) (claims) (Neon,
│ │ Upstash,
│ │ Cloudflare,
require_approval │ memory)
│ ▼
▼ binding sealed (Fernet)
human decides │
▼
agent receives bailment://binding/<uuid>
│
bailment exec ────┘──▶ subprocess env, nowhere else
ticker: ACTIVE → EXPIRING → EXPIRED → DEPROVISIONING → RELEASED
reconciler: provider reality ⇄ lease table, in both directions
```
1. **请求。** 代理调用`bailment_provision_postgres`通过MCP,或人类发布到`/api/v1/leases`,或CI系统执行`PUT /v2/service_instances/{id}`。所有三个请求都落在同一个服务层。
2. **策略。** golden path的规则链从上到下评估,第一个匹配项获胜,对固定上下文的普通数据进行评估。答案是`allow`、`deny`或`require_approval`。无法评估的规则拒绝。参见[docs/policy.md](docs/policy.md)。
3. **租期。** 写入具有确定性`external_name`的行,并在工作器看到它之前提交。TTL被限制在路径的天花板上,响应显示它,因此调用者了解其边界而不是猜测。
4. **配置。** 工作器采用具有条件声明的行,调用提供者,用Fernet密封返回的凭证,并将租期移至`ACTIVE`。时钟从这里开始,而不是在请求时间 - 等待批准的租期并未持有数据库三小时。
5. **拆除。** 记分器将租期移至`EXPIRING` → `EXPIRED`;工作器调用提供者的销毁路径,仅在提供者确认时写入`RELEASED`。
6. **协调。** 定期和按需,将提供者说存在的内容与租期表的内容进行比较。
### 预先命名
`Lease.external_name`在调用提供者之前计算并提交,从不从提供者的响应中分配。如果工作器在创建调用和响应之间死亡,则该行已经知道资源将要承担的名称,因此协调器可以去查找它。资源命名系统无法做到这一点:在此窗口中的崩溃留下了真实的、计费的资源,而世界上没有任何东西可以将它与请求关联起来。
### 协调器
两个方向,它们以完全不同的方式失败。
**孤儿。** 资源在提供者处存在,带有托管的标记,并且没有活动的租期对其进行核算。它正在运行,它正在计费,并且系统中没有任何东西会终止它。这些来自部分完成的配置、耗尽重试预算的拆除和在错误时刻被杀死的工作器。每个配置系统都会产生它们。
**消失的资源。** 我们认为租期为`ACTIVE`,但资源`GONE` - 通常被某人用控制台删除。租期现在是对不存在的事物的承诺,它保留一个可解析的绑定和一个配额槽,直到有人注意到。
报告始终是安全的。销毁是默认关闭的,需要两个开关:全局的`BAILMENT_RECONCILE_AUTO_DESTROY_ORPHANS`和显式的每个提供者允许列表,从终端意味着`--destroy`加上一个按名称列出的资源确认。小于十分钟的宽限期内不会标记任何内容,因为工作器创建了资源九十秒后尚未提交的内容看起来与孤儿完全一样。
而`UNKNOWN`不会改变任何内容 - 状态、时间戳或审计行。提供者超时告诉我们什么都没有,系统最危险的事情就是把沉默变成`RELEASED`。
---
## 为什么不直接使用 X
以下每个工具都比托管做得更好。这里介绍一下。
### HashiCorp Vault
Vault 是一个成熟、经过审计、经过实战测试的秘密平台,具有十几个身份验证后端、真正的 HSM 故事、传输加密、巨大的生态系统和十年的生产强化。其动态秘密引擎已经发出具有 TTL 的短期数据库凭证,并在到期时撤销它们,如果您需要*凭证*生命周期,Vault 在这方面比托管做得更好。使用 Vault。
差距在于 Vault 租用凭证*到*已经存在的资源,而该资源由其他人创建。 Vault 不会创建 Neon 分支,不会删除它,也不能告诉您存在一个分支,而没有任何人对其进行核算。它还将凭证值交给调用者,对于代理来说,这意味着该值进入上下文窗口。托管创建和销毁资源,从不返回值。
这两个工具可以很好地组合:Vault 作为托管的绑定存储下的秘密后端是一个合理的选择,并且今天尚未实现。
### Crossplane
Crossplane 的协调模型比托管的成熟度更高,其提供者生态系统大得多,其组合系统表达的基础设施关系是 flat golden path 无法实现的。如果您已经运行 Kubernetes 并希望使用声明式基础设施作为自定义资源,Crossplane 是正确的答案,而这不是一个接近的调用。
差距在于基板和接口。Crossplane 需要一个 Kubernetes 集群作为控制平面,对于“让代理拥有一个数据库两个小时”来说,这是一个很大的东西采用。对于 Kubernetes 集群来说,其接口是 `kubectl apply` 和 YAML 清单,而不是输入的工具和策略可以说“不是来自代理,不针对生产”。而 Crossplane Composite Resource 没有 TTL:它存在,直到有人删除它。
### Backstage
Backstage 是一个真正的软件目录,具有插件生态系统、服务所有权模型、TechDocs 和脚手架模板,这些比托管的 golden path 更强大。如果问题是“开发者无法找到任何东西,并且没有人知道存在什么”,Backstage 解决了更大的问题,而这个问题并不接近。
差距在于 Backstage 是一个门户 - 一个人类点击的地方,它运行起来非常重。它的脚手架会触发一个模板并且完成;没有租期,没有过期,没有拆除,也没有协调创建的内容。接口是一个网页,而代理无法调用。托管是一个有 UI 雄心的经纪人。
### Terraform(或OpenTofu)在CI中的应用
Terraform是描述基础设施的最佳工具,其供应商覆盖范围无与伦比,其状态模型、计划输出和漂移检测功能针对声明状态而言确实出色。对于任何永久性基础设施,请使用Terraform。
问题在于,Terraform运行是流水线中的一步骤。它没有TTL(生存时间):资源会一直存在,直到另一次运行销毁它,而本应运行的`destroy`作业因流水线被取消而被跳过。没有面向代理的界面——可以运行`terraform apply`的代理拥有您的整个云,这重述了问题而不是解决问题。`terraform plan`检测漂移是针对声明状态的,而声明状态由于构造原因无法看到在状态文件之外创建的资源。因应用崩溃而产生的孤儿资源正是它无法向您显示的。
### 那么剩下什么
| | 凭证代理 | 创建资源 | TTL + 自动关闭 | 查找未跟踪资源 | 代理可调用界面 |
|---|---|---|---|---|---|
| Vault | **是,优秀** | 否 | 仅限凭证 | 否 | 否 |
| Crossplane | 通过提供商 | **是** | 否 | 漂移 vs 声明状态 | 否 |
| Backstage | 否 | 通过模板 | 否 | 否 | 否 |
| CI中的Terraform | 否 | **是** | 否 | 漂移 vs 状态文件 | 否 |
| bailment | 是,通过引用 | 是 | 是 | **双向** | **是(MCP)** |
最后两列是此项目存在的原因。其他工具都不具备经纪资源的生命周期协调功能,也没有假设调用者是语言模型(可能在五分钟前被提示注入)而进行设计。
---
## 配置
每个设置都是以`BAILMENT_`为前缀的环境变量,或工作目录中的`.env`文件中的一行。参见[.env.example](.env.example)获取注释完整列表。
首日最重要的三个设置:
```bash
BAILMENT_ENCRYPTION_KEY= # 从`bailment keygen`生成。必须用于密封或打开绑定。
BAILMENT_API_TOKENS=agent:... # 可以请求和读取租期的主体:令牌对。
BAILMENT_ADMIN_TOKENS=alice:... # 还可以额外批准和吊销的主体:令牌对。
```
bailment不会在运行时为您生成加密密钥。单独出现的密钥是在重启时更改的密钥,每个绑定在重启前写入的密钥都变得永久不可读——静默地,您在事件发生时发现,而不是之前。将其移动到`BAILMENT_PREVIOUS_ENCRYPTION_KEYS`,它只用于解密,让旧租期耗尽。
未识别的`BAILMENT_*`变量将被忽略,并在启动时以警告级别报告。直接拒绝它们会将Compose文件中的一个错误变量变成崩溃循环;沉默地忽略它们会将`BAILMENT_NEON_KEY`(注意缺少的`_API`)变成无法解释的“提供商不可用”,这会让某人损失一个下午。
存储默认使用SQLite,以便克隆存储库并运行沙盒路径时不需要数据库。 将`BAILMENT_DATABASE_URL`指向`postgresql+asyncpg://...`以适用于多个工作线程。
---
## MCP客户端设置
bailment在`/mcp`上支持可流式HTTP MCP。对于Claude Code:
```json
{
"mcpServers": {
"bailment": {
"type": "http",
"url": "http://localhost:8080/mcp",
"headers": {
"Authorization": "Bearer demo-agent-token",
"X-Bailment-On-Behalf-Of": "alice@example.com"
}
}
}
}
```
将其放入项目根目录的`.mcp.json`中,或使用`claude mcp add --transport http bailment http://localhost:8080/mcp`添加。
代理看到的工具从目录中生成,每个黄金路径一个预置工具,加上五个生命周期工具:
```
bailment_provision_sandbox bailment_lease_status
bailment_provision_postgres bailment_list_leases
bailment_provision_redis bailment_renew_lease
bailment_provision_dns_record bailment_release_lease
bailment_catalog
```
没有MCP工具在任何配置下返回凭证值。调用者构造为`AGENT`,无论呈现哪个令牌,`AGENT`是`resolve_binding`和`approve`拒绝的种类——因此,通过MCP呈现的管理令牌仍然无法读取秘密或批准其自己的请求。安全边界是界面,而不是特权。
`X-Bailment-On-Behalf-Of`是一个声明,而不是身份。它被记录下来,以便审计日志可以分别回答“哪个代理执行了此操作”和“谁对此负责”这两个问题。它未经身份验证,不授予任何权限;永远不要编写根据它赋予权限的策略规则。
更多信息请参见[docs/mcp.md](docs/mcp.md),包括工具结果告诉模型的内容以及原因。
---
## 编写黄金路径
黄金路径是一个YAML文件。 bailment从中派生出MCP工具、仪表板表单、OSB目录条目、策略链、租期上限和绑定形状——没有任何地方有第二个定义。 这不是整洁: 如果代理面向的目录和人类面向的目录可能漂移,它们就会漂移,而第一次漂移时,代理获得人类从未显示的能力。
```yaml
id: postgres
name: Postgres数据库(Neon分支)
provider: neon
tags: [database, postgres, neon]
description: |-
专用的Postgres数据库,作为团队Neon项目的分支而创建...
用它来演练迁移或重现错误。不要将部署的服务指向它:
连接字符串在租期过期时停止工作。
inputs: # JSON模式,原样用作MCP工具的输入模式
type: object
additionalProperties: false
required: [env, name]
properties:
env: { type: string, enum: [dev, staging, prod] }
name: { type: string, pattern: "^[a-z][a-z0-9-]{1,38}[a-z0-9]$" }
lease:
default_ttl: "4h"
max_ttl: "72h"
warn_before: "30m"
renewable: true
max_renewals: 3
policy:
- when: input.env == "prod"
effect: require_approval
reason: >-
将生产副本分支到数据库会将真实的客户数据转移到数据库中,
代理将交给代理,因此平台团队成员必须同意。
- effect: allow
reason: 开发和预发布分支在标准支出内是自助服务。
cost:
estimated_hourly_usd: 0.14
estimated_monthly_usd: 102.20
outputs:
- name: DATABASE_URL
secret: true # 密封,永远不会按值返回
```
将`description`写成对有能力的陌生人的指示:它提供了什么,它有什么成本,以及何时不使用它。模型将其作为工具描述读取并据此操作。
将文件放入`BAILMENT_CATALOG_DIR`并在发货前检查:
```bash
bailment catalog validate
bailment catalog list
```
验证命名文件和每个失败的字段,警告可能合法但几乎肯定错误的内容——路径的提供程序已注册但未配置,两个ID折叠为相同的MCP工具名称,路径声明的输出为零。 `--strict`在部署管道中将警告转换为失败,这是正确的,但在CI中是错误的,因为CI中没有提供程序。
---
## 编写策略
规则从上到下评估,第一个匹配的规则获胜。最后一个规则必须是无条件的,因此每个请求都会有决定。
```yaml
policy:
- when: input.env == "prod" and input.eviction == true
effect: deny
reason: >-
生产环境下的Redis清空会将整个数据库变成沉默的数据丢失…
- when: is_agent and env == "prod"
effect: require_approval
reason: 在自主进程触及生产环境之前,由人工决策。
approvers: [platform-team]
- when: ttl_seconds > 129600 or estimated_hourly_cost_usd > 0.20
effect: require_approval
reason: 这比为单个分支预先批准的支出时间更长…
- effect: allow
reason: 开发和预发布环境在标准支出内是自助服务。
```
**在策略路径中没有`eval`、`exec`和第三方表达式引擎。** 表达式被解析为AST,并由只能访问调用者显式放入上下文中的值的树遍历器解释。 `getattr`永远不会被调用:`input.env`是`input["env"]`的语法糖,属性访问在不是映射的对象上是一个错误,而不是回退。 这条规则使沙盒保持——使用`getattr`,每个值都成为其类型、模块和解释器的门户。
算术运算直接被拒绝。 `2 ** 999999999`是一个用四个字符写的挂起,规则可以与经纪人预先计算的值进行比较。
**无法评估的规则会拒绝整个请求。** 策略链被编写为收窄的拒绝,以allow结尾,因此跳过损坏的规则会将拒绝变成允许,静默地,在操作员最不愿意查看的时刻。 大声且错误比安静且宽容更好。
`when`可以引用的上下文:`input`、`env`、`requester`、`on_behalf_of`、`is_agent`、`golden_path`、`ttl_seconds`、`estimated_monthly_cost_usd`、`estimated_hourly_cost_usd`、`active_leases_for_requester`、`hour_utc`、`weekday`。完整的引用和完整的语法在[docs/policy.md](docs/policy.md)中。
---
## 安全模型
设计假设代理可能变成敌对的。 不是因为代理是恶意的,而是因为读取网页、依赖README或GitHub问题的代理是可能被提示注入的代理,之后它就变成了拥有您的凭证和权限的攻击者。 所以有趣的问题不是“这个代理是否可信”——而是“如果这个请求在代理当前被其他人控制时能做什么”。
- **机密值永远不会跨越MCP边界。** 不在工具结果中,不在错误消息中,不在标志后面。 `resolve_binding`是解密的唯一函数,它拒绝`AGENT`和`SYSTEM`调用者基于调用者*类型*而不是范围字符串,并且REST API在导入时证明在其路径中没有解密处理程序。
- **值在静止时使用Fernet加密(AES-128-CBC + HMAC-SHA256),** 密封为每个绑定的一个信封,因此URL及其令牌被一起撤销和轮换。
- **批准门不能被它们控制的东西打开。** `AGENT`调用者被`approve`拒绝,而匿名访问映射到较低层级,而不是较高层级。
- **爆炸半径受时间限制。** 即使是完全受损的代理也拥有一个能力,该能力过期,针对一个人类选择的资源,该资源将被销毁,在一个人类选择的路径上。
- **每个决策都是可审计的,** 记录了“哪个代理”和“谁对此负责”作为单独的字段,因为在您需要审计日志的事件中,混淆它们是无用的。
bailment **不保护** 代理在租期内合法授予其的能力被滥用; 任何具有shell访问broker主机的人,他们拥有数据库和加密密钥; 或敌对平台工程师,因为策略文件是受信任的输入。 完整的威胁模型在[SECURITY.md](SECURITY.md)。
---
## 项目状态
**Alpha.** 版本0.1.0。 说得对:
- 引擎、策略评估器、状态机、协调器、MCP服务器、OSB接口和CLI已完成,并涵盖了**827个测试**,这些测试在二十秒内运行,无需云账户和网络。 `mypy --strict`在整个包中保持清洁。
- `memory`提供程序已完全实现,包括需要故意注入故障以有意产生双向漂移的功能。 Neon、Upstash和Cloudflare提供程序已针对其真实API实现,但与测试套件可以触及的部分相比,其实际世界暴露程度要低得多。
- **没有Alembic迁移。** `bailment db upgrade`直接在SQLite上创建模式,并拒绝在Postgres上猜测,这是正确的拒绝,但意味着Postgres部署目前需要手动运行`Base.metadata.create_all`。 这是在有人认真运行它之前要修复的第一件事。
- 沙盒路径为按需生成故障记录了一个`simulate`输入; `memory`提供程序尚未支持,因此`tests/test_reconciler.py`中的ORPHANED演示目前比命令行更活跃。
如果您正在评估它:运行quickstart,阅读 [`src/bailment/states.py`](src/bailment/states.py) 和 [`src/bailment/policy/evaluator.py`](src/bailment/policy/evaluator.py),并根据这些进行评估。
---
## 文档
| | |
|---|---|
| [docs/architecture.md](docs/architecture.md) | 组件、请求路径和影响设计的决策 |
| [docs/policy.md](docs/policy.md) | 表达式语言、上下文和如何编写有效的规则 |
| [docs/providers.md](docs/providers.md) | 提供者合同、四个内置提供者和如何编写第五个提供者 |
| [docs/mcp.md](docs/mcp.md) | 代理接口:工具、结果、会话和拒绝 |
| [SECURITY.md](SECURITY.md) | 威胁模型,保护和不保护的内容,以及如何报告漏洞 |
| [CONTRIBUTING.md](CONTRIBUTING.md) | 如何运行套件以及这里的良好变更是什么样子 |
---
## 贡献
欢迎贡献,特别是新的提供者和任何将此指向实时云账户的人的真实世界报告。
```bash
uv sync
uv run pytest
uv run ruff check .
uv run mypy
```
测试套件不需要凭证,也不涉及网络。 阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 在打开拉取请求之前——这里的房子风格是有意的,关于模块文档和什么不能出现在响应体中的部分值得阅读两次。
---
## 血统
这里的租赁和代理形状是 [**Open Service Broker API**](https://www.openservicebrokerapi.org/) 的故意实现,一个发布的Apache-2.0规范,用于通过标准HTTP接口配置和绑定服务。 其Kubernetes实现,即`service-catalog`项目,于2022年存档,这就是为什么人们经常认为该规范随着它而死亡。 它没有: 规范在v2.15及以上版本仍然有效, 任何曾经编写过代理的人在其存储库中都有一个客户端。
bailment在`/v2`实现了OSB v2.16(参见[docs/architecture.md](docs/architecture.md)了解它故意偏离的三个地方),并添加了规范从未涵盖的两项内容: 面向代理的MCP接口, 以及经纪人创建的资源的生命周期协调。
---
## 许可证
Apache License 2.0。 见[LICENSE](LICENSE)和[NOTICE](NOTICE)。
版权所有 2026 Alejandro Brito Olivera。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器