Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Air — Go live reload configuration
# Documentation: https://github.com/air-verse/air

root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"

[build]
# The command Air runs to rebuild when files change.
cmd = "go build -o ./tmp/ratify-server ./cmd/server"
# The binary Air runs after a successful build.
bin = "./tmp/ratify-server"
# Files that trigger a rebuild when changed.
include_ext = ["go"]
# Directories Air ignores.
exclude_dir = ["frontend", "tmp", "vendor", "testdata"]
# Kill the running process before rebuilding.
kill_delay = "200ms"
# Show the build log when a rebuild is triggered.
log = "build-errors.log"

[log]
# Show timestamps in Air's log output.
time = true

[color]
# Colour the different log levels differently.
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"

[misc]
# Delete tmp dir on exit.
clean_on_exit = true
57 changes: 57 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# =============================================================================
# Ratify — Environment Configuration
# =============================================================================
# Copy this file to .env and fill in your values:
# cp .env.example .env
#
# The defaults below work for local development with docker compose.
# Never commit your .env file — it contains real secrets.
# =============================================================================

# -----------------------------------------------------------------------------
# Application
# -----------------------------------------------------------------------------
PORT=8080
ENVIRONMENT=development

# -----------------------------------------------------------------------------
# Ratify's own metadata database
# This is NOT the database Ratify monitors — it is the database Ratify
# uses to store contracts, proposals, audit logs, and credentials.
# -----------------------------------------------------------------------------
DATABASE_URL=postgresql://ratify:ratify@postgres:5432/ratify?sslmode=disable

# -----------------------------------------------------------------------------
# Security
# ENCRYPTION_KEY: used to encrypt database credentials at rest (AES-256).
# Must be exactly 64 hex characters (32 bytes).
# Generate a new one with: openssl rand -hex 32
#
# JWT_SECRET: used to sign session tokens for the web UI.
# Generate a new one with: openssl rand -hex 32
#
# The values below are safe defaults for local development only.
# Use real random values in any other environment.
# -----------------------------------------------------------------------------
ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
JWT_SECRET=local-dev-jwt-secret-change-this-in-production

# -----------------------------------------------------------------------------
# Email (SMTP)
# Ratify sends notifications when proposals are raised and breaches detected.
# For local development, you can use a free service like Mailtrap.io
# which captures emails without sending them.
# -----------------------------------------------------------------------------
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=587
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM=ratify@example.com

# -----------------------------------------------------------------------------
# Breach detection
# How often Ratify compares live schemas against active contracts.
# Format: Go duration string — 1h, 30m, 24h
# Default is 1 hour. For local development 5m is easier to test with.
# -----------------------------------------------------------------------------
BREACH_DETECTION_INTERVAL=1h
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ go.work.sum
# Editor/IDE
# .idea/
# .vscode/

# Docker and Air live reload
tmp/
.air.toml.bak

# Frontend build output and dependencies
frontend/dist/
frontend/node_modules/
24 changes: 24 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# =============================================================================
# Ratify — Development overrides
# Use this with: docker compose -f docker-compose.yml -f docker-compose.dev.yml up
#
# Changes from the base docker-compose.yml:
# - Uses Dockerfile.dev instead of Dockerfile (live reload via Air)
# - Mounts source code as a volume so changes are reflected immediately
# - Enables more verbose logging
# =============================================================================

services:
app:
build:
context: .
dockerfile: docker/Dockerfile.dev
volumes:
# Mount the entire project into the container.
# Air watches this directory and recompiles on changes.
- .:/app
# Prevent the container's Go module cache from being
# overwritten by the host's directory.
- /app/tmp
environment:
ENVIRONMENT: development
77 changes: 77 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# =============================================================================
# Ratify — Docker Compose
# Starts the Ratify server and its PostgreSQL database.
#
# Usage:
# docker compose up — start everything (production image)
# docker compose up --build — rebuild and start
# docker compose down — stop and remove containers
# docker compose down -v — stop and remove containers + database volume
#
# For active development with live reload:
# docker compose -f docker-compose.yml -f docker-compose.dev.yml up
# =============================================================================

services:

# ---------------------------------------------------------------------------
# PostgreSQL — Ratify's metadata database
# Stores contracts, proposals, teams, audit logs, and encrypted credentials.
# This is NOT the database Ratify monitors — it is Ratify's own database.
# ---------------------------------------------------------------------------
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ratify
POSTGRES_PASSWORD: ratify
POSTGRES_DB: ratify
ports:
# Expose on localhost so you can connect with a database client
# (e.g. TablePlus, DBeaver, psql) during local development.
# Remove this in production — the app container connects internally.
- "5432:5432"
volumes:
# Persist database data across container restarts.
# Without this, all data is lost when the container stops.
- postgres_data:/var/lib/postgresql/data
healthcheck:
# Wait for PostgreSQL to be ready before starting the app.
# The app container will not start until this passes.
test: ["CMD-SHELL", "pg_isready -U ratify -d ratify"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s

# ---------------------------------------------------------------------------
# Ratify app — the compiled server binary
# Runs migrations on startup, then starts the HTTP server.
# ---------------------------------------------------------------------------
app:
build:
context: .
dockerfile: docker/Dockerfile
restart: unless-stopped
ports:
- "${PORT:-8080}:8080"
env_file:
# Load all environment variables from .env
# Copy .env.example to .env and fill in real values
- .env
environment:
# Override DATABASE_URL so the app connects to the postgres
# service by its service name, not localhost.
DATABASE_URL: postgresql://ratify:ratify@postgres:5432/ratify?sslmode=disable
depends_on:
postgres:
# Only start the app after PostgreSQL health check passes.
condition: service_healthy

# =============================================================================
# Volumes
# Named volumes persist data across container restarts.
# =============================================================================
volumes:
postgres_data:
driver: local
72 changes: 72 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# =============================================================================
# Ratify — Production Dockerfile
# Two-stage build: compile in a full Go environment, run in a minimal image.
# Final image size: under 50MB.
# =============================================================================

# -----------------------------------------------------------------------------
# Stage 1: Builder
# Uses the official Go image to compile the binary.
# This stage is discarded after the binary is copied to the runtime stage.
# -----------------------------------------------------------------------------
FROM golang:1.24-alpine AS builder

# Install ca-certificates and git.
# ca-certificates: needed for HTTPS connections inside the binary.
# git: needed by go mod download for some dependencies.
RUN apk add --no-cache ca-certificates git

WORKDIR /build

# Copy dependency files first.
# Docker caches this layer separately from source code.
# If go.mod and go.sum have not changed, Docker reuses the cache
# and skips re-downloading all dependencies on the next build.
COPY go.mod go.sum* ./
RUN go mod download

# Copy the rest of the source code.
COPY . .

# Build the server binary.
# CGO_ENABLED=0 produces a statically linked binary with no C dependencies.
# This is required for the binary to run in the minimal runtime image.
# -ldflags="-w -s" strips debug symbols — reduces binary size significantly.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-w -s" \
-o ratify-server \
./cmd/server

# -----------------------------------------------------------------------------
# Stage 2: Runtime
# Minimal image — only the compiled binary and its dependencies.
# No Go compiler, no source code, no package manager.
# -----------------------------------------------------------------------------
FROM alpine:3.20

# Install ca-certificates in the runtime image.
# Required for the binary to make outgoing HTTPS connections.
RUN apk add --no-cache ca-certificates tzdata

WORKDIR /app

# Copy the compiled binary from the builder stage.
COPY --from=builder /build/ratify-server .

# Copy migration files.
# The server runs migrations on startup, so they must be present.
COPY --from=builder /build/migrations ./migrations

# Copy email templates.
COPY --from=builder /build/templates ./templates

# The application listens on this port.
# Must match the PORT environment variable.
EXPOSE 8080

# Run as a non-root user for security.
# Never run application containers as root in production.
RUN addgroup -S ratify && adduser -S ratify -G ratify
USER ratify

CMD ["./ratify-server"]
24 changes: 24 additions & 0 deletions docker/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# =============================================================================
# Ratify — Development Dockerfile
# Uses Air for live reload — the server restarts automatically when
# Go files change. Mount the source code as a volume for this to work.
# =============================================================================

FROM golang:1.24-alpine

# Install air (live reload) and other development tools.
RUN apk add --no-cache ca-certificates git curl && \
go install github.com/air-verse/air@latest

WORKDIR /app

# Copy dependency files and download dependencies.
COPY go.mod go.sum* ./
RUN go mod download

# The source code is mounted as a volume at runtime (see docker-compose.yml).
# We do not copy it here — air watches the mounted directory directly.

EXPOSE 8080

CMD ["air", "-c", ".air.toml"]
Empty file added templates/.gitkeep
Empty file.
Loading