Security is not a feature you add later. It is a foundation you build early or spend the rest of the project regretting. On January 15, 2026, FLIN's security sprint began with Session 183 and ended with Session 200. Eighteen sessions. Two days. A complete security stack: AES-256-GCM encryption, Argon2 password hashing, JWT authentication, CSRF protection, security headers, rate limiting, guards, middleware, request validation, file upload security, WebSocket authentication, two-factor authentication, OAuth2 with social providers, and 75 dedicated security tests.
This is the story of how a programming language went from zero security primitives to a production-ready security framework in a single weekend.
The Security Landscape: Session 183
Session 183 established the foundation with 18 security functions across four categories: cryptography, JWT authentication, secrets management, and password utilities.
The cryptography functions used industry-standard algorithms with no room for compromise:
// AES-256-GCM encryption -- the gold standard
key = secure_random(32)
encrypted = encrypt("sensitive data", key)
decrypted = decrypt(encrypted, key)// Argon2id password hashing -- winner of the Password Hashing Competition hash = argon2_hash("user_password") valid = argon2_verify("user_password", hash)
// CSRF protection with constant-time comparison csrf = csrf_token() if csrf_verify(form_token, csrf) { // Process form safely } ```
The design principle was explicit: FLIN developers should not need to know the difference between AES-128 and AES-256, or between bcrypt and Argon2. The language provides one function for each operation, and that function uses the best available algorithm. encrypt() always uses AES-256-GCM. argon2_hash() always uses Argon2id. There is no configuration because there is no reason to use a weaker option.
The JWT implementation was equally opinionated:
// JWT creation with automatic claims (Session 183)
// Always includes iat (issued at) and exp (expiration)
// Default expiration: 1 hour
// Algorithm: HS256fn jwt_create(payload: &Map, secret: &str, expires_in: Option
let mut claims = payload.clone(); claims.insert("iat".to_string(), Value::Int(now)); claims.insert("exp".to_string(), Value::Int(now + expiration));
encode(&Header::default(), &claims, &EncodingKey::from_secret(secret.as_bytes())) .unwrap_or_default() } ```
By the end of Session 183, FLIN had 18 security functions, and the test count stood at 2,538. The security category had jumped from 20/160 to 38/160 (23.75%).
Sessions 184-186: Headers, Rate Limiting, and Guards
Session 184 implemented security headers -- the HTTP response headers that protect against common web attacks:
// Security headers applied automatically to every response
//
// Content-Security-Policy: default-src 'self'
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY
// X-XSS-Protection: 1; mode=block
// Strict-Transport-Security: max-age=31536000; includeSubDomains
// Referrer-Policy: strict-origin-when-cross-originFLIN applies these headers by default. Developers can customize them, but the defaults follow OWASP recommendations. A new FLIN application is more secure out of the box than most hand-configured Express or Django applications.
Session 184 also implemented rate limiting -- a critical defense against brute-force attacks, credential stuffing, and API abuse:
pub struct RateBucket {
count: u64,
window_start: u64,
window_ms: u64,
limit: u64,
}impl RateBucket { pub fn increment(&mut self) -> bool { let now = current_timestamp_ms(); if now - self.window_start >= self.window_ms { // New window self.count = 1; self.window_start = now; true } else if self.count < self.limit { self.count += 1; true } else { false // Rate limited } } } ```
Sessions 185-186 built the guards system -- FLIN's declarative approach to access control:
// Guards: declarative security at the route levelroute GET "/admin/dashboard" { guard auth // Must be authenticated guard role("admin") // Must have admin role guard rate_limit(100, "1m") // 100 requests per minute
// Route handler -- only reached if all guards pass users = User.all { users: users } } ```
Guards are composable and ordered. Each guard either passes (allowing the request to continue) or fails (returning an appropriate HTTP error). The guard auth directive checks for a valid JWT or session token. The guard role("admin") directive checks the authenticated user's role. The guard rate_limit(100, "1m") directive enforces request throttling.
Sessions 187-193: Middleware, Validation, and Response Helpers
Sessions 187-188 implemented FLIN's middleware system. Unlike guards (which either pass or fail), middleware can modify requests and responses:
// Middleware: transform requests and responsesmiddleware log_request { print("${request.method} ${request.path}") next() // Continue to next middleware or handler }
middleware cors { response.header("Access-Control-Allow-Origin", "*") if request.method == "OPTIONS" { return { status: 204 } } next() }
route GET "/api/data" { use log_request use cors
{ data: "response" } } ```
Sessions 189-192 built the request validation system. FLIN validates incoming request bodies against declared schemas, rejecting malformed input before it reaches the handler:
route POST "/api/users" {
validate {
name: text @required @min_length(2) @max_length(100)
email: text @required @email
age: int @min(13) @max(150)
password: text @required @min_length(8)
}// body.name, body.email, body.age, body.password // are all validated and type-safe at this point
hash = argon2_hash(body.password) save User { name: body.name, email: body.email, password_hash: hash } { status: "created" } } ```
The validation system uses annotations (@required, @email, @min_length, @max_length, @min, @max) that are checked at runtime. Invalid requests receive a 400 response with detailed error messages describing which fields failed and why.
Session 193 added response helpers -- utility functions for common HTTP response patterns:
// Response helpers
json({ data: users }) // 200 with JSON body
created({ id: user.id }) // 201 Created
no_content() // 204 No Content
bad_request("Invalid input") // 400 Bad Request
unauthorized("Token expired") // 401 Unauthorized
forbidden("Admin only") // 403 Forbidden
not_found("User not found") // 404 Not FoundSessions 194-196: File Upload and WebSocket Security
Session 194 secured file uploads with comprehensive validation:
route POST "/upload" {
validate {
file: file @required @max_size("5MB") @extension(".jpg", ".png", ".pdf")
}path = save_file(body.file, "uploads/") { path: path } } ```
The save_file() function includes security features built during the file upload session: path traversal protection (blocking .. in filenames), UUID-based filename generation (preventing filename collisions and information leakage), automatic directory creation, and SHA-256 hash computation for integrity verification.
Sessions 195-196 extended security to WebSocket connections and configuration guards.
Sessions 197-198: Two-Factor Authentication and OAuth2
Session 197 implemented TOTP-based two-factor authentication:
// Generate 2FA secret for a user
secret = totp_secret()
uri = totp_uri(secret, "[email protected]", "MyApp")
qr = totp_qr(uri) // Data URL for QR code display// Verify a 2FA code valid = totp_verify(secret, user_code)
// Backup codes for recovery codes = backup_codes(8) // Generate 8 backup codes ```
Session 198 was one of the most ambitious of the sprint: OAuth2 with social authentication providers. In a single session, FLIN gained support for Google, GitHub, and WhatsApp authentication:
// OAuth2 configuration
route GET "/auth/google" {
redirect(oauth2_url("google", {
client_id: env("GOOGLE_CLIENT_ID"),
redirect_uri: "https://myapp.com/auth/google/callback",
scope: "openid email profile"
}))
}route GET "/auth/google/callback" { token = oauth2_exchange("google", query.code, { client_id: env("GOOGLE_CLIENT_ID"), client_secret: env("GOOGLE_CLIENT_SECRET"), redirect_uri: "https://myapp.com/auth/google/callback" })
user_info = oauth2_userinfo("google", token.access_token) // Create or update user from OAuth profile } ```
Sessions 199-200: Testing Everything
Session 199 wrote integration tests for the complete security stack. Session 200 was dedicated entirely to unit tests -- 75 new tests covering every security function:
Security Unit Tests (Session 200):Rate Limiting: 8 tests Guards: 34 tests Password (Argon2): 7 tests JWT Tokens: 6 tests Secrets: 3 tests Crypto (AES/UUID): 5 tests (including roundtrip) CSRF: 5 tests 2FA/TOTP: 7 tests
Total New: 75 tests Total Project: 2,926 tests (0 failures) ```
The test coverage was comprehensive. Password hashing tests verified that Argon2 produced valid PHC-format strings and that different passwords produced different hashes. JWT tests verified creation, verification, decoding, expiration detection, and signature validation. Crypto tests verified encrypt/decrypt roundtrips and the uniqueness of random values. CSRF tests verified constant-time comparison to prevent timing attacks.
// Example: Testing constant-time CSRF comparison (Session 200)
#[test]
fn test_csrf_verify_matching_tokens() {
let token = "abc123def456";
assert!(csrf_verify(token, token));
}#[test] fn test_csrf_verify_different_tokens() { assert!(!csrf_verify("token_a", "token_b")); }
#[test] fn test_csrf_verify_empty_tokens() { // Empty tokens should not match assert!(!csrf_verify("", "non_empty")); assert!(!csrf_verify("non_empty", "")); } ```
The Complete Security Stack
By Session 200, FLIN's security stack comprised:
| Category | Functions/Features | Sessions |
|---|---|---|
| Cryptography | AES-256-GCM, secure random, UUID | 183 |
| Password Hashing | Argon2id, password strength | 183 |
| JWT Authentication | Create, verify, decode, expiry | 183 |
| Secrets Management | env_or, env_exists, secret (path-safe) | 183 |
| CSRF Protection | Token generation, constant-time verify | 183 |
| Security Headers | CSP, HSTS, X-Frame, X-Content-Type | 184 |
| Rate Limiting | Per-route, per-IP, sliding window | 184 |
| Guards System | auth, role, rate_limit, custom guards | 185-186 |
| Middleware | Request/response transformation | 187-188 |
| Request Validation | Type-safe body validation with annotations | 189-192 |
| Response Helpers | Standard HTTP response utilities | 193 |
| File Upload Security | Path traversal, size limits, type checking | 194 |
| WebSocket Auth | Token-based WS authentication | 195-196 |
| 2FA (TOTP) | Secret generation, QR codes, backup codes | 197 |
| OAuth2 | Google, GitHub, WhatsApp providers | 198 |
| Security Tests | 75 dedicated unit tests | 199-200 |
All of this is built into the FLIN runtime. No npm install passport. No pip install django-oauth-toolkit. No configuration files. A FLIN developer writes guard auth and authentication is enforced. They write validate { email: text @email } and input is validated. The security stack is part of the language.
Why Two Days Was Enough
Eighteen sessions in two days for a complete security framework sounds impossible by traditional development standards. The explanation is straightforward: security primitives are well-specified.
AES-256-GCM is not a design problem. It is an implementation problem with a clear specification (NIST SP 800-38D). JWT is defined by RFC 7519. TOTP is defined by RFC 6238. OAuth2 is defined by RFC 6749. Argon2 is the winner of the Password Hashing Competition with a published reference implementation.
The AI CTO excels at implementing well-specified standards. Given an RFC number and a target language (Rust), it can produce a correct implementation quickly. The human's role is choosing which standards to implement and how to expose them in FLIN's syntax. Juste decided that FLIN should use Argon2 rather than bcrypt. He decided that security headers should be applied by default. He decided that guards should be declarative. These are product decisions. The implementations are engineering.
What would take a traditional team weeks -- not because the code is hard, but because of code review cycles, testing coordination, documentation, and integration -- takes two days in the CEO + AI CTO model because the feedback loop is minutes, not days.
---
This is Part 200 of the "How We Built FLIN" series, documenting how a CEO in Abidjan and an AI CTO built a programming language from scratch.
Series Navigation: - [199] The Temporal Debugging Marathon - [200] The Security Sprint: 18 Sessions (you are here) - [201] The File Storage Marathon: 30 Sessions