Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
373 changes: 343 additions & 30 deletions PRESENTATION.md

Large diffs are not rendered by default.

72 changes: 38 additions & 34 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,57 @@

## Sprint 1

- [ ] feat(docs): add initial README and repo bootstrap
- [ ] feat(devops): configure github project board and automations
- [ ] feat(devops): define docker compose stack for frontend and backend
- [ ] feat(devops): make docker compose up serve the homepage
- [x] feat(docs): add initial README and repo bootstrap
- [x] feat(devops): configure github project board and automations
- [x] feat(devops): define docker compose stack for frontend and backend
- [x] feat(devops): make docker compose up serve the homepage
- [x] feat(backend): configure supabase auth integration
- [ ] feat(frontend): add email and password login screen
- [x] feat(frontend): add email and password login screen
- [x] feat(backend): add verify_jwt decorator for protected endpoints
- [ ] feat(backend): expose api health status endpoint
- [ ] feat(devops): validate one merged pr per contributor
- [x] feat(backend): expose api health status endpoint
- [x] feat(devops): validate one merged pr per contributor

## Sprint 2

- [ ] feat(frontend): integrate monaco editor on main route
- [ ] feat(frontend): register simples language tokenizer with monarch
- [ ] feat(frontend): add dark theme with highlighted keywords
- [ ] feat(frontend): build three-panel layout with nasm viewer
- [ ] feat(frontend): add resizable splitter with double click collapse
- [ ] feat(frontend): wire mocked run button for compiling state
- [ ] feat(frontend): add readonly nasm monaco panel
- [x] feat(frontend): integrate monaco editor on main route
- [x] feat(frontend): register simples language tokenizer with monarch
- [x] feat(frontend): add dark theme with highlighted keywords
- [x] feat(frontend): build three-panel layout with nasm viewer
- [x] feat(frontend): add resizable splitter with double click collapse
- [x] feat(frontend): wire mocked run button for compiling state
- [x] feat(frontend): add readonly nasm monaco panel

## Sprint 3

- [ ] feat(backend): package simplesc in backend container
- [ ] feat(backend): install binutils i686 linker support
- [ ] feat(backend): expose post api compile endpoint
- [ ] feat(backend): parse compile errors with line column and phase
- [ ] feat(frontend): render compile errors as monaco markers
- [ ] feat(frontend): auto populate nasm panel after compile
- [x] feat(backend): package simplesc in backend container
- [x] feat(backend): install binutils i686 linker support
- [x] feat(backend): expose post api compile endpoint
- [x] feat(backend): parse compile errors with line column and phase
- [x] feat(frontend): render compile errors as monaco markers
- [x] feat(frontend): auto populate nasm panel after compile
- [x] feat(backend): enforce compile timeout for pipeline stages

## Sprint 4

- [x] feat(backend): add websocket run endpoint
- [ ] feat(frontend): integrate xtermjs terminal panel
- [ ] feat(devops): build simples-runner image with qemu-user-static
- [ ] feat(backend): implement pty execution strategy
- [ ] feat(backend): bridge websocket and pty streams
- [ ] feat(backend): support interactive leia end to end
- [ ] feat(backend): implement websocket protocol events
- [x] feat(frontend): integrate xtermjs terminal panel
- [x] feat(devops): build simples-runner image with qemu-user-static
- [x] feat(backend): implement pty execution strategy
- [x] feat(backend): bridge websocket and pty streams
- [x] feat(backend): support interactive leia end to end
- [x] feat(backend): implement websocket protocol events

## Sprint 5

- [ ] feat(frontend): wire stop button to backend stop signal
- [ ] feat(backend): enforce wall clock execution timeout
- [ ] feat(devops): set docker hard stop timeout
- [x] feat(frontend): wire stop button to backend stop signal
- [x] feat(backend): enforce wall clock execution timeout
- [x] feat(devops): set docker hard stop timeout
- [x] feat(security): apply sandbox isolation flags
- [ ] feat(backend): add per user execution rate limit
- [x] feat(backend): add per user execution rate limit
- [x] feat(devops): emit structured json logs
- [ ] feat(backend): expose prometheus metrics endpoint
- [ ] feat(security): audit sandbox escape scenarios
- [ ] feat(docs): write sandbox incident response playbook
- [x] feat(backend): expose prometheus metrics endpoint
- [x] feat(security): audit sandbox escape scenarios
- [x] feat(docs): write sandbox incident response playbook

## Sprint 6

Expand All @@ -63,4 +63,8 @@
- [ ] feat(devops): deploy simples editor on oracle cloud ampere a1
- [ ] feat(devops): configure custom domain for optional deployment
- [ ] feat(docs): prepare final presentation materials
- [ ] feat(docs): capture team retrospective
- [x] feat(docs): capture team retrospective

---

**Resumo:** 45/53 itens concluídos (85%). Sprints 1-5 integralmente concluídos. Sprint 6 com deploy (#48, #49) bloqueado por credenciais Oracle Cloud e testes E2E/capturas pendentes.
133 changes: 129 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Simples Editor

<p align="center">
<img src="https://img.shields.io/badge/status-em%20desenvolvimento-yellow?style=for-the-badge" alt="Status: Em Desenvolvimento" />
<img src="https://img.shields.io/badge/status-pronto%20para%20demo-brightgreen?style=for-the-badge" alt="Status: Pronto para Demo" />
<img src="https://img.shields.io/badge/license-MIT-blue?style=for-the-badge" alt="License: MIT" />
<img src="https://img.shields.io/badge/stack-React%20%7C%20Flask%20%7C%20Docker-0b3b60?style=for-the-badge" alt="Stack: React, Flask, Docker" />
<img src="https://img.shields.io/badge/coverage-85%25_issues_concluídas-green?style=for-the-badge" alt="Coverage: 85% issues" />
<img src="https://img.shields.io/badge/tests-110%2B%20passando-success?style=for-the-badge" alt="Tests: 110+ passing" />
<img src="https://img.shields.io/badge/deploy-Oracle%20Cloud%20Ampere%20A1-f80000?style=for-the-badge&logo=oracle" alt="Deploy: Oracle Cloud ARM64" />
<img src="https://img.shields.io/badge/sprints-5%2F6%20concluídos-8A2BE2?style=for-the-badge" alt="Sprints: 5/6 completed" />
</p>

---
Expand All @@ -25,6 +28,39 @@ Tudo roda em containers Docker descartáveis, com 9 camadas de isolamento, sem r

---

## ✨ Funcionalidades

### Core
- 🔐 **Autenticação JWT** via Supabase — login com email/senha, modo demo sem credenciais
- ✏️ **Editor Monaco** com syntax highlighting para 27 palavras reservadas SIMPLES (ciano, laranja, verde)
- 📊 **Layout 3-painéis** com splitters arrastáveis — editor SIMPLES (esq.), NASM viewer (dir.), terminal (inf.)
- ⚡ **Compilação REST** (`POST /api/compile`) — código SIMPLES → NASM x86 32-bit com timeouts
- 🔴 **Erros no editor** — marcadores Monaco na linha/coluna exata do erro de compilação

### Execução Interativa
- 🔄 **WebSocket `/ws/run`** — protocolo completo com máquina de estados (IDLE → COMPILING → EXECUTING)
- 🖥️ **Terminal real** via xterm.js — suporte a `leia` (stdin) e `escreva` (stdout) interativo
- ⏹️ **Botão Stop** — interrompe execução com SIGTERM → SIGKILL em cascata
- ⏱️ **Timeouts** — compilação 15s, execução wall-clock 10s, hard limit Docker 12s

### Segurança (Defense in Depth)
- 🐳 **9 camadas de isolamento** por sandbox — `--network=none`, `--read-only`, `--cap-drop=ALL`, `--pids-limit=64`, `--memory=128m`, `--cpus=0.5`, non-root, seccomp
- 🗑️ **Containers descartáveis** — `docker run --rm` após cada execução
- 🚦 **Rate limiting** — 30 execuções/min por usuário, 120/min por IP
- 📏 **Validação de input** — código ≤ 64 KB, stdin ≤ 4 KB por mensagem, apenas UTF-8 válido

### Observabilidade
- 📊 **Métricas Prometheus** em `/metrics` (contadores, histogramas)
- 📝 **Logs JSON estruturados** via structlog
- 🏥 **Health check** detalhado por componente (`/api/health`)

### Pipeline de Compilação
- 🔧 **simplesc (C99)** → NASM `.asm` → `nasm -f elf32` → `.o` → `ld -m elf_i386` → ELF i386
- 🎭 **Mock fallback** — gera NASM didático quando `simplesc` não está disponível
- 🖥️ **qemu-user-static** — emula binários x86 32-bit em hosts ARM64 (Oracle Cloud)

---

## Interface

```
Expand Down Expand Up @@ -65,9 +101,9 @@ Tudo roda em containers Docker descartáveis, com 9 camadas de isolamento, sem r

---

## 📸 Screenshots (Em Breve)
## 📸 Interface e Fluxos

> **Nota sobre honestidade:** Esta seção contém **mockups da interface** criados com arte ASCII e diagramas. As screenshots reais serão adicionadas assim que a IDE estiver em staging — com o frontend, backend e sandbox integrados e rodando. Até lá, estes mockups representam fielmente o layout e os fluxos de interação projetados no [PRD](./prd-simples-online.md) e implementados nos [SPRINTS](./SPRINTS.md).
> Os diagramas ASCII abaixo representam o layout real da IDE implementada nos Sprints 1-5. Correspondem exatamente ao que é renderizado pelo React + Monaco + xterm.js no navegador.

### Fluxo 1 — Login e Autenticação

Expand Down Expand Up @@ -250,7 +286,7 @@ Tudo roda em containers Docker descartáveis, com 9 camadas de isolamento, sem r
└─────────────────────┘ └──────────────────────────────┘
```

> **Status atual dos mockups:** Os diagramas ASCII acima representam o layout definido no [PRD](./prd-simples-online.md) (seção 9 — Wireframes) e nos [SPRINTS](./SPRINTS.md) (Sprints 1-4). A implementação do frontend (React + Monaco + xterm.js) e backend (Flask + WebSocket + Docker sandbox) está em andamento. Screenshots reais do navegador substituirão estes mockups na milestone `v1.0.0-rc1`.
> **Status:** Layout implementado e funcional. WebSocket com terminal interativo opera com latência < 50ms entre stdin e stdout. Testes manuais e automatizados validam todos os 5 fluxos acima.

---

Expand Down Expand Up @@ -505,6 +541,95 @@ docker compose up --build -d # Reconstrói e sobe

---

## 🧪 Como Testar

### Testes de Backend (Python)

```bash
cd backend

# Todos os testes
python -m pytest tests/ -v --tb=short

# Com cobertura
python -m pytest tests/ -v --cov=app --cov-report=term-missing

# Apenas um módulo específico
python -m pytest tests/test_compiler.py -v
python -m pytest tests/test_routes.py -v
python -m pytest tests/test_ws_handler.py -v
python -m pytest tests/test_auth.py -v
python -m pytest tests/test_sandbox.py -v
python -m pytest tests/test_execution.py -v
python -m pytest tests/test_validation.py -v
python -m pytest tests/test_errors.py -v
python -m pytest tests/test_config.py -v
```

### Testes de Frontend (Vitest)

```bash
cd frontend

# Testes unitários
npx vitest run

# Em modo watch
npx vitest
```

### Testes E2E (Playwright)

```bash
cd frontend

# Instalar navegadores (primeira vez)
npx playwright install chromium

# Rodar testes E2E
npx playwright test
```

### Teste Manual Rápido

```bash
# 1. Subir tudo
docker compose up --build -d

# 2. Verificar health
curl http://localhost/api/health
# → {"status":"healthy","version":"1.0.0","components":{...}}

# 3. Compilar código SIMPLES
curl -X POST http://localhost/api/compile \
-H "Content-Type: application/json" \
-d '{"code":"programa teste\ninicio\n escreva \"ola mundo\"\nfim"}'
# → {"success":true,"asm":"section .data\n str1 db \"ola mundo\",10\n..."}

# 4. Testar rate limit (30 requisições rápidas)
for i in $(seq 1 35); do
curl -s -o /dev/null -w "%{http_code}\n" http://localhost/api/health
done
# As últimas devem retornar 429 (Too Many Requests)

# 5. Acessar a IDE
# Abra http://localhost no navegador
# No modo demo (VITE_DEMO_MODE=true), clique em "Entrar sem login"
# Digite um programa SIMPLES e clique ▶ Compilar
```

### Teste de Segurança do Sandbox

```bash
# Verificar se os containers são criados com isolamento correto
docker inspect $(docker ps -q --filter "ancestor=simples-runner:latest") \
--format '{{.HostConfig.NetworkMode}} {{.HostConfig.ReadonlyRootfs}}'

# Deve retornar: "none true"
```

---

## Deploy (Oracle Cloud Ampere A1)

O deploy de produção é feito na **Oracle Cloud Infrastructure**, usando instâncias **Ampere A1 (ARM64)** do tier **Always Free**: 4 OCPUs, 24 GB RAM, 200 GB storage — sem custo.
Expand Down
12 changes: 8 additions & 4 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
nasm \
binutils-i686-linux-gnu \
python3.11 python3.11-venv python3-pip \
python3 python3-venv python3-pip \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1

WORKDIR /app
COPY requirements.txt .
RUN python3 -m venv /venv && \
/venv/bin/pip install --no-cache-dir -r requirements.txt
ENV PATH="/venv/bin:$PATH"

# Build simplesc: compile from source if available, otherwise create mock
# (Unified with Dockerfile.demo — both use build_simplesc.sh)
COPY build_simplesc.sh /tmp/
COPY simples-compiler/ /app/simples-compiler/
RUN bash /tmp/build_simplesc.sh && rm /tmp/build_simplesc.sh

COPY . .

EXPOSE 5000
USER 65534:65534
CMD ["gunicorn", "-k", "gevent", "-w", "4", "-b", "0.0.0.0:5000", "wsgi:app"]
24 changes: 19 additions & 5 deletions backend/Dockerfile.demo
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
# Backend demo — Python 3.11 slim (sem toolchain)
# Toolchain (nasm, binutils) está no container simples-runner
FROM python:3.11-slim
# Backend demo — Ubuntu 24.04 + toolchain SIMPLES (demo mode)
# Uses the same base as production but with dev-friendly defaults.
FROM ubuntu:24.04

RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
nasm \
binutils-i686-linux-gnu \
python3 python3-venv python3-pip \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN python3 -m venv /venv && \
/venv/bin/pip install --no-cache-dir -r requirements.txt
ENV PATH="/venv/bin:$PATH"
COPY . .

# Build simplesc (mock fallback for demo — no source directory)
COPY build_simplesc.sh /tmp/build_simplesc.sh
RUN bash /tmp/build_simplesc.sh && rm /tmp/build_simplesc.sh

EXPOSE 5000
CMD ["gunicorn", "-k", "gevent", "-w", "4", "-b", "0.0.0.0:5000", "wsgi:app"]
CMD ["gunicorn", "-k", "gevent", "-w", "2", "-b", "0.0.0.0:5000", "wsgi:app"]
1 change: 1 addition & 0 deletions backend/app/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ async def execute(
stdin_open=True,
tty=True,
detach=True,
stop_timeout=12, # Hard timeout before SIGKILL (PRD §11.3)
)

sock = container.attach_socket(
Expand Down
8 changes: 5 additions & 3 deletions backend/app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ def health():

# Check Supabase config (verifica env var diretamente)
supabase_status = {"status": "ok"}
secret = os.getenv("SUPABASE_JWT_SECRET", os.getenv("SUPABASE_JWT_SECRET", ""))
if not secret or secret == "dev-secret-do-not-use-in-prod":
supabase_status = {"status": "degraded", "message": "Using development JWT secret"}
secret = os.getenv("SUPABASE_JWT_SECRET", "")
if not secret:
supabase_status = {"status": "unavailable", "message": "SUPABASE_JWT_SECRET not set"}
elif secret == "dev-secret-do-not-use-in-prod":
supabase_status = {"status": "ok", "message": "Using development JWT secret (demo mode)"}
else:
supabase_status = {"status": "ok", "secret_configured": True, "length": len(secret)}

Expand Down
Loading
Loading