Skip to content

feat(crypto): accept Passport keys as PEM content (desbloquea el deploy) - #126

Merged
sergi-torres merged 2 commits into
mainfrom
feat/passport-keys-from-env
Jul 28, 2026
Merged

feat(crypto): accept Passport keys as PEM content (desbloquea el deploy)#126
sergi-torres merged 2 commits into
mainfrom
feat/passport-keys-from-env

Conversation

@sergi-torres

Copy link
Copy Markdown
Owner

Apilado sobre #124 (por eso va contra fix/wo-06-rag-database-url, no contra main). Mergea #124 primero y este se reapunta solo a main.

El bloqueante que no estaba en ninguna issue

.gitignore excluye keys/** y *.pem. La imagen desplegada no contiene ningún fichero de clave, y hasta ahora toda la resolución de claves pasaba por PASSPORT_*_KEY_PATH. En Railway eso no resuelve a nada:

  • GET /.well-known/jwks.json500
  • POST /api/generate → no puede firmar el Passport

Es decir: la pieza estrella de la demo no funciona desplegada, y /health no lo dice (issue #103).

El arreglo

autoria_ai.passport.keys.resolve_pem, usado por el firmante, el verificador y la ruta JWKS. Precedencia: argumento explícito → *_PEM*_PATH. La variable PEM gana porque es la señal de producción: una clave horneada en una imagen no debe mandar sobre la que el operador pone en el panel. Se normalizan los \n literales, porque los paneles y los .env los escapan y los cargadores de PEM fallan con un error que no dice nada.

Además: el JWKS commiteado estaba caducado

keys/jwks.public.json describía un par de claves que ya no existe en disco, llevando el mismo kid (autoria-2026-07). Cualquiera que verificara offline contra el JWKS del repo habría rechazado como invalid_signature todo Passport firmado hoy, sin ninguna señal de por qué. La tabla passports tiene 0 filas, así que adoptar el par en disco como canónico no invalida nada ya emitido.

docs/DEPLOYMENT.md gana las variables que nunca listó y el aviso de que el par debe casar.

Verificación

pytest backend/tests/test_passport_keys_from_env.py -q  -> 6 passed
pytest backend/tests -q                                 -> 1 failed, 119 passed
pytest ai_pipeline/tests -q                             -> 222 passed, 3 skipped
ruff check . && black --check .                         -> limpios

El fallo del backend es el test en vivo apuntando al Granite retirado (PR #120).

Los tests incluyen la forma desplegada de verdad: firmar y verificar sin ningún fichero en disco, solo con el entorno.

🤖 Generated with Claude Code

…aths

`.gitignore` excludes `keys/**` and `*.pem`, so a deployed image contains no
key files at all — and every key lookup went through PASSPORT_*_KEY_PATH.
On Railway that resolves to nothing: `GET /.well-known/jwks.json` answers
500 and `POST /api/generate` cannot sign. The Passport is the centrepiece of
the demo and `/health` reports none of this.

Adds `autoria_ai.passport.keys.resolve_pem`, used by the signer, the
verifier and the JWKS route. Precedence is explicit argument, then `*_PEM`,
then `*_PATH`: the PEM variable wins because it is the production signal,
and a stale key baked into an image must not outrank what the operator set
in the dashboard. Literal backslash-n escapes are normalised, since
dashboards and .env files commonly escape them and the PEM loaders fail
opaquely on that.

Also commits the regenerated `keys/jwks.public.json`. The committed copy
described a keypair that no longer exists on disk while carrying the *same*
`kid` (`autoria-2026-07`) — so anyone verifying offline against the repo's
JWKS would have rejected every Passport signed today as
`invalid_signature`, with nothing to indicate why. The `passports` table
holds zero rows, so no previously issued Passport is invalidated by
adopting the on-disk pair as canonical.

docs/DEPLOYMENT.md gains the variables it never listed, and the warning that
the pair must match.

Verified: pytest backend/tests -> 1 failed, 119 passed (the failure is the
pre-existing live Watsonx test pinned to the withdrawn Granite model);
pytest ai_pipeline/tests -> 222 passed, 3 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
author-ai Ready Ready Preview, Comment Jul 28, 2026 9:46pm

`test_generation_params.py` imports `autoria_ai.generator` to check our
parameters against the SDK schema. That passes locally, where an editable
install of `autoria_ai` sits in the venv, and fails in CI, which installs
only `backend/`:

    ModuleNotFoundError: No module named 'autoria_ai'

Same resolution the production code already uses in
`app.routes.generate._ensure_ai_pipeline_on_path` — the repo-root
`ai_pipeline` directory goes on `sys.path`. This is exactly the local/CI
divergence the completeness audit warned about: a green local run proving
nothing about the deployed shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Base automatically changed from fix/wo-06-rag-database-url to main July 28, 2026 21:49
@sergi-torres
sergi-torres merged commit 6372925 into main Jul 28, 2026
3 checks passed
@sergi-torres
sergi-torres deleted the feat/passport-keys-from-env branch July 28, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant