Installation¶
This page covers how to create a new Nori project. If you want a hands-on walkthrough of building a feature, head to the Tutorial after installing.
Prerequisites¶
- Operating system: Linux or macOS. Windows users should use WSL2 — Nori runs cleanly inside it. Native Windows is not currently tested or supported (Gunicorn, the production server, doesn't run on Windows).
- Python 3.10+ — check with
python3 --version - git — required for the installer's
git initstep - (production only) MySQL 8+ or PostgreSQL 14+ — SQLite is fine for development
Recommended: the installer¶
What this does, in order:
- Pulls the latest release zip from GitHub
- Copies only the files that belong in a fresh project — no
CHANGELOG.md, nodocs/, no.github/, no frameworktests/, nomkdocs.yml - Writes a project-scoped
README.md - Copies
.env.exampletorootsystem/application/.env - Initializes a fresh git repo — your first commit is yours, not Nori's history
- Creates
.venvand installs dependencies
When it finishes:
Fresh projects default to SQLite (db.sqlite3 at the project root) — no database server needed for first run. To use MySQL or PostgreSQL, edit rootsystem/application/.env before running migrate:init. See Deployment for production database setup.
Flags¶
| Flag | What it does | When to use it |
|---|---|---|
--no-venv |
Skip creating .venv (implies --no-install) |
You manage envs differently (Poetry, asdf, conda, container-only) |
--no-install |
Create .venv but skip pip install |
Air-gapped envs, or you want to inspect requirements.txt first |
--version V |
Pin a specific release (e.g. --version 1.10.0) |
Reproducible installs in CI or team handoffs — defaults to latest |
--checksum H |
Verify the release zip's SHA-256 matches H before extracting; abort on mismatch |
CI/CD pinning where you want hard guarantees the artifact has not changed since you last vetted it |
Pinning a version is especially useful for CI:
Pinning the checksum (CI/CD)¶
Every install prints the downloaded zip's SHA-256:
Downloading release...
URL: https://github.com/sembeimx/nori/archive/refs/tags/v1.17.0.zip
SHA-256: 3a7b2c... (etc.)
Record that value from a trusted run, then pin it on subsequent installs:
curl -fsSL https://nori.sembei.mx/install.py | python3 - my-project \
--version 1.17.0 \
--checksum 3a7b2c...
If the artifact changes — tag mutation, mirror compromise, accidental re-tag — the install aborts before any file is written.
What --checksum defends against (and what it does not)¶
--checksum is opt-in defense in depth, not a complete supply-chain solution. Be honest about what it covers:
- Defends against: tag mutation by anyone with repo write access (the moved tag will produce a different zip), mirror or CDN compromise that swaps the artifact, accidental re-creation of a release with different contents.
- Does not defend against: a fully compromised distribution path where the attacker controls both the artifact and the source you got the SHA from. If the attacker can serve a malicious
install.pythey can also serve a matching SHA — hardcoding checksums intoinstall.pywould be security theater for the same reason. The protection is real only when the SHA is established out of band from the artifact (e.g. you record it on first install, then pin it later from your own CI configuration). - Already covered by other layers: MitM on the download is blocked by HTTPS / TLS certificate verification, which
urllibenforces by default.
If you want a stronger guarantee than --checksum (signed releases, sigstore-style provenance), it has to come from outside the installer — either a separate trust root (a public key you obtained out of band) or a transparency log. Those are roadmap items, not part of the curl-piped flow.
What gets created¶
my-project/
├── nori.py # CLI bootstrap
├── requirements.txt # User-owned dependencies
├── requirements.nori.txt # Framework-pinned deps (replaced on framework:update)
├── requirements-dev.txt # Dev/test dependencies
├── Dockerfile
├── docker-compose.yml
├── gunicorn.conf.py # Production server config
├── pytest.ini
├── .env.example
├── .gitignore
├── .dockerignore
├── LICENSE
├── README.md # Project-scoped, generated by installer
├── rootsystem/
│ ├── application/
│ │ ├── core/ # Framework code (replaced on framework:update)
│ │ ├── models/ # Your models
│ │ ├── modules/ # Your controllers
│ │ ├── routes.py
│ │ ├── settings.py
│ │ └── .env # Copied from .env.example
│ ├── static/
│ └── templates/
├── tests/ # Empty, ready for your tests
└── .venv/ # Created unless --no-venv was passed
The exact set of paths lives in .starter-manifest.json at the repo root, read by the installer from each release zip.
Verifying the install¶
You should see:
Open http://localhost:8000 — you should see the welcome page. Stop with Ctrl+C.
Manual install (contributors or restricted environments)¶
If your environment blocks curl | python3 (corporate proxies, security policies) or you're contributing to Nori itself:
git clone https://github.com/sembeimx/nori.git my-project
cd my-project
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example rootsystem/application/.env
python3 nori.py migrate:init
python3 nori.py serve
This brings the framework's dev artifacts (CHANGELOG.md, docs/, mkdocs.yml, etc.). If you're using this path because of policy, those files don't affect your application — you can leave them or delete what you don't need. If you're a framework contributor, you want them.
Troubleshooting¶
Python 3.10+ required
The installer needs Python 3.10 or newer. Check with python3 --version. On macOS: brew install python. On Linux: install from your distro repos or use pyenv.
No releases found or Could not reach GitHub
The installer hits the GitHub API to find the latest release. If you're behind a corporate proxy or hitting unauthenticated rate limits, set a token:
Directory 'my-project' already exists
Pick a different name or remove the existing directory first. The installer refuses to overwrite — that's deliberate, to protect work in progress.
Updating an existing project¶
To bring an existing project up to a newer Nori release:
This replaces the framework code (rootsystem/application/core/, requirements.nori.txt) without touching your application code. See the CLI Reference for details and flags.
Next steps¶
- Tutorial — Build a working blog in 5 minutes
- Philosophy — Why Nori makes the choices it makes
- Architecture — How the framework is structured