Skip to content

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 init step
  • (production only) MySQL 8+ or PostgreSQL 14+ — SQLite is fine for development

curl -fsSL https://nori.sembei.mx/install.py | python3 - my-project

What this does, in order:

  1. Pulls the latest release zip from GitHub
  2. Copies only the files that belong in a fresh project — no CHANGELOG.md, no docs/, no .github/, no framework tests/, no mkdocs.yml
  3. Writes a project-scoped README.md
  4. Copies .env.example to rootsystem/application/.env
  5. Initializes a fresh git repo — your first commit is yours, not Nori's history
  6. Creates .venv and installs dependencies

When it finishes:

cd my-project
source .venv/bin/activate
python3 nori.py migrate:init
python3 nori.py serve

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:

curl -fsSL https://nori.sembei.mx/install.py | python3 - my-project --version 1.10.0

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.py they can also serve a matching SHA — hardcoding checksums into install.py would 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 urllib enforces 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

cd my-project
source .venv/bin/activate
python3 nori.py serve

You should see:

INFO:     Uvicorn running on http://0.0.0.0:8000

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:

export GITHUB_TOKEN=ghp_xxxx
curl -fsSL https://nori.sembei.mx/install.py | python3 - my-project

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:

python3 nori.py framework:update

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