Skip to content

feat(hub): dedicated agent kbs with credentials bound to one kb - #742

Open
minion1227 wants to merge 1 commit into
vouchdev:testfrom
minion1227:feat/agent-kbs
Open

feat(hub): dedicated agent kbs with credentials bound to one kb#742
minion1227 wants to merge 1 commit into
vouchdev:testfrom
minion1227:feat/agent-kbs

Conversation

@minion1227

Copy link
Copy Markdown
Contributor

closes #609.

What changed

vouch kb create <name> --for-agent <agent> provisions an isolated KB outside any project tree, registers it in the machine registry with an owner and the agent it serves, and issues a credential bound to that KB and only that KB — printed once, never persisted. vouch kb issue is the rotation path; vouch kb list shows what exists.

The binding is the half that makes this more than vouch init in a subdirectory. It lives in a new module, kb_binding.py, and is enforced at the existing transport chokepoint (agents.subject_is_active) so every token transport inherits it without a second implementation.

Why

A scheduled agent — a PR triager in CI, an incident summariser — produces a lot of narrow, machine-shaped memory. Mixing it into the project KB buries the knowledge a human curated. The issue's framing is exact: you want it to remember things across runs, but you don't want a thousand machine-written memories mixed into your personal knowledge graph.

Isolation of storage alone is not enough. Which KB an agent hits is otherwise a function of its working directory, because discovery walks upward — so a leaked CI secret reaches the project KB from the wrong cwd. Binding fixes that: a bound subject authenticates against its own KB and fails closed everywhere else, regardless of working directory.

Three things follow, and they are the design:

The binding is keyed on the subject, never the secret. Registering a binding does not require storing, echoing, or even seeing the credential again — the same split agents.py already draws and the reason the agent registry can live in committed YAML. The secret is printed once; what lands on disk is the 16-hex subject trust.py already derives.

It lives machine-local, not inside a KB. A binding stored in the KB it names is circular: the project KB's copy has nothing to say about a token issued for another KB, so presenting that token there would still fall open. The question "which KB may this subject reach" is machine-scoped, so the answer has to be too — ~/.config/vouch/credentials.yaml, 0600, a sibling of the hub registry. Same trust level as the bearer accept-list it guards: anyone who can rewrite this file can already rewrite serve.bearer_tokens.

Unbound subjects are unaffected. A token with no binding authenticates exactly as before, against whatever KB it is presented to. Binding is opt-in, not a migration — the same back-compat rule agents.is_active follows. A missing or corrupt binding file denies nobody: an unreadable file must not lock a fleet out of a running server. A bound subject fails closed against a KB whose kb.id was stripped, so removing an identity is not an escape hatch.

Ownership on the registry row is routing metadata, and only that. It records whose KB this is so vouch kb list can say so and so ambient capture knows a machine-owned KB when it sees one. What a credential may actually reach is the separate, enforced question above. Both owner and agent are sticky across a plain vouch hub register, so a re-register cannot launder an agent KB into a human-owned one, and both are omitted when empty so existing rows read back unchanged. Ambient capture refuses to land in an agent KB reached by upward discovery — the same guard the personal catch-all already gets.

What might break

Honest list:

  • Nothing for an existing deployment. Unbound tokens keep working. No config change is required. No migration runs.
  • A bound credential presented to the wrong KB is refused. That is the whole point. If an operator reuses a CI secret against the project KB, it will now fail where it used to succeed — and that is the failure mode this exists to create.
  • A VOUCH_KB_PATH that points at a KB whose kb.id was stripped refuses every bound subject. Removing the identity of a KB does not become an escape hatch from the binding; unbound subjects still authenticate against it as before.
  • Ambient capture from a project directory refuses to write into an agent KB it reaches by walking up. Set VOUCH_KB_PATH when you mean it deliberately — same remedy as for the personal catch-all.

How it was tested

42 tests in tests/test_kb_binding.py, plus the existing hub / agents / trust / capabilities suites still green. Diff coverage is 100% of the 251 changed Python lines under src/vouch/. The isolation property is pinned end-to-end: a bound subject authenticates against its own KB from its own cwd, is refused against the project KB from the project cwd, and an unbound subject is untouched by any of it. The "token never reaches disk" assertion from test_agents is reused for the binding file and for the KB's own agents.yaml. CLI domain errors render as Error: …, never a traceback. make check (ruff, mypy, pytest) green locally.

VEP

Not filed. This adds no new kb.* method and changes no existing method signature — provisioning a KB is a human/operator act, CLI-only, following the agents / hub precedent. The binding is enforced at an existing internal chokepoint rather than exposed as a surface. Happy to file one if a maintainer wants the binding shape locked as a protocol decision.

Screenshots

N/A — CLI + transport gate, no UI.

a scheduled agent — a pr triager in ci, an incident summariser — produces
a lot of narrow, machine-shaped memory, and mixing it into the project kb
buries the knowledge a human curated. `vouch kb create <name> --for-agent
<agent>` provisions an isolated kb outside any project tree, registers it
with an owner and the agent it serves, and issues a credential bound to
that kb and only that kb.

the binding is the part that makes this worth more than `vouch init` in a
subdirectory. which kb an agent hits is otherwise a function of its
working directory, because discovery walks upward — so a leaked ci secret
reaches the project kb from the wrong cwd. binding fixes that at the
existing transport chokepoint (`agents.subject_is_active`), which already
funnels every token transport through one gate.

it lives machine-local in ~/.config/vouch/credentials.yaml, 0600, keyed on
the 16-hex subject `trust.py` already derives — never the secret, which is
printed once and never persisted. a binding stored inside the kb it names
would be circular: the project kb's copy has nothing to say about a token
issued for another kb, so presenting that token there would still fall
open. the question is machine-scoped, so the answer has to be too.

unbound subjects keep working everywhere, so this is opt-in rather than a
migration — the same back-compat rule `agents.is_active` follows. a bound
one fails closed everywhere but its own kb, including against a kb whose
`kb.id` was stripped, so removing an identity is not an escape hatch. a
missing or corrupt binding file denies nobody: an unreadable file must not
lock a fleet out of a running server.

the registry gains owner and agent. both are sticky across a plain
re-register, so `vouch hub register` cannot launder an agent kb into a
human-owned one, and both are omitted when empty so existing rows read
back unchanged. ambient capture refuses to land in an agent kb reached by
upward discovery — the same guard the personal catch-all already gets.

`vouch kb issue` is the rotation path, `vouch kb list` shows what exists,
`vouch agents revoke` still retires a credential, and `vouch adopt` still
promotes anything worth keeping into the project kb.

Co-authored-by: Cursor <cursoragent@cursor.com>
@minion1227
minion1227 requested a review from plind-junior as a code owner July 31, 2026 20:12
@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance cli command line interface tests tests and fixtures size: XL 1000 or more changed non-doc lines labels Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli command line interface docs documentation, specs, examples, and repo guidance size: XL 1000 or more changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(hub): dedicated agent kbs — vouch kb create --for-agent

1 participant