life-my--midst--in

Security Guidelines

in–midst–my-life Security Checklist & Best Practices

Version: 2.0 Last Updated: 2026-01-18 Status: Pre-production security guidance


Table of Contents

  1. Quick Checklist
  2. Authentication
  3. Authorization
  4. API Security
  5. Database Security
  6. Secret Management
  7. Input Validation
  8. Rate Limiting
  9. Audit Logging
  10. OWASP Top 10
  11. Deployment Security
  12. Incident Response
  13. Security Testing

Quick Checklist

Pre-Commit

Pre-Deployment

Production


Authentication

Current Status

Authentication is implemented via JWT bearer tokens with role-based access control.

Current Implementation

JWT Authentication

Middleware Stack

Global Auth Hook

A three-tier onRequest hook applies authentication globally:

OAuth2/OIDC Integration

Support social login via:

DID-Based Authentication (Future)

For Web3 users:

WebSocket Security

GraphQL WebSocket subscriptions (GET /graphql/ws) require JWT authentication at connection time:

Implementation Checklist


Authorization

Role-Based Access Control (RBAC)

Planned Roles

| Role | Permissions | |——|————-| | Owner | Full access to own profile, billing, data export | | Reviewer | Read-only access to public profile views | | Admin | System administration (internal only) | | Agent | API access with scoped permissions |

Resource-Based Authorization

// Check ownership before modification
function canModifyProfile(user: User, profile: Profile): boolean {
  return profile.identityId === user.identityId || user.role === 'admin';
}

Authorization Middleware

// Planned: apps/api/src/middleware/authorize.ts
export function requireAuth(req, reply, done) {
  if (!req.session?.userId) {
    return reply.code(401).send({ error: 'Unauthorized' });
  }
  done();
}

export function requireOwner(req, reply, done) {
  if (req.params.profileId !== req.session.profileId) {
    return reply.code(403).send({ error: 'Forbidden' });
  }
  done();
}

API Key Authentication (Agents)

For Hunter Protocol and external integrations:


API Security

Endpoint Protection

Public Endpoints (No Auth)

Protected Endpoints (Auth Required)

All profile CRUD, narrative generation, export endpoints.

Restricted Endpoints (Internal Only)

Security Headers (Implemented)

Security headers are enforced via @fastify/helmet in apps/api/src/index.ts:

fastify.register(helmet, {
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", 'data:', 'https:'],
      connectSrc: ["'self'", 'wss:', 'https:'],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      frameSrc: ["'none'"],
    },
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true,
  },
});

Headers set automatically on every response:

Note: Helmet is disabled in test mode to avoid CSP interfering with test harnesses.

CORS Configuration

app.register(require('@fastify/cors'), {
  origin: [
    'https://your-domain.com',
    process.env.NODE_ENV === 'development' && 'http://localhost:3000',
  ].filter(Boolean),
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
});

Request Size Limits

app.register(require('@fastify/multipart'), {
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB max file
    files: 5, // Max 5 files per request
  }
});

Database Security

Connection Security

Credential Isolation

| Environment | Database | Credentials | |————-|———-|————-| | Development | midst_dev | Local dev creds | | Testing | midst_test | Isolated test creds | | Integration | midst_integration | CI-specific creds | | Production | midst_prod | Vault-managed creds |

SQL Injection Prevention

Always use parameterized queries:

// CORRECT - Parameterized query
const result = await pool.query(
  'SELECT * FROM profiles WHERE id = $1',
  [profileId]
);

// WRONG - String concatenation (SQL injection risk)
// Never do this: `SELECT * FROM profiles WHERE id = '${profileId}'`

Database User Permissions

-- App user should NOT have superuser privileges
CREATE USER app_user WITH PASSWORD '...';
GRANT CONNECT ON DATABASE midst_prod TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
-- Deny DROP, TRUNCATE, CREATE

Data Encryption


Secret Management

Hierarchy of Secret Storage

Environment Method Tool
Local Dev .env.local File (gitignored)
CI/CD Environment variables GitHub Secrets
Production Secret manager 1Password / HashiCorp Vault

1Password Integration

# Load environment from 1Password
# ~/.config/op/load-env.sh

# Project-specific loader
# /path/to/project/*.env.op.sh
op inject -i .env.template -o .env

Required Secrets

| Secret | Used By | Rotation | |——–|———|———-| | DATABASE_URL | API, Orchestrator | On compromise | | REDIS_URL | API, Orchestrator | On compromise | | GITHUB_WEBHOOK_SECRET | Orchestrator | Monthly | | SERPER_API_KEY | Hunter Protocol | On compromise | | STRIPE_SECRET_KEY | API (billing) | Annual | | SESSION_SECRET | API (auth) | Quarterly |

Never Commit

Detection

# Pre-commit hook to detect secrets
pnpm add -D gitleaks
gitleaks detect --source . --verbose

# CI integration
# - name: Secret Scan
#   run: gitleaks detect --source . --fail

Input Validation

Validation Strategy

  1. Schema validation - Zod at API boundary
  2. Type coercion - Controlled type conversion
  3. Sanitization - HTML/XSS prevention
  4. Business rules - Domain-specific validation

Zod Schema Validation

// apps/api/src/validation/profile.ts
import { z } from 'zod';

export const CreateProfileSchema = z.object({
  displayName: z.string().min(1).max(100).trim(),
  slug: z.string().regex(/^[a-z0-9-]+$/).max(50),
  title: z.string().max(200).optional(),
  headline: z.string().max(500).optional(),
  summaryMarkdown: z.string().max(10000).optional(),
});

// Use in route handler
const parsed = CreateProfileSchema.safeParse(req.body);
if (!parsed.success) {
  return reply.code(400).send({ errors: parsed.error.errors });
}

XSS Prevention

File Upload Validation

const allowedMimeTypes = [
  'image/jpeg',
  'image/png',
  'image/gif',
  'application/pdf',
];

function validateUpload(file: MultipartFile): boolean {
  return (
    allowedMimeTypes.includes(file.mimetype) &&
    file.size <= 10 * 1024 * 1024 // 10MB
  );
}

Rate Limiting

Strategy

Implement multiple layers of rate limiting:

  1. Global - Protect infrastructure
  2. Per-IP - Prevent abuse from single source
  3. Per-User - Fair usage enforcement
  4. Per-Endpoint - Expensive operation protection

Implementation

// apps/api/src/plugins/rateLimit.ts
app.register(require('@fastify/rate-limit'), {
  global: true,
  max: 100, // requests
  timeWindow: '1 minute',
  keyGenerator: (req) => req.ip,
  errorResponseBuilder: (req, context) => ({
    statusCode: 429,
    error: 'Too Many Requests',
    message: `Rate limit exceeded. Try again in ${context.after}`,
  }),
});

// Stricter limit for expensive operations
app.route({
  method: 'POST',
  url: '/profiles/:id/narrative',
  config: {
    rateLimit: {
      max: 10,
      timeWindow: '1 minute',
    }
  },
  handler: narrativeHandler,
});

Rate Limits by Tier

| Tier | Requests/Min | Narrative Gen/Min | |——|————–|——————-| | Free | 30 | 5 | | Artisan | 100 | 20 | | Dramatist | 300 | 50 |

Redis-Based Tracking

// Track per-user usage
async function trackUsage(userId: string, action: string): Promise<void> {
  const key = `usage:${userId}:${action}:${getCurrentMinute()}`;
  await redis.incr(key);
  await redis.expire(key, 120); // 2 minute TTL
}

Audit Logging

What to Log

| Category | Events | |———-|——–| | Authentication | Login, logout, failed attempts, password reset | | Authorization | Permission denied, role changes | | Data Access | Profile views, exports, modifications | | API | Requests, errors, rate limit hits | | System | Startup, shutdown, config changes |

Log Format

interface AuditLog {
  timestamp: string;    // ISO 8601
  level: 'info' | 'warn' | 'error';
  event: string;        // e.g., 'profile.view', 'auth.login'
  userId?: string;
  profileId?: string;
  ip?: string;
  userAgent?: string;
  details?: Record<string, unknown>;
}

Implementation

// apps/api/src/services/audit.ts
export async function audit(event: AuditEvent): Promise<void> {
  const log: AuditLog = {
    timestamp: new Date().toISOString(),
    level: 'info',
    event: event.type,
    userId: event.userId,
    profileId: event.profileId,
    ip: event.request?.ip,
    userAgent: event.request?.headers['user-agent'],
    details: event.details,
  };

  // Write to database
  await db.query(
    'INSERT INTO audit_logs (data) VALUES ($1)',
    [JSON.stringify(log)]
  );

  // Also log to stdout for aggregation
  console.log(JSON.stringify(log));
}

PII Considerations


OWASP Top 10

Coverage Matrix

# Vulnerability Mitigation Status
1 Broken Access Control Authorization middleware, ownership checks Implemented
2 Cryptographic Failures TLS everywhere, secure password hashing Ready
3 Injection Parameterized queries, Zod validation Implemented
4 Insecure Design Security review, threat modeling Planned
5 Security Misconfiguration Security headers, defaults review Ready
6 Vulnerable Components pnpm audit, Dependabot Ready
7 Authentication Failures Session management, MFA Planned
8 Software/Data Integrity CI/CD security, signed commits Planned
9 Logging Failures Audit logging, monitoring Planned
10 SSRF URL validation, allowlists Ready

Injection Prevention

// SQL - Always parameterize
await pool.query('SELECT * FROM profiles WHERE id = $1', [id]);

// NoSQL/Redis - Validate keys
const key = `profile:${validateUUID(id)}`;

// OS Commands - Use execFile instead of exec
// See: src/utils/execFileNoThrow.ts for safe command execution

SSRF Prevention (Hunter Protocol)

// Validate URLs before fetching
const allowedDomains = ['linkedin.com', 'github.com', 'example.com'];

function validateExternalUrl(url: string): boolean {
  const parsed = new URL(url);
  return (
    ['http:', 'https:'].includes(parsed.protocol) &&
    allowedDomains.some(d => parsed.hostname.endsWith(d)) &&
    !isPrivateIP(parsed.hostname)
  );
}

Deployment Security

HTTPS Enforcement

Environment Isolation

Development       Staging           Production
-----------       -------           ----------
midst_dev         midst_stage       midst_prod
Local creds       Test creds        Vault creds
No auth           Test auth         Full auth

Container Security

# Use specific version, not latest
FROM node:22-alpine

# Run as non-root user
RUN addgroup -S app && adduser -S app -G app
USER app

# Don't expose unnecessary ports
EXPOSE 3001

Network Security


Incident Response

Contact

Security issues: security@in-midst-my-life.dev

Severity Levels

| Level | Description | Response Time | |——-|————-|—————| | Critical | Active exploit, data breach | Immediate | | High | Exploitable vulnerability | < 24 hours | | Medium | Vulnerability requiring auth | < 1 week | | Low | Minor issue, defense in depth | < 1 month |

Response Procedure

  1. Detect - Monitoring alert or report
  2. Contain - Isolate affected systems
  3. Assess - Determine scope and impact
  4. Remediate - Fix vulnerability
  5. Recover - Restore normal operations
  6. Review - Post-incident analysis

Playbook: Credential Leak

  1. Immediately rotate affected credentials
  2. Check audit logs for unauthorized access
  3. Invalidate all active sessions
  4. Notify affected users if data accessed
  5. Update gitleaks rules to prevent recurrence

Security Testing

Automated Testing

# Dependency vulnerability scan
pnpm audit

# Secret detection
gitleaks detect --source .

# SAST (future)
# Consider: Semgrep, CodeQL

Manual Testing Checklist

Penetration Testing

Schedule professional penetration testing before production launch:


Compliance Considerations

GDPR (If serving EU users)

SOC 2 (Future consideration)



Document Authority: This document defines security requirements for the project. All code must comply with these guidelines before production deployment.

Review Schedule: Security guidelines should be reviewed quarterly and after any security incident.