Spaces:
Running
β οΈ ADVERSARIAL AUDIT β meta_hack Customer Support RL Environment
Auditor: Antigravity AI
Date: 2026-04-08
Codebase:github.com/lebiraja/meta_hack
Total Files Audited: 15 source files, ~2,100 LoC
Audit Type: Zero-compromise adversarial, 12-phase, assuming 10,000 concurrent users under hostile conditions
EXECUTIVE SUMMARY
| Metric | Rating |
|---|---|
| Architecture | π’ 7.5/10 |
| Security | π΄ 2/10 |
| Scalability | π‘ 4/10 |
| RL Quality | π’ 7/10 |
| Code Quality | π’ 8/10 |
| Test Coverage | π‘ 6/10 |
| Production Readiness | π‘ 5/10 |
| Overall Verdict | 5.5/10 β Solid Hackathon Entry, Not Production |
This codebase is dramatically better than the original
customer_support_envrepository (rated 1.3/10). It demonstrates genuine software engineering competence. However, it has critical security gaps and scalability limitations that prevent production deployment.
PHASE 1: SURFACE-LEVEL AUDIT
β What's Right
| Item | Status |
|---|---|
Clean module structure (env/, server/, tests/) |
β |
| Pydantic v2 models with proper validation | β |
ActionType as str, Enum β allows JSON serialization |
β |
Single server entrypoint (server/app.py) |
β |
inference.py is a client, not embedded in server |
β |
pyproject.toml and requirements.txt aligned |
β |
Dockerfile uses layer caching, pip install -e . |
β |
| Docker HEALTHCHECK configured | β |
openenv.yaml matches actual implementation |
β |
β οΈ Minor Concerns
| Item | Severity |
|---|---|
re import inside _compute_unresolved_issues() at L244 of environment.py β should be top-level |
Low |
random.choice() not seeded β slightly non-deterministic test behavior |
Low |
Test file imports from env.models import Message redundantly (already in env) |
Cosmetic |
PHASE 2: LOGIC & CORRECTNESS AUDIT
β Core Logic Is Sound
The code path reset() β step() β compute_step_reward() β grade() is fully connected and functional:
server/app.py β CustomerSupportEnv.reset()
β ticket_store.get_random_by_task() β picks ticket
β _build_observation() β returns Observation
server/app.py β CustomerSupportEnv.step(action)
β compute_step_reward() β VADER + cosine + resolution + accuracy
β _simulate_customer_reply() β persona-driven follow-up
β _update_sentiment() β sentiment tracking
β returns (obs, reward, done, info)
On done β run_grader(task, state) β task-specific deterministic grading
β del _sessions[session_id] β cleanup
β οΈ Logic Issues Found
Issue 1: Reward can exceed 1.0 before clamping
In reward_engine.py L209-217, the composite reward is:
raw = (0.40 * resolution + 0.20 * tone + 0.20 * efficiency + 0.20 * accuracy
+ loop_penalty + escalation_penalty + info_gathering_bonus)
With info_gathering_bonus = 0.1, max theoretical:
0.40 + 0.20 + 0.20 + 0.20 + 0.0 + 0.0 + 0.1 = 1.1
The np.clip(raw, 0.0, 1.0) saves this, but the bonus is an unweighted additive on top of a weighted sum β architecturally sloppy.
Issue 2: _compute_unresolved_issues fallback is questionable
# L253-254: If no regex pattern, assume unresolved until customer replies β₯2 times
elif sum(1 for m in self._history if m.role == "customer") < 2:
unresolved.append(info_type)
This means any unknown info_type is considered "resolved" after 2 customer messages regardless of content. A customer saying "I don't know" twice would satisfy this.
Issue 3: Resolution score keyword matching is order-dependent
_RESOLUTION_SIGNALS["refund_initiated"] has 7 keywords. The score is:
score = min(matched / max(len(signals) * 0.4, 1), 1.0)
# = min(matched / 2.8, 1.0) β so 3 keyword matches = score 1.0
An agent saying "refund credit money back" scores 1.0 without actually initiating anything. The system measures intent language, not action execution.
Issue 4: Escalation penalty vs. resolution score double-penalize
In reward_engine.py:
compute_escalation_penalty()returns-0.3for escalating low/medium ticketscompute_resolution_score()returnsmax(score - 0.4, 0.0)for non-escalation-expected escalations
Both are applied simultaneously via raw = ... + escalation_penalty and 0.40 * resolution_score. This means escalating an easy task is penalized twice: once by -0.3 absolute and once by -0.4 on the 40%-weighted resolution score. Effective penalty: -0.46 β harsh but arguably intentional.
PHASE 3: API & ENDPOINT AUDIT
β Endpoints Well-Designed
| Endpoint | Method | Auth | Validation | Error Handling |
|---|---|---|---|---|
/reset?task=easy |
POST | β None | β
Literal["easy","medium","hard"] |
β |
/step?session_id=... |
POST | β None | β
Pydantic Action model |
β 404/409 |
/state/{session_id} |
GET | β None | β 404 if not found | β |
/health |
GET | β None | N/A | β |
/ |
GET | β None | N/A | β |
β Critical API Issues
No Authentication Whatsoever
Every endpoint is publicly accessible. Anyone can:
- Create unlimited sessions (
POST /reset) - Step any session they know the ID of
- Read full internal state including ticket data (
GET /state/{id})
No Rate Limiting
Zero rate limiting on any endpoint. A single curl loop can:
while true; do curl -X POST http://host:7860/reset; done
This will exhaust server memory in minutes.
Session ID is UUID β Not Secret
UUIDs are not cryptographically secret. They're guessable with enough samples. However, in practice, UUID4 has 122 bits of entropy which is sufficient for a hackathon. Not sufficient for production with PII.
PHASE 4: FAILURE PATH AUDIT
What Happens When Things Go Wrong
| Failure Mode | Server Behavior | Severity |
|---|---|---|
Invalid task in /reset |
Pydantic validates β 422 | β Handled |
Invalid action_type in /step |
Pydantic validates β 422 | β Handled |
Unknown session_id |
404 HTTPException |
β Handled |
| Step after done | RuntimeError β 409 HTTPException |
β Handled |
env.reset() not called |
RuntimeError β 409 |
β Handled |
| Grader throws exception | try/except β falls back to reward.value |
β Handled |
| Malformed JSON body | FastAPI/Pydantic β 422 | β Handled |
| 1MB payload | β No size limit β processed normally | π΄ Critical |
| Empty message string | Tone score β 0.5 (neutral) | β οΈ Benign |
None message + None reason |
Falls back to f"[{action_type}]" |
β Handled |
β Unhandled Failure Paths
1. Ticket Store Singleton Mutation
# ticket_store.py L420
def get_random_by_task(self, task: str) -> dict:
return dict(random.choice(pool)) # shallow copy
dict() is a shallow copy. The required_info_before_close list inside is shared. If any code mutated this list (currently none do), all sessions using that ticket would be affected. Safe today, fragile tomorrow.
2. TF-IDF Vectorizer Instantiation Per Call
# reward_engine.py L72
vec = TfidfVectorizer().fit_transform(last_two)
A new TfidfVectorizer is created for every step of every session. At 10,000 concurrent users Γ ~5 steps each = 50,000 vectorizer instantiations per minute. Not catastrophic (it's lightweight for 2 documents), but wasteful.
3. VADER Analyzer is a Module-Level Singleton
_analyzer = SentimentIntensityAnalyzer()
This is fine for single-threaded uvicorn. If deployed with --workers N (multi-process), each worker gets its own instance. If using asyncio with threads, VADER is not thread-safe (it reads/writes internal state). Currently safe because uvicorn default is single-process + asyncio.
PHASE 5: SCALABILITY AUDIT (10,000 CONCURRENT USERS)
Memory Analysis
Per session:
CustomerSupportEnvobject: ~2KB base- Ticket dict (shallow copy): ~1KB
- History (up to max_steps messages): ~500 bytes Γ 10 = 5KB
- Action log: ~200 bytes Γ 10 = 2KB
- Total per session: ~10KB
At 10,000 concurrent sessions: ~100MB β survivable if sessions are cleaned up.
β The _sessions Dict Problem
# server/app.py L28
_sessions: dict[str, CustomerSupportEnv] = {}
Sessions ARE cleaned up on done (L85: del _sessions[session_id]). This is a critical improvement over the original codebase. However:
Abandoned sessions are never cleaned up. If a client calls
/resetbut never reachesdone, the session lives forever. There is no TTL, no eviction, no cleanup sweep.At 10,000 users with 5% abandonment rate: 500 zombie sessions per hour Γ 24 hours = 12,000 zombies/day Γ 10KB = 120MB/day of leaked memory. Server dies in days.
No max session limit. A single attacker can call
/resetin a loop and create millions of sessions.
Concurrency Safety
The server uses synchronous FastAPI endpoints (no async def). With default uvicorn:
- Single worker, single thread
- Requests are processed sequentially (no parallelism)
_sessionsdict is safe (no race conditions)- But throughput is terrible under load
With uvicorn --workers N:
- Each worker has its own
_sessionsdict - Sessions are not shared between workers
- A session created in worker 1 will 404 in worker 2
- Multi-worker deployment is broken by design
Verdict At Scale
| Metric | At 10,000 Users |
|---|---|
| Memory (active sessions) | ~100MB β |
| Memory (zombie sessions/day) | ~120MB/day β οΈ |
| CPU (VADER + TF-IDF per step) | High but survivable β οΈ |
| Throughput (single worker) | ~100 req/s max π΄ |
| Multi-worker support | β Broken |
| Session cleanup | β On done, β On abandon |
PHASE 6: SECURITY AUDIT
π΄ Critical Vulnerabilities
1. Zero Authentication β CRITICAL
- No API keys, no tokens, no auth headers
- Anyone can create sessions, step them, and read state
- Severity: CRITICAL in any deployment with real users
2. State Endpoint Leaks Full Internal Data β HIGH
GET /state/{session_id}returns full ticket data, conversation history, sentiment, action log- Includes customer PII (emails, order IDs, phone numbers) embedded in ticket
follow_up_info - No access control β any session ID can be read by anyone
3. No Input Size Limit β HIGH
- No FastAPI request body size limit configured
- A 100MB message payload would be accepted and stored in history
- OOM attack: send 1000 requests with 10MB messages = 10GB memory consumed
4. No CORS Configuration β MEDIUM
- No
CORSMiddlewareconfigured - Not exploitable in server-to-server scenarios
- Exploitable if any web frontend is added
5. Ticket Data Contains Fake PII β LOW (for hackathon)
- Tickets contain realistic email addresses and names
- In a production system, this would be real PII requiring encryption at rest
- For hackathon evaluation, this is expected behavior
β Security Positives
| Item | Status |
|---|---|
| No SQL injection (no database) | β |
| No command injection | β |
| No path traversal | β |
| No SSRF | β |
| No deserialization attacks (Pydantic validates) | β |
| Session IDs are UUID4 (not sequential) | β |
| No secrets in source code | β |
| Environment variables for API keys | β |
PHASE 7: ARCHITECTURE AUDIT
β Architecture Is Clean
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β server/app.py (117 LoC) β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β _sessions: dict[str, CustomerSupportEnv] β β
β β POST /reset β new env β store β return obs β β
β β POST /step β lookup env β step β cleanup if done β β
β β GET /state β lookup env β return state β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β env/environment.py (258 LoC) β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β CustomerSupportEnv β β
β β .reset() β pick ticket, build obs β β
β β .step(action) β reward + sentiment + customer reply β β
β β .state() β full internal state β β
β ββββββββ¬βββββββββββββββ¬βββββββββββββββ¬βββββββββββββββββββββ β
β β β β β
β env/reward_engine.py env/ticket_store.py env/graders/ β
β (235 LoC) (434 LoC) (293 LoC) β
β VADER + TF-IDF 30 tickets 3 task graders β
β cosine loop detect 3 difficulty levels deterministic β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key architectural wins:
- No dead code. Every file is imported and used.
- No contradictions.
server/app.pyusesCustomerSupportEnvwhich usesreward_enginewhich usesgraders. The pipeline is end-to-end connected. - Single responsibility. Each module does one thing.
- Inference is a client.
inference.pytalks to the server via HTTP, not by importing env directly.
β οΈ Architectural Weaknesses
- In-memory only. No persistence. Server restart = all sessions gone.
- Single-process only. Cannot scale horizontally.
- No middleware. No logging middleware, no timing middleware, no error tracking.
PHASE 8: OBSERVABILITY AUDIT
β Minimal Observability
| Feature | Status |
|---|---|
| Structured logging | β None β no logging at all in server |
| Request timing | β Not tracked |
| Error tracking (Sentry etc.) | β Not configured |
| Metrics (Prometheus) | β Not configured |
| Health check | β
/health returns active_sessions count |
| Request IDs | β Not generated |
| Audit trail | β οΈ Action log per session, but lost on cleanup |
The only observability is the /health endpoint reporting active_sessions. In a production incident, you would have:
- No logs to investigate
- No metrics to correlate
- No request traces
- No error rates
However, uvicorn does emit its own access logs to stdout, which Docker captures. This provides basic request-level visibility.
PHASE 9: PERFORMANCE AUDIT
Hot Path Analysis
For each /step request:
- Dict lookup: O(1) β
- VADER sentiment: ~0.1ms per message β
- TF-IDF vectorizer: ~1-5ms (create + fit + transform on 2 docs) β οΈ
- Regex matching for info patterns: O(n) where n = conversation length β
- Resolution signal matching: O(k) where k = num signals β
- Grader execution (on done only): ~1ms β
Bottleneck: TF-IDF is the heaviest operation but still fast (~5ms).
Estimated max throughput: ~200 req/s single-threaded.
β No Blocking I/O
The server is entirely CPU-bound. No database calls, no external API calls, no file I/O during request handling. This is a significant advantage for predictable latency.
PHASE 10: RL ENVIRONMENT QUALITY AUDIT
β Reward Function Is Legitimate
| Signal | Implementation | Quality |
|---|---|---|
| Tone (20%) | VADER compound score, mapped [β1,1]β[0,1] | β Real NLP |
| Resolution (40%) | Category-aware keyword matching | β οΈ Passable |
| Efficiency (20%) | 1 - steps/max_steps |
β Clean |
| Accuracy (20%) | Regex check for email, order ID, etc. | β Deterministic |
| Loop penalty | Cosine similarity > 0.85 between consecutive agent messages | β Real NLP |
| Escalation penalty | -0.3 for escalating low/medium tickets | β Correct incentive |
| Info bonus | +0.1 for REQUEST_INFO when info is required | β Good shaping |
β Graders Are Correct
Each task has a deterministic grader that checks specific behavioral criteria:
- Easy: Close + resolution language + no unnecessary escalation + info gathered
- Medium: Info gathering + resolution attempted + sentiment β₯ -0.5 + multi-turn
- Hard: Escalate + early (β€2 steps) + urgency in reason + no self-resolve attempt
The hard task is genuinely counter-intuitive β the correct behavior is to immediately escalate, which is the opposite of what most LLMs will attempt.
β οΈ RL Weaknesses
Resolution scoring is still keyword-based. The system says it's not keyword-stuffing, but
_RESOLUTION_SIGNALSis literally a keyword list. An agent that says "refund credit reimburse" without context scores 1.0.Customer simulation is random.
random.choice(replies)means the same action can get different customer reactions. This adds noise to training signal.Tone scoring via VADER has known limitations. VADER rates "I understand you are frustrated" as negative (because of "frustrated"), even though it's empathetic in context.
No semantic understanding. The accuracy check uses regex (email pattern, order ID pattern), not comprehension. An agent that says "fake@fake.com" satisfies the email requirement.
PHASE 11: EDGE CASE AUDIT
Tested Edge Cases
| Edge Case | Behavior | Status |
|---|---|---|
action_type: "respond" with message: None |
Falls back to "[respond]" |
β Handled |
action_type: "escalate" with no reason |
Falls back to "[escalate]" |
β Handled |
Empty string message "" |
Tone = 0.5, loop detection safe | β Handled |
| All tickets exhausted | Not applicable β random choice from pool | β N/A |
| Same ticket picked twice | Shallow copy prevents cross-session mutation | β Safe |
max_steps = 0 |
Efficiency score returns 0.0 (guarded) | β Handled |
Unknown info_type in ticket |
Fallback to customer message count | β οΈ Weak but handled |
| Concurrent dict modification | Not possible with single-threaded uvicorn | β Safe (current config) |
β Untested Edge Cases
- Unicode/emoji in messages: VADER may not handle emoji well. TF-IDF may tokenize them unexpectedly.
- Extremely long messages (10K+ chars): No input sanitization. History grows unbounded within a session.
- HTML/script injection in messages: Messages stored as strings, not sanitized. If rendered in a web UI, XSS is possible.
PHASE 12: FINAL VERDICT
Comparison with Original Codebase
| Criterion | Original (customer_support_env) |
Meta Hack (meta_hack) |
|---|---|---|
| Architecture coherence | β Dead code, contradictions | β Clean, connected |
| Session isolation | β Global mutable state | β UUID-based, per-env |
| Session cleanup | β Never cleaned | β Cleaned on done |
| Reward function | β Hardcoded 1.0 | β Real multi-dimensional |
| Episode termination | β Hardcoded False | β Proper termination |
| Grading system | β Non-functional | β 3 deterministic graders |
| Tests | β None | β 363-line test suite |
| Observability | β Lying logs | β οΈ Minimal but honest |
| Authentication | β None | β None |
| Rate limiting | β None | β None |
| Scalability | β Crashes at 50 sessions | β οΈ Survives moderate load |
Scorecard
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FINAL AUDIT VERDICT: meta_hack β
β β
β Overall Score: 5.5/10 β
β Classification: Strong Hackathon Entry β
β β
β β
Architecture: 7.5/10 β Clean, modular, connected β
β β
RL Quality: 7.0/10 β Legitimate reward shaping β
β β
Code Quality: 8.0/10 β Well-written, Pythonic β
β β
Test Coverage: 6.0/10 β Good unit tests, no e2e β
β π‘ Scalability: 4.0/10 β Single-process, no eviction β
β π‘ Observability: 3.0/10 β Minimal logging β
β π΄ Security: 2.0/10 β Zero auth, zero rate limit β
β π΄ Prod. Readiness: 3.0/10 β Missing critical infra β
β β
β Compared to original repo: ββββββββββ (1.3 β 5.5) β
β A 4.2-point improvement. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
RECOMMENDATIONS
Must-Fix for Production
- Add authentication β JWT/API key middleware
- Add rate limiting β
slowapior custom middleware (e.g., 10 resets/min per IP) - Add session TTL β Background task to evict sessions older than N minutes
- Add max session limit β Reject
/resetwhenlen(_sessions) > MAX - Add request body size limit β FastAPI
Requestbody limit or nginx proxy - Externalize state β Redis/Postgres for multi-worker deployment
Should-Fix for Quality
- Add structured logging β
structlogorpython-json-logger - Add request timing middleware
- Add CORS configuration
- Seed
randomfor reproducible tests - Move
reimport to top-level inenvironment.py - Add integration/e2e tests β test the full HTTP flow, not just unit tests
Nice-to-Have
- Prometheus metrics endpoint
- OpenTelemetry tracing
- Session persistence across restarts
- Semantic resolution scoring (small embedding model instead of keywords)
SALVAGEABLE COMPONENTS
Unlike the original codebase, almost everything in meta_hack is worth keeping:
| Component | Verdict |
|---|---|
env/models.py |
β Keep as-is |
env/environment.py |
β Keep β add TTL tracking |
env/reward_engine.py |
β Keep β well-engineered |
env/ticket_store.py |
β Keep β rich ticket data |
env/graders/ |
β Keep β deterministic, correct |
server/app.py |
β οΈ Keep but add middleware layers |
inference.py |
β Keep β clean client with failover |
tests/test_env.py |
β Keep β extend with e2e tests |
| Dockerfile | β Keep |
| docker-compose.yml | β Keep |
Nothing needs to be deleted. This is a real codebase, not a proof-of-concept.
PHASE 13: LIVE SERVER TESTS (Real-Time Proof)
Target:
http://10.153.115.219:7860/
Tested: 2026-04-08T22:55+05:30
Method: curl + Pythonurllibagainst deployed Docker instance
13.1 Customer Interaction Tests β All 3 Difficulty Levels
Easy: TKT-001 β Double Charge on Invoice #4521
| Step | Action | Reward | Tone | Resolution | Efficiency | Accuracy |
|---|---|---|---|---|---|---|
| 1 | request_info β ask for account email |
β | β | β | β | β |
| 2 | respond β confirm refund processing |
β | β | β | β | β |
| 3 | close β confirm $49.99 refund initiated |
0.7327 | 0.8348 | 0.7143 | 0.4000 | 1.0000 |
Final Score: 1.0 β β Grader awarded perfect score for: close action + refund language + email gathered + no escalation.
Medium: Multi-Turn Complaint Handling
| Step | Action | Summary |
|---|---|---|
| 1 | respond β empathize with frustration |
Reward: varied, sentiment tracked |
| 2 | request_info β ask for account email + device info |
Info bonus: 0.1 |
| 3 | respond β provide fix (cache clear + reset) |
Resolution keywords hit |
| 4 | close β confirm fix applied |
Reward: 0.7373 |
Final Score: 1.0 β β Grader verified: info gathered, resolution attempted, sentiment β₯ -0.5, multi-turn β₯ 4.
Hard: SLA-Critical Escalation (Counter-Intuitive)
| Step | Action | Summary |
|---|---|---|
| 1 | escalate β immediate P0 escalation with urgency language |
Reward: 0.6482 |
Final Score: 1.0 β β Grader verified: escalated on step 1, SLA/critical/P0 in reason, no self-resolve before escalation.
All 3 graders produce legitimate, differentiated scores. The reward function is real β not hardcoded. This is a 100% improvement over the original repo which returned
reward: 1.0on every step regardless of agent behavior.
13.2 Adversarial Validation Tests
| Test | Input | Expected | Actual | Verdict |
|---|---|---|---|---|
| A. Invalid session | session_id=FAKE |
404 | 404 | β PASS |
| B. Invalid action | action_type=DROP_TABLES |
422 | 422 | β PASS |
| C. Invalid task | task=impossible |
422 | 422 | β PASS |
| D. Step after done | Step on closed session | 404/409 | 404 | β PASS |
| E. Session cleanup | Create β close β check | count+1 β count | 19β20β19 | β PASS |
| F. No authentication | Reset with no credentials | Should reject | HTTP 200 | β FAIL |
| G. Rate limiting | 20 resets in rapid fire | Should limit | 0.3s, no limit | β FAIL |
| H. PII via /state | Send "SSN: 123-45-6789" then GET /state | Should redact | Exposed | β FAIL |
Error handling: 5/5 PASSED β The server correctly validates all inputs.
Security: 0/3 PASSED β Zero authentication, zero rate limiting, full PII exposure via /state.
13.3 Session Flood Stress Test
βββββββββββββββββββββββββββββββββββββββββ
β STRESS TEST: Session Flood β
βββββββββββββββββββββββββββββββββββββββββ
Starting sessions: 41
After 200 resets: 241 sessions (200 success / 0 fail) β 0.20s (986 req/s)
After 700 resets: 741 sessions (500 success / 0 fail) β 0.52s (959 req/s)
FINAL: Server alive β
| 741 zombie sessions
Estimated memory leak: ~7,410 KB (~7.2 MB)
Key findings:
- Server absorbed 700 zombie sessions at ~960 req/s without degradation
- All 700 sessions were created by a single attacker with zero resistance
- No session eviction β all 741 zombies persist indefinitely
- Memory leak at ~10KB/session is linear β at this rate, 1 million sessions = ~10GB
- Server did NOT crash β a massive improvement over the original repo (which crashed at 50)
While the server survived 700 sessions, the attack surface is trivial. A sustained
while true; do curl -X POST .../reset; donewould create ~960 sessions/second, consuming ~9.6MB/s of memory. The server would OOM in approximately 17 minutes on a 1GB container.
13.4 Comparative Live Test Results
| Test | Original Repo | Meta Hack |
|---|---|---|
| Health check | β | β |
| Customer interaction (easy) | β Hardcoded reward | β Real score 1.0 |
| Customer interaction (hard) | β No grader | β Counter-intuitive grading works |
| Invalid input handling | β Crashes | β Proper 4xx codes |
| Session cleanup | β Never | β On done |
| 50 concurrent sessions | β CRASHED | β Survived |
| 700 concurrent sessions | π Would be dead | β Survived |
| Authentication | β None | β None |
| Rate limiting | β None | β None |
| Crash under normal use | β Crashed in 2 min | β Stable |
End of adversarial audit. The system is improvable, not replaceable.