repl: wire #13 secrets — scrub outbound, rehydrate stream + tool args
Plumbs the secrets.lua module (commit e4b818b) into the conversation
pipeline. Hook points:
ask_ai — scrub_messages(ctx:to_messages(), mode) before
call_broker; rehydrate streamed deltas via
streaming_rehydrator so the user sees real values
while text_parts accumulates rehydrated chunks
(final_resp is plain — CMD: / DELEGATE: extractors
see plain values)
MCP dispatch — dispatch_tool_call rehydrates the args table before
sess:call_tool so the trusted MCP server receives
real values (the model emitted placeholders because
it saw a scrubbed context)
DELEGATE: & :delegate
— scrub sub_msgs before broker.chat; rehydrate sub_text
before appending to context, so future turns see
real values restored
Phase 5 summarize-on-evict
— scrub sum_msgs before broker.chat; rehydrate the
reply that becomes ctx.summary
:memory summarize
— same scrub + rehydrate pair
Mode resolution per call: model_cfg.redact → config.secrets.default →
"vault+autodetect" if vault loaded, else "off".
ctx storage convention: PLAIN values throughout. The scrub happens at
the egress (broker call) per the active redact mode; ctx.turns never
holds placeholders for content the user typed or executor produced.
The model's own emissions (assistant tool_call arguments) may carry
placeholders because the model saw the scrubbed context — rehydrated
at MCP dispatch and otherwise harmless on re-serialization (idempotent
re-scrubbing).
New meta:
:secrets [status] vault entries, placeholders allocated this
session, active broker mode. Never prints
actual values (vault file is itself a
secret per gotcha 7).
:secrets check <text> dry-run scrub against the active broker's
mode — shows the output transformation.
Documented in config.lua with a commented-out block + per-broker
redact field example.
Deferred to a follow-up issue (clearly scoped):
- safety.lua broker call sites (Norris main loop, is_destructive
LLM second-opinion probe) — same wiring pattern, but they don't
currently see secrets_session; needs threading through helpers.
- @-mention file content is appended PLAIN to ctx and scrubbed at
egress alongside the rest of the user turn (covered by the
ask_ai scrub).
- exec output streamed live to terminal is pre-scrub (user sees
real values in their own shell — by design); the captured-for-
context copy is scrubbed at egress alongside the rest.
This is the "full scope" implementation chosen via AskUserQuestion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+18
@@ -71,6 +71,24 @@ return {
|
||||
-- post_cmd = (os.getenv("HOME") or ".") .. "/.aish/hooks/post-cmd",
|
||||
-- },
|
||||
|
||||
-- Issue #13: secret redaction. Vault is a separate file at ~/.aish/
|
||||
-- secrets.lua (mode 0600 enforced). When set, outbound broker messages
|
||||
-- are scrubbed: vault literals + autodetect heuristics (OpenAI sk-,
|
||||
-- OpenRouter sk-or-v1-, GitHub ghp_/gho_/ghs_, AWS AKIA, JWT eyJ...,
|
||||
-- SSH/GPG PRIVATE KEY headers) become $AISH_SECRET_NNN placeholders.
|
||||
-- The streamed reply is rehydrated before display so the user sees
|
||||
-- real values. Per-broker override via models[*].redact:
|
||||
-- "off" -- no scrubbing (trusted local)
|
||||
-- "vault" -- vault literals only
|
||||
-- "vault+autodetect" -- + heuristics (default when vault loaded)
|
||||
-- "stealth" -- + heuristics, opaque decoys, no rehydrate
|
||||
-- Default per-broker is the global config.secrets.default, falling
|
||||
-- back to "vault+autodetect" when vault loaded, else "off".
|
||||
-- secrets = {
|
||||
-- vault = "~/.aish/secrets.lua",
|
||||
-- default = "vault+autodetect", -- applies when models[*].redact is nil
|
||||
-- },
|
||||
|
||||
-- Issue #8: background CMD (CMD&: marker). Requires history.dir set
|
||||
-- (logs land at <history.dir>/bg/<id>.log + .status sidecar). The
|
||||
-- feature is always-on once history.dir exists — no config flag — but
|
||||
|
||||
Reference in New Issue
Block a user