agentknock 0.6.1

Developer secrets on your phone, provided only to approved commands.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
# Agentknock

Developer secrets on your phone, provided only to approved commands.

[Agentknock](https://agentknock.dev/) lets command-line tools use secrets
without storing long-lived credentials in agent configuration or project
files. A paired mobile device authorizes each use. It can return environment
values or perform operations such as Git signing without releasing a private
key.

> Early release · [Share feedback]mailto:agentknock@fulldisclosure.fi

## How Agentknock works

Agentknock delivers secrets from a paired mobile device when a command needs
them. For example:

1. Run a command with the `gh-token` secret:

   ```sh
   agentknock -s gh-token \
     --reason "GitHub token provides write access to the repository" \
     -- gh pr merge 123
   ```

2. The paired mobile device displays the request. Approve the use of
   `gh-token` for this command.

3. Agentknock runs `gh pr merge 123` with access to `gh-token`. The secret is
   available only to that execution and is not printed or written to disk.

## Install the Agentknock client

Choose one of the following installation methods.

### Use the installation script

The installation script selects the latest prebuilt release for the current
platform, verifies its published SHA-256 checksum, and installs `agentknock`
in `~/.local/bin`:

```sh
curl -fsSL https://agentknock.dev/install.sh | bash
```

Run the same command again to update Agentknock.

### Use mise

Use the GitHub release through mise:

```sh
mise use --global github:agentknock/agentknock-cli
```

mise verifies the GitHub build attestation when one is available for the
release artifact.

Run `mise upgrade github:agentknock/agentknock-cli` to update Agentknock.

### Use npm

Run Agentknock without installing it globally:

```sh
npx agentknock --help
```

To make the command persistently available, install it globally:

```sh
npm install --global agentknock
```

The npm package requires Node.js `^22.15.0` or `>=24.0.0`. Run
`npm update --global agentknock` to update Agentknock.

### Use Nix

Run Agentknock without installing it in a profile:

```sh
nix run github:agentknock/agentknock-cli -- --help
```

To make the command persistently available, install it in your Nix profile:

```sh
nix profile add github:agentknock/agentknock-cli
```

Run `nix profile upgrade agentknock-cli` to update Agentknock.

### Build from source with Cargo

Install Rust 1.89 or later, then build and install Agentknock from crates.io:

```sh
cargo install --locked agentknock
```

Run the same command again to update Agentknock.

### Verify a release

Agentknock publishes each release as an immutable GitHub release. GitHub locks
the release tag and attached files after publication and generates a release
attestation for them. The release archives also have build-provenance
attestations generated by the Agentknock release workflow.

Install the [GitHub CLI](https://cli.github.com/), then download and verify the
latest x86-64 Linux archive:

```sh
repository=agentknock/agentknock-cli
tag=$(gh release view --repo "$repository" --json tagName --jq .tagName)
target=x86_64-unknown-linux-musl
archive=agentknock-${target}.tar.gz

gh release download "$tag" --repo "$repository" \
  --pattern "$archive" --pattern "$archive.sha256"
sha256sum --check "$archive.sha256"
gh release verify "$tag" --repo "$repository"
gh release verify-asset "$tag" "$archive" --repo "$repository"
gh attestation verify "$archive" --repo "$repository" \
  --signer-workflow agentknock/agentknock-cli/.github/workflows/ci.yml
```

Use `aarch64-unknown-linux-musl` as `target` for an ARM64 Linux system or
`aarch64-apple-darwin` for an Apple Silicon Mac. On macOS, replace the
`sha256sum` command with `shasum -a 256 --check`. Release verification confirms
that the downloaded archive is part of the immutable release.
Build-provenance verification confirms that the Agentknock release workflow
built that archive from the identified source revision. Neither verification
determines whether the source code itself is safe.

The pinned Nix build can independently reproduce the release archive. On a
system with Nix installed, check out the release and compare the resulting
file:

```sh
git clone --branch "$tag" --depth 1 \
  https://github.com/agentknock/agentknock-cli.git "agentknock-cli-$tag"
cd "agentknock-cli-$tag"
make dist DIST_TARGET="$target"
cmp "../$archive" "target/dist/$archive"
```

A successful comparison means that the local build produced the same archive
as the GitHub release.

## Install the Agentknock mobile app

### Google Play

Install the Agentknock app from
[Google Play](https://play.google.com/store/apps/details?id=dev.agentknock).

### App Store

The Agentknock app for iOS isn't available yet.

## Get started

Agentknock includes its complete command-line reference in `--help`. Run
`agentknock --help` for an overview, or use `--help` with any command for
detailed instructions.

### Choose the Agentknock directory

Agentknock stores pairing state in `$HOME/.agentknock/pairing.json`. If your
execution environment needs another location, set `AGENTKNOCK_HOME` to a
nonempty absolute UTF-8 directory path before pairing:

```sh
export AGENTKNOCK_HOME=/persist/agentknock
```

Use the same directory for pairing and subsequent commands. It must remain
writable because Agentknock updates the pairing keys during use. Selecting
another directory does not copy or move an existing pairing.

The global `--agentknock-home` option overrides the environment variable for
one command. Relative option paths are resolved against the current working
directory:

```sh
agentknock --agentknock-home /persist/agentknock \
  -s gh-token --reason "GitHub token provides access to private repository issues" \
  -- gh issue list
```

Neither setting changes the wrapped command's `HOME`. An existing
`AGENTKNOCK_HOME` is inherited normally; the option does not set or change it
for the wrapped command. With either override, Agentknock does not require
`HOME` and does not fall back to another directory if the selected location
is unusable.

### Pair the client

Use the pairing address that you selected when you set up the mobile app. For
example, if the address is `calm-river-lantern`:

1. Start pairing on the client:

   ```sh
   agentknock pairing start calm-river-lantern
   ```

2. Confirm the full 12-digit verification code on the mobile device, then
   approve the pairing. If the code doesn't match, reject the pairing and run
   `agentknock pairing abort`.

3. After you approve the pairing, activate it on the client:

   ```sh
   agentknock pairing finish
   ```

The client can now request secrets from the paired mobile device.

### Run a command with secrets

The `run` command requires at least one secret. Repeat `-s` when a command
needs more than one. Use `--reason` to explain why each selected secret is
needed, such as API access, SSH authentication, or a signing operation. The
command and arguments already describe the action.

You can omit `run` and put its options directly after `agentknock`. For a
release script that needs both GitHub and Cloudflare access:

```sh
agentknock -s gh-token -s cloudflare \
  --reason "GitHub token for repository access; Cloudflare token for deployment access" \
  -- ./release.sh
```

The explicit form is equivalent:

```sh
agentknock run -s gh-token -s cloudflare \
  --reason "GitHub token for repository access; Cloudflare token for deployment access" \
  -- ./release.sh
```

The `--` separator is required. Agentknock passes the command and every
argument after the separator unchanged.

Agentknock waits for the paired mobile device to authorize the request and
return the secrets before it starts the command. If the request is still
waiting after 30 seconds, Agentknock writes a progress update with the elapsed
time to standard error every 30 seconds. Press Ctrl-C to cancel the request.

#### Control environment-variable delivery

By default, an environment secret provides all its variables under their
stored names. Delivery controls name both the secret and its stored variable,
so they remain unambiguous when a command uses multiple secrets.

Use `--only-env` to select a subset, or `--omit-env` to exclude variables:

```sh
agentknock -s github \
  --reason "GitHub token provides access to private repository issues" \
  --only-env github GH_TOKEN \
  -- gh issue list

agentknock -s development \
  --reason "Development credentials authenticate the server to its database" \
  --omit-env development DEBUG_TOKEN \
  -- ./run-development-server
```

Repeat either option for more variables. You cannot combine `--only-env` and
`--omit-env` for the same secret.

Use `--rename-env` to give a selected variable a different name in the command
environment:

```sh
agentknock -s github \
  --reason "GitHub token authenticates the command's API requests" \
  --only-env github GH_TOKEN \
  --rename-env github GH_TOKEN GITHUB_TOKEN \
  -- ./command-expecting-github-token
```

Use `--stdin` to send one stored variable to the command's standard input:

```sh
agentknock -s service-password \
  --reason "Service password is required to authenticate the service account" \
  --only-env service-password PASSWORD \
  --stdin service-password PASSWORD \
  -- ./command-reading-a-password
```

Agentknock sends the exact value without adding a newline, closes the input,
and does not add that variable to the command environment. Other variables
from the secret remain in the environment unless you restrict them with
`--only-env`. One command can receive at most one value on standard input.

Agentknock applies these controls separately to each secret before it combines
their environment values. If multiple values have the same final name, their
values must match.

### Use SSH keys

An SSH secret keeps its private key on the paired device. Agentknock makes the
selected public key available to the command through a temporary SSH agent.
The temporary agent also makes keys from an existing SSH agent available.
When SSH uses the selected key, Agentknock sends the exact authentication
request to the device for a separate decision. Other keys continue to use the
existing agent. Agentknock authentication currently supports Ed25519 and RSA
keys.

Use the SSH secret with a direct connection:

```sh
agentknock -s production-ssh \
  --reason "SSH key is required to authenticate to example.com" -- ssh example.com
```

The same setup works when Git uses an SSH remote:

```sh
agentknock -s github-ssh \
  --reason "SSH key authenticates Git access to the GitHub repository" -- git push
```

The command can select at most one SSH secret. Agentknock puts that key first
in the temporary agent and then lists keys from any existing agent. Agentknock
never sends the selected private key to the client or command.

Use `--no-ssh-passthrough` when the temporary agent should not expose keys from
the existing agent:

```sh
agentknock --no-ssh-passthrough -s production-ssh \
  --reason "SSH key is required to authenticate to example.com" -- ssh example.com
```

This option also makes Git SSH signing fail if Git requests a different key.
It does not prevent the command from using private-key files or another agent
selected in SSH configuration.

Use `--no-ssh-agent` when the command should receive no `SSH_AUTH_SOCK` at all.
Agentknock Git signing remains available unless you also use `--no-git-sign`.

An `IdentityAgent` setting in SSH configuration takes precedence over
`SSH_AUTH_SOCK`. Set `IdentityAgent SSH_AUTH_SOCK` for a host that should use
the Agentknock agent.

### Sign Git commits and tags

When Git requests a signature while running a command through Agentknock,
Agentknock sends Git's exact signing payload to the device so it can show the
commit or tag message for a separate decision. For commits, it also sends
available repository, branch, and changed-path context to help identify the
requested signature.

With Git configured to sign commits using SSH, wrap your usual command:

```sh
agentknock -s git-signing --reason "SSH key is required to sign the commit" -- git commit
```

Use `--no-git-sign` when Agentknock should not provide Git signing. Agentknock
then leaves the existing Git signing configuration unchanged; that
configuration can still sign commits or tags independently.

A command can select at most one SSH secret. If Git has no configured
`user.signingKey`, Agentknock offers the selected SSH key as the default. If
Git explicitly selects another key, signing proceeds through the ordinary
`ssh-keygen` command unless you use `--no-ssh-passthrough`. It can use a key
file or a key from the existing SSH agent as usual. Agentknock does not change
`gpg.format`, signing policy, or the configured signing key. Git signing can
use SSH key types that Agentknock does not yet support for SSH authentication.

### Use a network proxy

Agentknock uses `HTTPS_PROXY` for its secure WebSocket connection to the relay
and falls back to `ALL_PROXY`. The lowercase forms `https_proxy` and
`all_proxy` are also supported and take precedence over their uppercase forms.
Set `NO_PROXY` or `no_proxy` to exclude hosts from proxying. Proxy URLs can use
HTTP or HTTPS and can include HTTP Basic authentication credentials.

For example, connect through an HTTP proxy:

```sh
HTTPS_PROXY=http://proxy.example:8080 agentknock secret list
```

## Manage secrets

Manage secrets primarily in the mobile app. The commands in this section
contact the paired mobile device and wait for its response. During a long wait,
Agentknock reports progress and elapsed time every 30 seconds.

### List secrets

Request the secrets available to this client:

```sh
agentknock secret list
```

The command writes a JSON object to standard output. It maps each secret name
to its type, description, and type-specific public metadata, such as
environment variable names or an SSH public key. It never includes secret
values. Progress and errors go to standard error, so you can process or
redirect the JSON separately.

### Upload secrets

Agentknock can migrate existing environment variables and SSH private keys to
the mobile app. It reads values from the sources that you specify; the values
do not appear in the command arguments.

To migrate a variable from the current environment:

```sh
agentknock secret upload gh-token \
  --description "GitHub API access" \
  --from-env GH_TOKEN
```

To migrate all variables from a dotenv file:

```sh
agentknock secret upload development --from-env-file .env
```

To enter values without displaying them:

```sh
agentknock secret upload cloudflare --from-prompt CLOUDFLARE_API_TOKEN
```

To read one variable from a file:

```sh
agentknock secret upload npm --from-file NPM_TOKEN=/path/to/token
```

You can repeat and combine `--from-env`, `--from-env-file`, `--from-file`, and
`--from-prompt`. Use `--from-env-file -` to read dotenv data from standard
input, or `--from-file NAME=-` to read one value from standard input.

To upload an SSH private key in OpenSSH private-key format:

```sh
agentknock secret upload git-signing \
  --description "Git signing key" \
  --from-ssh-key ~/.ssh/id_ed25519
```

Use `--from-ssh-key -` to read the key from standard input. An SSH-key source
cannot be combined with environment-variable sources.

`ssh-keygen` can convert a legacy PEM key to OpenSSH format in place:

```sh
ssh-keygen -p -f ~/.ssh/id_rsa
```

Agentknock rejects an encrypted private key unless you explicitly provide its
passphrase source. To enter the passphrase without displaying it, add
`--passphrase-prompt`. For a noninteractive upload, use
`--passphrase-env NAME` to read the passphrase from an environment variable.
Agentknock removes the passphrase protection locally and sends only the
resulting passphrase-free OpenSSH key. Agentknock's end-to-end encryption
protects the key in transit, and the mobile app stores it encrypted. The
passphrase is never sent to the mobile app.

An upload is a proposal, not an immediate change to the secrets on the mobile
device. The command finishes after the mobile app confirms receipt of the
proposal. Review and accept the proposal in the mobile app before the secret
becomes available to this client.

By default, an upload proposes a new secret. Use `--update` to change the
values that you provide while retaining the other values in an existing
secret:

```sh
agentknock secret upload gh-token --update --from-prompt GH_TOKEN
```

Use `--replace` to propose a complete replacement. Values that you don't
provide are removed if you accept the proposal. When you propose a new secret,
you can change its name before you accept it in the mobile app.

Uploading does not modify or delete source environment variables, private
keys, or files. After you accept the proposal and verify the secret, remove
old local copies that you no longer need.

## Security

Agentknock uses a relay service to carry messages between the client and mobile
device. The security design treats the relay as untrusted. The client and
mobile app are both open source and protect messages with end-to-end encryption
and authentication. After you verify a pairing, the relay cannot obtain secret
values, read other protected contents, or alter an accepted protected message
without detection. It can observe routing metadata, message sizes, timing, and
traffic relationships, and it can deny service.

Starting a pairing is unauthenticated: anyone who knows the pairing address can
send a request. Confirm the full 12-digit verification code before you approve
a pairing on the mobile device. The code identifies the exact client that you
intend to trust, including when multiple pairing requests are pending. It also
detects substitution by the relay and lets the relay remain outside the trust
boundary. Reject the pairing if you cannot confirm the complete code.

Agentknock never writes delivered secret values to disk. It opens the selected
executable before asking the device to prepare the secrets, reports its path
and hash when available, and revalidates it after approval. Linux executes a
retained native executable directly. macOS must execute its path and therefore
has a small final pathname race after revalidation.

For directly executed shebang scripts at most 16 KiB, Agentknock also sends the
entire source to the device for use in AI review by supporting devices. Invalid
UTF-8 sequences become replacement characters (`�`); hashing and execution use
the original bytes. Larger scripts omit the source and retain their full-file
hash. Source is sent before approval, may contain sensitive information, and
reaches the review service when included in AI review. Only the selected script
is captured; interpreter arguments and dependencies are not followed.

Agentknock is not a sandbox or privilege boundary. The approved command
controls the secrets that it receives and can print them, write them to disk,
or otherwise disclose them. Only approve secret access for commands that you
trust to handle the values safely. Descendant commands and other processes
with sufficient same-user inspection access might also observe them.

For the complete design, threat model, and limitations, read the
[Agentknock v1 cryptosystem](docs/cryptosystem.md) and
[command execution design](docs/command-execution.md).
The repository also includes [reproducible symbolic
analysis](verification/cryptosystem/README.md) of the v1 cryptosystem, with
claim-by-claim results, assumptions, and limitations.
Report suspected vulnerabilities according to the
[security policy](https://github.com/agentknock/agentknock-cli/security/policy).

## Supported platforms

Agentknock supports these client platforms:

| Platform | Release archive |
| --- | --- |
| x86-64 Linux | `agentknock-x86_64-unknown-linux-musl.tar.gz` |
| ARM64 Linux | `agentknock-aarch64-unknown-linux-musl.tar.gz` |
| Apple Silicon macOS | `agentknock-aarch64-apple-darwin.tar.gz` |

Each archive contains the Agentknock binary and license files. The Linux
archives contain a statically linked musl binary and do not require a system C
library. Linux command execution requires Linux 5.8 or later. SSH authentication
and Git signing also require a mounted `/proc` file system. The macOS archive
requires Apple Silicon and macOS 15 or later.

Agentknock supports WSL2 through its Linux environment. Use the archive that
matches the WSL2 architecture. Native Windows clients and Intel Macs are not
supported.

## Documentation

- [Client-device protocol]docs/client-device-protocol.md defines the
  end-to-end messages and operations exchanged by a client and paired device.
- [Client-relay protocol]docs/client-relay-protocol.md defines the WebSocket
  interface used by clients.
- [Cryptosystem]docs/cryptosystem.md defines the end-to-end cryptographic
  construction and threat model.
- [Command execution]docs/command-execution.md defines how the CLI selects,
  inspects, and starts an approved command.

The [Rust API documentation](https://docs.rs/agentknock) describes the
unstable interface for embedding Agentknock in applications. Direct library
use is not currently recommended.

User documentation is available at
[agentknock.dev/docs](https://agentknock.dev/docs/).

## Contribute

Contributions are welcome as bug reports, feature proposals, and design
discussions in GitHub issues. The maintainers write the final implementation;
pull requests can serve as prototypes or reproductions but are not merged.
Read [Contributing to Agentknock](CONTRIBUTING.md) before you open an issue or
pull request.

## License

Agentknock is available under your choice of the
[Apache License 2.0](LICENSE-APACHE) or the [MIT License](LICENSE-MIT).