fix(canary-phase0): address audit findings before phase rollup

Phase 0 audit (superpowers:code-reviewer + general-purpose) returned FAIL
on shared findings. This commit resolves the blockers + important items.

Blockers:
- infra/docker/vite.prod: replace 'react-scss/' COPY paths with 'frontend/'
  (would have broken 'docker compose build nginx')
- Delete backend/Dockerfile (broken template debris referencing deleted
  cmd/api, missing migrations/, deleted keys/)
- Delete backend/Justfile (docker compose recipes pointed at deleted
  backend/compose.yml + backend/dev.compose.yml; project-root justfile
  [backend] group covers everything operationally)
- backend/internal/admin/handler.go: strip the residual JWT/auth scaffolding
  (AuthService interface, authSvc field, authenticator+adminOnly mw params).
  Phase 12 will gate /admin under OperatorBearer; for now RegisterRoutes
  takes only chi.Router.
- Delete backend/.env + backend/.env.example: stale template duplicates
  containing only template keys; project-root .env.example is the source
  of truth per spec §12.4.

Important:
- Add © glyph to file headers in cloudflared.compose.yml, infra/docker/
  vite.{dev,prod}, backend/.gitignore (operator's standing rule:
  '©AngelaMos | 2026', not 'AngelaMos | 2026')
- backend/.gitignore: drop stale 'keys/*.pem' / 'keys/*.key' entries
  (backend/keys/ no longer exists)
- scripts/init.sh: rename 'local_tmp' variable to 'tmp_dir' to remove
  visual ambiguity with the 'local' bash keyword (both audit agents
  briefly misread it as 'local local_tmp=...')
- .env.example: add inline comment explaining the dual NGINX_HOST_PORT
  defaults (22784 prod compose, 58495 dev compose; do not override in
  shared .env)

Deferred (logged but not blocking Phase 0):
- Pre-existing template Go files (internal/{config,core,server,health,
  middleware/{request_id,logging,headers,ratelimit},admin}/*.go) carry
  '// AngelaMos | 2026' without ©. Bulk-fixing these belongs to a
  hygiene pass; not Phase 0 scope.
- frontend/vite.config.ts and frontend/stylelint.config.js reference
  '2025'; operator-imported template files, separate concern.
- APP_ENVIRONMENT vs ENVIRONMENT spec/impl mismatch: spec §12.1 sample
  uses APP_ENVIRONMENT; envKeyMap in config.go uses ENVIRONMENT (template
  default). Implementation is internally consistent. Logged in plan
  Appendix C as a non-blocking spec amendment candidate.

Verification: go build ./... + go vet ./... clean. grep returns no
hits for react-scss / carterperez-dev/templates / JWTConfig /
InvalidateAllSessions across backend/, infra/, scripts/, compose files.
This commit is contained in:
CarterPerez-dev 2026-05-10 05:37:32 -04:00
parent a4811d1c04
commit 98c006897d
10 changed files with 16 additions and 190 deletions

View File

@ -10,6 +10,9 @@
# Public-facing
# ---------------------------------------------------------------------------
APP_NAME=canary-token-generator
# Production nginx host port (compose.yml). Dev compose has its own default
# of 58495 in dev.compose.yml; do not override NGINX_HOST_PORT in shared .env.
NGINX_HOST_PORT=22784
# Used to construct trigger URLs embedded in artifacts (e.g. canary docs).

View File

@ -1,27 +0,0 @@
# AngelaMos | 2026
# .env.example
# Environment: development, staging, production
ENVIRONMENT=development
# Server
HOST=0.0.0.0
PORT=8080
# Database
DATABASE_URL=postgres://postgres:postgres@localhost:5432/app?sslmode=disable
# Redis
REDIS_URL=redis://localhost:6379/0
# Rate Limiting
RATE_LIMIT_REQUESTS=100
RATE_LIMIT_WINDOW=1m
# OpenTelemetry
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
OTEL_SERVICE_NAME=canary-token-generator
# Logging
LOG_LEVEL=debug
LOG_FORMAT=text

View File

@ -1,4 +1,4 @@
# AngelaMos | 2026
# ©AngelaMos | 2026
# .gitignore
# Binaries
@ -33,10 +33,6 @@ Thumbs.db
.env.local
.env.*.local
# Keys (sensitive)
keys/*.pem
keys/*.key
# Vendor (if using)
vendor/

View File

@ -1,36 +0,0 @@
# AngelaMos | 2026
# Dockerfile - Multi-stage distroless build
# Build stage
FROM golang:1.25-bookworm AS builder
WORKDIR /build
# Copy dependency files first for layer caching
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Build binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app ./cmd/api
# Production stage - Distroless
FROM gcr.io/distroless/static-debian12
# Copy binary from builder
COPY --from=builder /app /app
# Copy migrations (embedded, but useful for debugging)
COPY --from=builder /build/migrations /migrations
# Copy keys if they exist (for JWT)
COPY --from=builder /build/keys /keys
# Run as non-root user
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/app"]

View File

@ -1,97 +0,0 @@
# AngelaMos | 2026
# Justfile
set dotenv-load := true
# Default recipe
default:
@just --list
# Development
# -----------
# Run the API server with hot reload (requires air)
dev:
air -c .air.toml
# Run the canary server without hot reload
run:
go run ./cmd/canary
# Build the binary
build:
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o bin/canary ./cmd/canary
# Run tests
test:
go test -v -race -coverprofile=coverage.out ./...
# Run tests with coverage report
test-coverage: test
go tool cover -html=coverage.out -o coverage.html
# Run linter
lint:
golangci-lint run --timeout=5m
# Format code
fmt:
gofumpt -w .
goimports -w .
# Tidy dependencies
tidy:
go mod tidy
# Database
# --------
# Run database migrations
migrate-up:
goose -dir migrations postgres "${DATABASE_URL}" up
# Rollback last migration
migrate-down:
goose -dir migrations postgres "${DATABASE_URL}" down
# Check migration status
migrate-status:
goose -dir migrations postgres "${DATABASE_URL}" status
# Create new migration
migrate-create name:
goose -dir migrations create {{name}} sql
# Docker
# ------
# Start development containers
up:
docker compose -f dev.compose.yml up -d
# Stop development containers
down:
docker compose -f dev.compose.yml down
# View container logs
logs:
docker compose -f dev.compose.yml logs -f
# Rebuild and restart containers
rebuild:
docker compose -f dev.compose.yml up -d --build
# Build production image
docker-build:
docker build -t canary-token-generator:latest .
# Clean
# -----
# Remove build artifacts
clean:
rm -rf bin/ coverage.out coverage.html tmp/
# Full clean including containers
clean-all: clean
docker compose -f dev.compose.yml down -v

View File

@ -1,4 +1,4 @@
// AngelaMos | 2026
// ©AngelaMos | 2026
// handler.go
package admin
@ -15,16 +15,11 @@ import (
"github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/core"
)
type AuthService interface {
InvalidateAllSessions(ctx context.Context) error
}
type Handler struct {
dbStats func() sql.DBStats
redisStats func() *redis.PoolStats
redisPing func(ctx context.Context) error
dbPing func(ctx context.Context) error
authSvc AuthService
}
type HandlerConfig struct {
@ -32,7 +27,6 @@ type HandlerConfig struct {
RedisStats func() *redis.PoolStats
RedisPing func(ctx context.Context) error
DBPing func(ctx context.Context) error
AuthSvc AuthService
}
func NewHandler(cfg HandlerConfig) *Handler {
@ -41,18 +35,11 @@ func NewHandler(cfg HandlerConfig) *Handler {
redisStats: cfg.RedisStats,
redisPing: cfg.RedisPing,
dbPing: cfg.DBPing,
authSvc: cfg.AuthSvc,
}
}
func (h *Handler) RegisterRoutes(
r chi.Router,
authenticator, adminOnly func(http.Handler) http.Handler,
) {
func (h *Handler) RegisterRoutes(r chi.Router) {
r.Route("/admin", func(r chi.Router) {
r.Use(authenticator)
r.Use(adminOnly)
r.Get("/stats", h.GetSystemStats)
r.Get("/stats/db", h.GetDatabaseStats)
r.Get("/stats/redis", h.GetRedisStats)

View File

@ -1,5 +1,5 @@
# =============================================================================
# AngelaMos | 2026
# ©AngelaMos | 2026
# cloudflared.compose.yml
# =============================================================================
# Cloudflare Tunnel for production remote access

View File

@ -1,5 +1,5 @@
# =============================================================================
# AngelaMos | 2026
# ©AngelaMos | 2026
# vite.dev
# =============================================================================
# Development Dockerfile for Vite/React frontend

View File

@ -1,5 +1,5 @@
# =============================================================================
# AngelaMos | 2026
# ©AngelaMos | 2026
# vite.prod
# =============================================================================
# Production Dockerfile: builds Vite app, serves via Nginx
@ -15,12 +15,12 @@ RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY react-scss/package.json react-scss/pnpm-lock.yaml* ./
COPY frontend/package.json frontend/pnpm-lock.yaml* ./
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
COPY react-scss/ .
COPY frontend/ .
ARG VITE_API_URL=/api
ARG VITE_APP_TITLE="My App"

View File

@ -60,14 +60,14 @@ if [[ -n "$ACCT" && -n "$KEY" ]]; then
if [[ ! -f "$MMDB_PATH" ]]; then
mkdir -p "$DATA_DIR"
echo " downloading GeoLite2-City.mmdb from MaxMind..."
local_tmp="$(mktemp -d)"
trap 'rm -rf "$local_tmp"' EXIT
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
if curl -sSf -u "$ACCT:$KEY" \
"https://download.maxmind.com/geoip/databases/GeoLite2-City/download?suffix=tar.gz" \
-o "$local_tmp/geolite.tar.gz"; then
tar -xzf "$local_tmp/geolite.tar.gz" -C "$local_tmp" --strip-components=1 \
-o "$tmp_dir/geolite.tar.gz"; then
tar -xzf "$tmp_dir/geolite.tar.gz" -C "$tmp_dir" --strip-components=1 \
"*/GeoLite2-City.mmdb"
mv "$local_tmp/GeoLite2-City.mmdb" "$MMDB_PATH"
mv "$tmp_dir/GeoLite2-City.mmdb" "$MMDB_PATH"
echo " GeoLite2-City.mmdb downloaded to $MMDB_PATH"
else
echo " Warning: GeoLite2 download failed (check MAXMIND_ACCOUNT_ID and MAXMIND_LICENSE_KEY)" >&2