Fulcrum Security Architecture Overview
Document Version: 1.0 Last Updated: January 6, 2026 Classification: Internal / Partner
Table of Contents
- Security Architecture Overview
- Defense in Depth Layers
- Authentication Mechanisms
- Authorization (RBAC)
- Data Encryption
- Security CI/CD Pipeline
- Vulnerability Management
- Incident Response
- Compliance
Security Architecture Overview
Fulcrum implements a defense-in-depth security architecture designed for enterprise AI governance workloads. The platform protects sensitive data across multiple layers, from network perimeter to database records.
Trust Boundaries
INTERNET
|
+----------------+----------------+
| PERIMETER LAYER |
| - TLS 1.2+ termination |
| - Rate limiting (per-tenant) |
| - DDoS protection (infra) |
+----------------+----------------+
|
+----------------+----------------+
| AUTHENTICATION LAYER |
| - API key validation (SHA-256)|
| - Clerk (dashboard SSO) |
| - Service-to-service mTLS |
+----------------+----------------+
|
+----------------+----------------+
| AUTHORIZATION LAYER |
| - Scope-based RBAC |
| - Tenant context injection |
| - Method-level enforcement |
+----------------+----------------+
|
+----------------+----------------+
| APPLICATION LAYER |
| - Input validation |
| - Parameterized queries |
| - Context propagation |
+----------------+----------------+
|
+----------------+----------------+
| DATA LAYER |
| - PostgreSQL RLS isolation |
| - Encrypted at rest |
| - Audit logging |
+----------------+----------------+
Security Principles
| Principle | Implementation |
|---|---|
| Defense in Depth | Multiple overlapping security controls at each layer |
| Least Privilege | Scope-based API keys with minimal permissions |
| Zero Trust | All requests authenticated; tenant context validated |
| Fail Securely | Errors return generic messages; no information leakage |
| Secure by Default | TLS required in production; restrictive defaults |
Defense in Depth Layers
Layer 1: Network Perimeter
- TLS 1.2+: All production traffic encrypted in transit
- gRPC + HTTP/2: Multiplexed connections with built-in security
- Rate Limiting: Redis-backed token bucket algorithm (10 req/s default, 20 burst)
- Network Isolation: Internal services on private networks (Docker/Kubernetes)
Layer 2: Authentication Gateway
- API Key Authentication: SHA-256 hashed keys with expiration support
- Clerk SSO: OAuth2/OIDC for dashboard users
- Service Accounts: Dedicated keys for machine-to-machine communication
Layer 3: Authorization Enforcement
- Scope-based RBAC: Fine-grained permissions per API key
- Tenant Isolation: Context injection at middleware layer
- Method Guards: Per-endpoint permission checks
Layer 4: Application Security
- Input Validation: All request fields validated before processing
- Parameterized Queries: No SQL string concatenation
- Output Encoding: XSS prevention on response data
Layer 5: Database Security
- Row-Level Security (RLS): PostgreSQL policies enforce tenant isolation
- Encryption at Rest: Database-level encryption (AES-256)
- Audit Logging: All policy evaluations logged with timestamps
Authentication Mechanisms
API Key Authentication (gRPC/REST)
Fulcrum uses SHA-256 hashed API keys for programmatic access. Keys are stored as hashes; the plaintext is never persisted.
Implementation (/internal/middleware/auth.go):
// API key validation flow
// 1. Extract x-api-key header from gRPC metadata
// 2. Compute SHA-256 hash of provided key
// 3. Lookup hash in fulcrum.api_keys table
// 4. Verify expiration (expires_at IS NULL OR expires_at > NOW())
// 5. Inject tenant_id and scopes into request context
hash := sha256.Sum256([]byte(apiKey))
hashStr := hex.EncodeToString(hash[:])
tenant, scopes, err := store.GetTenantByAPIKeyHash(ctx, hashStr)
Security Properties:
| Property | Implementation |
|---|---|
| Key Storage | SHA-256 hash only; plaintext never stored |
| Key Rotation | Keys can be revoked and re-issued via API |
| Expiration | Optional expires_at timestamp per key |
| Key Hint | Last 4-8 characters stored for identification |
| Timing Attacks | Hash comparison via database lookup (constant-time at DB level) |
Best Practice: Generate keys with 32+ bytes of entropy:
Clerk Integration (Dashboard SSO)
The Next.js dashboard uses Clerk for authentication, providing OAuth2/OIDC-based SSO.
Implementation (/dashboard/src/middleware.ts):
// Protected routes require Clerk authentication
const isProtectedRoute = createRouteMatcher([
'/app(.*)',
'/api/approvals(.*)',
'/api/policies(.*)',
'/api/traces(.*)',
'/api/keys(.*)',
]);
// Security: Block production access when Clerk is not configured
if (process.env.NODE_ENV === 'production' && !isClerkEnabled()) {
return NextResponse.redirect(new URL('/auth-error', request.url));
}
Security Properties:
- Production mode requires Clerk configuration (fails closed)
- Development mode allows unauthenticated access for testing only
- Session management handled by Clerk (not custom implementation)
Service-to-Service Authentication (mTLS)
For internal service communication, Fulcrum supports mutual TLS.
Configuration:
# Enable TLS
export GRPC_TLS_ENABLED=true
export GRPC_TLS_CERT_FILE=/path/to/server.crt
export GRPC_TLS_KEY_FILE=/path/to/server.key
export GRPC_TLS_CA_FILE=/path/to/ca.crt # For mTLS
TLS Configuration (/cmd/fulcrum-server/main.go):
config := &tls.Config{
Certificates: []tls.Certificate{serverCert},
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
},
}
Authorization (RBAC)
Role Definitions
Fulcrum implements scope-based RBAC. Each API key is assigned a set of scopes that determine permitted operations.
| Scope | Description | Endpoints |
|---|---|---|
* |
Full admin access (legacy keys) | All endpoints |
policy:read |
Read policy configurations | ListPolicies, GetPolicy |
policy:write |
Create/update policies | CreatePolicy, DeletePolicy |
policy:approve |
Approve pending policies | UpdatePolicy (status=ACTIVE), UpdateApproval |
cost:read |
View cost data | GetCostSummary, GetBudgetStatus |
cost:write |
Manage budgets | CreateBudget, UpdateBudget |
api_key:read |
List API keys | ListApiKeys |
api_key:write |
Manage API keys | CreateApiKey, RevokeApiKey |
admin |
Administrative operations | Key management, approvals |
Permission Enforcement Points
Middleware Layer (/internal/middleware/auth.go):
// HasScope checks if the context has a specific scope (or admin *)
func HasScope(ctx context.Context, scope string) bool {
scopes, ok := GetScopes(ctx)
if !ok {
return false
}
for _, s := range scopes {
if s == "*" || s == scope {
return true
}
}
return false
}
Service Layer (example from /cmd/fulcrum-server/main.go):
func (s *policyServiceWrapper) CreatePolicy(ctx context.Context, req *policyv1.CreatePolicyRequest) (*policyv1.CreatePolicyResponse, error) {
if !middleware.HasScope(ctx, "policy:write") {
return nil, status.Error(codes.PermissionDenied, "missing policy:write scope")
}
// ... create policy
}
Tenant Isolation
All authenticated requests include a tenant_id in the context. This ID is used to:
- Filter database queries via RLS
- Namespace resources (policies, budgets, envelopes)
- Apply per-tenant rate limits
Data Encryption
At Rest: PostgreSQL Encryption
Database-Level: - PostgreSQL Transparent Data Encryption (TDE) available via cloud providers - Column-level encryption for sensitive fields (future) - API key hashes stored as SHA-256 (one-way)
Row-Level Security (/infra/migrations/postgres/000002_enable_rls.up.sql):
-- Enable RLS on core tables
ALTER TABLE fulcrum.tenants ENABLE ROW LEVEL SECURITY;
ALTER TABLE fulcrum.budgets ENABLE ROW LEVEL SECURITY;
ALTER TABLE fulcrum.policies ENABLE ROW LEVEL SECURITY;
ALTER TABLE fulcrum.envelopes ENABLE ROW LEVEL SECURITY;
-- Tenant isolation policy
CREATE POLICY tenant_isolation_budgets ON fulcrum.budgets
FOR ALL
USING (tenant_id = current_setting('fulcrum.current_tenant')::uuid);
In Transit: TLS 1.2+
gRPC Server Configuration:
| Setting | Value |
|---|---|
| Minimum TLS Version | TLS 1.2 |
| Preferred Ciphers | ECDHE-RSA-AES256-GCM-SHA384, ECDHE-RSA-AES128-GCM-SHA256 |
| Certificate Type | X.509 (RSA or ECDSA) |
| Key Length | RSA 2048+ or ECDSA P-256+ |
Environment Variables:
GRPC_TLS_ENABLED=true
GRPC_TLS_CERT_FILE=/etc/fulcrum/tls/server.crt
GRPC_TLS_KEY_FILE=/etc/fulcrum/tls/server.key
GRPC_TLS_CA_FILE=/etc/fulcrum/tls/ca.crt # Optional: for mTLS
API Key Storage
- Hashing Algorithm: SHA-256
- Storage Format: Hexadecimal string (64 characters)
- Plaintext Handling: Returned once at creation; never stored or logged
- Key Hint: Last 4-8 characters stored for user identification
Security CI/CD Pipeline
Fulcrum runs automated security scans on every push and pull request via GitHub Actions.
Workflow: /.github/workflows/security.yml
Scan Matrix
| Scan | Tool | Schedule | Blocking |
|---|---|---|---|
| Dependency Vulnerabilities | govulncheck |
Push, PR, Daily 6AM UTC | HIGH/CRITICAL |
| OSV Database | osv-scanner |
Push, PR, Daily | Non-blocking |
| Static Analysis (SAST) | gosec |
Push, PR | Non-blocking |
| Secret Detection | gitleaks |
Push, PR | Non-blocking |
| Container Scanning | trivy |
Main branch push | CRITICAL/HIGH |
| SBOM Generation | syft |
Main branch push | N/A |
| OWASP ZAP (DAST) | zap-baseline |
Main branch push | Non-blocking |
| API Security Tests | Custom Go tests | Main branch push | Non-blocking |
Dependency Scanning (govulncheck)
- name: Run govulncheck
run: govulncheck -json ./... > vuln-report.json || true
- name: Check for critical vulnerabilities
run: |
if grep -q '"severity":"HIGH"\|"severity":"CRITICAL"' vuln-report.json; then
echo "::error::Critical or High vulnerabilities found"
exit 1
fi
Static Analysis (gosec)
- name: Run gosec
uses: securego/gosec@master
with:
args: '-fmt json -out gosec-report.json -stdout -verbose=text ./...'
Detected Issues (OWASP Reference):
| Rule | Description | OWASP |
|---|---|---|
| G101 | Hardcoded credentials | A07:2021 |
| G102 | Bind to all interfaces | A05:2021 |
| G104 | Unhandled errors | A09:2021 |
| G201 | SQL query construction | A03:2021 |
| G401 | Weak cryptographic primitives | A02:2021 |
| G501 | Blacklisted imports | A06:2021 |
Secret Detection (gitleaks)
- name: Run Gitleaks
run: |
gitleaks detect --source . --verbose --redact \
--report-format json --report-path gitleaks-report.json
Container Scanning (Trivy)
- name: Run Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'fulcrum-server:scan'
format: 'json'
output: 'trivy-results.json'
severity: 'CRITICAL,HIGH'
Vulnerability Management
Dependabot Configuration
File: /.github/dependabot.yml
| Ecosystem | Directory | Schedule | PR Limit |
|---|---|---|---|
| Go Modules | / |
Weekly (Monday) | 10 |
| GitHub Actions | / |
Weekly (Monday) | Auto |
| npm (Dashboard) | /dashboard |
Weekly (Tuesday) | 10 |
| pip (Python SDK) | /sdk/python |
Weekly (Wednesday) | Auto |
| npm (TS SDK) | /sdk/typescript |
Weekly (Wednesday) | Auto |
| Docker | / |
Weekly (Thursday) | Auto |
| Terraform | /infra/terraform |
Monthly | Auto |
CVE Response Process
| Severity | Response SLA | Actions |
|---|---|---|
| Critical | 24 hours | Immediate patch, notify affected customers |
| High | 72 hours | Patch in next release, security advisory |
| Medium | 14 days | Include in scheduled release |
| Low | 30 days | Include in scheduled release |
Known Vulnerabilities
Current tracked issues (from KNOWN_VULNERABILITIES.md):
| CVE | Package | Severity | Status | Mitigation |
|---|---|---|---|---|
| GO-2025-3824 | ollama/ollama | Medium | Accepted | Network isolation, rate limiting |
| GO-2025-3695 | ollama/ollama | Medium | Accepted | Resource limits configured |
| GO-2025-3689 | ollama/ollama | Medium | Accepted | Planned replacement Q1 2026 |
Incident Response
Security Contacts
| Contact | Response Time | |
|---|---|---|
| Security Team | security@fulcrum.dev | 24 hours |
| Urgent Issues | security@fulcrum.dev (subject: URGENT) | 4 hours |
| Product Owner | td@fulcrum.dev | 24 hours |
Response Timeline
| Phase | Timeframe | Activities |
|---|---|---|
| Acknowledgment | 24 hours | Confirm receipt, assign responder |
| Assessment | 72 hours | Severity classification, scope determination |
| Containment | 24-72 hours | Isolate affected systems, revoke credentials |
| Remediation | 7-14 days | Develop and test patch |
| Disclosure | 14-28 days | Coordinated public disclosure |
Disclosure Policy
- Day 0: Vulnerability reported via security@fulcrum.dev or GitHub Security Advisory
- Day 1: Acknowledgment sent to reporter
- Day 3: Initial assessment complete
- Day 7-14: Patch developed and tested
- Day 14-21: Patch released
- Day 21-28: Public disclosure (coordinated with reporter)
Incident Response Checklist
- Isolate: Revoke compromised credentials immediately
- Investigate: Review audit logs for unauthorized access
- Contain: Disable affected components/endpoints
- Notify: Contact security@fulcrum.dev
- Remediate: Apply patches, rotate secrets
- Review: Post-incident analysis and documentation
Compliance
Framework Alignment
| Framework | Status | Notes |
|---|---|---|
| SOC 2 Type II | Planned Q2 2026 | Audit controls in place |
| GDPR | Design compliant | Privacy-by-design, tenant isolation |
| HIPAA | Configurable | Encryption at rest/transit available |
| ISO 27001 | Roadmap | Security controls mapped |
Security Controls Summary
| Control Category | Implementation |
|---|---|
| Access Control | Scope-based RBAC, API key authentication |
| Audit Logging | All policy evaluations logged |
| Encryption | TLS 1.2+ in transit, AES-256 at rest |
| Incident Response | Documented process, 24h acknowledgment SLA |
| Vulnerability Management | Daily scans, Dependabot, govulncheck |
| Network Security | Internal service isolation, rate limiting |
| Data Protection | RLS tenant isolation, no secret logging |
Prometheus Security Metrics
# Security event metrics
- fulcrum_auth_failures_total # Failed authentication attempts
- fulcrum_rate_limit_exceeded_total # Rate limit violations
- fulcrum_invalid_requests_total # Malformed requests
- fulcrum_policy_violations_total # Policy enforcement blocks
Security Testing
Automated Security Tests
Location: /tests/security/api_security_test.go
| Test Category | Description | OWASP Reference |
|---|---|---|
| Authentication Bypass | Attempts access without valid credentials | A07:2021 |
| Authorization Escalation | Cross-tenant access attempts | A01:2021 |
| SQL Injection | Malicious query payloads | A03:2021 |
| XSS Prevention | Script injection in metadata | A03:2021 |
| Rate Limiting | Burst request handling | A04:2021 |
| Error Leakage | Sensitive info in error messages | A04:2021 |
| Timing Attacks | Constant-time auth comparison | A02:2021 |
Running Security Tests
# Run all security tests (requires running server)
go test -v -timeout 5m ./tests/security/...
# Run in CI (uses docker-compose)
docker compose -f docker-compose.unified.yml up -d
go test -v -timeout 5m ./tests/security/...
docker compose -f docker-compose.unified.yml down -v
References
- OWASP Top 10 (2021)
- CWE Top 25 (2024)
- Go Security Policy
- gRPC Security Guide
- PostgreSQL Row-Level Security
- Clerk Security
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-06 | Security Team | Initial release |
Next Review: February 6, 2026 Owner: Security Team (security@fulcrum.dev)