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.
This commit is contained in:
parent
73e3488edc
commit
7835aa4dc5
|
|
@ -87,12 +87,25 @@ export framework="soc2" out="evidence.zip" db=db_default:
|
||||||
demo:
|
demo:
|
||||||
shards build cre && {{binary}} demo
|
shards build cre && {{binary}} demo
|
||||||
|
|
||||||
|
[group('demo')]
|
||||||
|
tui-demo seconds="8":
|
||||||
|
shards build cre && {{binary}} tui-demo --seconds={{seconds}}
|
||||||
|
|
||||||
[group('demo')]
|
[group('demo')]
|
||||||
demo-full:
|
demo-full:
|
||||||
docker compose -f docker/docker-compose.yml up -d
|
docker compose -f docker/docker-compose.yml up -d
|
||||||
@echo "Waiting for services..."
|
@echo "Waiting for services to be healthy..."
|
||||||
@sleep 8
|
@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')]
|
[group('demo')]
|
||||||
demo-full-down:
|
demo-full-down:
|
||||||
|
|
@ -102,6 +115,10 @@ demo-full-down:
|
||||||
demo-full-logs:
|
demo-full-logs:
|
||||||
docker compose -f docker/docker-compose.yml logs -f --tail=50
|
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
|
# Test
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ module CRE::Cli
|
||||||
export --framework=<name> generate signed compliance evidence bundle
|
export --framework=<name> generate signed compliance evidence bundle
|
||||||
audit verify verify hash chain + HMAC ratchet + Merkle batches
|
audit verify verify hash chain + HMAC ratchet + Merkle batches
|
||||||
demo tier-1 zero-deps demo (SQLite + .env rotator)
|
demo tier-1 zero-deps demo (SQLite + .env rotator)
|
||||||
|
tui-demo 8-second TUI preview with synthetic events
|
||||||
version print version
|
version print version
|
||||||
help this message
|
help this message
|
||||||
|
|
||||||
|
|
@ -43,14 +44,15 @@ module CRE::Cli
|
||||||
when "version"
|
when "version"
|
||||||
io.puts CRE::VERSION
|
io.puts CRE::VERSION
|
||||||
0
|
0
|
||||||
when "run" then Commands::Run.new.execute(argv, io)
|
when "run" then Commands::Run.new.execute(argv, io)
|
||||||
when "watch" then Commands::Watch.new.execute(argv, io)
|
when "watch" then Commands::Watch.new.execute(argv, io)
|
||||||
when "check" then Commands::Check.new.execute(argv, io)
|
when "check" then Commands::Check.new.execute(argv, io)
|
||||||
when "rotate" then Commands::Rotate.new.execute(argv, io)
|
when "rotate" then Commands::Rotate.new.execute(argv, io)
|
||||||
when "policy" then Commands::Policy.new.execute(argv, io)
|
when "policy" then Commands::Policy.new.execute(argv, io)
|
||||||
when "export" then Commands::Export.new.execute(argv, io)
|
when "export" then Commands::Export.new.execute(argv, io)
|
||||||
when "audit" then Commands::Audit.new.execute(argv, io)
|
when "audit" then Commands::Audit.new.execute(argv, io)
|
||||||
when "demo" then Commands::Demo.new.execute(argv, io)
|
when "demo" then Commands::Demo.new.execute(argv, io)
|
||||||
|
when "tui-demo" then Commands::TuiDemo.new.execute(argv, io)
|
||||||
else
|
else
|
||||||
io.puts "unknown subcommand: #{subcommand}"
|
io.puts "unknown subcommand: #{subcommand}"
|
||||||
io.puts USAGE
|
io.puts USAGE
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,5 @@ require "./commands/policy"
|
||||||
require "./commands/export"
|
require "./commands/export"
|
||||||
require "./commands/audit"
|
require "./commands/audit"
|
||||||
require "./commands/demo"
|
require "./commands/demo"
|
||||||
|
require "./commands/tui_demo"
|
||||||
require "./commands/version"
|
require "./commands/version"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue