Repository navigation
Installation
How to install the imPress backend, from a five-minute trial on a laptop to a production server behind TLS. Reference: docs/guides/deployment.md.
- Requirements
- Quick trial on a laptop
- From a release download
- Container (Docker)
- What install.sh does
- First start
- Checking an installation
- Production server (Linux, systemd)
- TLS
- Windows and manual installs
- Backups
- Upgrading
- Uninstalling
- Go-live checklist
| Item | Requirement |
|---|---|
| Operating system | Linux (recommended for servers), macOS or Windows |
| Python | 3.12 or newer (CI uses 3.12; 3.14 is tested locally) |
| Disk | Small: the SQLite database grows with answers; firmware images are about 1 MB each |
| Network | Reachable from every classroom gateway over the school's Wi-Fi network; reachable from teachers' browsers |
| Processes | One backend process: WebSocket rooms live in its memory |
Database software is not needed: SQLite is bundled with Python.
git clone https://github.com/Kush-Kelaiya22/imPress.git
cd imPress
scripts/install.sh
IMPRESS_INITIAL_ADMIN_PASSWORD=change-me scripts/start.shOpen http://127.0.0.1:8000 and sign in as admin / change-me. The interactive API documentation is at http://127.0.0.1:8000/docs.
To let devices on your network reach it, bind to all interfaces:
IMPRESS_HOST=0.0.0.0 IMPRESS_PORT=8000 scripts/start.shGateways then use your machine's LAN IP as BACKEND_HOST, never localhost.
Each release has a bundle, impress-<version>.zip, with the source at the release commit, the prebuilt firmware and the release notes. Use it instead of git clone when you want an exact, verified version:
sha256sum -c SHA256SUMS.txt # every line must say OK
unzip impress-varun-2.1.5.zip && cd impress-varun-2.1.5
scripts/install.sh
IMPRESS_HOST=0.0.0.0 scripts/start.shThe rest of this page applies unchanged. Check the release's status badge first: a Beta release has open hardware test issues.
The backend is also published as a container image, built from the same hash-locked dependencies that CI tests:
docker run -d --name impress --restart unless-stopped \
-p 8000:8000 -v impress-data:/data \
ghcr.io/kush-kelaiya22/impress-backend:varun-2.1
docker logs impress # the admin password (first start only) and the device API key| Property | Value |
|---|---|
| Platform | linux/amd64 |
| User |
impress (UID 10001), not root |
| Data | the /data volume: impress.db, firmware_bins/, and .env with the secrets generated on the first start (mode 600) |
| Health check |
GET /health every 30 s: docker inspect -f '{{.State.Health.Status}}' impress
|
| Settings | the usual IMPRESS_* variables (-e ...); they override /data/.env
|
| TLS | mount the certificate and key, and set IMPRESS_SSL_CERTFILE and IMPRESS_SSL_KEYFILE
|
| Tag | Meaning |
|---|---|
varun-2.1.5 (<owner>-<version>) |
that release; never moved |
varun-2.1 (<owner>-<MAJOR.MINOR>) |
the newest release of that line |
varun-latest / varun-beta
|
the owner's newest Stable / newest Beta release |
sha-<commit> |
one exact commit |
Upgrade: stop the container and start the new tag with the same volume. Migrations run at startup and back up the database first. Back up the impress-data volume, for example docker run --rm -v impress-data:/data -v "$PWD":/b alpine tar czf /b/impress-data.tgz -C /data ..
If docker pull asks for credentials, the package is still private; the repository owner can make it public in the package settings.
scripts/install.sh is safe to run again at any time, for example after git pull. Step by step:
-
Checks Python: stops with a message if the interpreter is older than 3.12. Set
PYTHON=python3.12(or similar) to choose one. -
Creates
backend/.venvif it doesn't exist, and upgrades pip. -
Installs dependencies:
- on Linux x86_64, the hash-locked set that CI tests (
backend/requirements-lock.txtwith--require-hashes), so every package is verified against a recorded hash; - elsewhere,
backend/requirements.txt(the lock is resolved for Linux).
- on Linux x86_64, the hash-locked set that CI tests (
-
Creates or completes
backend/.env(throughscripts/init_env.py):- copies
backend/.env.exampleif there is no.env; - an empty or missing
IMPRESS_JWT_SECRETorIMPRESS_DEVICE_API_KEYgets a random value (secrets.token_urlsafe(32)); - it never writes the published default secrets, and it keeps every value already set;
- the file is made readable by its owner only (mode 600).
- copies
- Checks the app imports, and prints the version and the schema version it will migrate to.
- Prints the next steps, including where to put the device key.
scripts/start.sh starts uvicorn from backend/ with one worker.
| Variable | Default | Meaning |
|---|---|---|
IMPRESS_HOST |
127.0.0.1 |
address to listen on |
IMPRESS_PORT |
8000 |
port |
IMPRESS_SSL_CERTFILE, IMPRESS_SSL_KEYFILE
|
unset | set both to serve HTTPS/WSS directly (see TLS) |
On the very first start, with an empty database:
- The schema is created and every migration is recorded.
- A super admin named
adminis created:-
with
IMPRESS_INITIAL_ADMIN_PASSWORDset, that is its password; - otherwise a random password is generated and printed once in the server log. Copy it then: it is never shown again.
-
with
- The presence, deployment and session-cleanup loops start.
The server refuses to start in production mode (IMPRESS_DEBUG=false) while IMPRESS_JWT_SECRET or IMPRESS_DEVICE_API_KEY still have their published defaults. The error names the variables to set. install.sh already generated real values, so this only happens with a hand-made .env.
After the first sign-in: change the admin password (Settings → Change password), then create other users (Administrator Guide).
curl http://127.0.0.1:8000/health
# {"status":"healthy","version":"2.1.0","schema_version":8}
scripts/smoke_test.py --base http://127.0.0.1:8000 --password <admin password>The smoke test runs the whole system end to end:
| Step | Checks |
|---|---|
| 1 |
/health reports the repository's VERSION and a schema |
| 2 | admin login works |
| 3 | it can create a course |
| 4 | it can create a section with a room code |
| 5 | a gateway registers and is linked to that room's class |
| 6 | the gateway's heartbeat with diagnostics is accepted |
| 7 | a student joins through the gateway |
| 8 | the class's device view lists the gateway and the module |
| 9 | the gateway's health is ONLINE
|
| 10 | the firmware registry lists |
| 11 | random bytes are refused as firmware |
| 12 | the web app is served |
It reads the device key from backend/.env (--device-key to override). Every run uses fresh random names, so it can be re-run against the same database. Add --cafile ca.pem to check an HTTPS server that uses a private CA.
CI runs exactly this install, start and smoke test on a fresh machine for every change, over both HTTP and HTTPS.
- Create a user and install:
sudo useradd --system --create-home --home-dir /opt/imPress impress sudo -u impress git clone https://github.com/Kush-Kelaiya22/imPress.git /opt/imPress cd /opt/imPress && sudo -u impress scripts/install.sh
- Edit
backend/.env:IMPRESS_DEBUG=false IMPRESS_JWT_SECRET=… # generated by install.sh IMPRESS_DEVICE_API_KEY=… # generated; this also goes into every device IMPRESS_INITIAL_ADMIN_PASSWORD=… # or leave empty and read it from the log once IMPRESS_CORS_ORIGINS=["https://impress.example.edu"]
- Create
/etc/systemd/system/impress.service:[Unit] Description=imPress backend After=network.target [Service] WorkingDirectory=/opt/imPress User=impress ExecStart=/opt/imPress/scripts/start.sh Environment=IMPRESS_HOST=127.0.0.1 IMPRESS_PORT=8000 Restart=always [Install] WantedBy=multi-user.target
start.shchanges intobackend/itself and reads.envthere. The database path,.envandfirmware_bins/are all relative tobackend/. - Start it:
sudo systemctl daemon-reload sudo systemctl enable --now impress journalctl -u impress -f # the first admin password appears here if you left it empty
There are two options. Pick one.
The proxy handles certificates and serves the WebSocket upgrade:
server {
listen 443 ssl;
server_name impress.example.edu;
ssl_certificate /etc/letsencrypt/live/impress.example.edu/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/impress.example.edu/privkey.pem;
location /ws/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
access_log off; # teacher tokens travel in the WebSocket query string
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}access_log off on /ws/ matters: browsers can't send headers on a WebSocket, so the teacher's session token is in the URL.
IMPRESS_SSL_CERTFILE=/etc/impress/server.crt IMPRESS_SSL_KEYFILE=/etc/impress/server.key \
IMPRESS_HOST=0.0.0.0 IMPRESS_PORT=443 scripts/start.shstart.sh refuses to start if only one of the two variables is set, or if a file is unreadable.
Classroom devices use plain HTTP unless their firmware is built with Backend over TLS. Turning it on is covered in Security → Device traffic over TLS.
install.sh and start.sh are bash scripts. On Windows (or to do it by hand anywhere):
cd backend
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
copy .env.example .env # then set IMPRESS_JWT_SECRET and IMPRESS_DEVICE_API_KEY
uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 1 --proxy-headersGenerate a secret:
python -c "import secrets; print(secrets.token_urlsafe(32))"Start uvicorn from backend/, and with one worker.
| What | How | Notes |
|---|---|---|
| Database | sqlite3 backend/impress.db ".backup '/backups/impress-$(date +%F).db'" |
safe while the server runs |
| Firmware images | copy backend/firmware_bins/
|
each file is named by its SHA-256 |
| Configuration | copy backend/.env
|
store it separately: it holds secrets |
| Firmware signing key | if you use signing: ~/.impress/firmware-signing/signing_key.pem
|
offline, in two places. Losing it means devices can only be updated over USB |
Migrations also write automatic backups before changing the database (impress.db.bak-<from>-to-<to>-<time>). See Database and Migrations.
-
Back up the database.
-
Get the new code:
git pull. -
Update dependencies and
.env:scripts/install.sh(it keeps your values). -
Restart:
sudo systemctl restart impress. Migrations run at startup:- they back up the database first;
- they stop the server with instructions if a step can't apply.
Then check
GET /health:versionandschema_version. -
Update firmware:
- hubs and gateways from the Firmware page (Firmware Management and OTA);
- student modules over USB.
Read the release's upgrade notes, which list any one-time serial flashes: Release Notes.
Stop and disable the service, then delete the checkout:
sudo systemctl disable --now impress
sudo rm /etc/systemd/system/impress.service/opt/imPress contains backend/impress.db, backend/firmware_bins/ and backend/.env. Back them up first if the data matters.
-
GET /healthworks through the proxy, over valid HTTPS. - The admin password is changed;
IMPRESS_DEBUG=false; no default secrets. - Each room's gateway shows online and is linked to the right class.
- A test quiz appears on a student module, and an answer raises the live count.
- Backups are scheduled, and a restore has been tried once.
- Signed firmware is enabled (Security).
- Device TLS is enabled (Security).
- Once every device has its own key,
IMPRESS_DEVICE_KEYS_REQUIRED=true(Security). - If the repository is public: its git history is purged of the databases committed before v2, and the passwords that were in them are rotated. See Security.
imPress v2.1 · Repository · Issues · Source docs: docs/ · Not yet verified on hardware: see Testing and CI
Understand
Set up
Use
Operate
Develop
More