From 7835aa4dc512c94dc680550846c407d3904b6de4 Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Wed, 29 Apr 2026 01:34:25 -0400 Subject: [PATCH] feat(demo): add tui-demo command + fix demo-full recipe cre tui-demo (8 seconds default) renders the live TUI with synthetic events: 4 fake credentials going through rotation states, a policy violation, drift detection, an apply-step failure, and a critical alert. Lets you see what watch mode looks like without seeding any real data. demo-full: dropped the bogus --config=... flag (cre run never implemented config-file loading; uses env vars + --db). Now exports the four service URLs (DATABASE_URL, AWS_ENDPOINT, VAULT_ADDR, GITHUB_API_BASE) at the user's manually-set ports (6022/45666/18201/7115) and runs the daemon against the real Postgres. Added demo-full-status to peek at container health. --- .../credential-rotation-enforcer/justfile | 21 +++- .../src/cre/cli/cli.cr | 18 +-- .../src/cre/cli/commands.cr | 1 + .../src/cre/cli/commands/tui_demo.cr | 23 ++++ .../src/cre/demo/tui_demo.cr | 116 ++++++++++++++++++ 5 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/commands/tui_demo.cr create mode 100644 PROJECTS/intermediate/credential-rotation-enforcer/src/cre/demo/tui_demo.cr diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/justfile b/PROJECTS/intermediate/credential-rotation-enforcer/justfile index 5293beb8..553f7bfa 100644 --- a/PROJECTS/intermediate/credential-rotation-enforcer/justfile +++ b/PROJECTS/intermediate/credential-rotation-enforcer/justfile @@ -87,12 +87,25 @@ export framework="soc2" out="evidence.zip" db=db_default: demo: shards build cre && {{binary}} demo +[group('demo')] +tui-demo seconds="8": + shards build cre && {{binary}} tui-demo --seconds={{seconds}} + [group('demo')] demo-full: docker compose -f docker/docker-compose.yml up -d - @echo "Waiting for services..." + @echo "Waiting for services to be healthy..." @sleep 8 - shards build cre && {{binary}} run --config=config/demo-full.cr + shards build cre + @echo "" + @echo "Stack up. Daemon will run against Postgres on port 6022." + @echo "Press Ctrl+C to stop the daemon, then 'just demo-full-down' to tear down the stack." + @echo "" + DATABASE_URL=postgres://cre:cre@localhost:6022/cre \ + AWS_ENDPOINT=http://localhost:45666 \ + VAULT_ADDR=http://localhost:18201 \ + GITHUB_API_BASE=http://localhost:7115 \ + {{binary}} run --db=postgres://cre:cre@localhost:6022/cre [group('demo')] demo-full-down: @@ -102,6 +115,10 @@ demo-full-down: demo-full-logs: docker compose -f docker/docker-compose.yml logs -f --tail=50 +[group('demo')] +demo-full-status: + docker compose -f docker/docker-compose.yml ps + # ============================================================================= # Test # ============================================================================= diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/cli.cr b/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/cli.cr index 362eb0aa..3d7b60a7 100644 --- a/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/cli.cr +++ b/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/cli.cr @@ -24,6 +24,7 @@ module CRE::Cli export --framework= generate signed compliance evidence bundle audit verify verify hash chain + HMAC ratchet + Merkle batches demo tier-1 zero-deps demo (SQLite + .env rotator) + tui-demo 8-second TUI preview with synthetic events version print version help this message @@ -43,14 +44,15 @@ module CRE::Cli when "version" io.puts CRE::VERSION 0 - when "run" then Commands::Run.new.execute(argv, io) - when "watch" then Commands::Watch.new.execute(argv, io) - when "check" then Commands::Check.new.execute(argv, io) - when "rotate" then Commands::Rotate.new.execute(argv, io) - when "policy" then Commands::Policy.new.execute(argv, io) - when "export" then Commands::Export.new.execute(argv, io) - when "audit" then Commands::Audit.new.execute(argv, io) - when "demo" then Commands::Demo.new.execute(argv, io) + when "run" then Commands::Run.new.execute(argv, io) + when "watch" then Commands::Watch.new.execute(argv, io) + when "check" then Commands::Check.new.execute(argv, io) + when "rotate" then Commands::Rotate.new.execute(argv, io) + when "policy" then Commands::Policy.new.execute(argv, io) + when "export" then Commands::Export.new.execute(argv, io) + when "audit" then Commands::Audit.new.execute(argv, io) + when "demo" then Commands::Demo.new.execute(argv, io) + when "tui-demo" then Commands::TuiDemo.new.execute(argv, io) else io.puts "unknown subcommand: #{subcommand}" io.puts USAGE diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/commands.cr b/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/commands.cr index 3727574a..1f8d69b4 100644 --- a/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/commands.cr +++ b/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/commands.cr @@ -11,4 +11,5 @@ require "./commands/policy" require "./commands/export" require "./commands/audit" require "./commands/demo" +require "./commands/tui_demo" require "./commands/version" diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/commands/tui_demo.cr b/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/commands/tui_demo.cr new file mode 100644 index 00000000..704026fd --- /dev/null +++ b/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/cli/commands/tui_demo.cr @@ -0,0 +1,23 @@ +# =================== +# ©AngelaMos | 2026 +# tui_demo.cr +# =================== + +require "../../demo/tui_demo" + +module CRE::Cli::Commands + class TuiDemo + def execute(argv : Array(String), io : IO) : Int32 + _help_requested = false + seconds = 8 + OptionParser.parse(argv) do |parser| + parser.banner = "Usage: cre tui-demo [--seconds=N]" + parser.on("--seconds=N", "duration in seconds (default 8)") { |s| seconds = s.to_i } + parser.on("-h", "--help") { _help_requested = true; io.puts parser } + end + return 0 if _help_requested + + CRE::Demo::TuiDemo.run(io, seconds) + end + end +end diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/demo/tui_demo.cr b/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/demo/tui_demo.cr new file mode 100644 index 00000000..dc0d0212 --- /dev/null +++ b/PROJECTS/intermediate/credential-rotation-enforcer/src/cre/demo/tui_demo.cr @@ -0,0 +1,116 @@ +# =================== +# ©AngelaMos | 2026 +# tui_demo.cr +# =================== + +require "../engine/event_bus" +require "../tui/tui" +require "../events/credential_events" +require "../events/system_events" + +module CRE::Demo + # TuiDemo synthesizes a stream of fake events so you can SEE what the + # live TUI looks like without running the full daemon. Eight seconds of + # narrated activity, then it shuts down cleanly. + module TuiDemo + def self.run(io : IO = STDOUT, seconds : Int32 = 8) : Int32 + bus = CRE::Engine::EventBus.new + tui = CRE::Tui::Tui.new(bus, io: io, refresh_interval: 150.milliseconds) + bus.run + tui.start + + script_fiber = spawn do + run_script(bus, seconds) + end + + sleep seconds.seconds + 0.5.seconds + tui.stop + bus.stop + 0 + end + + private def self.run_script(bus : CRE::Engine::EventBus, seconds : Int32) : Nil + cred_a = UUID.random + cred_b = UUID.random + cred_c = UUID.random + cred_d = UUID.random + + # 0.5s: a policy violation arrives + sleep 0.5.seconds + bus.publish CRE::Events::PolicyViolation.new(cred_a, "production-databases", "credential exceeded max_age=30.days (last rotated 47 days ago)") + + # 1.0s: rotation A starts + sleep 0.5.seconds + rot_a = UUID.random + bus.publish CRE::Events::RotationStarted.new(cred_a, rot_a, "aws_secretsmgr") + bus.publish CRE::Events::RotationStepStarted.new(cred_a, rot_a, :generate) + + # 1.5s: drift detected on B + sleep 0.5.seconds + bus.publish CRE::Events::DriftDetected.new(cred_b, "abc123def456", "ffe098cba321") + + # 2.0s: rotation A finishes generate, starts apply + sleep 0.5.seconds + bus.publish CRE::Events::RotationStepCompleted.new(cred_a, rot_a, :generate) + bus.publish CRE::Events::RotationStepStarted.new(cred_a, rot_a, :apply) + + # 2.5s: rotation A finishes apply, starts verify + sleep 0.5.seconds + bus.publish CRE::Events::RotationStepCompleted.new(cred_a, rot_a, :apply) + bus.publish CRE::Events::RotationStepStarted.new(cred_a, rot_a, :verify) + + # 3.0s: rotation C starts (parallel) + sleep 0.5.seconds + rot_c = UUID.random + bus.publish CRE::Events::RotationStarted.new(cred_c, rot_c, "github_pat") + bus.publish CRE::Events::RotationStepStarted.new(cred_c, rot_c, :generate) + + # 3.5s: rotation A finishes verify, starts commit + sleep 0.5.seconds + bus.publish CRE::Events::RotationStepCompleted.new(cred_a, rot_a, :verify) + bus.publish CRE::Events::RotationStepStarted.new(cred_a, rot_a, :commit) + + # 4.0s: rotation A complete! + sleep 0.5.seconds + bus.publish CRE::Events::RotationStepCompleted.new(cred_a, rot_a, :commit) + bus.publish CRE::Events::RotationCompleted.new(cred_a, rot_a) + + # 4.5s: alert + a fourth rotation (vault) starts + sleep 0.5.seconds + bus.publish CRE::Events::AlertRaised.new(CRE::Events::Severity::Warn, "5 credentials approaching expiry within 7 days") + rot_d = UUID.random + bus.publish CRE::Events::RotationStarted.new(cred_d, rot_d, "vault_dynamic") + bus.publish CRE::Events::RotationStepStarted.new(cred_d, rot_d, :generate) + + # 5.0s: rotation C finishes generate, then fails on apply + sleep 0.5.seconds + bus.publish CRE::Events::RotationStepCompleted.new(cred_c, rot_c, :generate) + bus.publish CRE::Events::RotationStepStarted.new(cred_c, rot_c, :apply) + + # 5.5s: rotation C apply fails! + sleep 0.5.seconds + bus.publish CRE::Events::RotationStepFailed.new(cred_c, rot_c, :apply, "GitHub API 403 - PAT lacks admin:org scope") + bus.publish CRE::Events::RotationFailed.new(cred_c, rot_c, "GitHub API 403 - PAT lacks admin:org scope") + + # 6.0s: rotation D progresses + sleep 0.5.seconds + bus.publish CRE::Events::RotationStepCompleted.new(cred_d, rot_d, :generate) + bus.publish CRE::Events::RotationStepStarted.new(cred_d, rot_d, :apply) + bus.publish CRE::Events::RotationStepCompleted.new(cred_d, rot_d, :apply) + bus.publish CRE::Events::RotationStepStarted.new(cred_d, rot_d, :verify) + + # 6.5s: critical alert + sleep 0.5.seconds + bus.publish CRE::Events::AlertRaised.new(CRE::Events::Severity::Critical, "1 critical: rotation_failure github_pat/deploy-bot needs manual intervention") + + # 7.0s: rotation D finishes + sleep 0.5.seconds + bus.publish CRE::Events::RotationStepCompleted.new(cred_d, rot_d, :verify) + bus.publish CRE::Events::RotationStepStarted.new(cred_d, rot_d, :commit) + bus.publish CRE::Events::RotationStepCompleted.new(cred_d, rot_d, :commit) + bus.publish CRE::Events::RotationCompleted.new(cred_d, rot_d) + rescue ex + # silence: we may be torn down mid-script + end + end +end