@akalsey/openclaw-gatepass 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @akalsey/openclaw-gatepass
2
+
3
+ OpenClaw plugin for [gatepass](https://github.com/akalsey/gatepass) secrets management.
4
+
5
+ Installs the `gatepass` CLI and the `secrets-management` skill in one step.
6
+
7
+ ## Installation
8
+
9
+ ```
10
+ clawhub install @akalsey/openclaw-gatepass
11
+ ```
12
+
13
+ Or via npm:
14
+
15
+ ```
16
+ npm install @akalsey/openclaw-gatepass
17
+ ```
18
+
19
+ ## What it installs
20
+
21
+ - **`gatepass` CLI** — decrypt and retrieve secrets stored in `pass`
22
+ - **`secrets-management` skill** — agent instructions for retrieving credentials at the moment of use
23
+
24
+ ## Updating skills
25
+
26
+ Skills are pulled from the installed `@akalsey/gatepass` package at install time. To get updated skills, update the plugin:
27
+
28
+ ```
29
+ npm update @akalsey/openclaw-gatepass
30
+ ```
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@akalsey/openclaw-gatepass",
3
+ "description": "Secrets management for OpenClaw agents via gatepass",
4
+ "skills": ["skills/secrets-management"],
5
+ "pluginApi": "1.0"
6
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@akalsey/openclaw-gatepass",
3
+ "version": "0.1.0",
4
+ "description": "OpenClaw plugin for gatepass secrets management",
5
+ "keywords": ["openclaw", "plugin", "secrets", "gatepass", "credentials"],
6
+ "license": "MIT",
7
+ "author": "Adam Kalsey",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/akalsey/openclaw-gatepass.git"
11
+ },
12
+ "files": [
13
+ "scripts",
14
+ "openclaw.plugin.json",
15
+ "skills",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "postinstall": "node scripts/postinstall.js",
20
+ "test": "node --test 'test/**/*.test.js'"
21
+ },
22
+ "dependencies": {
23
+ "@akalsey/gatepass": "*"
24
+ },
25
+ "openclaw": {
26
+ "compat": {
27
+ "pluginApi": "1.0"
28
+ }
29
+ },
30
+ "engines": {
31
+ "node": ">=18"
32
+ }
33
+ }
@@ -0,0 +1,26 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function copySkills(src, dest) {
5
+ if (!fs.existsSync(src)) {
6
+ process.stderr.write(`openclaw-gatepass: skills source not found at ${src} — skipping\n`);
7
+ return;
8
+ }
9
+ copyDir(src, dest);
10
+ process.stdout.write('openclaw-gatepass: gatepass skills installed\n');
11
+ }
12
+
13
+ function copyDir(src, dest) {
14
+ fs.mkdirSync(dest, { recursive: true });
15
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
16
+ const srcPath = path.join(src, entry.name);
17
+ const destPath = path.join(dest, entry.name);
18
+ if (entry.isDirectory()) {
19
+ copyDir(srcPath, destPath);
20
+ } else {
21
+ fs.copyFileSync(srcPath, destPath);
22
+ }
23
+ }
24
+ }
25
+
26
+ module.exports = { copySkills };
@@ -0,0 +1,11 @@
1
+ const path = require('path');
2
+ const { copySkills } = require('./copy-skills');
3
+
4
+ try {
5
+ const gatepassPkg = require.resolve('@akalsey/gatepass/package.json');
6
+ const sourceSkills = path.join(path.dirname(gatepassPkg), 'skills');
7
+ const destSkills = path.join(__dirname, '..', 'skills');
8
+ copySkills(sourceSkills, destSkills);
9
+ } catch (err) {
10
+ process.stderr.write(`openclaw-gatepass: could not install skills: ${err.message}\n`);
11
+ }
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: secrets-management
3
+ description: Retrieve credentials at the moment of use for authenticating to web services, APIs, or browser-based logins. Use whenever a task requires a password, API token, or other secret. Credentials are retrieved through the `gatepass` CLI, which decrypts them with a bot GPG key that is unlocked once at boot.
4
+ ---
5
+
6
+ # Secrets Management
7
+
8
+ ## When to use
9
+
10
+ Whenever a task requires authenticating to a service — typing a password into a browser form, calling an API that needs a token, or anything else that requires a secret. Retrieve credentials only at the moment they are needed, never speculatively or in advance.
11
+
12
+ ## Retrieving a credential
13
+
14
+ ```bash
15
+ gatepass get <service>
16
+ ```
17
+
18
+ Example: `gatepass get metabase`
19
+
20
+ The output follows an opinionated format:
21
+
22
+ - **Line 1** is the password, with no prefix.
23
+ - **Subsequent lines** are `key: value` fields, lowercase keys.
24
+
25
+ ```
26
+ the-actual-password
27
+ user: alice@example.com
28
+ url: https://metabase.example.com
29
+ note: anything else relevant
30
+ ```
31
+
32
+ Standard field names you may encounter:
33
+
34
+ | Field | Meaning |
35
+ |---|---|
36
+ | `user` | Username or login. Type into the username field. |
37
+ | `url` | Service URL. Navigate here for browser logins. |
38
+ | `email` | Email, when distinct from `user` (e.g. recovery email). |
39
+ | `otp` | TOTP secret (otpauth:// URI or base32). Use `gatepass get --otp` to generate a current code — never try to compute one yourself. |
40
+ | `passkey` | WebAuthn credential blob (base64 JSON). When present, use passkey authentication — do not type a password. Decode and load into your browser automation tool's virtual authenticator before navigating to the login page. See `docs/passkey-automation.md` for examples. |
41
+ | `note` | Free-form text. |
42
+
43
+ Other keys may appear — they follow the same `key: value` form and are always lowercase.
44
+
45
+ For browser-based logins: navigate to the `url`, type the `user` into the username field, and type the first line into the password field.
46
+
47
+ ## Getting a TOTP code
48
+
49
+ If a service requires a one-time password (2FA) and the entry has an `otp`
50
+ field, ask gatepass for a current code instead of trying to compute one yourself:
51
+
52
+ ```bash
53
+ gatepass get --otp <service>
54
+ ```
55
+
56
+ Example: `gatepass get --otp metabase` prints a single 6-digit code on one line and
57
+ nothing else. Use it immediately — TOTP codes rotate every 30 seconds.
58
+
59
+ If the credential isn't stored, or it exists but has no `otp` field, the
60
+ command exits with code 2 and tells you what `gatepass add` invocation to suggest
61
+ to the human. Treat it the same way as a missing credential: stop and ask.
62
+
63
+ ## Listing what's available
64
+
65
+ ```bash
66
+ gatepass list
67
+ ```
68
+
69
+ ## When a credential is missing
70
+
71
+ If `gatepass get <service>` exits with code 2, the credential is either not
72
+ stored, or (when called with `--otp`) is stored without an `otp` field. The
73
+ error message on stderr will name the missing piece and quote the exact
74
+ command the user should run, e.g.:
75
+
76
+ ```
77
+ gatepass: credential '<service>' is not stored.
78
+ Ask the user to add it by running:
79
+ gatepass add <service>
80
+ ```
81
+
82
+ Stop the task at that point. Tell the user which service is missing and quote the `gatepass add` command back to them. Do not try to authenticate by other means, do not guess credentials, and do not retry.
83
+
84
+ ## Rules
85
+
86
+ - **Retrieve credentials only at the moment you need them.** Do not fetch them in advance "to have them ready."
87
+ - **Never include credential values in messages to the user.** Not in summaries, not in confirmations, not in error reports.
88
+ - **Never write credential values to memory files, daily notes, or any workspace file.** Including `MEMORY.md`, scratchpads, logs, or transcripts you control.
89
+ - **Never pass credential values into sub-agent task instructions.** If a sub-agent needs a credential, the sub-agent should retrieve it itself using this skill.
90
+ - **If `gatepass get` exits non-zero with code 2,** the credential is missing — ask the user to add it. Do not try alternatives.
91
+ - **If `gatepass get` exits non-zero with any other code,** something is wrong with the runtime (key not unlocked, config missing, etc.). Run `gatepass doctor` to diagnose, and report the result to the user rather than working around it.
92
+ - **If authentication fails after `gatepass get` succeeded,** report which service failed and the error message — never the credential value.
93
+ - **If a browser session expires mid-task,** stop and ask the user to re-authenticate. Do not silently retrieve the credential and re-login without telling them.
94
+ - **If a credential has a `passkey:` field,** authenticate using the passkey credential rather than the password. The `passkey:` value is a base64-encoded JSON blob. Load it into your browser automation tool's virtual authenticator before navigating to the login page. The password field may also be present but should be ignored for login.
95
+
96
+ ## Why these rules matter
97
+
98
+ For API-based auth, credentials can sometimes be injected without the agent seeing them. For browser-based auth, the agent must see the credential to type it into a form — that's unavoidable. The mitigation is to minimize where the value appears: it shows up in exactly one tool result (the `gatepass get` call) and is never copied into any other artifact.
99
+
100
+ ## Adding new credentials
101
+
102
+ This is a human task, not an agent task. `gatepass add` is interactive only and refuses to run without a TTY. If a credential is missing, ask the user to run `gatepass add <service>` themselves.
103
+
104
+ ## Reference
105
+
106
+ - `setup.md` — what `gatepass setup` does under the hood, and how to debug it
107
+ - `security.md` — trust assumptions, threat model, rejected alternatives
@@ -0,0 +1,64 @@
1
+ # Security Model
2
+
3
+ ## Trust assumptions
4
+
5
+ - Host machine is trusted.
6
+ - Disk encryption (FileVault on macOS, LUKS on Linux) is enabled.
7
+ - The bot user is isolated from other users on the system.
8
+
9
+ ## Tradeoffs
10
+
11
+ This system prioritizes unattended automation over interactive security. The bot must run across reboots without anyone present to type a passphrase, which forces some compromises:
12
+
13
+ - The bot's GPG passphrase is stored on disk in `~/.bot-pass.txt`.
14
+ - The GPG key is unlocked automatically at boot.
15
+ - `gpg-agent` holds the decrypted key in memory indefinitely (cache TTL is one year).
16
+
17
+ `~/.bot-pass.txt` is the weakest link: any process running as the bot user can read it.
18
+
19
+ ## Protection layers
20
+
21
+ | Layer | Protects |
22
+ |---|---|
23
+ | GPG encryption | secrets at rest |
24
+ | Passphrase file permissions (`chmod 600`) | the GPG passphrase |
25
+ | OS user isolation | the passphrase file from other users |
26
+ | Disk encryption | the entire system from offline attack |
27
+ | Per-file recipient lists | bot from decrypting human-only secrets |
28
+
29
+ ## Why the bot can't decrypt human-only secrets
30
+
31
+ Even though the bot user can read the encrypted files in `~/.password-store/personal/`, they're encrypted only to `my-key`. The bot's `bot-key` is not a recipient, so GPG cannot decrypt them. This is enforced cryptographically, not by filesystem permissions.
32
+
33
+ ## Why browser auth requires LLM credential visibility
34
+
35
+ For API-based services, credentials can be injected server-side without the LLM ever seeing the value. For browser-based authentication, the LLM must see the credential to type it into a form field. This is unavoidable.
36
+
37
+ The mitigation is to minimize exposure: the credential is retrieved at the moment of use via `pass show`, appears in exactly one tool result (the turn where it's typed), and is never stored in workspace files, memory, or messages. See `SKILL.md` for the rules the agent follows.
38
+
39
+ Preferred direction: move services from browser-based login to API-based access wherever possible, so credentials never need to enter the LLM context at all.
40
+
41
+ ## Rejected alternatives
42
+
43
+ **Plaintext credentials file** (e.g. `~/.openclaw/credentials.json`)
44
+ Works, but credentials are unencrypted at rest. Any process running as the bot user can read them. No access scoping.
45
+
46
+ **OpenClaw native secrets** (`openclaw secrets configure`)
47
+ Interactive wizard limited to credentials OpenClaw itself needs (provider API keys, bot tokens). No support for arbitrary secrets. `openclaw secrets set <key> <value>` is an open feature request, not implemented.
48
+
49
+ **AgentSecrets**
50
+ Sound local architecture (OS keychain storage, `agentsecrets call` for zero-knowledge API calls). Rejected due to dependency on a cloud backend of uncertain operational status. Alpha software.
51
+
52
+ **Python `keyring` library**
53
+ Cross-platform and mature. But on macOS the Keychain prompts for the user's password after timeout or reboot; in a headless VM with no one to click "Allow," credential retrieval fails silently. On headless Linux, requires `keyrings.alt` fallback backend.
54
+
55
+ **`age` encryption**
56
+ Simpler than GPG, modern cryptography, portable. But `pass` provides directory structure, git integration, `pass ls` / `pass grep`, and a mature ecosystem. Since GPG is already part of the workflow, `pass` wins on organizational features.
57
+
58
+ ## Future work
59
+
60
+ - Secret rotation workflows.
61
+ - Automated re-encryption of namespaces when keys change.
62
+ - Auditability — who can decrypt what.
63
+ - Guardrails to prevent accidental over-sharing (wrong recipient sets on insert).
64
+ - Atomic wrapper for dual-recipient insert (`pass init` / `insert` / revert) so a human can't leave the store in dual-recipient mode by accident.
@@ -0,0 +1,216 @@
1
+ # Setup
2
+
3
+ The fast path is `gatepass setup`. This document covers what that wizard does under the hood and how to debug it.
4
+
5
+ ## Fast path
6
+
7
+ ```bash
8
+ npm install -g gatepass # or: npm install -g github:akalsey/Gatepass
9
+ gatepass setup
10
+ ```
11
+
12
+ The wizard:
13
+
14
+ 1. Verifies `gpg` and `pass` are installed (prints platform-specific install commands if not).
15
+ 2. Picks or generates your **personal** GPG key.
16
+ 3. Picks or generates a **bot** GPG key. If one or more keys with a `Gatepass Bot` name or `gatepass-bot@…` email already exist in your keyring, the wizard offers to reuse one (prompting for its passphrase, which it verifies by unlocking the key in `gpg-agent`). Otherwise it generates a new bot key with a strong random passphrase.
17
+ 4. Writes the (verified or generated) passphrase to `~/.bot-pass.txt` (mode 600).
18
+ 5. Configures `~/.gnupg/gpg.conf` (`pinentry-mode loopback`) and `~/.gnupg/gpg-agent.conf` (`allow-loopback-pinentry`, one-year cache TTL).
19
+ 6. Initializes the password store: default recipient is your personal key; the bot-readable namespace (default `bot/`) is dual-recipient (bot key + your key).
20
+ 7. Saves `~/.config/gatepass/config.json` with the namespace, both fingerprints, the passphrase file path, and the password store location.
21
+ 8. Unlocks the bot key in `gpg-agent`.
22
+ 9. Inserts a test secret, retrieves it, and removes it — to confirm the pipeline works.
23
+ 10. Offers to install boot-time unlock as a launchd agent (macOS) or systemd user service (Linux).
24
+
25
+ After setup, the runtime calls `gatepass get <service>` and gets the credential without prompting.
26
+
27
+ ## Storage layout
28
+
29
+ `pass` stores each secret as an individually GPG-encrypted file. Paths represent namespaces:
30
+
31
+ ```
32
+ ~/.password-store/
33
+ bot/ # bot-readable (dual-recipient)
34
+ metabase.gpg
35
+ posthog.gpg
36
+ personal/ # human-only (single-recipient)
37
+ bank.gpg
38
+ ```
39
+
40
+ Access control is enforced by *which GPG keys each file is encrypted to*, not filesystem permissions alone. The bot's key physically cannot decrypt entries it isn't a recipient of. `gatepass setup` configures the bot-readable namespace so that everything inserted under it is automatically encrypted to both keys — there's no insert-time dance.
41
+
42
+ ## Configuration
43
+
44
+ `~/.config/gatepass/config.json`:
45
+
46
+ ```json
47
+ {
48
+ "namespace": "bot",
49
+ "botKeyId": "ABCDEF0123456789...",
50
+ "humanKeyId":"FEDCBA9876543210...",
51
+ "passphraseFile": "/Users/alice/.bot-pass.txt",
52
+ "passwordStore": "/Users/alice/.password-store",
53
+ "bootUnlockInstalled": true
54
+ }
55
+ ```
56
+
57
+ Environment overrides:
58
+
59
+ - `ZUUL_NAMESPACE` — bot-readable namespace
60
+ - `ZUUL_CONFIG_DIR` — config directory (default `~/.config/gatepass`)
61
+ - `PASSWORD_STORE_DIR` — pass storage location
62
+
63
+ ## Boot-time unlock
64
+
65
+ `gatepass setup` offers to install one of:
66
+
67
+ - **macOS**: `~/Library/LaunchAgents/ai.openclaw.gatepass-unlock.plist`, loaded with `launchctl`.
68
+ - **Linux**: `~/.config/systemd/user/gatepass-unlock.service`, enabled with `systemctl --user`.
69
+
70
+ Either runs `gatepass unlock` at every login, which seeds `gpg-agent` so subsequent `gatepass get` calls don't prompt. If the service isn't installed, the bot will need `gatepass unlock` manually after every reboot.
71
+
72
+ ## Importing existing keys
73
+
74
+ Three common cases:
75
+
76
+ ### 1. You already have a personal GPG key in this machine's keyring
77
+
78
+ Just run `gatepass setup`. The personal-key picker lists every secret key in your keyring (`gpg --list-secret-keys`) and lets you reuse one. Nothing is regenerated, and your existing key isn't modified — it just becomes the recipient for everything you store under `pass`.
79
+
80
+ The only side-effect on your existing GPG setup: `gatepass setup` appends `pinentry-mode loopback` to `~/.gnupg/gpg.conf` and `allow-loopback-pinentry`, `default-cache-ttl`, and `max-cache-ttl` to `~/.gnupg/gpg-agent.conf`. These are required for the bot to decrypt without a TTY. If that conflicts with how you use GPG day-to-day (e.g., you rely on a graphical pinentry for signing email), run gatepass under a dedicated keyring:
81
+
82
+ ```bash
83
+ export GNUPGHOME="$HOME/.gnupg-gatepass"
84
+ mkdir -p -m 700 "$GNUPGHOME"
85
+ gatepass setup
86
+ ```
87
+
88
+ Then export `GNUPGHOME=$HOME/.gnupg-gatepass` in whatever environment runs `gatepass get` (the agent's launchd/systemd unit, your shell rc, etc.). Gatepass, gpg-agent, and pass all honour `GNUPGHOME`, so this fully isolates the two keyrings.
89
+
90
+ ### 2. You have a personal key in a backup file (not yet in the keyring)
91
+
92
+ ```bash
93
+ gatepass import-key /path/to/my-key.asc
94
+ gatepass setup # the imported key now shows up in the picker
95
+ ```
96
+
97
+ `gatepass import-key` with no role flag is a friendly wrapper around `gpg --import` — it imports the file and reports the new fingerprints. After that, `gatepass setup` treats the key like any other in-keyring key.
98
+
99
+ ### 3. You're moving a bot key from another machine
100
+
101
+ ```bash
102
+ gatepass import-key bot-key.asc --as-bot --passphrase-file old-bot-pass.txt
103
+ ```
104
+
105
+ This:
106
+
107
+ 1. Imports the key file into the GPG keyring.
108
+ 2. Reads the bot passphrase from the file you pass (or prompts if the flag is omitted).
109
+ 3. Writes it to `~/.bot-pass.txt` (mode 600, replacing any prior file — backed up in memory and restored if the unlock test fails).
110
+ 4. Verifies by unlocking the key in `gpg-agent`.
111
+ 5. Saves `botKeyId` (and `passphraseFile`) to `~/.config/gatepass/config.json`.
112
+
113
+ If the new machine has never run `gatepass setup`, follow up with `gatepass setup` — it will skip bot-key generation (sees the configured key) and just configure the personal key, pass store, and boot-time unlock.
114
+
115
+ ### `gatepass import-key` flags
116
+
117
+ | Flag | Purpose |
118
+ |---|---|
119
+ | `--as-bot` | Configure the imported key as the Gatepass bot key. |
120
+ | `--as-personal` | Configure the imported key as your personal recipient. |
121
+ | `--passphrase-file FILE` | Read the bot passphrase from a file (otherwise prompted). |
122
+ | `--fingerprint FPR` | Pick a specific fingerprint when the file holds more than one key, or to assign a key that's already in the keyring. |
123
+
124
+ Switching keys when the password store already has entries: changing `humanKeyId` orphans every existing entry until you re-run `pass init <new-fingerprint>` and `pass init --path <namespace> <bot-fpr> <new-fingerprint>`, which re-encrypts the store. `gatepass import-key --as-personal` warns about this and asks for confirmation before updating config.
125
+
126
+ ## Cross-machine replication
127
+
128
+ `~/.password-store/` syncs between machines via Syncthing (or any tool that copies files). Files in transit are GPG-encrypted blobs — safe even if the sync channel is compromised. Both machines need the same bot key imported.
129
+
130
+ To migrate to a new machine using the import command:
131
+
132
+ ```bash
133
+ # on the old machine
134
+ gpg --export-secret-keys <bot-fingerprint> > bot-key.asc
135
+ scp bot-key.asc ~/.bot-pass.txt ~/.config/gatepass/config.json new-machine:
136
+
137
+ # on the new machine
138
+ mkdir -p ~/.config/gatepass && mv config.json ~/.config/gatepass/
139
+ gatepass import-key bot-key.asc --as-bot --passphrase-file .bot-pass.txt
140
+ rm .bot-pass.txt # the import wrote ~/.bot-pass.txt for you
141
+ gatepass doctor
142
+ ```
143
+
144
+ Or, if you'd rather do the steps by hand:
145
+
146
+ ```bash
147
+ # on the new machine
148
+ gpg --import bot-key.asc
149
+ mkdir -p ~/.config/gatepass && mv config.json ~/.config/gatepass/
150
+ mv .bot-pass.txt ~/.bot-pass.txt
151
+ chmod 600 ~/.bot-pass.txt
152
+ gatepass unlock
153
+ gatepass doctor
154
+ ```
155
+
156
+ ## Debugging
157
+
158
+ `gatepass doctor` checks every prerequisite individually: tools installed, config present, both keys in keyring, passphrase file mode, password store initialized, namespace recipients, agent unlocked, boot-time unlock installed. Run it whenever something feels off.
159
+
160
+ If `gatepass get` fails:
161
+
162
+ - Exit code 2 — credential not stored. Run `gatepass add <service>`.
163
+ - Exit code 3 — `gatepass setup` hasn't been run.
164
+ - Exit code other — check `gatepass doctor`.
165
+
166
+ ## Manual install (if `gatepass setup` won't work)
167
+
168
+ If you need to do this by hand, the commands `gatepass setup` runs under the hood are roughly:
169
+
170
+ ```bash
171
+ # Generate bot key
172
+ gpg --batch --pinentry-mode loopback --gen-key <<EOF
173
+ Key-Type: RSA
174
+ Key-Length: 4096
175
+ Name-Real: Gatepass Bot
176
+ Name-Email: gatepass-bot@$(hostname)
177
+ Expire-Date: 0
178
+ Passphrase: <generated>
179
+ %commit
180
+ EOF
181
+
182
+ # Configure agent
183
+ echo 'pinentry-mode loopback' >> ~/.gnupg/gpg.conf
184
+ cat >> ~/.gnupg/gpg-agent.conf <<EOF
185
+ allow-loopback-pinentry
186
+ default-cache-ttl 31536000
187
+ max-cache-ttl 31536000
188
+ EOF
189
+ gpgconf --reload gpg-agent
190
+
191
+ # Initialize pass
192
+ pass init <your-fingerprint>
193
+ pass init --path bot <bot-fingerprint> <your-fingerprint>
194
+
195
+ # Stash passphrase
196
+ echo '<bot-passphrase>' > ~/.bot-pass.txt
197
+ chmod 600 ~/.bot-pass.txt
198
+
199
+ # Unlock
200
+ gpg --batch --yes --pinentry-mode loopback \
201
+ --passphrase-file ~/.bot-pass.txt \
202
+ --local-user <bot-fingerprint> \
203
+ --sign <<< gatepass-unlock
204
+ ```
205
+
206
+ ## Dependencies
207
+
208
+ `pass` requires `gpg` and standard Unix tools. `gatepass` itself requires Node 18+.
209
+ For TOTP support (`gatepass add --otp` verification and `gatepass get --otp`), install
210
+ `oathtool` from the `oath-toolkit` package.
211
+
212
+ | Platform | Install |
213
+ |---|---|
214
+ | macOS | `brew install pass gnupg oath-toolkit` |
215
+ | Debian/Ubuntu | `apt install pass gnupg oathtool` |
216
+ | Fedora/RHEL | `dnf install pass gnupg2 oathtool` |