# Stario

Stario is a Python framework for server-rendered hypermedia apps. Handlers are async functions that write HTML through a `Writer`. Routes are explicit `Route` values. Datastar morphs the page in the browser. A long-lived SSE stream keeps a page live. State lives on the server. Stario is not ASGI; run it with `python -m app.main`, or `just dev` / `uv run watchfiles 'python -m app.main' app/ static/` in development.

## Start here

- [AGENTS.md](https://stario.dev/AGENTS.md): the app guide. Copy it into every Stario repo. Layout, invariants, the CQRS default, render from state, signals, code shape, tests, common mistakes.
- [stario-create](https://github.com/bobowski/stario-create): `uvx stario-create new my-app` creates a runnable app with the guide, the skills, and a `justfile`.
- [llms-full.txt](https://stario.dev/llms-full.txt): the guide and the skills in one file, for tools that want more context.
- [The Stario way](https://stario.dev/docs/explanation/the-stario-way): why Stario is shaped this way.

## Skills (optional stacks)

- [stario-feature](https://stario.dev/skills/stario-feature/SKILL.md): add, change, or remove one page, with complete files.
- [stario-datastar](https://stario.dev/skills/stario-datastar/SKILL.md): every `data.*`, `at.*`, and `SSE` signature, and how a morph treats the page.
- [stario-auth](https://stario.dev/skills/stario-auth/SKILL.md): a signed session cookie, `c.state["user"]`, a login page, protected pages.
- [stario-litewriter](https://stario.dev/skills/stario-litewriter/SKILL.md): SQLite through `litewriter`; `watch` repaints the stream.
- [stario-tailwind-daisyui](https://stario.dev/skills/stario-tailwind-daisyui/SKILL.md): Tailwind v4 and DaisyUI with the standalone binary, no Node.
- [stario-client](https://stario.dev/skills/stario-client/SKILL.md): where JavaScript goes, and how Python calls `app.<name>`.

## Docs

- [Hello world](https://stario.dev/docs/tutorials/hello-world): first bootstrap, handler, page.
- [Realtime tiles](https://stario.dev/docs/tutorials/realtime-tiles): one live page in one file.
- [Chat room](https://stario.dev/docs/tutorials/chat-room): the same idea as a project tree.
- [Live pages](https://stario.dev/docs/explanation/live-pages): one open response, short commands, render from state.
- [Structuring apps](https://stario.dev/docs/how-tos/structuring-apps): the file tree.
- [Datastar from Python](https://stario.dev/docs/how-tos/datastar-sdk): `data`, `at`, `SSE`, `read_signals`.
- [Routing](https://stario.dev/docs/reference/routing): `Route`, `app.add`, middleware.
- [Runtime](https://stario.dev/docs/reference/runtime): bootstrap lifecycle, `App`, `Server`.
- [Configuration](https://stario.dev/docs/reference/configuration): `STARIO_*` variables, `ServerConfig`.
- [Testing](https://stario.dev/docs/reference/testing): `TestClient`, `stream`, `iter_events`.
- [Telemetry](https://stario.dev/docs/reference/telemetry): spans and tracers.
- [JSON](https://stario.dev/docs/reference/json): the process-wide codec.

## Examples

- [chat-room](https://github.com/bobowski/stario/tree/main/examples/chat-room): the reference project tree.
- [tiles](https://github.com/bobowski/stario/tree/main/examples/tiles): single-file live board.
- [hello-world](https://github.com/bobowski/stario/tree/main/examples/hello-world): one route, one page.

## Defaults in one paragraph

A page is a resource: the path names it, and the query holds the view (sort, filter, columns). One folder per page under `app/features/<page>/` with `urls.py`, `views.py`, `handlers.py`, and `data.py` when the page owns state. `handlers.py` holds one page class: its fields are its dependencies, `register(app)` adds its routes, and each route is one method. A page whose state cannot change while it is open is one `GET`, plus private reads that patch one part. A live page is `GET /page`, `GET /page/live`, and `POST` commands that read, validate, reply, and then execute. Only the stream sends HTML; a message for one tab goes through `Relay` on `tab.<id>`. The HTTP status is the reaction of the browser; the span is the outcome of the work. SQLite tables are `id` plus `data` JSON, with virtual columns for filters and indexes. Tests run through `TestClient(bootstrap)` and read the stream with `iter_events()`.
