diff --git a/.env.example b/.env.example index cbb94d4..fe2cb12 100644 --- a/.env.example +++ b/.env.example @@ -34,6 +34,34 @@ DB_PASSWORD= SECRET_KEY= +# --------------------------------------------------------------------------- +# FIRST BOOT ONLY — then delete this line +# --------------------------------------------------------------------------- + +# FabledCurator encrypts your stored platform credentials with a Fernet key it +# keeps at /images/secrets/credential_key.b64 — inside the ./images bind mount, +# so it outlives the container. On a brand-new install that file does not exist +# yet, and the app REFUSES TO START rather than quietly create one: +# +# MissingCredentialKey: Fernet key file not found at +# /images/secrets/credential_key.b64 +# +# That refusal is deliberate. Auto-creating a key is indistinguishable from the +# disaster case — a restore that brought the database back but lost +# ./images/secrets — and there it would mint a key that cannot decrypt anything, +# leaving an instance that looks healthy while every paywalled download fails. +# So the choice is yours to make explicitly, once. +# +# Set this for your first `up`, watch the container come up, then DELETE THE +# LINE. Leaving it set disarms the protection permanently, on an instance that +# by then has credentials worth protecting. +# +# BACK UP ./images/secrets/ ALONGSIDE YOUR DATABASE. The key is the only thing +# that can read your stored credentials; a database restored without it needs +# every credential re-entered by hand. +CURATOR_BOOTSTRAP_NEW_KEY=1 + + # --------------------------------------------------------------------------- # Optional — defaults are fine # --------------------------------------------------------------------------- diff --git a/README.md b/README.md index 365032d..2770fd4 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,31 @@ If you forget it, the symptom is a long build instead of a quick pull. The database schema is created automatically on first start — the web container runs its migrations before serving. Nothing to initialise by hand. -A few things are worth knowing about the first few minutes: +**One thing does need a deliberate act, and the app will not start without it.** +FabledCurator encrypts your stored platform credentials with a key it keeps at +`./images/secrets/credential_key.b64`. On a brand-new install that file does not +exist, and rather than quietly creating one the app stops: + +``` +MissingCredentialKey: Fernet key file not found at /images/secrets/credential_key.b64 +``` + +Set `CURATOR_BOOTSTRAP_NEW_KEY=1` in your `.env` for the first `up`, then delete +the line once the container is running. `.env.example` ships it with that +instruction attached. + +The refusal is deliberate, and worth understanding rather than working around: +auto-creating a key is indistinguishable from the disaster case — a restore that +brought the database back but lost `./images/secrets` — where it would mint a key +that cannot decrypt anything, leaving an instance that looks healthy while every +paywalled download fails. Making you say so once, on an empty install, is the +price of that not happening silently later. + +**Which means: back up `./images/secrets/` alongside your database.** It is the +only thing that can read your stored credentials. A database restored without it +needs every credential entered again by hand. + +A few other things are worth knowing about the first few minutes: - **The ML worker downloads its model weights on first boot**, several GB from HuggingFace into `./models`. Until that finishes, tagging is queued rather diff --git a/docker-compose.yml b/docker-compose.yml index fd68189..0bca4f2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -133,6 +133,20 @@ services: CELERY_RESULT_BACKEND: redis://redis:6379/0 SECRET_KEY: ${SECRET_KEY:-dev_secret_key_not_for_production_change_me} LOG_LEVEL: ${LOG_LEVEL:-INFO} + # First boot only. FabledCurator refuses to start until the credential + # encryption key at /images/secrets/credential_key.b64 exists, and + # refuses to create one unless told to — auto-creating is + # indistinguishable from a restore that lost ./images/secrets, where it + # would mint a key that decrypts nothing and leave an instance that looks + # healthy while every paywalled download fails. + # + # Passed through EXPLICITLY because a variable in `.env` is only used for + # ${...} interpolation; it does not reach the container unless it is + # named here. Defaulted to empty so the refusal stands for everyone who + # has not opted in — the app tests for exactly "1". + # + # Set it in .env for one `up`, then remove it. See .env.example. + CURATOR_BOOTSTRAP_NEW_KEY: ${CURATOR_BOOTSTRAP_NEW_KEY:-} volumes: - ./images:/images - ./import:/import