fix(issue-12): scrub-secrets selects only records with a stored password

Review of 4176d75 (planner-reviewer-claude-01, N1): _cmd_scrub_secrets chose
records by the presence of broker.password, not by its value. The old
to_registry_block wrote "password": null even for an unauthenticated broker,
so on this repo's registry (901 of 903 records carry the null key, none a
real password) --dry-run reported "would scrub 901 record(s)" and a real run
would rewrite every one of them while holding the registry lock.

- registry.py: select on broker.get("password"); an empty key is no secret,
  and _atomic_write_record drops it on the record's next write anyway
- tests: the migration test gains a legacy "password": null record that must
  be neither listed nor rewritten (fails on 4176d75 with "would scrub 2")
- VERSIONS.md v4.4.4 item 2 and registry.md now describe the stored-value
  rule

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
This commit is contained in:
GodopuandClaude Opus 5 committed 2026-09-13 12:07:42 +09:00
1 parent 4176d7570f
commit 0fedae2f46
4 files changed
+23 -5

No files matched your search

+8 -2
View File
@@ -345,13 +345,19 @@ def _build_parser() -> argparse.ArgumentParser:
def _cmd_scrub_secrets(args) -> int:
"""Issue #12: strip broker.password from records written before
to_registry_block stopped persisting it. Every other field is kept."""
to_registry_block stopped persisting it. Every other field is kept.
Only a stored value counts. The old rule wrote `"password": null` even
for an unauthenticated broker; that empty key is no secret, and
_atomic_write_record drops it on the record's next write anyway, so
reporting or rewriting it here would list phantom secrets and hold the
registry lock over every record in a PoC registry."""
rd = args.registry_dir
found = []
with registry_lock(rd):
for record in list(_iter_records(rd)):
broker = record.get("broker")
if not (isinstance(broker, dict) and "password" in broker):
if not (isinstance(broker, dict) and broker.get("password")):
continue
found.append(record.get("job_id", "?"))
if not args.dry_run:
@@ -57,8 +57,9 @@ Reference implementation: [`./scripts/registry.py`](./scripts/registry.py)
overrides toggles like `MQTT_TLS`). It never carries the password (Issue #12):
every sub-agent can read the record, so `MQTT_PASSWORD` is layered back from
the environment (`.mam.env`) at connect time. Records written before that rule
are cleaned on their next write; `registry.py scrub-secrets [--dry-run]` cleans
a whole registry at once. `auth_token` and `dispatch_token` stay in the record
are cleaned on their next write; `registry.py scrub-secrets [--dry-run]` strips
every stored password from a whole registry at once (an empty `null` key left
by the old rule is not a secret; it goes on the record's next write). `auth_token` and `dispatch_token` stay in the record
because the agent's own `publish_event` needs them to sign and to pin its
attempt.
- `topic_prefix` → the events topic is `<topic_prefix>/events`.