Experimental: this project is currently in experimental mode. Interfaces and behavior may change between releases.
gloves is a secure secrets control plane for OpenClaw and other multi-agent/human operator runtimes.
- Agent-owned secrets encrypted in-process via the
rageproject (ageformat) - Human-owned secrets resolved through
pass - Access requests, approvals, metadata, audit trails, and TTL reaping
- One command surface for both agent and human secret domains
- Secure-by-default storage permissions and atomic writes
- Integrity checks with audit events for sensitive operations
- Explicit request lifecycle for human-gated access
- Rust implementation with comprehensive tests and CI gates
curl+tar(for release-binary installs)- Rust stable toolchain (edition 2021, only for source builds)
- In-process crypto library from
rage(agecrate, no external crypto binary required) pass+ GPG (required for human-owned secret access)gocryptfs+fusermount+mountpoint(required forvaultcommands)- Writable secrets root (default:
.openclaw/secrets)
Quick pass install:
# macOS
brew install pass gnupg
# Ubuntu/Debian
sudo apt-get install pass gnupgThis script installs:
- prebuilt
glovesCLI from GitHub Releases (no local build) skills/gloves-cli-usageinto~/.openclaw/skills/gloves-cli-usageskills/gloves-setup-migrateinto~/.openclaw/skills/gloves-setup-migrate- initialized secrets root at
~/.openclaw/secrets
curl -fsSL https://raw.githubusercontent.com/openclaw/gloves/main/scripts/setup-openclaw.sh | bashPin a specific release:
curl -fsSL https://raw.githubusercontent.com/openclaw/gloves/main/scripts/setup-openclaw.sh | bash -s -- --release-ref vX.Y.Zcargo install glovesgit clone https://github.com/openclaw/gloves
cd gloves
cargo install --path .npx skills add heyAyushh/gloves --skill gloves-cli-usage
npx skills add heyAyushh/gloves --skill gloves-setup-migrateRun this repository script to:
- install
glovesfrom GitHub Releases (default--install-mode release) - install both skill directories to
~/.openclaw/skills/:gloves-cli-usagegloves-setup-migrate
- initialize
~/.openclaw/secretswithgloves --root ... init
curl -fsSL https://raw.githubusercontent.com/openclaw/gloves/main/scripts/setup-openclaw.sh | bashUseful options:
--install-mode <release|source>: choose release binary (default) or local source build--release-ref <latest|vX.Y.Z>: choose release version for binary install--repo <OWNER/REPO>: override GitHub repo used for release/skill downloads--repo-root <PATH>: use local repository for source install and/or local skill copy--skill-ref <REF>: fetch skill files from a specific tag/branch when local skill files are unavailable--dry-run: preview commands without applying changes--skip-cli-install: only install skill files and init root--skip-init: skipgloves --root <PATH> init--secrets-root <PATH>: override default secrets root--skills-dest <PATH>: override destination root for installed skill directories--skill-dest <PATH>: deprecated alias for--skills-dest
Each tagged release now includes these installable assets:
gloves-<version>-x86_64-unknown-linux-gnu.tar.gzgloves-<version>-x86_64-apple-darwin.tar.gzgloves-<version>-aarch64-apple-darwin.tar.gzgloves-<version>-x86_64-pc-windows-msvc.zipchecksums.txt
# 1) initialize runtime layout
gloves --root .openclaw/secrets init
# 2) create an agent secret (1 day TTL)
gloves --root .openclaw/secrets set service/token --generate --ttl 1
# 3) inspect state
gloves --root .openclaw/secrets list
# 4) read secret (prints raw value)
gloves --root .openclaw/secrets get service/tokenFor production-style virtual machine deployments with multiple agents and human operators, use:
# create
gloves --root .openclaw/secrets set app/api-key --stdin --ttl 7
# read
gloves --root .openclaw/secrets get app/api-key
# revoke
gloves --root .openclaw/secrets revoke app/api-key# request access to a human-owned secret
gloves --root .openclaw/secrets request prod/db --reason "run migration"
# list pending + metadata
gloves --root .openclaw/secrets list
# resolve request (human action)
gloves --root .openclaw/secrets approve <request-uuid>
# or
gloves --root .openclaw/secrets deny <request-uuid>
# check request status by secret name
gloves --root .openclaw/secrets status prod/db# verify state and reap expired secrets
gloves --root .openclaw/secrets verify# initialize a vault
gloves --root .openclaw/secrets vault init agent_data --owner agent
gloves --root .openclaw/secrets vault init personal --owner human
# mount with TTL
gloves --root .openclaw/secrets vault mount agent_data --ttl 1h
# mount, execute, and unmount automatically
gloves --root .openclaw/secrets vault exec agent_data -- \
sh -c 'ls -la ~/.openclaw/vault/agent_data'
# trusted handoff prompt (request file from trusted mounted agent)
gloves --root .openclaw/secrets vault ask-file agent_data \
--file docs/notes.txt \
--requester agent-a \
--trusted-agent agent-b
# unmount / inspect
gloves --root .openclaw/secrets vault unmount agent_data
gloves --root .openclaw/secrets vault status
gloves --root .openclaw/secrets vault list# strict startup checks (permissions + loopback bind policy + bind availability)
gloves --root .openclaw/secrets daemon --check --bind 127.0.0.1:7788
# start local daemon on loopback TCP
gloves --root .openclaw/secrets daemon --bind 127.0.0.1:7788Set OpenClaw sidecar endpoint to the exact same address and port as --bind.
For OpenClaw process supervisors (for example systemd or qmd), run the same daemon command with restart policy enabled.
Install unit files from systemd/:
# user-level units
mkdir -p ~/.config/systemd/user
cp systemd/glovesd.service ~/.config/systemd/user/
cp systemd/gloves-verify.service ~/.config/systemd/user/
cp systemd/gloves-verify.timer ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now glovesd.service
systemctl --user enable --now gloves-verify.timerIf your gloves binary is not in ~/.cargo/bin/gloves, edit ExecStart and ExecStartPre in the copied unit files.
| Command | Purpose | Options / Notes |
|---|---|---|
init |
Initialize runtime directories/files | none |
set <name> |
Store agent-owned secret | --generate, --stdin, --value, --ttl <days> (days > 0) |
get <name> |
Retrieve secret value | --pipe-to <command> streams bytes to stdin; --pipe-to-args "<command> {secret}" interpolates UTF-8 secrets into args; executable must be in GLOVES_GET_PIPE_ALLOWLIST; optional GLOVES_GET_PIPE_ARG_POLICY (exact templates) and .gloves.toml [secrets.pipe.commands.<command>] (URL prefixes) can tighten --pipe-to-args; warns on TTY |
env <name> <var> |
Print redacted env export | outputs export VAR=<REDACTED> |
request <name> --reason <text> |
Create human access request | optional --allowlist and --blocklist accept *, namespace/*, exact id |
approve <request_id> |
Approve pending request | request UUID; returns JSON review payload |
deny <request_id> |
Deny pending request | request UUID; returns JSON review payload |
status <name> |
Request status for secret | pending / fulfilled / denied / expired |
list |
List metadata and pending requests | --pending filters to pending request entries |
audit |
View audit trail entries | --limit <n> (default 50), --json for structured output |
revoke <name> |
Revoke caller-owned secret | removes ciphertext + metadata |
verify |
Reap expired items and verify runtime state | logs expiry events |
daemon |
Run local sidecar daemon | loopback TCP only (--bind, default 127.0.0.1:7788) |
| `vault init --owner <agent | human>` | Create encrypted vault metadata + ciphertext dir |
vault mount <name> |
Mount encrypted vault with TTL session | --ttl <duration>, --mountpoint, --agent |
vault exec <name> -- <command...> |
Mount vault, run command, then unmount | supports --ttl, --mountpoint, --agent; returns wrapped command exit code; strips extpass env vars from wrapped command |
vault unmount <name> |
Unmount vault and clear session | --agent |
vault status |
Show mounted/locked status and remaining TTL | JSON output |
vault list |
List configured vaults | JSON output |
vault ask-file <name> |
Generate trusted-agent handoff prompt for one file | --file, --requester, --trusted-agent, --reason |
config validate |
Validate effective bootstrap config | honors --config, --no-config, GLOVES_CONFIG; checks vault deps when mode is required |
access paths --agent <id> |
Show one agent's configured private path visibility | add --json for machine-readable output |
gpg create |
Create a per-agent GPG key when missing | idempotent; prints JSON with fingerprint and created |
gpg fingerprint |
Show per-agent GPG key fingerprint | fails with not found when no key exists |
Global flags:
--root <PATH>: override runtime root.--agent <ID>: override the caller agent id for one invocation.--config <PATH>: use one config file.--no-config: disable config loading/discovery.--vault-mode <auto|required|disabled>: override vault runtime mode for one invocation.
Environment variables:
GLOVES_GET_PIPE_ALLOWLIST: comma-separated executable names allowed bygloves get --pipe-toand--pipe-to-args.GLOVES_GET_PIPE_ARG_POLICY: optional JSON map of executable to exact allowed--pipe-to-argstemplates. Example:{"curl":["curl -u ayush:{secret} http://127.0.0.1:4001/carddav/principal/ayush/"]}GLOVES_GET_PIPE_URL_POLICY: optional JSON map of executable to allowed URL prefixes for--pipe-to-args(env fallback when config does not define command URL policy). Example:{"curl":["https://api.example.com/v1/","http://127.0.0.1:4001/carddav/"]}GLOVES_REQUEST_ALLOWLIST: optional comma-separated request allowlist patterns (*,namespace/*, exact id).GLOVES_REQUEST_BLOCKLIST: optional comma-separated request blocklist patterns (*,namespace/*, exact id).
Config-managed URL policy for --pipe-to-args:
[secrets.pipe.commands.curl]
require_url = true
url_prefixes = ["https://api.example.com/v1/"]
[secrets.pipe.commands.wget]
require_url = true
url_prefixes = ["https://downloads.example.com/"]When a command has a config policy entry:
- URL args in
--pipe-to-argsmust match one allowed prefix. require_url = truerequires at least one URL argument in the template.- The rule applies to any configured executable name, not only
curl/wget. - Prefix matching is strict by scheme + authority + path-segment boundary (for example
/v1does not match/v10). - Prefixes must not include query (
?) or fragment (#) components. - URL policy scopes only URL arguments; non-URL flags and payload arguments remain flexible.
Policy selection guide:
- Executable-only guard:
GLOVES_GET_PIPE_ALLOWLIST(lowest friction, broadest risk envelope). - URL-scoped guard:
.gloves.toml [secrets.pipe.commands.<command>]withrequire_url = true(same URLs allowed with varying payload/flags). - Exact template guard:
GLOVES_GET_PIPE_ARG_POLICY(strictest; safest for high-risk commands).
Security notes:
--pipe-to-argsonly accepts UTF-8 secrets without control characters; use--pipe-tofor raw byte-safe forwarding.- Use
GLOVES_GET_PIPE_ARG_POLICYfor high-risk tools (for examplecurl) so only exact approved argument templates are permitted. - Prefer
.gloves.toml [secrets.pipe.commands.<command>]for URL-scoped policy in shared environments; useGLOVES_GET_PIPE_URL_POLICYas a compatibility fallback. vault execremovesGLOVES_EXTPASS_ROOTandGLOVES_EXTPASS_AGENTfrom wrapped command env.- Keep allowlists narrow and prefer stdin-based secret piping when possible.
Audit quick view:
gloves audit --limit 25
gloves audit --json --limit 200audit --limit gives a human-readable stream; audit --json is suited for automation and SIEM ingestion.
Secret ACL operations map:
read:gloves getwrite:gloves setlist:gloves list(plus list filtering by path)revoke:gloves revokerequest:gloves requeststatus:gloves statusapprove:gloves approvedeny:gloves deny
Comprehensive secret ACL example (.gloves.toml):
[secrets.acl.agent-main]
paths = ["github/*", "shared/*"]
operations = ["read", "write", "list", "revoke", "request", "status", "approve", "deny"]
[secrets.acl.agent-relationships]
paths = ["contacts/*", "shared/contacts/*"]
operations = ["read", "write", "list", "request", "status"]
[secrets.acl.agent-workflows]
paths = ["workflows/*", "shared/webhooks/*"]
operations = ["read", "write", "list", "request", "status", "approve", "deny"]When [secrets.acl] is present, only listed agents/operations/path patterns are allowed.
Path pattern forms:
"*": all secrets"namespace/*": all secrets in one namespace"namespace/secret-name": one exact secret
Full CLI implementation: src/cli/mod.rs
Bootstrap config spec: GLOVES_CONFIG_SPEC.md
Bootstrap config parser module: src/config.rs
gloves now includes a production parser/validator for .gloves.toml:
- Config schema parsing with unknown-field rejection
- Discovery and precedence resolution (
flag,env,discovered,none) - Path normalization and
~expansion handling - Daemon/defaults/agent policy validation
- Unix file permission checks and symlink rejection for config files
Current status:
- Implemented as library API in
src/config.rs - CLI runtime wiring implemented in
src/cli/commands.rs gloves config validateandgloves access pathsare available- Vault mode enforcement (
auto/required/disabled) is active
Example usage from Rust:
use gloves::config::{resolve_config_path, GlovesConfig};
let cwd = std::env::current_dir()?;
let selection = resolve_config_path(None, std::env::var("GLOVES_CONFIG").ok().as_deref(), false, &cwd)?;
if let Some(path) = selection.path {
let config = GlovesConfig::load_from_path(path)?;
println!("effective root: {}", config.root.display());
}
# Ok::<(), gloves::error::GlovesError>(())Default root: .openclaw/secrets
.openclaw/secrets/
store/ # encrypted *.age files
meta/ # per-secret metadata JSON
gpg/ # per-agent GPG homedirs
vaults/ # per-vault config + sessions JSON
encrypted/ # vault ciphertext directories
mnt/ # default vault mountpoints
pending.json # request lifecycle state
audit.jsonl # append-only audit events
default-agent.agekey # generated age identity (rage project format)
default-agent.signing.key # generated Ed25519 signing key
Path model: SecretsPaths
- Secret values wrapped in non-
Debugtype:SecretValue - Agent secret encryption and decryption:
src/agent/backend.rs - Human backend via
pass:src/human/backend.rs - Vault orchestration via
gocryptfs:src/vault/ - Bootstrap config parsing and validation:
src/config.rs - Pending request signature verification:
src/human/pending.rs - Restricted file permissions and atomic writes:
src/fs_secure.rs - TTL reaping with audit events:
TtlReaper::reap,AuditLog::log
If another coding agent is installed for this repo, configure memory/indexing excludes:
~/.password-store/**(or$PASSWORD_STORE_DIR/**).openclaw/secrets/**(or any custom--rootdirectory)- Never persist raw
gloves getoutput in memory summaries or notes
flowchart LR
CLI["gloves CLI\nsrc/cli/mod.rs"] --> Router["SecretsManager\nsrc/manager.rs"]
Router --> Agent["Agent backend (in-process age format)\nsrc/agent/backend.rs"]
Router --> Human["Human backend (pass)\nsrc/human/backend.rs"]
Router --> Meta["Metadata store\nsrc/agent/meta.rs"]
Router --> Pending["Pending request store\nsrc/human/pending.rs"]
Router --> Audit["Audit log\nsrc/audit.rs"]
Router --> Reaper["TTL reaper\nsrc/reaper.rs"]
sequenceDiagram
participant AgentCLI as Agent via gloves CLI
participant Manager as SecretsManager
participant Pending as PendingRequestStore
participant Human as Human reviewer
AgentCLI->>Manager: request(secret, reason)
Manager->>Pending: create() with Ed25519 signature
Pending-->>AgentCLI: pending request id
Human->>Manager: approve(request_id) or deny(request_id)
Manager->>Pending: update status
AgentCLI->>Manager: status(secret)
Manager-->>AgentCLI: pending/fulfilled/denied/expired
cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo doc --no-depsstablechannel:- Branches:
mainandrelease/* - Tag format:
vX.Y.Z(example:v1.4.0)
- Branches:
betachannel:- Branch:
next - Tag format:
vX.Y.Z-beta.N(example:v1.5.0-beta.1)
- Branch:
alphachannel:- Branch:
canary - Tag format:
vX.Y.Z-alpha.N(example:v1.5.0-alpha.1)
- Branch:
Publishing is tag-driven. The publish workflow validates:
- tag format matches one of the channel patterns
- tag version matches
Cargo.tomlpackage version - tagged commit belongs to an allowed branch for that channel
Release commands and examples: RELEASE.md
.github/workflows/ci.yml: lint + docs.github/workflows/test.yml: full test suite.github/workflows/coverage.yml: coverage thresholds.github/workflows/publish.yml: publish on matching version tags (requiresCARGO_REGISTRY_TOKEN)
- License:
LICENSE - Changelog:
CHANGELOG.md