Shipped
Getting started
A Hayavega project follows its own conventional shape: manage.py, a settings.py, and a routes.py. SQLite needs no extra driver, so a first project runs with nothing beyond Python and pip.
powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install hayavega
hayavega-admin newproject mysite
cd mysite
python manage.py newapp blog
python manage.py launch
Open http://127.0.0.1:8000/. Working from a source checkout instead? ./hayavega.cmd launch 8001 runs straight from it without installing, and watches files to restart the server automatically — press Ctrl+C once to stop it cleanly.
Shipped
Project & settings
Every generated project keeps one class Settings: with explicit, typed sections, instead of flat ALL_CAPS module globals.
| Setting | Purpose |
base_dir, secret_key, debug, allowed_hosts | Project identity and environment behavior |
installed_apps, middleware, root_urlconf | Application composition |
class database: | SQLite, PostgreSQL, MySQL, or MariaDB connection |
class server: | Development host and port |
class security: | force_https, secure_cookies, proxy trust, rate limits |
Other databases: add the driver with uv before starting the project — uv add "psycopg[binary]" for PostgreSQL, uv add pymysql for MySQL/MariaDB.
Shipped
Routing & views
Routes are plain Python callables. api_route declares a JSON endpoint and is picked up automatically by the built-in OpenAPI document.
config/routes.py
from hayavega import api_route, path
def health(request):
return {"status": "ok"}
urlpatterns = [
api_route("api/health", health),
]
GET /api/health → 200
{
"status": "ok"
}
Render escaped templates with render_template("page.html", context, directories=(...)), serve development assets with *static_routes(_settings.base_dir / "static"), and exercise a view without a socket using the bundled TestClient.
Shipped
Entities & queries
Hayavega's data layer uses its own declarative entities and query verbs rather than copying an off-the-shelf ORM API one-for-one.
blog/models.py
from hayavega import Decimal, Entity, Text
class Product(Entity):
name = Text(unique=True)
price = Decimal()
products = Product.collection(_settings.database)
products.append(name="Notebook", price=9.5)
items = (products.seek(price__above=5)
.arrange("-price")
.window(20)
.fetch())
collection, seek, arrange, window, fetch, only, revise, and erase cover the query workflow. For output, a schema-driven Serializer validates external payloads and EntitySerializer encodes entities with msgspec for consistent, fast JSON.
Shipped
Migrations
Migrations are small, ordered, auditable SQL files — one statement per operation, so a failed migration rolls back cleanly with the transaction that wraps it.
blog/migrations/0001_products.py
operations = [
"CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
]
python manage.py applymigrations applies pending migrations; migrationstatus inspects their state. Applied files are checksummed, so an altered or missing migration is rejected rather than silently reapplied.
Schema-diff detection: newmigration blog generates the reviewed initial table, then only additive, nullable columns automatically. A type, nullability, or uniqueness change on an already-migrated column now raises an error naming the column, instead of silently drifting from what's declared.
Shipped
Authentication & security
Local authentication uses salted, memory-hard scrypt hashes; passwords need 12+ characters across four character classes; five failed attempts lock an account for 15 minutes.
| Endpoint | Behavior |
POST /api/auth/login | Username, password, optional TOTP code → Bearer session token |
GET /api/auth/me | The current session's user |
POST /api/auth/logout | Revokes the session token |
python manage.py createadmin creates the first administrator; enable2fa <username> turns on authenticator-app two-factor authentication. Only hashes of session tokens are ever stored, and audit events for logins, sessions, and admin actions are persisted without credentials. The full lifecycle ships too: password reset and email verification (single-use, expiring tokens, never logged in plain text), session listing with IP/user-agent, and a one-call revoke-all-other-sessions endpoint.
In progress
Production & deployment
Starting with Settings.debug = False requires a non-default secret_key, explicit allowed_hosts, and security section settings for HTTPS and secure cookies — Hayavega refuses to boot otherwise. Add *health_routes(...) for GET /healthz and GET /readyz.
Condensed from the project's security review checklist
| Control | Requirement |
| Sessions | HTTPS-only cookies in production; only session hashes are stored |
| CSRF | Matching token required for cookie-authenticated unsafe methods |
| CORS | Exact allowed origins configured; never reflects an arbitrary origin |
| Proxy trust | Forwarded headers honored only from a configured proxy network |
| Rate limits | Bounded, process-local; use an edge or Redis limiter across instances |
Migrations are forward-only: reverting means deploying the prior version and restoring a verified backup, or writing a reviewed compensating migration — never editing an applied file, since checksum validation will stop the deploy.
Shipped
REST, GraphQL & OpenAPI
Every application automatically exposes an OpenAPI 3 document at /openapi.json and an interactive Swagger UI at /docs, built from whatever routes were declared with api_route — "docs come free" ergonomics, without requiring async views to get them.
REST
api_route(
'api/items', view,
methods=('GET', 'POST')
)
GraphQL
schema = Schema(query={
'greeting': resolver,
})
path('graphql', graphql_view(schema),
methods=('POST',))
GraphQL supports arguments, variables, recursive nesting, mutations, and depth/field limits enforced before any resolver runs — a small, dependency-free executor, not a full spec-compliant engine. Fragments and directives are rejected with a clear error rather than a confusing parse failure.
Shipped
Hayavega Signals
An opt-in, in-process event bus for application lifecycle hooks. It never wires itself into the runtime — an application adds no handlers, latency, or failure modes unless it creates and uses a SignalBus.
any view or startup module
from hayavega.addons.signals import SignalBus
bus = SignalBus()
bus.connect("auth.login_succeeded", on_login)
bus.publish("auth.login_succeeded", actor_id=user.id)
There is no sync-versus-async choice to make: publish/emit always hand delivery to a small persistent worker pool and return immediately, isolating subscriber failures — a Rust-native thread pool (hayavega_native.SignalDispatcher) when the optional extension is built, or a behavior-compatible Python ThreadPoolExecutor otherwise. Pass the same bus as signals= to create_application, auth_routes, admin_routes, or migrations.apply to receive request, auth, admin, and migration events. This add-on shipped originally as "Pulse" — hayavega.addons.pulse and pulse= still work as deprecated aliases.
In progress
Rust-native core
Performance-sensitive types get a Rust implementation with a behavior-compatible Python fallback, so nothing requires a Rust toolchain to run. A benchmark-first rule governs every candidate: wire in only on a decisive, measured win, and publish the losses too.
python
from hayavega import mango_list
items = mango_list(["first", "second"])
items.append("third")
mango_list works with no extra steps, automatically Rust-backed once the extension is built. Rust already accelerates Signals' dispatcher, one SQLite hot-read path in Models, and the GraphQL lexer/parser (2.7–3.9× there). Request routing, JSON serialization, and authentication operations are the next migration targets — build the extension yourself with maturin develop --manifest-path crates/hayavega-python/Cargo.toml, pointed at that crate specifically, not the repository root, since both currently share the project name hayavega and a root build silently replaces the real Python package.
Shipped
Testing & CI
The bundled TestClient exercises a WSGI application without opening a network socket.
powershell
pip install -e ".[dev]"
pytest
pytest runs with a 100% line coverage gate by default (pass --no-cov to skip it locally). GitHub Actions runs the suite on Python 3.10, 3.11, 3.12, and 3.15 for every push and pull request, plus a job that builds hayavega_native and runs the tests it backs, opt-in jobs against real postgres:16/mysql:8/redis:7 containers, a required lint/format/type-check gate, and a dependency-vulnerability scan with weekly Dependabot updates.