VaultysClaw
CLAUDE.md84 starsChanged 6 months agoMITView on GitHub
VaultysClaw is an agent identity and trust platform. A control plane (Next.js + WebSocket server) maintains an append-only ledger of signed capability certificates bound to VaultysId DIDs, and decides what every Actor — agent, device, sensor, or human — is allowed to do. Clients connect over WebSocket, prove their identity with a Challenger handshake, and receive certificates they can verify offline. Monorepo: pnpm workspaces + Turborepo. Outside the pnpm workspace:
- Reads credentials
- Installs packages
In this repository3 other files it gives its agents.
- .github/copilot-instructions.mdCopilot instructions
- add-api-routeSkill
- add-ws-messageSkill
The file
We split this CLAUDE.md into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 209
packages/policyandpackages/truststay pure: No Prisma, Next.js, or WebSocket coupling.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
VaultysClaw is an agent identity and trust platform. A control plane (Next.js + WebSocket server) maintains an append-only ledger of signed capability certificates bound to VaultysId DIDs, and decides what every Actor — agent, device, sensor, or human — is allowed to do. Clients connect over WebSocket, prove their identity with a Challenger handshake, and receive certificates they can verify offline.
Monorepo: pnpm workspaces + Turborepo.
| Package | Description | CLAUDE.md |
packages/policy | Certificate wire format (sign/verify/pack), AgentCapability, CertScope, resource limits, runtime enforcement gates. Pure, no I/O. | → |
packages/trust | Trust ledger engine: ABAC/multi-certificate permission resolution (resolvePermission) over the CapabilityCertificate ledger. Pure, no I/O. | → |
packages/shared | The webhook event catalog + the LLM provider union. Deliberately tiny. | → |
packages/controlplane | The control plane: Next.js App Router admin console + WebSocket server, Prisma/Postgres, the certificate ledger. | → |
packages/sdk | TypeScript client for the control plane's protocol — handshake, capability state, certificate-status refresh, resolvePermission. The counterpart of sdk-go/. | → |
packages/webhook-dispatcher | Standalone worker: consumes events from BullMQ, signs them (HMAC) and POSTs to endpoints; also fans out to Apprise notification channels. | → |
packages/videos | Narrated demo videos of the console: Playwright drives the real control plane on its own stack (docker/video.env, 3013 / ws 8093 / pg 5436) with a live simulated fleet, then a Remotion intro/outro + ffmpeg mix. Same pipeline as VaultysHub's. | → |
packages/simulator | Fleet simulator: thousands of real-VaultysId Actors — plus the people who own them — driven at a live control plane, on its own isolated stack. | → |
Outside the pnpm workspace:
| Directory | Description |
sdk-go/ | Go client SDK — authz (a port of resolvePermission), grant (offline packcert verification), rules (signed rule sets). |
vaultysclaw-sensor/ | Go workload sensor: detects local AI/agent processes and reports classified telemetry to the control plane. Also hosts the tier-1 interception proxy. What it recognises as AI is data, not code: the built-in rules are internal/config/default-catalog.yaml (embedded), extended by a hot-reloadable operator catalog. Add a newly-released harness to a catalog file, never to the detector — see vaultysclaw-sensor/docs/DETECTION_CATALOG.md. |
conformance/ | The TS↔Go contract. permission-vectors.json (37 cases) is run by both packages/trust and sdk-go/authz; capability-names.json (31 cases) by both packages/policy and sdk-go/capability; grant-fixture.json, rules-fixture.json and kindconfig-fixture.json are TypeScript-signed fixtures the Go side verifies. Never change a fixture without re-running both suites. |
docs-site/ | Docusaurus documentation site. |
Commands
# Development
pnpm install
pnpm controlplane:dev # docker up --wait + control plane dev server
pnpm controlplane:docker:up # just its docker stack (postgres 5433 / redis 6381 / apprise 8000)
pnpm controlplane:docker:down
pnpm controlplane:webhook:dev # webhook dispatcher against the controlplane schema (own terminal)
pnpm sensor:start # the Go sensor against a local collector
# Fleet simulator — its own isolated stack (postgres 5434 / control plane 3003, ws 8083), never
# your dev database. docker/simulator.env is the single source of truth for its coordinates.
pnpm simulator:up # database + migrations + build + control plane
pnpm simulator:demo # 2,000 estate + 5,000 agent Actors against it, owned by 1,400 people
pnpm simulator stats # what that control plane currently holds
pnpm simulator people # (re)mint the human population and reassign agent ownership
pnpm simulator admin --passphrase "…" # mint an admin human + export its VaultysID for the browser
pnpm demo:ecw # ECW demonstrator: drone fleet + drone console at localhost:3012
pnpm simulator:down # stop, keeping the data ( :nuke also deletes the volume )
# Demo videos — their own stack again (docker/video.env), see packages/videos/CLAUDE.md
pnpm video:up # database + migrations + build
pnpm generate:videos # → packages/videos/.generated-videos/<name>.<lang>.mp4 + .srt
# Build / quality
pnpm build # all packages via Turborepo
pnpm lint
pnpm type-check
pnpm format
# Testing — supported suites are per-package; root __tests__ contains excluded legacy tests
pnpm test # turbo run test, all packages
pnpm --filter @vaultysclaw/policy test
pnpm --filter @vaultysclaw/trust test # includes the conformance vectors
pnpm --filter @vaultysclaw/controlplane test
pnpm --filter @vaultysclaw/webhook-dispatcher test
cd sdk-go && go test ./... # the Go half of conformance
cd vaultysclaw-sensor && go test ./...The dispatcher must point at the same Postgres the control plane writes to, not just one with
the same schema — otherwise jobs are consumed and every event reports zero targets while the
console shows an active subscription. controlplane:webhook:dev sources
packages/controlplane/.env for exactly this reason; don't write that URL down a second time.
packages/webhook-dispatcher has no Prisma schema of its own — run
pnpm controlplane:webhook:prisma (or the controlplane:webhook:dev script, which does it for
you) to copy the control plane's schema in and generate a client, or its type-check and tests fail
on a missing PrismaClient export.
Communication Protocol
Clients connect to the control plane via WebSocket. Messages follow the typed envelope in
packages/controlplane/lib/protocol.ts — deliberately small: identity, registration, the
certificate/status protocol, actor_config, and sensor_telemetry.
Actor lifecycle:
- Client connects →
register→ VaultysId Challenger handshake (auth_challenge) - Unknown DID →
PendingRegistrationrow +registration_pending; known DID →auth_complete - Admin approves, choosing capabilities → the control plane starts a second, independent
service: "certificate"Challenger exchange (cert_challenge) →cert_issued - A client re-checks its grant with
cert_status_request→ a control-plane-signedcert_status_responseit can verify, cache, or staple - Kind-specific config (and signed policy artefacts) arrive as
actor_config
Certificates are signed artefacts, not session state: packages/policy verifies them with no
network and no database. See docs/CERTIFICATE_WEB_OF_TRUST.md.
Capabilities
A capability is either a built-in (a closed list in packages/policy/src/types.ts) or an
admin-defined custom vendor:action name from the control plane's registry
(docs/CUSTOM_CAPABILITIES.md). Both travel through identical machinery — the same certificates,
CertScope scoping, expiry, revocation and resolvePermission.
Three things to know before touching this area:
AgentCapabilityis no longer a closed union, so exhaustiveness checking does not apply. AswitchorRecord<AgentCapability, …>over it silently stops being checked instead of failing to compile — always give such a map an explicit fallback.- Never validate a capability name by hand.
assertValidCapabilityName/isCustomCapabilityinpackages/policyare the only grammar, mirrored insdk-go/capabilityand pinned byconformance/capability-names.json. Changing the grammar means changing both and the table. - Deleting a registry entry is a mass revoke.
handleCertStatusRequestfilters held custom capabilities against the live registry before signing acert_status_response, so a deleted name stops resolving on every holder's next refresh. That signed response — not the stored certificate row — is what a client keeps.
Kill switches
The reversible emergency control, alongside (not instead of) revocation: an admin can suspend every grant org-wide or per-workspace and put it all back with no certificate re-issuance.
- Nothing is written to the certificate ledger. Suspension is computed from a separate
KillSwitchrow at the moment a decision is made —packages/controlplane/lib/kill-switch.tsis the single predicate, and every enforcement point calls it. Revocation stays the one-way ledger write it always was. CertificateStatusis still the closed four-value union. A suspended certificate is signed asrevokedwhile its row staysactive. No new state meanspackages/policy,packages/trust,sdk-go/and theconformance/fixtures are untouched by this feature — if a change here starts wanting a fifth status, reconsider the change rather than the fixtures.- Humans are always exempt.
admin_console_accessis itself a certificate capability, so a switch that covered humans would lock every admin out of the only UI that can disarm it.
See packages/controlplane/CLAUDE.md → "Kill switches" for the four enforcement points and the
sensor-telemetry tradeoff.
Webhooks & Notification Channels
One event pipeline, two kinds of delivery. A domain event is recorded in the audit log and enqueued on a BullMQ queue; a standalone worker delivers it as a signed HTTP POST and as a rendered human-facing alert through Apprise.
domain event → recordEvent() → AuditLogEntry row (awaited)
(controlplane/lib/audit.ts) → enqueueWebhook() → BullMQ "webhooks" (Redis)
→ webhook-dispatcher:
load active Webhook subscriptions → filter by event
→ sign (HMAC-SHA256) + POST
load active NotificationChannels → render
→ POST to the Apprise API- Event catalog is the single source of truth:
packages/shared/src/webhooks.ts(WEBHOOK_EVENTS,WebhookEventDef,WebhookJob,WEBHOOK_QUEUE_NAME,getWebhookEvent). - Emit via
recordEvent({ eventType, payload, performedBy, targetType, targetId })(packages/controlplane/lib/audit.ts) — neverenqueueWebhookdirectly from a domain site. One call drives both the audit trail and delivery. - Sanitized payloads: build with an explicit per-entity helper in
packages/controlplane/lib/webhook-payloads.ts— allow-lists only, never a secret.enqueueWebhookadditionally runsstripSensitiveas defence in depth. - BullMQ prefix: the control plane's producer uses
prefix: "vaultysclaw-controlplane"; a dispatcher instance serving it must setBULLMQ_PREFIXto the same value. - Requires Redis. Producers are fire-and-forget and no-op when
REDIS_URLis unset.
Adding or changing a webhook event — ALWAYS update the docs
The /admin/integrations/webhooks/docs reference is generated from data you must keep current.
Whenever you add, remove, or change the payload of an event, do all of this in the same change:
- Catalog — add/edit the entry in
packages/shared/src/webhooks.ts(type,label,description,group). This drives both the config UI and the docs. - Emit — add/adjust the
recordEventcall at the domain site. - Payload builder — update the matching helper in
packages/controlplane/lib/webhook-payloads.ts(explicit allow-list — never add a secret field). - Notification template — add a renderer to
RENDERERSinpackages/webhook-dispatcher/src/render.ts, or the event silently never reaches a channel. - Docs example — update
EXAMPLE_PAYLOADSinpackages/controlplane/lib/webhook-docs.ts. A new event with no entry falls back to{}in the docs — that is a bug, not acceptable. - Verify — confirm every event has a non-empty, secret-free example, and
pnpm type-checkis clean.
Note stripSensitive matches the substring apikey case-insensitively, so a boolean field named
hasApiKey is silently deleted from delivered payloads. Name such flags hasProviderKey.
Environment Variables
| Variable | Package | Purpose |
DATABASE_URL | controlplane, webhook-dispatcher | PostgreSQL connection string (Prisma) |
REDIS_URL | controlplane, webhook-dispatcher | Redis URL for the BullMQ webhook queue |
BULLMQ_PREFIX | webhook-dispatcher | Must match the producer's prefix (vaultysclaw-controlplane) |
WEBHOOK_TIMEOUT_MS | webhook-dispatcher | Per-endpoint delivery timeout (default 10000) |
APPRISE_API_URL | controlplane, webhook-dispatcher | Self-hosted Apprise API base URL. Unset turns Notification Channels off entirely; webhook delivery is unaffected. |
NEXTAUTH_URL / APP_URL | controlplane | Browser-facing base URL; used to build adminUrl deep links (APP_URL wins) |
NEXTAUTH_SECRET | controlplane | NextAuth session secret |
CONTROLPLANE_PORT / CONTROLPLANE_WS_PORT | controlplane | HTTP + WebSocket ports (defaults 3001 / 8081) |
LITELLM_BASE_URL / LITELLM_MASTER_KEY | controlplane | Deployment-time fallback for the Model Registry. The Setting rows an admin edits under Integrations → Models win, and the master key is stored encrypted there (lib/vault.ts). |
Design rules
packages/policyandpackages/truststay pure. No Prisma, Next.js, or WebSocket coupling; signing takes aVaultysId, enforcement takes an injected clock.packages/controlplaneis the only place their outputs get persisted or driven by I/O.- Humans are Actors (
kind: "human"), not a separate identity or permission table. There is norolefield anywhere — access is a ledger lookup (lib/access-control.ts'shasCapability). - A Server Action must authorize itself. Next.js dispatches an action as a POST to its own
endpoint without re-running the layout it is defined under, so the
admin_console_accessgate inapp/admin/layout.tsxdoes not protect it. Start every mutating admin action withawait requireAdmin()(lib/require-admin.ts). - Never change a
conformance/fixture on one side only — the TS and Go suites both consume them, and that is the entire point.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

