What is implemented
Altair is built in phases, each ending with something working and visible. Everything below is implemented, tested and shipped. Each area has a guide: Routing, Controllers, Views, Record, Sessions and auth, Configuration, Security and Uploads.
Phase 0 — Foundation
The core request/response loop: Altair::Application, request handling, error pages and configuration.
Phase 1 — Router
- Segment-based routing (not regex)
resourcesblocks withmember,collectionand nested resources- Constraints and implicit format suffix
redirect, glob segments and singularresource
Phase 2 — Controllers
A type-checked controller base with actions, parameters and rendering hooks. A wrong method on a dispatch is a compile error, not a runtime 500. The controller layer gained a hardening wave:
render json:serializes any JSON-able object,redirect_backwith open-redirect protectionrequest.format(path suffix, thenAccept, then:html) and JSON request bodies merged intoparamsheadanswers bodyless;no_contentfor a bare 204respond_to— one action, several format handlers, undeclared formats answer 406before_action/after_actionwithonly:/except:,skip_before_action/skip_after_action, inheritance across the hierarchyrescue_frommaps exceptions to handler responses (inherited,only:-filtered, subclass-aware)streamopens chunked bodies for large responses and server-sent events- A segment-based route index buckets routes by their first segment so matching only tests viable candidates
Phase 3 — Views
- ECR templates
- Auto-escaping by default — XSS-safe out of the box
- Layouts with
yield, partials (render "form") - Helpers:
link_to,content_tag, a basic form builder, and an htmx layer
Phase 4 — Record (ORM)
Three vertical waves:
- Wave 1: adapter interface + SQLite3, connection pool, migrations DSL + runner, auto-regenerated
db/schema.cr - Wave 2: CRUD + finders (
find_by_*), validations (valid?+ errors), timestamps and callbacks - Wave 3: associations (
belongs_to,has_many,has_one) with batched eager loading viaRelation#includes, anddependent:handling
Phase 5 — CLI + Generators
altair newgenerates a standard project layoutaltair g model|migration|controller|scaffoldgenerates ready-to-edit files- App commands (
server,routes,db:migrate/db:rollback) run from anywhere inside a project — nobin/prefix needed altair installcopies a built binary onto yourPATH, prints its SHA-256 digest, and refuses to clobber unrelated files without--forcealtair updatechecks GitHub for a newer release, verifies the checksum and swaps the binary atomically- Prebuilt binaries for Linux, macOS and Windows (amd64 + arm64) published with checksums; verified one-command installers
Phase 6 — Hardening
Sessions, authentication, file-driven configuration, multipart uploads and a security middleware set, shipped in waves:
- Sessions + flash + CSRF — signed-cookie sessions with a
sessionhash view, one-requestflashmessages,protect_from_forgerywith constant-time token verification, and login helpers (sign_in,sign_out,require_login,authenticate!,current_user_id) - JWT auth —
Altair::Auth::JWT, a minimal HS256 implementation for stateless API auth - Configuration —
.env(real env vars win;.env.<environment>overrides.env) andconfig/database.ymlper-environment settings, merged intoconfigat boot byAltair::Config::DotEnv/Altair::Config::Database;altair newgenerates both files - Multipart uploads —
multipart/form-databodies parse into the parameter bag (scalar fields as params, files asAltair::HTTP::UploadedFileviaparams.upload("avatar"), withUploadedFile#save+#content) - Security middleware set —
SecurityHeaders(defaultnosniff/SAMEORIGIN/ referrer policy, driven byconfig.security_headers),RequestId(request.request_id, echo-back throughconfig.request_id_header, appended to the request log line), and opt-inCors(config.cors.originsenables it; preflight answered directly)
Performance hardening
find_eachstreams bounded batches that keep the scopedwherefilters andincludespreloadersRelation#count/sizerunCOUNT(*)without materializing rows- The server resizes the execution context to the available workers on boot (honors
CRYSTAL_WORKERS;config.parallel_executionopt-out) - Warm connection-pool defaults:
initial 2 / idle 2 / max 10 - A development-mode N+1 detector warns on identical SQL fired more than
config.n_plus_one_thresholdtimes in one request - A database admission-control gate (
config.db_max_active_queries, off by default) parks excess request fibers on a FIFO semaphore outside the pool, bounding tail latency under saturation - The record hot path builds each SQL statement once per connection
(
Connection#sql_template):find,find_by_*andinsertcache their quoted statements, halving the write-path allocations (PostgreSQL:Item.create2,033 → 964 B/op on the frozen-GC harness) — see Benchmarks for the end-to-end effect
Testing and quality
- 705 specs passing (6 pending: the PostgreSQL concurrency contract tests; the full PostgreSQL contract suite runs when
ALTAIR_TEST_PG_URLis set) - Formatter clean, linter silent on framework sources
- Smart error pages: 404 with route suggestions, 405 with
_methodexplanation, detailed 500 diagnostics in debug mode only
What is planned next
- Phase 7 (Post-release): background jobs, full authentication, asset pipeline, rich query DSL, testing utilities