You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Update SIGNING.md with comprehensive token documentation including
CI/CD example, key resolution order, and migration guide. Update
README.md, QUICK_START.md, and CONTEXT.md to reflect both token
and pairing signing options.
Agent-Logs-Url: https://github.com/codeSamuraii/pyfuse/sessions/8f882d7e-4c69-4476-b493-2971452e71ac
Co-authored-by: codeSamuraii <17270548+codeSamuraii@users.noreply.github.com>
Cryptographically sign serialized tasks so that workers only execute code from trusted clients. The signing system uses a PIN-based pairing protocol — no manual key management required.
3
+
Cryptographically sign serialized tasks so that workers only execute code from trusted clients. Two key distribution methods are available:
4
+
5
+
-**Token-based** (recommended for automation): Generate a shared token offline, distribute via environment variables or secrets management. No real-time coordination needed.
6
+
-**PIN-based pairing** (recommended for interactive use): A client and worker exchange keys via a 6-digit PIN displayed on one side and entered on the other.
7
+
8
+
Both methods use the same underlying HMAC-SHA256 signing — they differ only in how the shared secret is established.
4
9
5
10
## Overview
6
11
7
12
By default, pyfuse workers execute any task they receive from the backend. When signing is enabled:
8
13
9
-
1. A client and worker **pair** once using a short PIN code.
10
-
2. Both sides derive a shared HMAC key from the PIN.
11
-
3. The client **signs** every task with HMAC-SHA256 before submitting it.
12
-
4. The worker **verifies** the signature before executing the task.
14
+
1. A client and worker share a cryptographic key (via **token** or **pairing**).
15
+
2. The client **signs** every task with HMAC-SHA256 before submitting it.
16
+
3. The worker **verifies** the signature before executing the task.
13
17
14
18
Tasks with missing or invalid signatures are rejected.
15
19
16
-
## Quick start
20
+
## Quick start — Token (recommended for CI/CD)
21
+
22
+
### 1. Generate a token
23
+
24
+
```bash
25
+
pyfuse token generate
26
+
```
27
+
28
+
```
29
+
Token generated and saved to ~/.pyfuse/token
30
+
31
+
Token: a1b2c3d4e5f6...
32
+
33
+
Set this on both client and worker:
34
+
export PYFUSE_SIGNING_TOKEN=a1b2c3d4e5f6...
35
+
```
36
+
37
+
### 2. Distribute the token
38
+
39
+
Copy the token to both the client and worker machines. The recommended method is an environment variable:
40
+
41
+
```bash
42
+
# On both client and worker
43
+
export PYFUSE_SIGNING_TOKEN=a1b2c3d4e5f6...
44
+
```
45
+
46
+
For CI/CD, store the token as a secret in your CI provider (GitHub Actions secrets, GitLab CI variables, etc.) and inject it as `PYFUSE_SIGNING_TOKEN`.
47
+
48
+
Alternatively, copy the `~/.pyfuse/token` file to both machines.
2.**`~/.pyfuse/token` file** — hex-encoded token written by `pyfuse token generate`
137
+
3.**`~/.pyfuse/{client,worker}.key` file** — raw bytes from PIN-based pairing
138
+
139
+
This means you can migrate from pairing to tokens without disruption: set the environment variable and it takes precedence over any existing pairing key.
140
+
141
+
### Token signing
142
+
143
+
```
144
+
Generate (once) Distribute
145
+
────────────── ──────────
146
+
pyfuse token generate Copy token to CI secrets,
147
+
│ env vars, or config
148
+
└─→ random 32-byte token │
149
+
saved to ~/.pyfuse/token │
150
+
▼
151
+
Client Worker
152
+
────── ──────
153
+
Load token Load token
154
+
│ │
155
+
├── HMAC-SHA256(key, task_json) │
156
+
├── attach signature │
157
+
├── submit to backend ──────────────→ │
158
+
│ ├── extract signature
159
+
│ ├── HMAC-SHA256(key, task_json)
160
+
│ ├── constant-time compare
161
+
│ │
162
+
│ ├── match? → execute
163
+
│ └── mismatch? → reject
164
+
```
165
+
83
166
### Pairing protocol
84
167
85
168
The pairing protocol is inspired by SPAKE2 and SAS-based verification:
@@ -123,7 +206,7 @@ Enter PIN: 482913 Enter PIN: 482913
123
206
124
207
### Task signing
125
208
126
-
Once paired, the client signs tasks with HMAC-SHA256:
209
+
Once a shared key is established (via token or pairing), the client signs tasks with HMAC-SHA256:
127
210
128
211
```
129
212
Client Worker
@@ -144,13 +227,41 @@ The signature covers the entire task payload — graph JSON, function name, argu
144
227
145
228
## CLI reference
146
229
230
+
### `pyfuse token generate`
231
+
232
+
```bash
233
+
pyfuse token generate [--force]
234
+
```
235
+
236
+
Generates a random 32-byte signing token and saves it to `~/.pyfuse/token`. Prints the hex-encoded token and usage instructions.
237
+
238
+
| Flag | Description |
239
+
|------|-------------|
240
+
|`--force`| Overwrite an existing token |
241
+
242
+
### `pyfuse token show`
243
+
244
+
```bash
245
+
pyfuse token show
246
+
```
247
+
248
+
Displays the current token source (environment variable or file) and a truncated preview.
249
+
250
+
### `pyfuse token clear`
251
+
252
+
```bash
253
+
pyfuse token clear
254
+
```
255
+
256
+
Removes the saved `~/.pyfuse/token` file.
257
+
147
258
### `pyfuse worker --pair`
148
259
149
260
```bash
150
261
pyfuse worker --backend URL --pair
151
262
```
152
263
153
-
Generates a PIN, pairs with a client, then starts serving with signing automatically enabled. This is the recommended way to set up a signed worker.
264
+
Generates a PIN, pairs with a client, then starts serving with signing automatically enabled. This is the recommended way to set up a signed worker interactively.
When `--require-signing` is set, the worker loads `~/.pyfuse/worker.key`and rejects any task that is unsigned or has an invalid signature. If no key file is found, the worker exits with an error.
287
+
When `--require-signing` is set, the worker loads signing key material using the standard resolution order (env var → token file → pairing key) and rejects any task that is unsigned or has an invalid signature. If no key material is found, the worker exits with an error.
177
288
178
289
## Programmatic usage
179
290
@@ -182,7 +293,7 @@ When `--require-signing` is set, the worker loads `~/.pyfuse/worker.key` and rej
182
293
```python
183
294
from pyfuse.core.signing import derive_key, compute_signature, verify_signature
184
295
185
-
# After pairing, both sides have the same shared_key
296
+
# After pairing or with a token, both sides have the same shared_key
Then update the `PYFUSE_SIGNING_TOKEN` secret in your CI provider and restart workers.
406
+
407
+
### Clearing credentials
248
408
249
409
```bash
410
+
# Token
411
+
pyfuse token clear
412
+
413
+
# Pairing keys
250
414
pyfuse pair --clear
251
415
pyfuse pair --role worker --clear
252
416
```
253
417
254
418
## Troubleshooting
255
419
256
-
**"Signing is enabled but no shared key found"**
257
-
-Run `pyfuse worker --pair` or `pyfuse pair --role worker` first to establish a shared key.
420
+
**"Signing is enabled but no key material found"**
421
+
-Set `PYFUSE_SIGNING_TOKEN`, run `pyfuse token generate`, or run `pyfuse pair`to establish key material.
258
422
259
423
**"Task is unsigned but signing is enabled"**
260
-
- The client is not signing tasks. Ensure `~/.pyfuse/client.key` exists (run `pyfuse pair`).
424
+
- The client is not signing tasks. Ensure the token is set via `PYFUSE_SIGNING_TOKEN` or `~/.pyfuse/token`, or that `~/.pyfuse/client.key` exists (from pairing).
261
425
262
426
**"Task signature verification failed"**
263
-
- The client and worker have different keys. Re-pair both sides.
427
+
- The client and worker have different keys. Ensure both sides use the same token or re-pair.
264
428
265
429
**"PIN mismatch — pairing failed"**
266
430
- The PINs entered on client and worker don't match. Try again.
267
431
268
432
**"Pairing timed out"**
269
-
- Both sides must run pairing within the timeout window (default: 60s).
433
+
- Both sides must run pairing within the timeout window (default: 60s). Consider using tokens instead for automated setups.
0 commit comments