All notable changes to this project will be documented in this file. The format is based on Keep a Changelog and the project adheres to Semantic Versioning.
- BREAKING:
SECRET_KEYis now required in every environment. The config validator rejects the literal"secret-key"placeholder and any value shorter than 32 characters. Generate one withpython -c "import secrets; print(secrets.token_urlsafe(32))". - BREAKING:
ADMIN_PASSWORDis now required in every environment. The validator rejects the literal"!Ch4ng3Th1sP4ssW0rd!"placeholder and any value shorter than 12 characters. - BREAKING:
CORS_ORIGINS,CORS_METHODS, andCORS_HEADERSno longer default to"*". The validator rejects"*"in non-LOCAL environments.allow_credentialsis only enabled when the origins list does not contain"*". - bcrypt now runs in a thread pool (
anyio.to_thread.run_sync) so it does not block the event loop during login or registration. - Passwords are pre-hashed with SHA-256 before bcrypt to prevent silent truncation of passwords longer than 72 bytes.
authenticate_userruns a dummy bcrypt verification when the username does not exist, so the response time does not leak user existence.Cache-Control: private, no-storeis now set on any response to a request that carried anAuthorizationheader, and on any error response. Previously every response waspublic, max-age=60.- The
POST /api/v1/userpassword field now has an explicitfield_validatorenforcing one lowercase, one uppercase, one digit, one special character, and 8–128 chars. The previous regex^.{8,}|[0-9]+|[A-Z]+|[a-z]+|[^a-zA-Z0-9]+$accepted"a"as valid. GET /api/v1/usersnow requires superuser.GET /api/v1/user/{username}now requires authentication. Both were previously public.CRUDAdminis now disabled by default and paired with a localhost-only IP allowlist when enabled. It previously defaulted to on with no allowlist.- The Dockerfile now ships a
.dockerignorethat excludes.env,.git,__pycache__, the local SQLite database, tests, and scripts from the build context. requirements.txtandrequirements-dev.txtare now split. The production Docker image no longer shipsruff,mypy,pytest, or other dev tools.python-multipartlower bound bumped to>=0.0.18to dodge the known CVE in older 0.0.x releases.
- BREAKING: The
POST /security-agents/{id}/execute-commandendpoint is gone. It was a fake:asyncio.sleep(5)followed by a hardcoded"Command executed successfully"result. The background task also captured a request-scopedAsyncSessionthat was already closed by the time the task woke up, so tasks stayed inPENDINGforever. Replaced byPOST /security-agents/{id}/command, a registry-only endpoint that creates aCommandTaskrow inPENDINGstate and explicitly does not execute anything. - BREAKING: The
RateLimiterstub (core/utils/rate_limit.py) is removed. It always returnedFalse, was never wired to any router, and was advertised in the README as a feature. - BREAKING: The
cache()decorator (core/utils/cache.py) is removed. Its docstring literally said "does nothing". It was advertised in the README as "Redis caching". - BREAKING: The
Postmodel,crud_posts,posts.pyrouter, andschemas/post.pyare removed. They were inherited from a social-media starter template and had no purpose in a security agent orchestrator. - BREAKING: The
RateLimitmodel,crud_rate_limit,rate_limits.pyrouter, andschemas/rate_limit.pyare removed. They were a dead config: nothing in the runtime read the rows. - BREAKING:
PostgresSettingsandMySQLSettingsare removed fromconfig.py. The runtime has always used SQLite; the Postgres and MySQL settings blocks were dead code advertised as "multi-DB support". - BREAKING: The
RedisCacheSettings,RedisQueueSettings, andRedisRateLimiterSettingsblocks are removed fromconfig.py. profile_image_urlcolumn removed from theUsermodel. It was inherited from a social-media starter template.cache_exceptions.pyremoved (only used by the removedcache.py).uuid>=1.30PyPI dependency removed (the stdlibuuidmodule is what the code actually imports).
tests/directory with 29 pytest tests covering config validators, password complexity, bcrypt pre-hash boundary, and the security-critical API flows (auth, registration, login, logout, cache-control, execute-command removal, RBAC).scripts/smoke_test.py— a single-process end-to-end smoke test..github/workflows/ci.yml— CI pipeline running ruff, pytest with coverage, pip-audit security scan, and a Docker build smoke test..dockerignore— excludes.env,.git,__pycache__, the local SQLite database, tests, and scripts from the build context.SECURITY.md,CONTRIBUTING.md,CHANGELOG.md.requirements-dev.txtfor dev dependencies.[tool.pytest.ini_options],[tool.coverage.run],[tool.coverage.report],[tool.mypy]blocks inpyproject.toml.- The lifespan now calls
await engine.dispose()on shutdown so worker reloads do not leak SQLAlchemy async sockets. docs_url,redoc_url, andopenapi_urlare now fully disabled in PRODUCTION (previously they were gated behind superuser auth but still mounted).
pyproject.tomlversion bumped from0.1.0to0.2.0to reflect the breaking changes.render.yamlCORS_ORIGINS changed from["*"]to the explicit demo URL. The validator would have rejected*in staging anyway.DockerfileCOPYtightened fromCOPY . .toCOPY src ./srcandCOPY alembic.ini ./alembic.ini.mccabemax-complexity reduced from 24 to 15.- README rewritten. All fabricated claims removed ("100% secure", "Production Ready", "99.9% uptime", "1000+ concurrent agents", "Rate limited", "Redis caching", "Sub-100ms command execution", "Enterprise-grade security"). Replaced with honest descriptions of what the service actually does and does not do.
src/scripts/create_first_tier.pywas broken at import time: it importedconfig(a python-decouple pattern) from a module that only exportssettings. Now usessettingsdirectly.src/scripts/create_first_superuser.pyimportedsqlalchemy.dialects.postgresql.UUID(the runtime is SQLite), redeclared the user table by hand with aTable()construct that included the now-removedprofile_image_urlcolumn, and used the deprecatedasyncio.get_event_loop(). Now uses theUserORM model directly,asyncio.run(), andget_password_hash_async.core/security.pyblacklist_tokenandblacklist_tokensno longer crash withJWTErrorwhen called with an already-expired token (the common case at logout if the access token is past its 30-minute window).core/security.pycreate_access_token/create_refresh_tokennow usedatetime.now(timezone.utc)consistently instead of mixingdatetime.now(UTC).replace(tzinfo=None).client_cache_middleware.pyno longer setsCache-Control: publicon authenticated responses.
Initial public release. Subsequently found to contain eight critical security issues and eleven fabricated README claims; see 0.2.0 above.