Changelog¶
All notable changes to pachifast are documented here. The format follows Keep a Changelog and the project uses semantic versioning (while the major version is 0, minor releases may contain breaking changes).
[Unreleased]¶
Changed¶
- New logo (an elephant in a Viking helmet):
docs/assets/logo.svgandfavicon.svg.
[0.15.0] - 2026-10-12¶
Added¶
pachifast.auth.profile.register_profile(): your own columns on the user, in a one-to-one table of the project.GET /auth/usershows aprofileandPATCH /auth/useredits it (validated by your schema;422 user.profile_not_configuredwhen none is registered). The user table of the core stays fixed.AppModule.requires_pachifast: a specifier of the pachifast versions a module works with, checked at startup with a message that names the module. Extension packages are namedpachifast-<name>(see Extending the core).db_pool_size,db_max_overflow,db_pool_timeoutanddb_pool_recycle: the connection pool of PostgreSQL, per process (the values used to be fixed at 5, 10, 30 and 1800).pachifast audit purge [--days N] [--dry-run]andactivity_log_retention_days: delete the activity log rows older than the retention.- An index on
activity_log (user_id, timestamp). An existing project generates one migration for it (pachifast migrations revision --target-name auth --autogenerate -m "activity log index"). AppModule.setup_hooks: functions called with the app while it is built, to add a middleware or an exception handler (a startup hook is too late for that).- New dependency:
packaging.
[0.14.0] - 2026-10-12¶
Added¶
pachifast.signals: lifecycle hooks a project connects to (USER_CREATED,USER_LOGGED_IN,LOGIN_FAILED,PASSWORD_CHANGED,USER_LOGGED_OUT). Handlers are functions or coroutine functions; an error in one is logged and does not touch the request. See Extending the core.pachifast.auth.permission_provider.set_permission_provider(): a project says where the permissions of a user come from (routepermissions=[...], the model admin andGET /auth/useruse it). The default is still the database.
[0.13.0] - 2026-10-12¶
Added¶
GET /api/health/live(liveness: never touches the database) andGET /api/health/ready(readiness:503 health.not_readywhen the database does not answer within 3 seconds).GET /api/health/is unchanged, and now logs why the database cannot be reached.- A request id: every response has an
X-Request-IDheader and the log lines of the request carry it (request_id_enabled, on by default; a sane id sent by the client is kept). log_format: json, one JSON object per log line for a log collector.
[0.12.0] - 2026-10-12¶
Added¶
- CI: Python 3.11 to 3.14, a
lowest-directjob (the oldest allowed dependencies) and a coverage threshold of 85%. - Tests for the module loader, the discovery helpers, the health route, the permissioned router and the migration runtime (coverage 90% to 93%).
- Documentation: a Stability and support page (what is stable, versioning, deprecation policy, supported Python, SQLAlchemy and databases), Upgrading with one section per version from 0.7,
CONTRIBUTING.md, the install command for a git tag in both READMEs, a Without the CLI section in Getting started and the 90-day disclosure term inSECURITY.md.
Changed¶
- The oldest allowed
pydanticis now 2.11.0: with 2.10 a superuser was answered403on the permissioned routes.
Removed¶
pachifast.admin.cruds, an unused duplicate ofpachifast.auth.cruds.get_users.
[0.11.0] - 2026-10-12¶
Added¶
pachifast.__version__and thepy.typedmarker: the package already declaredTyping :: Typed, but type checkers ignored its hints without the marker.mfa_send_cooldown_seconds(default 30): a user waits that long between two codes sent by/mfa/email/setupand/mfa/sms/setup(429 auth.rate_limited). The two enrolment routes and theirconfirmroutes also count againstauth_rate_limit.Cache.incr(key, ttl), an atomic counter with expiry (in the memory cache and in Redis). A custom cache backend must implement it.
Changed¶
- Breaking:
GET /admin/audit/logsandGET /admin/usersanswer page 1 of 25 whenpageandpageSizeare missing, and at most 200 rows per page (they returned the whole table).paginate()itself is unchanged: the lists of your own routes stay unbounded unless you callbound_page_params(). - Breaking: only
pachifast.yaml/pachifast.ymlare read as the configuration file.config.yaml,config.ymland<directory>.yamlare not any more: a file of another tool in the same directory (say aconfig.yamlwithdebug: true) was taken for settings. UsePACHIFAST_CONFIG_FILEfor another name. - Breaking:
phone_numberof/mfa/sms/setupmust be an international number (+and 7 to 15 digits; spaces, dots, dashes and brackets are dropped). Anything else is422. ALGORITHMacceptsHS256,HS384orHS512; the token lifetimes must be at least 1.- Argon2 hashing (creating or updating a user, the backup codes, the email and SMS codes) runs in a thread, so a burst of registrations or enrolments does not stop the event loop.
- SQLite connections enable
foreign_keys(soON DELETE CASCADEworks),busy_timeout(5 s instead of "database is locked") and, for a file,journal_mode=WAL. - The rate limit of
auth_rate_limitis one atomic counter per address and window (it used one cache entry per allowed request), and an IPv6 client counts as its /64 network. POST /auth/logoutcloses the WebSocket connections of the user.- The activity log skips requests to paths that match no route (404, 405): a client that picks random paths no longer fills the table. A failed login is still logged.
swagger_noauthrefuses a request that carries proxy headers (X-Forwarded-For,Forwarded, ...): behind a reverse proxy on the same machine the peer is always the loopback.
Fixed¶
- The email and SMS code of
POST /mfa/verifyis claimed atomically: two parallel requests with the same correct code no longer both get a session.
Removed¶
SnakeCaseRouteandpachifast.utils.requests, unused and logging whole request bodies when switched on.
[0.10.1] - 2026-10-12¶
Fixed¶
- A revision generated for the encrypted TOTP column says
sa.String(length=255)instead of naming the pachifast class, so it does not depend on an import that the generated script did not have.
Changed¶
- The documentation shows the version: in the footer and on the home page of the site, under the title of the reference pages and in
README.md,README.it.mdanddocs/index.md. It is read frompyproject.toml(a MkDocs hook for the site,scripts/gen_reference.pyfor the markdown files, which a test keeps in step).
[0.10.0] - 2026-10-12¶
Added¶
PATCH /auth/user: a user edits their ownfirstName,lastNameandemail(never privileges or the username). Changing the email needscurrentPassword, because the email receives the codes of the email MFA method.- The TOTP secrets are encrypted at rest (
mfa_config.totp_secret, Fernet, key derived with HKDF fromMFA_ENCRYPTION_KEY, or fromSECRET_KEYwhen it is empty). New dependency:cryptography. The column is now 255 characters wide: an existing project needs one migration (pachifast migrations revision --target-name auth --autogenerate ..., see Upgrading from 0.9 in the authentication guide), thenpachifast users encrypt-mfa-secretsencrypts the secrets saved as plain text. Plain values keep working until then.examples/demohas the migration. SECURITY.mdhas a contact address and the list of known limitations.
Changed¶
- Breaking: a password that breaks the policy is
422onPOST /auth/registerand on the admin routes (it was400), like onPOST /auth/user/password. The code staysauth.password_too_short; it points atbody.passwordon registration.
[0.9.0] - 2026-10-12¶
Changed¶
- Breaking: the default
hostis127.0.0.1(it was0.0.0.0), forpachifast devandpachifast serve, and the generatedpachifast.yamlsays so. Reaching the server from another machine now takes--host 0.0.0.0,PACHIFAST_HOST=0.0.0.0orhost: 0.0.0.0; the container image and the generated pipelines already setPACHIFAST_HOST=0.0.0.0. - Changing the password to the current one is refused:
422 auth.password_unchanged.
Fixed¶
POST /auth/registertakes the time of one password hash also when the username exists, so the response time no longer tells which usernames are taken.
[0.8.0] - 2026-10-12¶
Added¶
docs_enabled(defaulttrue):falseremoves the Swagger UI and every OpenAPI schema, so a public API does not publish its route list.auth_rate_limitandauth_rate_window_seconds: a limit of requests per client address onPOST /auth/token,POST /auth/registerandPOST /mfa/verify(429 auth.rate_limited, withRetry-After). Off by default; it counts in the shared cache, so usecache_urlwith several workers.
Fixed¶
GET /admin/audit/logsnow applies the defaultactivity_log_exclude_paths(refresh-token) like/activity, as the guide always said;excludePaths=(empty) shows everything.- The text search of the lists and of the model admin, and the
path_containsfilter of the activity log, treat%and_as plain characters. - The model admin refuses at registration a column in
excludethat is also inlist_display,search_fields,list_filter,ordering,order_fields,readonly_fieldsorlabel_field; alabel_fieldthat is not a column is refused too. - A slow WebSocket client no longer holds up the event broadcast to the others (events go out in parallel, a client has 5 seconds to take one).
- Fire-and-forget tasks (login event, activity log) are kept alive until they end.
- Without an authentication database the
503has a fixed message (db.not_configured); the reason goes to the log. - A startup warning when
swagger_noauthorglobal_noauthis on withoutdebug.
Tests¶
- New suites for MFA, audit and authentication (about 120 tests): coverage of the package went from 80% to 89% (
mfa88-100%,audit91-100%,auth95-100%).pytest-covis in the development group and the coverage configuration follows the greenlets and threads of SQLAlchemy, sopytest --cov=pachifastgives true numbers.
[0.7.2] - 2026-10-12¶
Added¶
- SMS one-time codes are back (they were removed in 0.7.1):
POST /mfa/sms/setupsends the code to the number and the method is active only afterPOST /mfa/sms/confirmreceives it (400 mfa.sms_code_invalid,400 mfa.no_sms_setup), like the email method.MfaSenderAdapter.send_sms_otpis the hook for the provider. Thephone_numbercolumn never left the table, so accounts that were set up with SMS before 0.7.1 keep working.
Changed¶
- The CI workflow runs lint and the tests on Python 3.11 and 3.13 with PostgreSQL, plus an optional run on SQLAlchemy 2.1.
[0.7.1] - 2026-10-12¶
Security¶
- MFA is not enrolled while it is switched off.
POST /mfa/{totp,email}/setupanswer403 mfa.disabledunlessMFA_ENABLED=true: before, a user could enrol a second factor that the login never asked for. - Email OTP needs a confirmed address.
POST /mfa/email/setupsends a code and leaves the method inactive untilPOST /mfa/email/confirmreceives it (400 mfa.email_code_invalid,400 mfa.no_email_setup). Before, a stolen access token could turn the second factor on with any address.
Fixed¶
- Two refreshes with the same token at the same moment (two browser tabs) no longer revoke the whole login: a second use within
refresh_reuse_grace_seconds(default 10,0restores the old behaviour) is refused with401, the login survives. - A duplicate or a dangling reference in the admin of groups, permissions and users answers
409 db.conflict, and a value the database refuses answers400 db.invalid_value, instead of500. Names, descriptions and codenames now have amax_lengthin their schemas. - Alembic
path_separatordeprecation warning of the migration runner. - Broken anchor of the private index section in the publishing guide.
Changed¶
- Python 3.11 or later is required (the scaffold already used
tomllib, which does not exist on 3.10). - The generated
pyproject.tomlrequirespachifast>=0.7.0. - The dependencies
fastapi,fastapi-pagination,uvicornandtyperhave an upper bound below1.0;orjson(never imported) is gone andhttpxmoved to the development group. pyproject.tomlhas[project.urls]; the source distribution holds only the sources, the README files, the license, the changelog and the demo.
Removed¶
- Breaking: SMS one-time codes:
POST /mfa/sms/setup,MfaSenderAdapter.send_sms_otpand thesmsmethod. It may come back as a separate package; until then a project can add it itself. Accounts that were set up with SMS must be reset by an administrator (delete theirmfa_configrow); thephone_numbercolumn stays in the table so no migration is needed. - Breaking: SQL Server support: the
pachifast[mssql]extra,pachifast.config.normalize_odbc18sql_url, themssql+aioodbcdriver mapping and the SQL Server label in the engine info. It may come back as a separate package; until then a project can wire the driver itself. - The stale MkDocs build
site/is no longer tracked.
[0.7.0] - 2026-10-08¶
Added¶
examples/demo: a complete, self-contained demo backend (core, MFA, model admin, an app module with its own tables and migrations, uploads, bulk actions, per-user settings). It is exercised by the test suite.
Changed¶
- Breaking:
registration_enablednow defaults tofalse:POST /auth/registeranswers403 auth.registration_disabledunlessPACHIFAST_REGISTRATION_ENABLED=true. A project that relies on self-service sign-up must set it.
Removed¶
- Settings
admin_emailanddata_path: the core never used them. A project that needs them declares them in its own settings (the.envvalues are still read, because the project settings accept extra variables).
[0.6.1] - 2026-10-07¶
Fixed¶
- Model admin schema: with SQLAlchemy 2.1 the
defaultof every field of a model was5(the internal markerDONT_SET) instead of the real default of the column.
[0.6.0] - 2026-10-07¶
Added¶
- The user settings schema (
GET /auth/user/settings/schema) gives every field that is not required itsdefault, so a frontend can reset a form without copying the defaults.
Changed¶
- Gitea workflow generated by
pachifast new pipeline: the private index credentials fall back toREGISTRY_USERNAME/REGISTRY_TOKEN, and the optional variablesBUILD_ADD_HOSTSandBUILDX_DRIVERhandle registries with custom host names or a private certificate authority.
[0.5.0] - 2026-10-07¶
Added¶
- Model admin: change history per row (
GET .../{pk}/history, recorded with the change in one transaction; tablepachifast_admin_change, migration targetadmin), bulk actions (@admin_action, built-indelete_selected,POST .../_actions/{name}),component: "lookup"for foreign keys to registered models,actionsandhistoryin the schema,pachifast locale admin, error codeadmin.action_not_found. - Model admin lists filter by range on numbers, dates and times (
<column>__gte,<column>__lte), datetimes with an offset are stored as UTC in columns without time zone, error locations always use the schema's camelCase names anddelete_selectedanswers a message.
[0.4.0] - 2026-10-07¶
Added¶
- Generic model admin (optional
AdminModule):@register_admin(Model)on aModelAdmingives list (search, filters, sorting, paging), detail, create, update and delete routes under/api/admin/models/{app}/{model}, a_schemafor generic frontends, one permission per action (<app>.<model>.view|add|change|delete, created at startup) and writes in the activity log. See the Model admin guide. GET /admin/models/{app}/{model}/_options(value/label pairs for selects) andtype,default,primaryKeyTypein the admin schema.- Error codes
admin.model_not_found,admin.object_not_foundandadmin.integrity_error.
[0.3.0] - 2026-10-07¶
Added¶
- New built-in settings section
appearance(theme,accent,density,fontScale,reduceMotion) and newpreferencesfieldslocale,timezone,dateFormatandtablePageSize. ui_field(..., apply="theme")names an applier the frontend runs with the value; exposed in the settings schema asapply.- A
Literalsetting is described in the schema as aselectwith itsoptions(withoptionKeys),ge/leasmin/maxof anumber. PATCH /auth/user/settingssets several values at once, all or nothing.
Changed¶
- Breaking:
preferences.theme(thetheme-togglecomponent) is removed in favour ofappearance.theme. - Setting values are validated against the field: a refused value answers
422 user.settings_value_invalid(loc: ["body", "value"]);PUTalso accepts decimal numbers andnull. A stored value that no longer validates reads as the default.
[0.2.2] - 2026-10-07¶
Fixed¶
POST /auth/user/password,POST /mfa/disableandPOST /mfa/backup-codes/regenerateanswered400while the account was locked: they now answer429withRetry-After(new codeauth.too_many_attempts,mfa.too_many_attemptsfor the MFA routes) so a client can show a countdown. The login keeps its uniform401.
[0.2.1] - 2026-10-07¶
Fixed¶
POST /mfa/verifyanswered inside the envelope while/auth/tokendoes not: both login endpoints now answer the OAuth2 token shape (wrap_auth_token_responsewraps both). A client that unwraps by shape is not affected.- The last accepted TOTP step is stored on the MFA row (
last_totp_step), not in the cache: a code cannot be replayed through another worker or after a restart, and an older step is refused too. A migration is needed (one nullable column; autogenerate proposesadd_column). - Argon2 checks of the MFA step (one-time codes, up to eight backup codes) run in a thread pool instead of blocking the event loop.
pachifast userschecks the password againstpassword_min_length(it had a fixed minimum of 8).- The core tables created at startup come from the core model modules, so a new core model cannot be forgotten (the refresh token table had been).
Added¶
Retry-After(seconds until the account is unlocked) on the429of a locked account at the second step of the login; the CORS middleware exposes it.registration_enabled(defaulttrue):falseanswers403 auth.registration_disabledonPOST /auth/register.
Changed¶
init_dbmoved topachifast.startup(the core module's startup hook already points there);pachifast.databaseno longer imports the feature packages, so the list of known layering violations is empty.
[0.2.0] - 2026-10-06¶
Breaking¶
- SQLModel is gone. Models are SQLAlchemy 2.0 ORM classes on
pachifast.database.Base(keyword-only dataclasses, so Python defaults apply at creation).AliasedSqlModelis removed;AliasedBaseModelstays and now reads ORM rows (from_attributes=True). The core tables keep the same columns and types (identical DDL on SQLite and PostgreSQL); the only schema change is the foreign keys listed under Fixed. Upgrading an application: - Rewrite the models with
Mapped[...]andmapped_column()(see the Models section of the database guide). Integer auto-increment keys:mapped_column(primary_key=True, default=None, nullable=False). - Return Pydantic schemas (
Schema.model_validate(row)) instead of table models; a model cannot be aresponse_modelany more. from sqlmodel import selectbecomesfrom sqlalchemy import select;col(x)is justx.- Existing migration files that contain
import sqlmodelandsqlmodel.sql.sqltypes.AutoString(...): runpachifast migrations strip-sqlmodel(new). New migrations no longer import it. - Step by step: the Upgrading from 0.1.x guide in the documentation.
- Database drivers are extras.
asyncpgmoved topachifast[postgres];aiosqlitestays a core dependency. PostgreSQL users must installpachifast[postgres].
Security¶
Fixes from a review against OWASP ASVS 4.0.3 level 1 (each one has a test in tests/test_security_hardening.py):
- Lockout is atomic. Attempts are counted before the password is checked, in one
UPDATE ... RETURNING, and refused in the same statement while the account is locked. Before, parallel requests could overwrite each other's counter and a burst could run far more password checks thanlogin_max_attempts(a correct password was accepted after 20 wrong ones in a reproduction). A locked account now costs the same time as an unknown one. Password checks run in a thread pool, off the event loop. - MFA brute force. Wrong codes now count on the account as well as on the challenge, the attempt is spent before the code is checked, and logging in again no longer resets the counter. The password step gives its attempt back only because the second factor is pending.
- TOTP codes and backup codes work once (the TOTP step is recorded in the cache; a backup code is consumed with a compare-and-set).
- MFA cannot be replaced with an access token alone.
POST /mfa/{totp,email,sms}/setupanswer409 mfa.already_enabledwhile MFA is active;POST /mfa/disable(password) is the only way. Disabling, regenerating backup codes and the new change-password route use the same counted password check. - The one-time code of an email or SMS login is stored before the response (it used to be created in a task that could lose the race with the client or outlive the database session).
swagger_noauthno longer trusts anyReferercontaining/docs: it needs a loopback client and the docs page as the path of the referer.get_current_user_optionalapplies the revocation and active-user checks likeget_current_user;get_current_ws_userhonoursevents_allow_query_token.orderByaccepts only mapped columns (orderBy=metadatawas a 500) and the user list cannot sort by the password hash or the lockout counters (order_fieldson the params class).POST /mfa/totp/confirmanswered 500 after enabling MFA (missingokfield).- Refresh tokens are recorded, single use and revocable.
POST /auth/refresh-tokenreturns a new refresh token and spends the presented one; presenting a spent token again revokes the whole login (reuse detection);POST /auth/logoutrevokes one login (body{"refresh_token"}) or all of them (no body); a password change or a deactivation revokes them all. New tablerefresh_token(a migration is needed) and settingrefresh_token_rotation(defaulttrue). Tokens issued by 0.1.x are refused: log in again once. Before, logout revoked nothing and a refresh token could be reused for 7 days.
Changed (defaults)¶
password_min_lengthis12(was8) andevents_allow_query_tokenisfalse(wastrue): existing deployments that rely on the old values must set them explicitly.
Fixed¶
- The foreign keys of
group_permission,user_groupanduser_permission(group and permission columns) were never created because of a declaration mistake. They are now declared withON DELETE CASCADE; existing databases need one migration (see Upgrading from 0.1.x).
Added¶
POST /auth/user/password(currentPassword,newPassword,204): change your own password. Tokens issued before the change stop working.pachifast migrations strip-sqlmodelto clean the revisions generated by 0.1.x; SQLite migrations are rendered in batch mode, soALTER-style changes work there.- Synchronous sessions:
get_sync_engine_from_url,get_sync_sessionmaker_from_url,sync_session_dependency_from_url,to_sync_url; extrapachifast[postgres-sync](psycopg). paginate_sync()for synchronous SQLAlchemy sessions, andpagesin the list response.- Documentation site:
mkdocs.yml(Material), thedocsdependency group and a workflow that builds it and publishes it to GitHub Pages (.github/workflows/docs.yml), with a custom look: light and dark palettes, system fonts, a landing page.
Changed¶
- List queries (
paginate()) now delegate counting and paging tofastapi-pagination, which becomes a dependency. The query parameters and the{items, total, page, pageSize}response are unchanged; the list response gainspages.
Documentation¶
- Frontend integration: when
envelope_enabled=false, configure the client explicitly (QCLI_HTTP_ENVELOPE=false); shape-based unwrapping cannot tell a bare payload withsuccessanddatakeys from an envelope.
[0.1.8] - 2026-10-06¶
Changed¶
- The admin delete routes (user, permission, group) answer
204without a body instead of200with an enveloped message, likePOST /api/auth/logout.
Fixed¶
- The Redis cache no longer needs Redis 6.2: the atomic read-and-delete is a
MULTI/EXECtransaction instead ofGETDEL(found by running against a real, older Redis server).
[0.1.7] - 2026-10-06¶
Added¶
- Documentation in English: getting started, twelve guides and generated CLI, settings and message-code references;
README.it.md; link checks in the test suite. - Message codes
api.invalid_order_by(withparams.field) anduser.settings_key_invalid.
Changed¶
PUT /api/auth/user/settings/{category}/{key}accepts the key as camelCase (as in the schema) or snake_case; an unknown section or key is a422with a code instead of an unhandled error.- An invalid
orderByanswers422with the codeapi.invalid_order_byand a localized message (it was a fixed Italian text without a code).
Fixed¶
get_current_active_ws_user,ActiveWsUserDepandPermissionedRouter.websocket(permissions=...)failed with aTypeErrorbecause the core session dependency required an HTTP request.as_paginated_responseraisedUnboundLocalErrorwhenpageorpageSizewas missing.pachifast users update --passwordnow updatespassword_changed_at(revoking issued tokens) and lifts a lockout, likePATCH /api/admin/user/{id}.
[0.1.6] - 2026-10-05¶
Fixed¶
GET /api/versionreports the installed version instead of a placeholder.
[0.1.5] - 2026-10-05¶
Added¶
- One-time WebSocket tickets:
POST /api/core/events/ticket, then/api/core/events/ws?ticket=.... Tickets are stored in the auth database (only their SHA-256) or in the cache (events_ticket_store);events_allow_query_token=falserefuses the legacy?token=. pachifast.cache: a bounded in-memory cache or Redis (cache_url,cache_prefix,cache_max_entries, extrapachifast[redis]).- JSON Schema of
pachifast.yaml(pachifast config schema), also written to new projects. envelope_enabledsetting: turn the{success, message, data}envelope off for the whole application.ui_action(...): declarative actions in the user settings schema.- Documentation rewritten in English, with generated settings, CLI and message-code references.
Changed¶
- The response envelope only wraps 2xx responses; a JSON error returned directly by an endpoint is no longer wrapped
with
success: true. Contract: every 2xx hassuccess: true, every error is a non-2xx with{"detail": [...]}. ui_field(events=..., watch=...)accept action names, never JavaScript code (ValueErrorotherwise).- Activity log:
path,descriptionandusernameare stripped of control characters and truncated.
Fixed¶
- The
authmigration target includes the activity log, MFA and WebSocket ticket tables, which Alembic projects never created. - The activity-log "skip" cache no longer grows without bound.
- Pydantic V2 deprecation in
pop_non_fields_from_dict_by_model.
[0.1.4] - 2026-10-02¶
Added¶
pachifast locale export --checklists the messages that are out of date.
[0.1.3] - 2026-10-02¶
Added¶
- Translatable names of permissions and groups (
pachifast locale permissions), exported with the catalogs. - The generated Dockerfile checks the message catalogs.
[0.1.2] - 2026-10-02¶
Added¶
--ca-certand--index-namefor pipelines that use a package registry served with a private certificate authority.
Changed¶
- Application logs follow
debug/log_levelinstead of always logging at DEBUG.
[0.1.1] - 2026-10-01¶
Added¶
- Stable error codes,
paramsand localized API messages (en,it); per-module catalogs, project overrides,pachifast locale check|export|settings. POST /api/auth/logout, which emits alogoutevent.- WebSockets of deleted, deactivated or demoted users are closed (
4401/4403). pachifast users delete.- Gitea Actions pipeline generator and a private uv index for the image build.
Changed¶
debugis off by default.
Fixed¶
.envis found from the current directory at use time, not at import.- Events go to superusers and the user concerned only.
[0.1.0] - 2026-09-29¶
Added¶
- Initial release: modular FastAPI framework with
create_app, module registry, JWT authentication with RBAC, standard response envelope, multi-target Alembic migrations, audit log, events/WebSocket, MFA and thepachifastCLI (new,init,dev,serve,users,migrations).