diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/Makefile b/PROJECTS/intermediate/credential-rotation-enforcer/Makefile deleted file mode 100644 index 2c958897..00000000 --- a/PROJECTS/intermediate/credential-rotation-enforcer/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -# ©AngelaMos | 2026 -# Makefile - -.PHONY: build build-dev test test-unit test-integration lint format format-check demo demo-full demo-full-down check ci clean - -CRYSTAL ?= crystal -SHARDS ?= shards - -build: - $(SHARDS) build cre --release --no-debug - -build-dev: - $(SHARDS) build cre - -test: - $(CRYSTAL) spec --order=random - -test-unit: - $(CRYSTAL) spec spec/unit --order=random - -test-integration: - $(CRYSTAL) spec spec/integration --order=random - -lint: - @if [ -x ./bin/ameba ]; then \ - ./bin/ameba; \ - else \ - echo "ameba not installed (requires libpcre3-dev on Debian 13+); skipping lint"; \ - fi - -format: - $(CRYSTAL) tool format - -format-check: - $(CRYSTAL) tool format --check - -demo: - $(SHARDS) build cre && ./bin/cre demo - -demo-full: - docker compose -f docker/docker-compose.yml up -d - @echo "Waiting for services..." - @sleep 8 - $(SHARDS) build cre && ./bin/cre run --config=config/demo-full.cr - -demo-full-down: - docker compose -f docker/docker-compose.yml down -v - -check: - $(MAKE) format-check - $(MAKE) lint - $(MAKE) test - -ci: check - -clean: - rm -rf bin lib .shards .crystal coverage tmp diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/README.md b/PROJECTS/intermediate/credential-rotation-enforcer/README.md index af6d4d57..76ec532d 100644 --- a/PROJECTS/intermediate/credential-rotation-enforcer/README.md +++ b/PROJECTS/intermediate/credential-rotation-enforcer/README.md @@ -45,7 +45,7 @@ You'll see live narration of an in-memory SQLite + tempfile rotation, with audit ### Tier 2 - Full mocked stack (under 2 minutes) ```bash -make demo-full +just demo-full ``` Brings up Docker Compose with PostgreSQL 16, LocalStack (AWS Secrets Manager), HashiCorp Vault dev mode, and a fake-GitHub Flask service. CRE talks to all four with real network calls. @@ -109,7 +109,7 @@ All components are fibers in one OS process. The bus is in-process - Crystal cha ``` credential-rotation-enforcer/ ├── shard.yml Crystal manifest (1.20+) -├── Makefile build / test / demo / lint targets +├── justfile build / test / demo / lint recipes ├── policies/ USER policy files (compiled in) ├── src/cre/ │ ├── cli/ subcommand dispatch + 9 commands @@ -147,7 +147,7 @@ crystal spec # all 200+ tests crystal spec spec/unit # unit only (no DB required) DATABASE_URL=postgres://cre_test:cre_test@localhost:5432/cre_test \ crystal spec spec/integration # integration with real PG -make check # format + lint + unit +just ci # format + lint + unit ``` ## License diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/docker/docker-compose.yml b/PROJECTS/intermediate/credential-rotation-enforcer/docker/docker-compose.yml index 0f384a56..5794825d 100644 --- a/PROJECTS/intermediate/credential-rotation-enforcer/docker/docker-compose.yml +++ b/PROJECTS/intermediate/credential-rotation-enforcer/docker/docker-compose.yml @@ -3,12 +3,12 @@ services: postgres: - image: postgres:16 + image: postgres:18 environment: POSTGRES_USER: cre POSTGRES_PASSWORD: cre POSTGRES_DB: cre - ports: ["5432:5432"] + ports: ["6022:5432"] healthcheck: test: ["CMD-SHELL", "pg_isready -U cre"] interval: 3s @@ -20,7 +20,7 @@ services: environment: SERVICES: secretsmanager DEBUG: 0 - ports: ["4566:4566"] + ports: ["45666:4566"] vault: image: hashicorp/vault:latest @@ -28,7 +28,7 @@ services: environment: VAULT_DEV_ROOT_TOKEN_ID: dev-root-token VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200 - ports: ["8200:8200"] + ports: ["18201:8200"] command: vault server -dev fake-github: @@ -36,4 +36,4 @@ services: working_dir: /app volumes: ["./fake-github:/app:ro"] command: ["sh", "-c", "pip install --quiet flask && python /app/app.py"] - ports: ["7000:7000"] + ports: ["7115:7000"] diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/justfile b/PROJECTS/intermediate/credential-rotation-enforcer/justfile new file mode 100644 index 00000000..5293beb8 --- /dev/null +++ b/PROJECTS/intermediate/credential-rotation-enforcer/justfile @@ -0,0 +1,194 @@ +# ============================================================================= +# AngelaMos | 2026 +# Justfile - Credential Rotation Enforcer (cre) +# ============================================================================= + +set export +set shell := ["bash", "-uc"] +set windows-shell := ["powershell.exe", "-NoLogo", "-Command"] + +project := file_name(justfile_directory()) +version := `git describe --tags --always 2>/dev/null || echo "dev"` +binary := "bin/cre" +db_default := "sqlite:cre.db" +test_db_url := "postgres://cre_test:cre_test@localhost:5432/cre_test" + +# ============================================================================= +# Default +# ============================================================================= + +default: + @just --list --unsorted + +# ============================================================================= +# Build +# ============================================================================= + +[group('build')] +build: + shards build cre --release --no-debug + +[group('build')] +build-dev: + shards build cre + +[group('build')] +deps: + shards install + +[group('build')] +clean: + rm -rf bin lib .shards .crystal coverage tmp + +[group('build')] +rebuild: clean deps build + +# ============================================================================= +# Run (cre lifecycle) +# ============================================================================= + +[group('run')] +run db=db_default: + shards build cre && {{binary}} run --db={{db}} + +[group('run')] +watch db=db_default: + shards build cre && {{binary}} watch --db={{db}} + +[group('run')] +check db=":memory:": + shards build cre && {{binary}} check --db={{db}} + +[group('run')] +rotate ID db=db_default: + shards build cre && {{binary}} rotate {{ID}} --db={{db}} + +[group('run')] +audit-verify db=db_default: + shards build cre && {{binary}} audit verify --db={{db}} + +[group('run')] +policy-list: + shards build cre && {{binary}} policy list + +[group('run')] +policy-show name: + shards build cre && {{binary}} policy show {{name}} + +[group('run')] +export framework="soc2" out="evidence.zip" db=db_default: + shards build cre && {{binary}} export --framework={{framework}} --out={{out}} --db={{db}} + +# ============================================================================= +# Demos (Tier 1 / Tier 2 / Tier 3) +# ============================================================================= + +[group('demo')] +demo: + shards build cre && {{binary}} demo + +[group('demo')] +demo-full: + docker compose -f docker/docker-compose.yml up -d + @echo "Waiting for services..." + @sleep 8 + shards build cre && {{binary}} run --config=config/demo-full.cr + +[group('demo')] +demo-full-down: + docker compose -f docker/docker-compose.yml down -v + +[group('demo')] +demo-full-logs: + docker compose -f docker/docker-compose.yml logs -f --tail=50 + +# ============================================================================= +# Test +# ============================================================================= + +[group('test')] +test: + crystal spec --order=random + +[group('test')] +test-unit: + crystal spec spec/unit --order=random + +[group('test')] +test-integration: + DATABASE_URL={{test_db_url}} crystal spec spec/integration --order=random + +[group('test')] +test-watch: + @echo "Watching spec/ for changes..." + @while true; do \ + inotifywait -qre close_write src/ spec/ 2>/dev/null && crystal spec --order=random; \ + done + +[group('test')] +coverage: + @echo "Coverage requires kcov. Skipping if not installed." + kcov --include-path=src coverage/ ./bin/cre || true + +# ============================================================================= +# Quality (format / lint) +# ============================================================================= + +[group('quality')] +format: + crystal tool format + +[group('quality')] +format-check: + crystal tool format --check + +[group('quality')] +lint: + @if [ -x ./bin/ameba ]; then \ + ./bin/ameba; \ + else \ + echo "ameba not installed (requires libpcre3-dev on Debian 13+); skipping lint"; \ + fi + +[group('quality')] +check-all: format-check lint test + +[group('quality')] +ci: check-all + +# ============================================================================= +# Database (Tier 2 helpers) +# ============================================================================= + +[group('db')] +db-up: + docker run -d --rm --name cre-pg -e POSTGRES_USER=cre_test -e POSTGRES_PASSWORD=cre_test -e POSTGRES_DB=cre_test -p 5432:5432 postgres:16 + +[group('db')] +db-down: + docker stop cre-pg 2>/dev/null || true + +[group('db')] +db-shell: + docker exec -it cre-pg psql -U cre_test cre_test + +# ============================================================================= +# Utility +# ============================================================================= + +[group('util')] +version: + @echo "{{project}} {{version}}" + +[group('util')] +loc: + @echo "Source LOC:" + @find src -name "*.cr" -exec wc -l {} \; | awk '{total+=$1} END{print " " total}' + @echo "Spec LOC:" + @find spec -name "*.cr" -exec wc -l {} \; | awk '{total+=$1} END{print " " total}' + @echo "Spec files: $(find spec -name '*_spec.cr' | wc -l)" + +[group('util')] +binary-info: + @ls -la {{binary}} 2>/dev/null || echo "binary not built; run 'just build'" + @file {{binary}} 2>/dev/null || true diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/learn/00-OVERVIEW.md b/PROJECTS/intermediate/credential-rotation-enforcer/learn/00-OVERVIEW.md index 9ba6a9cf..6d0501b5 100644 --- a/PROJECTS/intermediate/credential-rotation-enforcer/learn/00-OVERVIEW.md +++ b/PROJECTS/intermediate/credential-rotation-enforcer/learn/00-OVERVIEW.md @@ -49,7 +49,7 @@ Runtime: under 1 second. ### Tier 2 - Docker Compose ``` -$ make demo-full +$ just demo-full ``` Brings up: PostgreSQL 16, LocalStack (AWS Secrets Manager), HashiCorp Vault dev mode, a fake-GitHub Flask service. CRE connects to all four and rotates one credential through each rotator. Demonstrates real network calls, real auth (SigV4 to LocalStack, token to Vault, bearer to fake-GitHub), real persistence to Postgres, real append-only audit triggers. diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/spec/unit/notifiers/telegram_spec.cr b/PROJECTS/intermediate/credential-rotation-enforcer/spec/unit/notifiers/telegram_spec.cr index 035b59e9..5c74ab8a 100644 --- a/PROJECTS/intermediate/credential-rotation-enforcer/spec/unit/notifiers/telegram_spec.cr +++ b/PROJECTS/intermediate/credential-rotation-enforcer/spec/unit/notifiers/telegram_spec.cr @@ -87,8 +87,7 @@ describe CRE::Notifiers::TelegramSubscriber do bus = CRE::Engine::EventBus.new sub = CRE::Notifiers::TelegramSubscriber.new( - bus, CRE::Notifiers::Telegram.new("FAKE"), [1_i64], notify_on_success: false, - ) + bus, CRE::Notifiers::Telegram.new("FAKE"), [1_i64], notify_on_success: false) sub.start bus.run bus.publish CRE::Events::RotationCompleted.new(UUID.random, UUID.random) diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/spec/unit/rotators/vault_dynamic_spec.cr b/PROJECTS/intermediate/credential-rotation-enforcer/spec/unit/rotators/vault_dynamic_spec.cr index 1571e890..e1a71482 100644 --- a/PROJECTS/intermediate/credential-rotation-enforcer/spec/unit/rotators/vault_dynamic_spec.cr +++ b/PROJECTS/intermediate/credential-rotation-enforcer/spec/unit/rotators/vault_dynamic_spec.cr @@ -83,7 +83,7 @@ describe CRE::Rotators::VaultDynamicRotator do end it "skips lease revocation when no current_lease_id" do - cred = vault_credential # no current_lease_id + cred = vault_credential # no current_lease_id WebMock.stub(:get, "http://vault.test/v1/database/creds/myrole") .to_return(body: %({"lease_id":"new","lease_duration":3600,"data":{"username":"u","password":"p"}})) rotator = CRE::Rotators::VaultDynamicRotator.new(vault_client)