Skip to content

Repository files navigation

Valet

Agents need access. Not your keys.

An authorization representative for AI agents. A worker requests an exact operation; the operator approves it; Valet uses an upstream credential and returns the result with a receipt. HTTP, CLI, JSONL, and MCP clients share one authority model.

Product page · Security boundary · Product contract · Deployment

Vertical slice

  • Separate worker/operator authentication, strict request schemas and operator-configured ceilings.
  • Interactive deny, allow-once, and remembered exact-scope approvals.
  • Opaque one-use leases, expiry, persistent revocation and worker disablement.
  • SQLite transactions for approvals, dispatch reservations and receipts; interrupted operations become unknown after restart rather than being replayed.
  • GitHub file reads, task-branch creation, bounded file writes and draft PR creation. No workflow editing, default-branch writes, force push, merge or repository administration.
  • GitHub App installation-token minting with explicit repository/permission restrictions and post-operation revocation; configured credential proxy fallback.
  • Separately approved exact-SHA deployments through Deploy Manager, with branch-tip/CI checks and signed acceptance/terminal receipt handling.
  • Standard MCP stdio initialization, discovery and tool calls through the official SDK.

This is an early developer slice, not a hosted credential service. A public page is not a public broker. Fixture tests do not establish live third-party integrations; consult verification for recorded evidence.

Run the safe local rehearsal

Requires Node.js 24+ (uses Node's experimental built-in SQLite API).

git clone https://github.com/YesterdaysLemon/valet.git
cd valet
npm ci
npm test
npm run demo

The demo runs the real broker against synthetic GitHub/Deploy Manager responses. It uses no accounts and performs no real external mutation. npm run page serves the public page on http://127.0.0.1:4320.

Configure a real broker

Run these steps in the operator environment, outside the worker's control. An unrestricted worker sharing the operator's OS identity can bypass file/process isolation. Give it only its own client file, never this directory, provider session, or Docker socket.

Look up the immutable GitHub repository ID and initialize a new operator-owned directory:

node src/cli.mjs init --repository you/project --repository-id 123456 --out /operator/valet

The command refuses existing output directories and writes three files without printing token values:

File Owner/purpose
broker.json Broker policy, hashed enrollment credentials, provider references
operator-client.json Operator approval credential; never share with a worker
worker-client.json Only file provisioned to the enrolled worker

The default ceiling permits README and docs/ operations on valet/ task branches, plus reads from the configured default branch. It creates no standing grant and allows no deployments. Review the generated config. Worker identity worker is authenticated, not inferred from the harness name.

Start the daemon using operator-owned absolute paths. Environment examples below are PowerShell; use equivalent exports on Linux:

$env:VALET_CONFIG='/operator/valet/broker.json'
$env:VALET_STATE='/operator/valet/state.db'
node src/server.mjs

It binds only 127.0.0.1:4318. For a dedicated broker host, connect through a constrained SSH tunnel. Both client and server intentionally reject casual public/browser access. The public page deployment is entirely separate.

Credential sources

The generated provider uses env://VALET_GITHUB_TOKEN. Set that variable only in the broker's environment with a dedicated, minimally privileged credential. Alternatively configure file:// for an operator-controlled file, op:// for 1Password, bw:// for Bitwarden, pass://, aws-sm://, or doppler://. External CLI adapters need their binaries and dedicated sessions installed inside the broker environment; they are not bundled in the Docker image.

Prefer a GitHub App installed on the required repository. Replace the repository's provider object with:

{
  "mode": "app",
  "appId": "12345",
  "installationId": 67890,
  "privateKeyRef": "file:///operator/valet/github-app.pem"
}

The App requires repository contents permission for file/branch operations, pull requests write for draft PRs, and actions read for deployment evidence. Valet mints only the needed permissions for each operation and keeps the token behind the broker. Neither the App key nor the installation token goes to the worker. A failed upstream token cleanup is recorded; GitHub's expiry still bounds it. The broker enforces its shorter local expiry.

Ask, approve, run

In the worker environment, set VALET_CLIENT to its provisioned client file. Copy and edit an example under config/, then:

node src/cli.mjs request config/request.read.example.json
node src/cli.mjs status REQUEST_ID

In a separate operator terminal, set VALET_CLIENT to operator-client.json and run:

node src/cli.mjs approvals

Review the full operation, its hash, exact scope, intent, worker and expiry. Choose deny, once, or always this scope. Always authorizes future content/prose in the displayed scope for seven days; it never allows production deployment. It can be revoked through grants and revoke-grant.

Back in the worker environment:

node src/cli.mjs run REQUEST_ID
node src/cli.mjs receipts
node src/cli.mjs revoke ANOTHER_REQUEST_ID

The run command claims and consumes one opaque lease. It cannot modify the approved request. Replays fail. If a network failure leaves the outcome unknown, reconcile upstream before requesting another mutation.

Supported operations

All requests are { "operation": ..., "intent": "...", "ttlSeconds": 300 }.

Kind Operation fields
github.read repository, branch, path
github.branch repository, branch (new task branch from configured default)
github.write repository, branch, path, content, message, optional expectedSha
github.pr repository, branch, title, body (draft PR to configured default)
deploy app, sha (exact configured application and full commit)

Existing file updates require its expected blob SHA; new files require absence. File writes use a pinned base tree and non-forced ref update. Paths reject traversal, symlinks, submodules and workflow edits. Repository transfers/default-branch changes fail closed until the operator reviews configuration.

Deploy Manager

Add a deployment mapping and its ID to the worker's deployApps ceiling:

{
  "id": "your-app",
  "repository": "you/project",
  "branch": "main",
  "origin": "https://deploy.example.com",
  "secretRef": "file:///operator/valet/your-app-webhook-secret",
  "workflow": "ci.yml"
}

The repository must also exist in broker config, with matching default branch. The configured workflow must have a successful completed push run for the exact branch-tip SHA. Deploy approval is always once and cannot be inferred from repository access. The broker constructs and signs the fixed manager request and records the job ID. Then:

node src/cli.mjs deployment REQUEST_ID

This follows the trusted receipt for up to 20 minutes and exits unsuccessfully unless the terminal state is succeeded. Already accepted deployments cannot be recalled with lease revocation.

MCP and other harnesses

Configure your harness's standard stdio MCP entry with an absolute checkout path:

{
  "mcpServers": {
    "valet": {
      "command": "node",
      "args": ["/path/to/valet/src/mcp.mjs"],
      "env": { "VALET_CLIENT": "/worker/valet-client.json" }
    }
  }
}

Use the equivalent configuration shape in Codex, Claude Code, Pi, OMP, Hermes, or another MCP host. This example is a common host convention, not a claim that every harness uses this JSON layout. MCP exposes request/status/execute/revoke/receipts/deployment-status tools. It exposes no approval, enrollment, admin, generic-fetch, or secret-resolution tool. CLI/JSONL/HTTP are alternatives for harnesses without MCP.

Tests and deployment

npm test covers API boundary, authorization lifecycle, GitHub App minting, deployment signing and MCP. npm run check syntax-checks the JavaScript. CI also builds the Docker image and runs a separate-UID adversarial worker test in a disposable Linux container with no external network. Browser QA checks the public page's illustrative controls at desktop and mobile sizes.

The public page uses Cloudflare static assets via npm run deploy:page. It has no credential bindings and serves no broker API. The Dockerfile packages only the private broker. See deployment notes before running it.

Keysquire was the prototype name; the preserved initial commit records its inspection. Valet is the chosen product name. No trademark or package-name exclusivity is claimed.

About

An authorization representative for AI agents. Agents need access, not keys.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages