gloves 🥊🥊
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
Why gloves
- 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
Installation
Prerequisites
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
# Ubuntu/Debian
Install from GitHub release binaries (recommended)
This script installs:
- prebuilt
glovesCLI from GitHub Releases (no local build) skills/gloves-cliinto~/.openclaw/skills/gloves-cli- initialized secrets root at
~/.openclaw/secrets
|
Pin a specific release:
|
Install from crates.io
Install from source
Install Agent skill
OpenClaw setup script (CLI + skill)
Run this repository script to:
- install
glovesfrom GitHub Releases (default--install-mode release) - install
skills/gloves-clito~/.openclaw/skills/gloves-cli - initialize
~/.openclaw/secretswithgloves --root ... init
|
Useful 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--skill-dest <PATH>: override skill destination path
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
Quick Start
# 1) initialize runtime layout
# 2) create an agent secret (1 day TTL)
# 3) inspect state
# 4) read secret (prints raw value)
Common Workflows
Agent secret lifecycle
# create
# read
# revoke
Human request lifecycle
# request access to a human-owned secret
# list pending + metadata
# resolve request (human action)
# or
# check request status by secret name
Verification and cleanup
# verify state and reap expired secrets
Encrypted vault lifecycle
# initialize a vault
# mount with TTL
# trusted handoff prompt (request file from trusted mounted agent)
# unmount / inspect
Sidecar daemon (systemd-friendly TCP)
# strict startup checks (permissions + loopback bind policy + bind availability)
# start local daemon on loopback TCP
Set 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.
systemd (recommended for OpenClaw)
Install unit files from systemd/:
# user-level units
If your gloves binary is not in ~/.cargo/bin/gloves, edit ExecStart and ExecStartPre in the copied unit files.
Commands
| 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 | warns when printing to TTY |
env <name> <var> |
Print redacted env export | outputs export VAR=<REDACTED> |
request <name> --reason <text> |
Create human access request | reason is required |
approve <request_id> |
Approve pending request | request UUID |
deny <request_id> |
Deny pending request | request UUID |
status <name> |
Request status for secret | pending / fulfilled / denied / expired |
list |
List metadata and pending requests | JSON 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 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 |
Global flags:
--root <PATH>: override runtime root.--config <PATH>: use one config file.--no-config: disable config loading/discovery.--vault-mode <auto|required|disabled>: override vault runtime mode for one invocation.
Full CLI implementation: src/cli/mod.rs
Bootstrap config spec: GLOVES_CONFIG_SPEC.md
Bootstrap config parser module: src/config.rs
Bootstrap Config Parser (Library)
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 ;
let cwd = current_dir?;
let selection = resolve_config_path?;
if let Some = selection.path
# Ok::
Runtime Layout
Default root: .openclaw/secrets
.openclaw/secrets/
store/ # encrypted *.age files
meta/ # per-secret metadata JSON
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
Security Model
- 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
Agent Memory Exclusions
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
Architecture
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"]
Request flow
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
Development
Release Channels
stablechannel:- 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
CI/CD
.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 and Changelog
- License:
LICENSE - Changelog:
CHANGELOG.md