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
Copy file name to clipboardExpand all lines: docs/architecture.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -228,7 +228,7 @@ Daemon installation paths per OS, log locations, and the configuration table liv
228
228
Three places own the security contract:
229
229
230
230
-**Encryptor**: the only path that generates age identities, derives recipients, and encrypts content. Plaintext never leaves this layer for any artefact going to disk or to the network.
231
-
-**Sanitiser**: the only place that decides what is safe to encrypt. Never-sync paths and literal-secret detection are hard-coded rules, not opt-in policy. Loosening either requires a documented reason.
231
+
-**Sanitiser**: the only place that decides what is safe to encrypt. Never-sync paths are hard-coded rules. Literal-secret detection is a **known-credential-format** guard (vendor key prefixes, AWS/GitHub/GitLab/Slack/Google tokens, age identities, PEM private keys, and JWTs in strict mode) — not a general secret scanner: a plain password or bespoke token with no recognised shape is not caught, so encryption, not the scan, is the real protection. The scan's job is to keep well-known credentials out of git history. Its breadth and the base64 redactor are tuned through the `[security]` config, resolved by `securityToPolicy`; the never-sync rules are not configurable.
232
232
-**Tar bundler**: exists because some agent assets are directory-shaped. The tar is built in memory before encryption so an intermediate plaintext archive never lands on disk.
233
233
234
234
Private keys stay on disk in the local runtime directory (`~/.config/agentsync/key.txt` by default on Unix, with restrictive permissions). They are never committed and never logged.
Copy file name to clipboardExpand all lines: docs/commands.md
+16-11Lines changed: 16 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -346,18 +346,23 @@ agentsync config set security.allowSecretValues '["AKIA-not-a-real-key"]'
346
346
|`sync.debounceMs`| integer 50–10000 | Daemon quiet-window before an auto-push. |
347
347
|`sync.autoPush`| boolean | Whether the daemon auto-pushes on change. |
348
348
|`claudePlugins.syncPlugins`| boolean | Record the Claude plugin reinstall manifest on push. |
349
-
|`security.secretScan`|`standard`\|`strict`\|`off`| Push-time secret-scan mode (see note). |
350
-
|`security.allowSecretValues`| string[] (JSON) | Literal values exempt from secret detection and base64 redaction (see note). |
351
-
|`security.redactBase64Values`| boolean | Replace long base64-looking JSON values with a redaction placeholder (see note). |
352
-
353
-
> **`security.*` are recorded but not yet enforced.** This release stores the
354
-
> policy in `agentsync.toml`; the push-time secret scanner starts honouring
355
-
> `secretScan`, `allowSecretValues`, and `redactBase64Values` in a follow-up
356
-
> change. Until then the scan runs with its built-in defaults regardless of
357
-
> these values. Note `agentsync.toml` is committed in **plaintext** (only
358
-
> artefacts are encrypted), so `allowSecretValues` is for exempting legitimate
349
+
|`security.secretScan`|`standard`\|`strict`\|`off`| Push-time secret-scan mode. `standard` = built-in credential patterns; `strict` also flags JWTs; `off` disables the artefact-body scan. |
350
+
|`security.allowSecretValues`| string[] (JSON) | Literal values exempt from secret detection and base64 redaction. |
351
+
|`security.redactBase64Values`| boolean | When `true` (default), redact long base64-looking JSON values; set `false` if a config legitimately stores base64 that must round-trip. |
352
+
353
+
> **What the secret scan is — and is not.** It matches a fixed set of
> tokens, age identities, PEM private-key headers; `strict` adds JWTs). It is
356
+
> **not** a general secret scanner — a plain password, a bespoke token, or a
357
+
> connection string with no recognised shape passes through. Encryption is the
358
+
> real protection; the scan only stops well-known credentials from entering git
359
+
> history. `off` disables the artefact-body scan, but **skill-bundle interiors
360
+
> are always scanned at `standard`** as a fail-safe. `agentsync.toml` itself is
361
+
> committed in **plaintext**, so `allowSecretValues` is for exempting legitimate
359
362
> high-entropy *non-secret* values — never paste a real credential there.
360
-
> `config set` refuses a recognised credential in any other value.
363
+
> `config set` refuses to store a recognised credential in any key other than
364
+
> `security.allowSecretValues`.
365
+
> See [Push aborts because secrets were detected](operations.md#push-aborts-because-secrets-were-detected).
361
366
362
367
**Outcome**: `list` and `get` are read-only. `set` validates the new value against the full config schema (so an out-of-range debounce or an invalid enum is rejected before anything is written), then — because `agentsync.toml` is shared across machines — reconciles fast-forward, commits, and pushes the change, exactly like `key add`.
Copy file name to clipboardExpand all lines: docs/operations.md
+22-1Lines changed: 22 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -310,7 +310,7 @@ and behaviour reference.
310
310
311
311
### Push aborts because secrets were detected
312
312
313
-
The sanitiser found literal tokens or credentials in content that would otherwise be encrypted and committed. Sanitiser hits are intentionally a hard stop — they prevent the agent from leaking a secret into the vault, where it would persist even after subsequent pushes.
313
+
The sanitiser found a literal token or credential in content that would otherwise be encrypted and committed. Sanitiser hits are intentionally a hard stop — a secret in git history persists even after later pushes and is one key-compromise away from retroactive exposure.
314
314
315
315
Fix:
316
316
@@ -320,6 +320,27 @@ Fix:
320
320
321
321
Do not bypass this by editing the vault manually.
322
322
323
+
#### What the scan actually covers
324
+
325
+
Be precise about the guarantee. The scan is a **known-credential-format** detector, not a general secret scanner. It matches a fixed set of high-precision patterns:
- JWTs (`eyJ….eyJ….…`) — only when `security.secretScan = "strict"`.
331
+
332
+
What it does **not** catch: a plain password, a bespoke or internal API token, a database connection string, or any credential with no recognised shape. Those flow into the (encrypted) vault unflagged. **Encryption is the real protection** — the scan exists only to keep well-known credentials out of git history. Treat a clean push as "no recognised credential format found", not "no secrets present".
333
+
334
+
#### Tuning the scan
335
+
336
+
`agentsync config set security.secretScan <mode>`:
337
+
338
+
-`standard` (default) — the built-in credential patterns above, minus JWTs.
339
+
-`strict` — adds JWT detection. Use when no legitimate JWT appears in your config.
340
+
-`off` — disables the artefact-body scan. **Skill-bundle interiors are still scanned at `standard`** as a fail-safe, and encryption still applies.
341
+
342
+
`agentsync config set security.allowSecretValues '["<literal>"]'` exempts a specific value the scanner false-positives on (and exempts it from base64 redaction). `agentsync config set security.redactBase64Values false` stops AgentSync replacing long base64-looking JSON values with a placeholder, for configs that legitimately store such values. See [config](commands.md#config).
0 commit comments