Read interesting content in English, adapted to your CEFR level. Stateless, fast, no account needed.
LevelRead generates short or long reading texts tailored to your English level — from real news (BBC/Reuters RSS) or any topic you choose. Powered by Groq (Llama 3.3 70B) for near-instant generation. Zero data saved. Zero cookies. Just read.
# 1. Clone & enter
git clone https://github.com/Gabriele06-local/LevelRead.git
cd LevelRead
# 2. Backend
cp .env.example backend/.env
# edit backend/.env → paste your GROQ_API_KEY
cd backend
go run ./cmd/server
# → listening on :8080
# 3. Frontend (new terminal)
cd frontend
npm install
npm run dev
# → http://localhost:4321Open http://localhost:4321 — pick your level, pick a topic, read.
Browser (Astro static) Go/Fiber API (stateless)
┌─────────────────┐ POST ┌──────────────────────┐
│ Level Picker │ /api/gen │ Rate limiter │
│ Topic Picker │ ──────────▶ │ RSS fetcher (gofeed)│
│ Reading View │ ◀────────── │ Groq client (LLM) │
└─────────────────┘ JSON │ CEFR validator │
└──────────────────────┘
| Layer | Technology | Why |
|---|---|---|
| Frontend | Astro + vanilla JS | Static by default, instant TTI, no framework bloat |
| Backend | Go + Fiber | Fast I/O, single binary, stateless |
| AI | GroqCloud (Llama 3.3 70B) | ~100 tok/s inference, OpenAI-compatible API |
| News | RSS (BBC, Reuters) | Fetched server-side, cached 60s, cascading fallback |
| Rate limit | In-memory token bucket | Resets on restart — no Redis, no persistence |
- Zero persistent state — no database, no accounts, no localStorage
- API key never leaves the server — Groq key is server-side only
- CEFR verification — post-processing checks sentence length and complexity, retries if off-target
- RSS resilience — if feeds fail, falls back to generic topic generation instead of returning an error
{
"level": "B1",
"topic": "artificial intelligence",
"isNews": false,
"length": "short"
}Parameters
| Field | Type | Values |
|---|---|---|
level |
string | A1-A2, B1, B1-B2, C1, C1-C2, C2 |
topic |
string | Free text, max 200 chars |
isNews |
boolean | true → fetches RSS context |
length |
string | short (~200 words) or long (~500 words) |
Response
{
"title": "AI and Everyday Life",
"text": "Artificial intelligence helps us in many ways...",
"level": "B1",
"source": "https://feeds.bbci.co.uk/news/world/rss.xml",
"cefrVerified": true
}Errors
| Status | Meaning |
|---|---|
400 |
Invalid parameters |
429 |
Rate limited (configurable via env) |
502 |
Groq or RSS unreachable |
GROQ_API_KEY= # Required: get one at console.groq.com
RATE_LIMIT_RPS=1 # Requests per second per IP
RATE_LIMIT_BURST=3 # Burst allowance
RSS_FEED_URLS=... # Comma-separated RSS URLs
PORT=8080 # Backend port
ALLOWED_ORIGIN=http://localhost:4321
LevelRead/
├── backend/
│ ├── cmd/server/main.go # Entry point
│ └── internal/
│ ├── handlers/ # POST /api/generate
│ ├── groq/ # GroqCloud client
│ ├── rss/ # RSS fetcher + cache
│ ├── ratelimit/ # Token bucket
│ └── cefrcheck/ # CEFR post-validation
├── frontend/
│ ├── src/pages/index.astro # Main page
│ ├── src/components/ # LevelPicker, TopicPicker, ReadingView
│ └── public/favicon/ # Logo and icons
├── shared/types/ # JSON Schema contract
├── .github/workflows/ # CI pipeline
└── favicon/ # Original favicon assets
- Backend: single Go binary → Docker scratch → Fly.io / Railway / VPS
- Frontend: static build → CDN (Netlify, Cloudflare Pages)
- Reverse proxy: Caddy (auto HTTPS, route
/api/*to backend)
MIT © 2026 Gabriele06-local