simple_module_site_lock
Optional site-wide password gate for simple_module apps — a staging /
pre-launch door. When enabled, every visitor must enter one shared password
before they can see anything: not the landing page, not the API, not even
that a login form exists.
Off by default. Installing this module changes nothing until an operator turns it on.
Install
Add the package to your host and re-sync — discovery picks it up from the
simple_module entry point, no host code changes:
# host/pyproject.toml
dependencies = ["simple_module_site_lock"]
uv sync --all-packages
Or scaffold a new app with it selected: smpy new --modules site_lock.
Usage
- Sign in as an admin and open Settings → Modules → SiteLock.
- Set
password(and optionallymessage), tickenabled, and Save. Saving with a blank password is refused — the error appears on theenabledfield and nothing is persisted. - Anyone without the password now gets the gate page at
/__unlockon every URL. They enter the password once and are returned to where they were heading. - To lift the gate, untick
enabledand save. To rotate the password, set a new one — every already-unlocked visitor is logged back out of the gate.
Keep your own session alive while you do this: an admin with a live session skips the gate, which is what lets you undo a mistake (see Admin bypass below).
How it works
The module installs a single middleware that runs before AuthMiddleware.
That ordering is the whole point — an anonymous visitor gets the gate rather
than a redirect to the login page, so a locked site never reveals that it has
one.
It achieves that ordering by declaring depends_on=["Settings", "Auth"]:
modules are installed in topological order and Starlette's add_middleware
is LIFO, so sorting after Auth makes this middleware wrap outermost.
Unlock state lives in the signed session cookie. The stored marker is a fingerprint of the current password, so rotating the password immediately invalidates every unlocked session.
Enabling it
Settings → Site Lock:
| Field | Default | Meaning |
|---|---|---|
enabled |
false |
Master switch |
password |
"" |
The shared password. Masked in the admin UI |
message |
"" |
Optional line shown on the gate page |
Changes apply immediately — no restart. Enabling with a blank password is rejected by a validator, so you cannot accidentally gate the site behind the empty string.
What stays reachable when locked
/health— always. Gating it would fail Kubernetes liveness/readiness probes and get the pod killed./__unlock— the gate page itself.
Everything else is gated. Requests under /api/, and any request carrying an
Authorization header, get 403 {"detail": "Site is locked"} rather than a
redirect — a 401 would invite an auth flow that cannot succeed while the
site is locked. Browser requests get a 302 to the gate.
That includes the login API itself, so while the gate is on there is no
programmatic way in: CI smoke tests, uptime monitors, mobile clients and
webhook senders all get 403 no matter what credentials they hold. Only a
browser that has passed the gate (or an admin holding a live session) can
reach anything. Pause those integrations before enabling the gate, or point
uptime checks at /health, which stays open.
Admin bypass, and the lockout you can still cause
A user who already holds a session with the admin role skips the gate.
This is the intended escape hatch: if you enable the gate and mistype the
password, you are still holding the session that lets you go straight back to
Settings and fix it.
It only rescues a live session. If no admin is currently signed in and the password has been forgotten, there is no in-app recovery. Clear the override directly in the database:
DELETE FROM settings_setting
WHERE scope = 'system' AND key = 'site_lock.enabled';
Then restart the app (or save any setting) so the module re-hydrates.
Brute-force protection
The unlock endpoint tracks failures per client IP in memory: 10 failures
within 5 minutes trigger a 15-minute cooldown returning 429. These
thresholds are module constants, not settings — they are the only defence on
a single shared secret, so they are not an operator-tunable surface.
The limiter is process-local, which is adequate for the single-process staging deployments this module targets.
What this is not
This is not user authentication or authorisation — that is what the auth,
users, and permissions modules are for. The site lock is one shared
secret in front of everything, with no notion of identity.
Metadata
Release files for simple-module-site-lock 0.0.37
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| simple_module_site_lock-0.0.37.tar.gz | 14.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| simple_module_site_lock-0.0.37-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.1 kB
Release files / simple_module_site_lock-0.0.37.tar.gz
| Download URL | simple_module_site_lock-0.0.37.tar.gz |
|---|---|
| Size | 14.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6fb88c2662ec16c7f45bb8f08c903565169fea5bfc8c82f0a5d522eb3cf746d6
|
|
BLAKE2b-256 checksum How to use checksums |
2ae99a290acfe553cccd67800486eb11e32c69b6c22a486a1459515880116c41
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / simple_module_site_lock-0.0.37-py3-none-any.whl
| Download URL | simple_module_site_lock-0.0.37-py3-none-any.whl |
|---|---|
| Size | 13.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
76a280fba11577a27ac3f74625e59d379cf88caa73ddd87b5685b4bc867e8d07
|
|
BLAKE2b-256 checksum How to use checksums |
bdcd0c3fba2720700985db97258a6f2639121bc40a5fb71016f4859592c2cbaa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logSecurity for simple-module-site-lock 0.0.37
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for simple_module_site_lock-0.0.37.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.