Magic Containers UX & safety + secret redaction (iter-21)#19
Conversation
- .claude/skills/hyalo/SKILL.md: document BM25 full-text search, links auto, and updated read/find syntax - .claude/skills/hyalo-tidy/SKILL.md: add views, types/lint, and broaden phase guidance - .claude/rules/knowledgebase.md: align example commands with current hyalo CLI - .gitignore: ignore .DS_Store everywhere Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses MC.1–MC.6 from the 2026-05-05 magic-containers bug report and
introduces the cross-cutting secret-redaction layer that iter-19 (storage
zone passwords) and iter-20 (database tokens) will consume.
Highlights:
- container template env: bare invocation no longer silently wipes all
vars. New flags --add / --remove / --update (granular merge), --replace-all
--env (explicit replace), --clear (explicit wipe), --list (show, redacted).
Destructive shrinks/wipes require typing "replace" / "wipe".
- container app delete: refuses by default if auto-managed Pull Zones
exist. --cascade also deletes them; --no-cascade prints orphan IDs and
a manual cleanup recipe.
- container app create: returns the full app document by default so
callers can chain template / endpoint ids without a follow-up app get.
--minimal opts back into {"id": "..."}. New --env KEY=VAL (repeatable)
applies env vars in one call.
- Cross-cutting redaction: env-var values are masked as <set, length=N>
in JSON, table, and text output by default. Opt in with the global
--reveal (all secrets) or --reveal-env KEY (a specific var).
Decision log + bunny-api-quirks updated. Adds wiremock tests for every
new behaviour (zero-arg refusal, --add merge, --remove, --list redaction,
cascade delete refusal/cascade/no-cascade).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (2)
📝 WalkthroughWalkthroughAdds a cross-cutting secret redaction layer and CLI reveal controls; implements granular container template env operations (add/update/remove/replace-all/clear/list) with typed confirmations; changes container app create to support repeatable --env and default full-object output with --minimal; makes app delete refuse orphaning auto-managed Pull Zones unless --cascade/--no-cascade is provided. Also updates extensive hyalo and knowledgebase docs and tests. ChangesDocumentation & Knowledgebase
Magic Containers UX & Safety Implementation
Sequence Diagram(s)sequenceDiagram
participant CLI
participant Commands as "commands::container"
participant API as "bunny API"
participant Redact as "redact::redact_env_in_json"
CLI->>Commands: container app create --env KEY=VAL (no --minimal)
Commands->>API: POST /apps (create)
API-->>Commands: 201 { id }
Commands->>API: GET /apps/{id} (fetch full object)
API-->>Commands: 200 { app with containerTemplates, endpoints, environmentVariables }
Commands->>Redact: redact_env_in_json(app_json, redact_cfg)
Redact-->>Commands: redacted app_json
Commands-->>CLI: print redacted JSON/table output
CLI->>Commands: container app delete {id}
Commands->>API: GET /apps/{id} (enumerate endpoints)
API-->>Commands: 200 { endpoints with pullZoneId }
alt auto-PZ found & no cascade flags
Commands-->>CLI: abort with orphan IDs and guidance
else cascade requested
Commands->>API: DELETE /apps/{id}
API-->>Commands: 204
Commands->>API: DELETE /pull-zones/{pzId} (for each)
API-->>Commands: 204 / error
Commands-->>CLI: report success/failure
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
Pull request overview
This PR improves the Magic Containers CLI UX/safety (env-var management, safer deletes, richer create output) and introduces a cross-cutting secret redaction layer that masks sensitive values by default unless explicitly revealed.
Changes:
- Add global redaction controls (
--reveal,--reveal-env) and a newsrc/redact.rsredaction module; wire redaction into Container JSON output andtemplate env --list. - Rework
container template envinto explicit modes (--add/--remove/--update,--replace-all --env,--clear,--list) and refuse the historical zero-flag footgun. - Make
container app createreturn the full app object by default (with--minimalfor legacy), and makecontainer app deletedetect/refuse orphaning auto-managed Pull Zones unless--cascade/--no-cascadeis chosen.
Reviewed changes
Copilot reviewed 13 out of 15 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/cli_container.rs | Adds/updates CLI tests for new create output, env command modes, redaction, and delete cascade behavior. |
| src/redact.rs | Introduces redaction config + JSON redaction walker for env-var values. |
| src/main.rs | Constructs a RedactConfig from global CLI flags and passes it into container handlers. |
| src/commands/container.rs | Implements new env subcommand behavior, app create output changes, app delete cascade logic, and JSON printing with redaction. |
| src/cli.rs | Adds global --reveal / --reveal-env flags; expands container app/template subcommand flags. |
| hoppy-knowledgebase/iterations/iteration-21-magic-containers-ux.md | New iteration plan doc capturing MC.1–MC.6 scope and redaction foundation. |
| hoppy-knowledgebase/decision-log.md | Records design decisions for redaction, destructive confirmations, cascade delete, and create output changes. |
| hoppy-knowledgebase/api/bunny-api-quirks.md | Documents MC API quirks and the CLI mitigations (auto-PZ orphaning, env PUT semantics, plaintext env values). |
| CHANGELOG.md | Adds Unreleased entry describing the UX/safety changes and redaction behavior. |
| Cargo.toml | Enables serde_json preserve_order feature for deterministic JSON key ordering. |
| Cargo.lock | Locks new transitive dep (indexmap) from serde_json/preserve_order. |
| .gitignore | Ignores .DS_Store. |
| .claude/skills/hyalo/SKILL.md | Expands hyalo skill docs (search modes, flags, examples). |
| .claude/skills/hyalo-tidy/SKILL.md | Updates hyalo-tidy workflow examples to newer --index usage and adds view suggestions. |
| .claude/rules/knowledgebase.md | Updates knowledgebase rules/examples for hyalo usage. |
| if !yes { | ||
| let prompt = format!( | ||
| "Clear ALL {n} environment variable(s) on container {container_id}? \ | ||
| Type \"wipe\" to confirm:" | ||
| ); |
| if !yes && cur_n > 0 && cur_n > new_n { | ||
| let prompt = format!( | ||
| "Replace {cur_n} environment variable(s) with {new_n}? \ | ||
| Type \"replace\" to confirm:" | ||
| ); |
| // Read current set, apply add/update/remove in order, write back. | ||
| let current = c.get_container(app_id, container_id).await?; | ||
| let mut map: HashMap<String, String> = current | ||
| .environment_variables | ||
| .iter() | ||
| .map(|e| (e.name.clone(), e.value.clone().unwrap_or_default())) | ||
| .collect(); | ||
| for pair in add.iter().chain(update.iter()) { | ||
| let (k, v) = pair | ||
| .split_once('=') | ||
| .ok_or_else(|| anyhow::anyhow!("env '{pair}' is not in KEY=VALUE format"))?; | ||
| map.insert(k.to_owned(), v.to_owned()); |
| let output = mock_cmd("test-api-key", &server.uri()) | ||
| .args([ | ||
| "--yes", | ||
| "container", | ||
| "template", |
| - **Read frontmatter/metadata**: `hyalo find --file <path>`, `hyalo properties`, `hyalo tags` | ||
| - **Read content/sections**: `hyalo read <path>` or `hyalo read <path> --section "Heading"` | ||
| - **Mutate frontmatter**: `hyalo set`, `hyalo remove`, `hyalo append` | ||
| - **Auto-link**: `hyalo links auto --first-only --exclude-target-glob 'templates/*' --apply` |
- Always require typed phrase for `template env --clear` and a shrinking `--replace-all`, regardless of `--yes` (matches the iter-21 decision-log promise that `--yes` alone is not sufficient for these destructive ops) - Drop unused `yes` parameter from handle_template_env now that the gate no longer branches on it - Clarify granular ordering comment: adds/updates run before removes in a single invocation (clap groups values by flag name) - Document the `--clear` ↔ `--replace-all` exclusivity, the granular flag precedence, and the `--reveal` → `--reveal-env` precedence in the spec - Fix awkward "no env var unlocks it" wording in api quirks doc Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Addresses MC.1–MC.6 from the 2026-05-05 magic-containers field report and lands the cross-cutting secret-redaction layer that iter-19 (storage zone passwords) and iter-20 (database tokens) will consume.
container template envno longer silently wipes env vars. Bare invocation errors with a recipe. New flags--add/--remove/--update(granular merge),--replace-all --env(explicit destructive replace),--clear(explicit wipe),--list(show, redacted). Destructive shrinks/wipes require typing "replace" / "wipe" —--yesalone isn't enough.container app deletewon't orphan auto-managed Pull Zones. Refuses by default if any are detected;--cascadedeletes app + auto-PZs,--no-cascadedeletes app and prints orphan IDs with a cleanup recipe.container app createreturns the full app by default so callers can chain template / endpoint ids without a follow-upapp get.--minimalopts back into the legacy{"id": "..."}shape. New--env KEY=VAL(repeatable) applies env vars in one call.<set, length=N>in JSON, table, and text output by default. Opt in with the global--reveal(all secrets) or--reveal-env KEY(a specific var). Off by default even for--format jsonto keepjqpipelines safe.Test plan
cargo fmtcargo clippy --workspace --all-targets -- -D warningscargo test --workspace(all suites green; 9 new container tests covering MC.1/MC.3/MC.5/MC.6)--revealraw outputpullZoneId)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
--minimalavailable)--revealand per-key reveal options--cascadeor--no-cascadeprovidedDocumentation
Tests