Web IDE para a linguagem SIMPLES — escreva, compile e execute código SIMPLES diretamente no navegador, com visualização lado-a-lado do assembly gerado e terminal interativo real. Projeto acadêmico da disciplina de Compiladores do IFSULDEMINAS — Campus Poços de Caldas.
O Simples Editor elimina a fricção de configurar toolchain local para a disciplina de Compiladores. O aluno acessa a IDE web, autentica, escreve código na linguagem didática SIMPLES (27 palavras reservadas em português estruturado) e com um clique vê:
- O assembly NASM x86 32-bit gerado pelo compilador, lado-a-lado com o código fonte.
- A execução real do binário em um terminal interativo no navegador — suporte completo a
leia(stdin) eescreva(stdout). - Erros de compilação destacados diretamente no editor, com linha e coluna.
Tudo roda em containers Docker descartáveis, com 9 camadas de isolamento, sem rede e com timeouts automáticos — o aluno experimenta sem medo de travar o serviço para os colegas.
- 🔐 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
- 🔄 WebSocket
/ws/run— protocolo completo com máquina de estados (IDLE → COMPILING → EXECUTING) - 🖥️ Terminal real via xterm.js — suporte a
leia(stdin) eescreva(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
- 🐳 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 --rmapó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
- 📊 Métricas Prometheus em
/metrics(contadores, histogramas) - 📝 Logs JSON estruturados via structlog
- 🏥 Health check detalhado por componente (
/api/health)
- 🔧 simplesc (C99) → NASM
.asm→nasm -f elf32→.o→ld -m elf_i386→ ELF i386 - 🎭 Mock fallback — gera NASM didático quando
simplescnão está disponível - 🖥️ qemu-user-static — emula binários x86 32-bit em hosts ARM64 (Oracle Cloud)
┌──────────────────────────────────────────────────────────────────────────┐
│ HEADER: Simples Editor [aluno@email.com] [Sair] │
├──────────────────────────────────────────────────────────────────────────┤
│ TOOLBAR: [▶ Run] [■ Stop] [Limpar] [exemplos ▾] │
├────────────────────────────────┬─────────────────────────────────────────┤
│ │ │
│ EDITOR (Monaco) │ NASM x86 32-bit (read-only) │
│ ───────────────────── │ ─────────────────────────── │
│ programa exemplo │ section .bss │
│ inteiro x │ x resd 1 │
│ inicio │ │
│ leia x │ section .text │
│ escreva x │ global _start │
│ fim │ _start: │
│ │ mov eax, 3 │
│ │ mov ebx, 0 │
│ │ mov ecx, x │
│ │ mov edx, 4 │
│ │ int 0x80 │
│ │ ... │
│ │ │
├────────────────────────────────┴─────────────────────────────────────────┤
│ TERMINAL (xterm.js) [⌃C interrompe] │
│ ─────────────────────────────────────────────────────────────────────── │
│ Digite um numero: 42 │
│ 42 │
│ │
│ [exit code: 0 — 0.18s] │
└──────────────────────────────────────────────────────────────────────────┘
3 painéis, 2 splitters arrastáveis:
- Splitter vertical entre Editor SIMPLES (esquerda) e NASM (direita) — double-click colapsa/restaura o painel NASM.
- Splitter horizontal entre área de código (superior) e Terminal (inferior).
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.
┌──────────────────────────────────────────────────────┐
│ │
│ 🖥️ Simples Editor │
│ Web IDE para a linguagem SIMPLES │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📧 Email │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ aluno@ifsuldeminas.edu.br │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ │ │ │
│ │ 🔒 Senha │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ ●●●●●●●●●● │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ ▶ Entrar │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ │ │ │
│ │ Não tem conta? Cadastre-se │ │
│ │ │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ 🔐 Autenticação via Supabase (JWT) │
│ │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ Simples Editor [aluno@email.com] [Sair] │
├──────────────────────────────────────────────────────────────────────────┤
│ [▶ Run] [■ Stop] [🧹 Limpar] [exemplos ▾] │
├────────────────────────────────┬─────────────────────────────────────────┤
│ 1│ programa soma_dois │ ;; NASM x86 32-bit │
│ 2│ inteiro a, b, resultado │ ;; Compile para ver o assembly │
│ 3│ │ │
│ 4│ inicio │ │
│ 5│ leia a │ │
│ 6│ leia b │ │
│ 7│ resultado := a + b │ │
│ 8│ escreva resultado │ │
│ 9│ fim │ │
├────────────────────────────────┴─────────────────────────────────────────┤
│ Terminal │
│ ──────────────────────────────────────────────────────────────────── │
│ Aguardando compilação... │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ Simples Editor [aluno@email.com] [Sair] │
├──────────────────────────────────────────────────────────────────────────┤
│ [▶ Run] [■ Stop] [🧹 Limpar] [exemplos ▾] │
├────────────────────────────────┬─────────────────────────────────────────┤
│ 1│ programa soma_dois │ section .bss │
│ 2│ inteiro a, b, resultado │ a resd 1 │
│ 3│ │ b resd 1 │
│ 4│ inicio │ resultado resd 1 │
│ 5│ leia a │ │
│ 6│ leia b │ section .text │
│ 7│ resultado := a + b │ global _start │
│ 8│ escreva resultado │ _start: │
│ 9│ fim │ ; leia a │
│ │ mov eax, 3 │
│ │ mov ebx, 0 │
│ │ mov ecx, a │
│ │ mov edx, 4 │
│ │ int 0x80 │
│ │ ; leia b │
│ │ mov eax, 3 │
│ │ mov ebx, 0 │
│ │ mov ecx, b │
│ │ mov edx, 4 │
│ │ int 0x80 │
│ │ ; resultado := a + b │
│ │ mov eax, [a] │
│ │ add eax, [b] │
│ │ mov [resultado], eax │
│ │ ; escreva resultado │
│ │ ... │
├────────────────────────────────┴─────────────────────────────────────────┤
│ Terminal [✅ 0.23s] │
│ ──────────────────────────────────────────────────────────────────── │
│ [simplesc] Compilação concluída (0.02s) │
│ [nasm] Montagem concluída (0.01s) │
│ [ld] Linkagem concluída (0.01s) │
│ Binário ELF i386 pronto para execução. │
│ ▶ Pressione Run novamente para executar. │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ Simples Editor [aluno@email.com] [Sair] │
├──────────────────────────────────────────────────────────────────────────┤
│ [▶ Run] [■ Stop] [🧹 Limpar] [exemplos ▾] │
├────────────────────────────────┬─────────────────────────────────────────┤
│ 1│ programa soma_dois │ section .bss ◄── readonly │
│ 2│ inteiro a, b, resultado │ ... │
│ 3│ │ │
│ 4│ inicio │ section .text │
│ 5│ leia a │ ... │
│ 6│ leia b │ │
│ 7│ resultado := a + b │ │
│ 8│ escreva resultado │ │
│ 9│ fim │ │
├────────────────────────────────┴─────────────────────────────────────────┤
│ Terminal [executando... 0.8s] │
│ ──────────────────────────────────────────────────────────────────── │
│ [runner] Container sandbox iniciado (--network=none --read-only) │
│ [runner] qemu-i386-static /sandbox/prog │
│ Digite o primeiro número: 42 ◄── leia via xterm.js │
│ Digite o segundo número: 17 │
│ 59 ◄── escreva via PTY │
│ │
│ [exit code: 0 — 1.42s] │
│ [runner] Container destruído (--rm) │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ Simples Editor [aluno@email.com] [Sair] │
├──────────────────────────────────────────────────────────────────────────┤
│ [▶ Run] [■ Stop] [🧹 Limpar] [exemplos ▾] │
├────────────────────────────────┬─────────────────────────────────────────┤
│ 1│ programa teste │ ;; Erro de compilação │
│ 2│ inteiro x │ ;; Corrija os erros e compile │
│ 3│ │ novamente │
│ 4│ inicio │ │
│ 5│ x := 10 │ │
│ 6│🔴 escreva x │ ← Erro: 'escreva' não declarado │
│ 7│ fim │ │
├────────────────────────────────┴─────────────────────────────────────────┤
│ Terminal [❌ 0.02s]│
│ ──────────────────────────────────────────────────────────────────── │
│ [simplesc] ERRO na linha 6, coluna 4: │
│ identificador 'escreva' não declarado │
│ Sugestão: use 'escreva' (com 'v') │
│ │
│ Compilação falhou. Corrija os erros e tente novamente. │
└──────────────────────────────────────────────────────────────────────────┘
┌─────────────────────┐ ┌──────────────────────────────┐
│ DESKTOP (≥1024px) │ │ TABLET / NOTEBOOK (≥768px) │
│ │ │ │
│ ┌───────┬────────┐ │ │ ┌────────────────────────┐ │
│ │Editor │ NASM │ │ │ │ Editor SIMPLES │ │
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │ └────────────────────────┘ │
│ ├───────┴────────┤ │ │ ┌────────────────────────┐ │
│ │ Terminal │ │ │ │ NASM (toggle) │ │
│ └────────────────┘ │ │ └────────────────────────┘ │
│ │ │ ┌────────────────────────┐ │
│ 3 painéis lado a │ │ │ Terminal │ │
│ lado com splitters │ │ └────────────────────────┘ │
│ arrastáveis │ │ │
│ │ │ Painéis empilhados com │
│ │ │ toggle NASM │
└─────────────────────┘ └──────────────────────────────┘
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.
┌──────────────────────────┐
│ Cliente (Browser) │
│ React + TanStack Start │
│ ┌─────────┐┌──────────┐ │
│ │ Monaco ││ NASM │ │
│ │ SIMPLES ││ viewer │ │
│ └─────────┘└──────────┘ │
│ ┌──────────────────────┐ │
│ │ xterm.js (terminal) │ │
│ └──────────────────────┘ │
└───────┬──────────┬─────────┘
│ HTTPS │ WSS
▼ ▼
┌──────────────────────────┐
│ Nginx (reverse proxy) │
│ TLS + WebSocket upgrade │
└───────┬──────────┬─────────┘
│ │
┌───────────▼──┐ ┌────▼──────────────┐
│ Frontend │ │ Backend │
│ (estático) │ │ Flask + flask-sock│
│ nginx:alp │ │ asyncio + docker │
└──────────────┘ │ + simplesc │
│ + nasm + ld │
│ + ptyprocess │
└────────┬───────────┘
│ docker run --rm
│ --network=none
│ --read-only
▼
┌────────────────────┐
│ Sandbox container │
│ simples-runner │
│ qemu-i386-static │
│ (emula x86 32-bit │
│ em hosts ARM64) │
└────────────────────┘
┌─────────────────┐
│ Supabase │
│ (Auth cloud) │
└─────────────────┘
| Componente | Tecnologia | Responsabilidade |
|---|---|---|
| Frontend | React 18, TanStack Start, Monaco Editor, xterm.js | UI da IDE, edição de código, destaque de sintaxe, terminal interativo, autenticação |
| Backend | Python 3.11+, Flask, flask-sock, docker SDK | Orquestração do pipeline de compilação, bridge WebSocket↔PTY, gerenciamento de sandboxes |
| Nginx | Nginx 1.25+ Alpine | Reverse proxy HTTPS/WSS, TLS termination, sticky session |
| Sandbox | qemu-user-static, Docker | Execução isolada dos binários compilados — sem rede, sem root, sem FS gravável |
| Auth | Supabase (cloud free tier) | Autenticação JWT — sem banco local em v1 |
| Padrão | Aplicação |
|---|---|
| Strategy | PtyExecutionStrategy — troca modo de execução (PTY vs batch) sem alterar pipeline |
| Façade | CompilerService.compile_and_run() — esconde orquestração simplesc → nasm → ld → docker |
| Factory | SandboxFactory.create() — centraliza criação de containers com limites consistentes |
| Observer | Eventos WebSocket (compile_started, asm_generated, stdout, exit) — frontend reage sem polling |
| Command | Mensagens tipadas no protocolo WS (compile_and_run, stdin, stop) |
Código SIMPLES ──▶ simplesc (C99) ──▶ NASM .asm ──▶ nasm -f elf32 ──▶ .o
│
ld -m elf_i386
│
▼
Binário ELF i386
│
┌──────────────────┘
▼
Docker Sandbox descartável
qemu-i386-static /sandbox/prog
│
┌──────────┴──────────┐
▼ ▼
stdout/stderr stdin
│ │
└──────────┬───────────┘
▼
xterm.js (navegador)
Fases com timeouts independentes:
- Compilação (
simplesc): 15s - Montagem (
nasm -f elf32): 15s - Linkagem (
ld -m elf_i386): 15s - Execução wall-clock: 10s (soft) + 12s (hard limit Docker)
O compilador
simplescusabinutils-i686-linux-gnupara cross-linkagem i386 em qualquer arquitetura de host (x86_64 ou ARM64). A execução usaqemu-user-staticpara emular binários x86 32-bit em hosts ARM64 (Oracle Cloud Ampere A1).
| Tecnologia | Versão | Descrição |
|---|---|---|
| React | 18.x | Biblioteca UI |
| TypeScript | 5.x | Tipagem estática |
| TanStack Start | latest | Framework full-stack React |
| TanStack Query | 5.x | Gerenciamento de estado assíncrono |
| TanStack Router | 1.x | Roteamento tipado |
| Tailwind CSS | 3.x | Estilização utility-first |
| Monaco Editor | @monaco-editor/react |
Editor de código (mesmo núcleo do VS Code) |
| xterm.js | 5.x | Emulador de terminal |
| xterm-addon-fit | latest | Resize dinâmico do terminal |
| react-resizable-panels | latest | Splitters arrastáveis |
| @supabase/supabase-js | 2.x | Cliente de autenticação |
| @supabase/auth-ui-react | latest | UI de login pronta |
| Tecnologia | Versão | Descrição |
|---|---|---|
| Python | 3.11+ | Linguagem do backend |
| Flask | 3.x | API REST |
| flask-sock | latest | WebSocket sobre Flask |
| gevent | 23.x | Worker assíncrono para WS |
| docker SDK | 7.x | Spawn de containers sandbox |
| PyJWT | 2.x | Validação de tokens Supabase |
| structlog | 24.x | Logs estruturados JSON |
| prometheus-client | latest | Métricas em /metrics |
| flask-limiter | latest | Rate limiting |
| pytest | 8.x | Testes automatizados |
| Ferramenta | Descrição |
|---|---|
simplesc |
Compilador SIMPLES → NASM (C99, já existente) |
| NASM 2.15+ | Assembler x86 (cross-platform) |
| binutils-i686-linux-gnu | Cross-linker i386 (i686-linux-gnu-ld) |
| qemu-user-static | Emulação user-mode x86 32-bit em ARM64 |
| Tecnologia | Descrição |
|---|---|
| Docker 24+ | Containerização e sandbox |
| Docker Compose v2 | Orquestração local e produção |
| Nginx 1.25 | Reverse proxy + TLS |
| Supabase (cloud) | Autenticação JWT |
| Oracle Cloud Ampere A1 | Deploy ARM64 Always Free |
simples-editor/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── feature.md # Template de issue para features
│ └── pull_request_template.md
├── frontend/ # ⚛️ React + TanStack Start
│ ├── src/
│ │ ├── components/ # Componentes React (Editor, NASM, Terminal)
│ │ ├── routes/ # Rotas da aplicação
│ │ └── lib/ # Clientes (Supabase, WebSocket)
│ ├── Dockerfile
│ └── nginx-static.conf
├── backend/ # 🐍 Flask + WebSocket
│ ├── app/
│ │ ├── auth/ # Decorator @verify_jwt
│ │ ├── compiler/ # Façade CompilerService
│ │ ├── executor/ # PtyExecutionStrategy, SandboxFactory
│ │ └── ws/ # Handlers WebSocket
│ ├── simples-compiler/ # Submódulo: código fonte do simplesc
│ ├── requirements.txt
│ └── Dockerfile
├── runner/ # 🏃 Imagem do sandbox
│ └── Dockerfile # debian:12-slim + qemu-user-static
├── nginx/ # 🔀 Reverse proxy
│ ├── nginx.conf
│ └── certs/ # TLS (gerado pelo certbot)
├── terraform/ # 🏗️ IaC Oracle Cloud
│ ├── main.tf
│ ├── variables.tf
│ └── cloud-init.yaml
├── docker-compose.yml # Stack completa
├── .env.example # Template de variáveis de ambiente
├── README.md # 📖 Este documento
├── PRD.md # 📋 Product Requirements Document
├── SPRINTS.md # 🏃 Planejamento de sprints
├── PROGRESS.md # ✅ Acompanhamento de tarefas
├── RETROSPECTIVE.md # 🔄 Retrospectiva da equipe
├── DOMAIN.md # 🌐 Guia de domínio próprio + TLS
├── PRESENTATION.md # 🎤 Slides Marp para apresentação
└── LICENSE # MIT
- Docker Engine 24+ (ou Docker Desktop)
- Docker Compose v2
- Conta no Supabase (free tier) com projeto criado
- Git (para clonar com submódulos)
Funciona em: Linux x86_64, macOS (Intel e Apple Silicon), Windows com WSL2.
# 1. Clone o repositório com submódulos
git clone --recurse-submodules https://github.com/c4rlosfb/simples-editor.git
cd simples-editor
# 2. Configure as variáveis de ambiente
cp .env.example .env
# Edite .env com:
# SUPABASE_URL=https://<seu-projeto>.supabase.co
# SUPABASE_ANON_KEY=<sua-anon-key>
# SUPABASE_JWT_SECRET=<seu-jwt-secret>
# 3. Suba todos os serviços
docker compose up --build -d
# 4. Verifique
curl http://localhost/api/health
# → {"status":"ok","version":"1.0.0"}
# 5. Acesse no navegador
# http://localhostdocker compose ps # Status de todos os serviços
docker compose logs -f backend # Logs do backend em tempo real
docker compose down # Derruba tudo
docker compose up --build -d # Reconstrói e sobecd 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 -vcd frontend
# Testes unitários
npx vitest run
# Em modo watch
npx vitestcd frontend
# Instalar navegadores (primeira vez)
npx playwright install chromium
# Rodar testes E2E
npx playwright test# 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# 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"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.
Via console OCI:
- Compute → Instances → Create Instance
- Image: Canonical Ubuntu 22.04 (aarch64)
- Shape:
VM.Standard.A1.Flex— 2 OCPUs, 12 GB RAM - Networking: VCN com subnet pública e IP público
- SSH: subir sua chave pública
Na Security List da subnet, adicionar Ingress Rules:
| Source | Protocol | Port | Descrição |
|---|---|---|---|
0.0.0.0/0 |
TCP | 80 | HTTP (redirect→HTTPS) |
0.0.0.0/0 |
TCP | 443 | HTTPS |
<seu-ip>/32 |
TCP | 22 | SSH (restrito) |
iptables bloqueando. Libere também no host:
sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 80 -j ACCEPT
sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 443 -j ACCEPT
sudo netfilter-persistent savessh ubuntu@<ip-público>
# Docker Engine + Compose v2 (ARM64)
sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu jammy stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
newgrp dockergit clone --recurse-submodules https://github.com/c4rlosfb/simples-editor.git
cd simples-editor
cp .env.example .env
nano .env # Preencher SUPABASE_*, ajustar EXEC_TIMEOUT_S=15 (ARM)
docker compose up --build -d
docker compose ps
curl -k https://localhost/api/health# Apontar DNS tipo A: simples.seu-dominio.edu.br → <IP público>
# Depois:
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
docker compose stop nginx
sudo certbot certonly --standalone -d simples.seu-dominio.edu.br \
--non-interactive --agree-tos -m admin@seu-dominio.edu.br
sudo cp /etc/letsencrypt/live/simples.seu-dominio.edu.br/fullchain.pem \
./nginx/certs/
sudo cp /etc/letsencrypt/live/simples.seu-dominio.edu.br/privkey.pem \
./nginx/certs/
docker compose start nginx
# Renovação automática (cron)
echo "0 3 * * * certbot renew --quiet --post-hook \
'cd /home/ubuntu/simples-editor && docker compose restart nginx'" \
| sudo crontab -Para detalhes completos, consulte DOMAIN.md e a seção 14.7 do PRD.
O Simples Editor foi projetado com defense in depth — 9 camadas de isolamento por execução, além de timeouts em cascata e rate limiting.
| # | Camada | Mecanismo |
|---|---|---|
| 1 | Container descartável | docker run --rm — destruído após cada execução |
| 2 | Isolamento de rede | --network=none — zero acesso à rede |
| 3 | Filesystem imutável | --read-only + tmpfs:/tmp,size=8m |
| 4 | Limite de memória | --memory=128m --memory-swap=128m |
| 5 | Limite de CPU | --cpus=0.5 (cgroups v2) |
| 6 | Limite de processos | --pids-limit=64 — bloqueia fork bomb |
| 7 | Usuário não-root | --user=65534:65534 (nobody) |
| 8 | Sem capabilities | --cap-drop=ALL |
| 9 | Seccomp | Perfil padrão do Docker — bloqueia syscalls perigosas |
┌──────────────────────────────────────────────────────────┐
│ [1] Compile timeout subprocess.run(timeout=15) │
│ [2] Wall-clock asyncio.wait_for(exec, timeout=10) │
│ → SIGTERM (1s) → SIGKILL │
│ [3] Hard limit Docker --stop-timeout=12 │
└──────────────────────────────────────────────────────────┘
| Ameaça | Mitigação |
|---|---|
| Loop infinito | Wall-clock timeout (10s) |
| Fork bomb | --pids-limit=64 |
| Consumo de memória | --memory=128m |
| Exfiltração de dados | --network=none |
| Escape do container | Não-root + --cap-drop=ALL + seccomp |
| Abuso de execuções | Rate limit: 30/min por usuário, 120/min por IP |
| JWT roubado | Expiração curta (1h, Supabase) + RLS (v2) |
| Code injection | subprocess com lista de args — nunca shell=True |
- 30 execuções/minuto por
user_id - 120 execuções/minuto por IP
- Implementado via
flask-limiter
- Código fonte limitado a 64 KB
- UTF-8 válido, apenas caracteres imprimíveis ASCII/extended
- Stdin ≤ 4 KB por mensagem WebSocket
| Documento | Descrição |
|---|---|
PRD (prd-simples-online.md) |
Product Requirements Document completo — 1630 linhas cobrindo arquitetura, API, segurança, UI, testes |
SPRINTS.md |
Planejamento de 6 sprints com entregáveis e Definition of Done |
PROGRESS.md |
Acompanhamento granular de tarefas (checklist por sprint) |
RETROSPECTIVE.md |
Retrospectiva da equipe — aprendizados e próximos desafios |
DOMAIN.md |
Guia de configuração de domínio próprio + TLS |
PRESENTATION.md |
Slides Marp para apresentação final |
Desenvolvido para a disciplina de Compiladores
IFSULDEMINAS — Campus Poços de Caldas
Engenharia de Computação — 2026/1
| Integrante | GitHub | Papel |
|---|---|---|
| Carlos Barboa | @c4rlosfb | Arquiteto, Backend, DevOps, Segurança |
| Luan Dias | @LuanCasDias | Frontend, UI/UX, Monaco Editor |
| Kauan Simão | @KauaN-png | Infraestrutura, Docker, Oracle Cloud |
Este projeto está licenciado sob a MIT License — veja o arquivo para detalhes.
Feito com ❤️ no sul de Minas Gerais
Poços de Caldas, 2026