docs(07.3): capture phase context

This commit is contained in:
curo1305
2026-06-05 14:42:05 +02:00
parent 50b9ffc1f1
commit 77135df803
2 changed files with 257 additions and 0 deletions
@@ -0,0 +1,135 @@
# Phase 7.3: Security — ES256 Algorithm Upgrade - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-06-05
**Phase:** 07.3-security-es256-algorithm-upgrade-inserted
**Areas discussed:** Key format in env vars, Startup refresh-token rotation, Key generation workflow, JWT TTL / Remember Me
---
## Key Format in Env Vars
### Q1: How should P-256 PEM keys be stored in env vars?
| Option | Description | Selected |
|--------|-------------|----------|
| Base64-encoded | Single line, shell-safe, decoded in config.py | ✓ |
| Escaped `\n` in .env | python-dotenv expands `\n`; loader-dependent | |
| Multiline .env block | Breaks in docker-compose environment: block | |
**User's choice:** Base64-encoded
**Notes:** Standard 12-factor approach; no loader ambiguity.
---
### Q2: Both keys required, or derive public from private at startup?
| Option | Description | Selected |
|--------|-------------|----------|
| Both required env vars | Clean separation; workers can hold public key only | ✓ |
| Private key only — derive public | Fewer env vars but more magic in config.py | |
**User's choice:** Both required env vars
---
### Q3: What happens to SECRET_KEY after ES256?
| Option | Description | Selected |
|--------|-------------|----------|
| Remove from JWT code | SECRET_KEY no longer used for signing; stays in env for future HMAC | ✓ |
| Repurpose as HMAC key for Phase 7.4 | Forward-compatible but couples phases | |
**User's choice:** Remove SECRET_KEY from JWT code
---
## Startup Refresh-Token Rotation
### Q1: How to implement startup rotation?
| Option | Description | Selected |
|--------|-------------|----------|
| DELETE all refresh_tokens | Simple, one-shot; no schema change | |
| Bulk revoke (set revoked=True) | Cleaner audit trail; same user impact | ✓ |
| Let natural expiry handle it | Doesn't satisfy ROADMAP requirement | |
**User's choice:** Bulk revoke (set revoked=True)
---
### Q2: How to detect "first boot after ES256 migration"?
| Option | Description | Selected |
|--------|-------------|----------|
| Check system_settings table | Stores `jwt_algorithm`; idempotent on restart | ✓ |
| New env var JWT_TOKEN_GENERATION=N | Simple but requires operator bump on deploy | |
| One-time Alembic migration | Runs at migrate time, not startup | |
**User's choice:** Check system_settings table (`jwt_algorithm` key)
---
## Key Generation Workflow
### Q1: How should developers generate the key pair?
| Option | Description | Selected |
|--------|-------------|----------|
| Python one-liner in README | Uses existing `cryptography` dep; no extra tooling | ✓ |
| openssl CLI command | Familiar to ops but multi-step, requires openssl | |
| Auto-generate on first boot | Convenient for dev; dangerous in prod (ephemeral keys) | |
**User's choice:** Python one-liner documented in README
---
### Q2: docker-compose key placeholder vs. .env reference?
| Option | Description | Selected |
|--------|-------------|----------|
| .env file reference only | Follows existing ${SECRET_KEY} pattern | ✓ |
| Placeholder comment in docker-compose | Helpful but could mislead | |
**User's choice:** .env file reference only
---
## JWT TTL / Remember Me
### Q1 (freeform): What token should default to 12 hours?
User note: "JWT Tokens should only be valid for 12 hours and the user can opt-in to create a token which is valid for 30 days."
Asked for clarification on whether this applied to the access token or refresh token. User asked: "What do you think is the most secure way to handle tokens?"
**Claude's recommendation:** Keep access token at 15 min (CLAUDE.md requirement). Change refresh token default to 1624 hours with explicit "remember me" opt-in for 30 days.
**User's decision:** 16 hours — covers a full workday; guarantees session is revoked overnight.
---
### Q2: Fold "remember me" into Phase 7.3 or separate phase?
| Option | Description | Selected |
|--------|-------------|----------|
| Fold into Phase 7.3 | Already touching token creation code; incremental work | ✓ |
| Separate phase 7.5 | Keeps 7.3 purely as ES256 upgrade | |
**User's choice:** Fold into Phase 7.3
---
## Claude's Discretion
- PyJWT API form: `jwt.encode(payload, pem_str, algorithm="ES256")` — PEM strings passed directly, no key object conversion needed
- Bulk revoke via raw SQL `UPDATE` rather than ORM row-by-row — efficiency decision
- "Stay signed in for 30 days" as checkbox label (clearer than "Remember me")
- system_settings key name: `jwt_algorithm`, value `"ES256"`
## Deferred Ideas
- **Phase 7.4:** Token fingerprinting / token binding (`fgp` claim = HMAC of User-Agent + Accept-Language)
- **Key rotation ceremony:** Dual-key overlap process for rotating P-256 keys in production — deferred until production hardening milestone