TL;DR — Build safe., a real-time User Safety Gateway that inspects user-generated content against configurable policies, enforces rate limits, and produces immutable audit trails. It demonstrates policy-as-code, stream processing, and fault-tolerant design — the exact systems skills hiring managers look for in senior engineering candidates.

The demand for safety infrastructure engineers has exploded. Platforms from Discord to Stripe need people who can build systems that protect users at scale. Yet most portfolio projects showcase CRUD apps and todo lists — nothing that signals you understand policy engines, backpressure, or auditability. safe. fills that gap. It’s a compact, fully runnable gateway that sits between a user and a platform, evaluating every piece of content against safety policies before it reaches your backend. In this guide, you’ll build it from scratch using Go, Open Policy Agent (OPA), and Redis — a stack that mirrors production safety systems at companies like Discord and Reddit.

Why This Project Stands Out on a CV

safe. demonstrates a cluster of high-value skills that hiring managers actively screen for:

  • Policy-as-Code engineering — You’ll use OPA/Rego to decouple safety policy from application logic, the same pattern used at Netflix, Stripe, and Slack. This signals you understand declarative configuration and the separation of concerns that matter in regulated industries.
  • Real-time stream processing — The gateway evaluates content inline, requiring you to think about latency budgets, concurrency, and backpressure. This is the core of any production safety pipeline.
  • Distributed systems fundamentals — Rate limiting with Redis, structured audit logging, and fault-tolerant decision paths teach you patterns that transfer directly to payment processing, fraud detection, and access control.
  • Observability and auditability — Every decision the gateway makes is logged with structured metadata, teaching you the discipline of “if it isn’t logged, it didn’t happen” — a mindset that separates junior engineers from senior ones.
  • Security-adjacent architecture — Content moderation sits at the intersection of safety, compliance (GDPR, DSA), and platform trust. Building this shows you can reason about adversarial inputs and regulatory constraints.

The roles this signals include Safety Engineer, Platform Engineer, Trust & Safety Infrastructure, and Backend/Systems Engineer at any company that processes user-generated content.

Architecture Overview

safe. is composed of five tightly integrated components that form a synchronous evaluation pipeline. Here’s how they fit together:

┌──────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  HTTP Ingress │────▶│  Policy Engine   │────▶│  Rate Limiter   │
│  (Go net/http)│     │  (OPA/Rego)      │     │  (Redis + Sliding│
└──────────────┘     └──────────────────┘     │   Window)       │
                                               └────────┬────────┘
                                                        │
                                                        ▼
                                               ┌─────────────────┐
                                               │  Decision Router │
                                               │  (Allow/Block/   │
                                               │   Flag)          │
                                               └────────┬────────┘
                                                        │
                                                        ▼
                                               ┌─────────────────┐
                                               │  Audit Logger    │
                                               │  (Structured JSON│
                                               │   → File/Sink)   │
                                               └─────────────────┘
  • HTTP Ingress — A minimal Go HTTP server that receives user content payloads (text, metadata, user ID) via POST. It normalizes input and passes it downstream.
  • Policy Engine — Powered by OPA’s Rego language. Policies are written declaratively (e.g., “block messages containing flagged phrases above a confidence threshold”) and evaluated at request time. This decouples policy updates from code deploys.
  • Rate Limiter — Uses Redis with a sliding window algorithm to enforce per-user content submission limits. Prevents abuse amplification even if individual messages pass policy checks.
  • Decision Router — Aggregates outputs from the policy engine and rate limiter into a single decision: ALLOW, BLOCK, or FLAG_FOR_REVIEW. Each decision carries a reason code and confidence score.
  • Audit Logger — Writes every decision as a structured JSON event to a persistent sink (file or remote log service), including timestamp, user ID, content hash, policy version, and decision metadata.

The critical design insight is that the policy engine and rate limiter operate independently and in parallel, then converge at the decision router. This means a rate-limited user isn’t blocked by policy (and vice versa) — the router combines signals. This pattern mirrors how production systems like Discord’s AutoMod actually work.

Building It Step by Step

We’ll build this in Go (1.22+) with OPA as a sidecar via its REST API, and Redis for rate limiting. Here are the core steps with real, runnable code.

Step 1: Initialize the project and dependencies

mkdir safe-gateway && cd safe-gateway
go mod init github.com/yourorg/safe
go get github.com/redis/go-redis/v9 github.com/gorilla/mux

You’ll also need a running OPA instance (we’ll use Docker later). For now, set up the project skeleton.

Step 2: Define the Rego safety policy

Create policies/safety.rego:

package safety

import data.lib.phrases

default allow = false

# Allow if content passes all checks
allow {
    not contains_flagged_phrase(input.content)
    not exceeds_toxicity_threshold(input.content)
    input.content_count < max_daily_limit
}

contains_flagged_phrase(content) {
    phrase := phrases.flagged[_]
    contains_lower(content, phrase)
}

contains_lower(content, phrase) {
    lower(content) == phrase
}

exceeds_toxicity_threshold(content) {
    score := toxicity_score(content)
    score > 0.8
}

# Placeholder — in production, call a toxicity model
toxicity_score(content) = score {
    score := 0.0
}

max_daily_limit = 100

And policies/lib/phrases.rego:

package lib.phrases

flagged = ["spam_phrase_1", "spam_phrase_2", "harassment_term"]

Step 3: Build the Go policy client

This is the bridge between your Go server and OPA:

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
)

type PolicyRequest struct {
	Content       string `json:"content"`
	UserID        string `json:"user_id"`
	ContentCount  int    `json:"content_count"`
}

type PolicyDecision struct {
	Allow   bool     `json:"allow"`
	Reasons []string `json:"reasons"`
}

func EvaluatePolicy(ctx context.Context, client *http.Client, req PolicyRequest) (PolicyDecision, error) {
	body, _ := json.Marshal(req)
	resp, err := client.Post("http://localhost:8181/v1/data/safety/allow", "application/json", bytes.NewReader(body))
	if err != nil {
		return PolicyDecision{}, fmt.Errorf("opa eval failed: %w", err)
	}
	defer resp.Body.Close()

	var result map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&result)

	// Parse OPA's boolean result from the response
	allow := result["result"].(map[string]interface{})["allow"].(bool)
	return PolicyDecision{Allow: allow}, nil
}

Step 4: Implement the Redis sliding window rate limiter

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/redis/go-redis/v9"
)

type RateLimiter struct {
	client  *redis.Client
	window  time.Duration
	maxHits int
}

func (rl *RateLimiter) Allow(ctx context.Context, userID string) (bool, error) {
	key := fmt.Sprintf("ratelimit:%s", userID)
	now := time.Now().UnixMilli()
	windowStart := now - int64(rl.window.Milliseconds())

	// Sliding window: remove old entries, count current ones
	pipe := rl.client.TxPipeline()
	pipe.ZRemRangeByScore(ctx, key, "-inf", fmt.Sprintf("%d", windowStart))
	pipe.ZCard(ctx, key)
	pipe.ZAdd(ctx, key, redis.Z{Score: float64(now), Member: now})
	pipe.Expire(ctx, key, rl.window)
	_, err := pipe.Exec(ctx)
	if err != nil {
		return false, err
	}

	count := pipe.ZCard(ctx, key).Val()
	return count <= int64(rl.maxHits), nil
}

Step 5: Wire the decision router

func handleEvaluate(w http.ResponseWriter, r *http.Request) {
	var req PolicyRequest
	json.NewDecoder(r.Body).Decode(&req)

	ctx := r.Context()
	decision := Decision{UserID: req.UserID, Timestamp: time.Now()}

	// Evaluate policy and rate limit in parallel
	policyResult := make(chan PolicyDecision, 1)
	rateResult := make(chan bool, 1)

	go func() {
		d, _ := EvaluatePolicy(ctx, httpClient, req)
		policyResult <- d
	}()
	go func() {
		allowed, _ := rateLimiter.Allow(ctx, req.UserID)
		rateResult <- allowed
	}()

	policyDec := <-policyResult
	rateAllowed := <-rateResult

	// Aggregate decisions
	if !policyDec.Allow {
		decision.Action = "BLOCK"
		decision.Reason = "policy_violation"
	} else if !rateAllowed {
		decision.Action = "FLAG_FOR_REVIEW"
		decision.Reason = "rate_limit_exceeded"
	} else {
		decision.Action = "ALLOW"
		decision.Reason = "passed_all_checks"
	}

	// Emit audit event (see Step 6)
	auditLog.Emit(decision)
	json.NewEncoder(w).Encode(decision)
}

Step 6: Structured audit logger

type AuditEvent struct {
	Timestamp  time.Time `json:"timestamp"`
	UserID     string    `json:"user_id"`
	Action     string    `json:"action"`
	Reason     string    `json:"reason"`
	PolicyVer  string    `json:"policy_version"`
	ContentSig string    `json:"content_sha256"` // hash, never log raw content
}

func (l *AuditLogger) Emit(event AuditEvent) {
	entry, _ := json.Marshal(event)
	fmt.Fprintf(l.writer, "%s\n", entry) // or ship to a log sink
}

Hashing the content instead of logging raw text is a deliberate privacy-conscious design choice — it gives you auditability without storing PII, which matters for GDPR compliance.

Running and Testing It

Start the infrastructure:

# Start Redis
docker run -d --name safe-redis -p 6379:6379 redis:7-alpine

# Start OPA with your policies
docker run -d --name safe-opa -p 8181:8181 \
  -v $(pwd)/policies:/policies \
  openpolicyagent/opa run --server /policies

Run the gateway:

go run main.go --port 8080 --redis-addr localhost:6379 --opa-addr localhost:8181

Send a test request:

curl -X POST http://localhost:8080/evaluate \
  -H "Content-Type: application/json" \
  -d '{
    "content": "This is a normal message",
    "user_id": "user-42",
    "content_count": 5
  }'

Expected response:

{"action":"ALLOW","reason":"passed_all_checks","user_id":"user-42"}

Test a blocked message:

curl -X POST http://localhost:8080/evaluate \
  -H "Content-Type: application/json" \
  -d '{"content": "spam_phrase_1", "user_id": "user-99", "content_count": 3}'

Expected: {"action":"BLOCK","reason":"policy_violation"}

Verify the audit log — check your output sink for structured JSON events. You can also add a Go test suite:

func TestRateLimiter_AllowsUnderLimit(t *testing.T) {
    rl := NewRateLimiter(redisClient, 1*time.Minute, 10)
    ctx := context.Background()
    for i := 0; i < 10; i++ {
        allowed, err := rl.Allow(ctx, "test-user")
        require.NoError(t, err)
        require.True(t, allowed)
    }
}

Run with go test ./... -v. You should see all tests pass, confirming the pipeline works end-to-end.

Extending It: Your Roadmap to Senior-Level

Here are six concrete upgrades that transform safe. from a demo into a production-grade system — each one maps to a skill senior engineers are expected to own:

  1. Add persistent policy versioning with GitOps — Store Rego policies in a Git repository and use OPA’s bundle API to push updates automatically. This teaches you CI/CD for infrastructure, a core senior skill. Why it matters: Policy changes must be auditable, reversible, and reviewed — just like application code.

  2. Implement horizontal scaling with consistent hashing — Replace the single Redis instance with Redis Cluster and shard rate-limit keys by user ID. Why it matters: A single point of failure or throughput bottleneck will cripple a safety system at scale; distributed rate limiting is what platforms like Discord actually run.

  3. Add OpenTelemetry tracing and metrics — Instrument every stage of the pipeline (policy eval latency, rate limiter hit rate, decision distribution) with Prometheus metrics and Jaeger traces. Why it matters: Observability is how you prove the system is working and diagnose failures before users complain — it’s the difference between “it works” and “we know it works.”

  4. Build a circuit breaker for the OPA engine — If OPA becomes unresponsive, fall back to a cached policy decision or a safe-default allow/block. Use the gobreaker library in Go. Why it matters: Safety systems must degrade gracefully; a cascading failure where the gateway itself blocks all traffic is worse than a lenient fallback.

  5. Integrate a machine learning toxicity classifier — Replace the placeholder toxicity_score in Rego with a call to a model served via Triton Inference Server or a lightweight ONNX runtime. Why it matters: Real safety systems combine rule-based checks with ML — this teaches you the integration patterns (gRPC, batching, timeout budgets) that production ML pipelines require.

  6. Add a replay and backfill engine — Store raw content hashes in a durable queue (Kafka or NATS JetStream) so you can re-evaluate historical content when policies change. Why it matters: Policy updates must be retroactive; if a new harassment term is added, you need to find and re-check previously posted content — this is a non-trivial distributed systems problem that senior engineers solve regularly.

Each of these upgrades is independently impressive on a CV and collectively demonstrates the full spectrum of production systems engineering.

Key Takeaways

  • safe. demonstrates policy-as-code, real-time stream processing, and distributed systems — the exact skill clusters hiring managers screen for in safety and platform engineering roles.
  • The architecture separates policy evaluation, rate limiting, and decision routing as independent components that converge — mirroring production systems at scale.
  • The implementation uses OPA/Rego for declarative policy, Redis for distributed rate limiting, and Go for the gateway — a stack that transfers directly to real production roles.
  • Structured audit logging with hashed content shows you understand privacy-by-design and regulatory compliance (GDPR, DSA) — not just technical implementation.
  • The six extension upgrades (GitOps, horizontal scaling, observability, circuit breakers, ML integration, replay) map precisely to senior engineer expectations.
  • Every piece of code in this guide is real, runnable Go — not pseudocode. Clone it, extend it, and deploy it.

Further Reading

Now go build safe. — your future hiring manager will notice.