Changelog¶
Unreleased¶
Removed Beads and Beadsflow project-tracking files and instructions
Added
BACKLOG.mdandDONE.mdas the repository-local work tracking workflowLinked the backlog workflow from the project documentation
Clarified that completed backlog items must keep docs and changelog entries aligned with implementation changes
Switched hook execution from
pre-committoprekand updated hook revisions for Python 3.14 compatibilityAdded a dedicated backlog page to the documentation navigation
Raised dependency floors and refreshed
uv.lockto resolve open Dependabot alerts for Django, pytest, requests, urllib3, sqlparse, Pygments, filelock, and virtualenvAdded
Token.expires_atplus theINDIEWEB_TOKEN_EXPIRES_INsetting (default 86400 seconds);TokenViewnow persists the expiration and reports the live remaining lifetime inexpires_inTokenAuthMixinnow rejects expired access tokens with HTTP 401; tokens issued before this change haveexpires_at=NULLand remain valid until reissuedFixed the IndieAuth auth-code timeout calculation to use
timedelta.total_seconds()so codes older than one day are now correctly rejectedValidated IndieAuth
redirect_urivalues: the authorization endpoint rejects malformed values (invalid URL, any#delimiter, userinfo, or a non-http/httpsscheme) with HTTP 400 before issuing a code, and the token endpoint rejects malformed submissions withinvalid_grant; the comparison between the submitted and stored values now lowercases scheme and host while preserving path and query verbatimFixed the consent approval and denial redirects to merge
code/state/me(orerror/state) into an existingredirect_uriquery string instead of breaking it with a duplicate?separatorAdded PKCE (RFC 7636) support to the IndieAuth authorization and token endpoints: the authorization endpoint accepts
code_challengeandcode_challenge_method(S256andplain, defaulting toplainwhen only the challenge is sent) and stores them on theAuthrow; the token endpoint requires a matchingcode_verifierwhenever a challenge was stored, comparing in constant time. Auth codes issued before this change continue to be redeemable without acode_verifier, preserving backwards compatibility for legacy clients. Migration0011_auth_code_challengeadds the new nullableAuth.code_challengeandAuth.code_challenge_methodcolumns.Added
client_idaccess control. The authorization endpoint (GET, consent POST, code-verification POST) and the token endpoint now reject malformedclient_idvalues (invalid URL, any#delimiter, userinfo, or a non-http/httpsscheme) with HTTP 400 before creating any state. A new optionalINDIEWEB_CLIENT_ID_VALIDATORsetting (dotted path to a(client_id: str) -> boolcallable) runs on top of structural validation at all four authorization/token paths and again on the Micropub resource-server path, so revoking a client takes effect immediately for previously-issued tokens. A misconfigured validator fails closed: the authorization paths return plain-text HTTP 400 (invalid client_idfor structural failures,invalid_clientfor validator failures), the token endpoint returns HTTP 400invalid_requestwith content typeapplication/x-www-form-urlencoded, and the Micropub resource server returns HTTP 403invalid_client. Storedclient_idvalues are not re-validated structurally on use (matching theredirect_urirule); the configured validator IS re-applied on use. Default behavior (setting unset) is unchanged: every structurally-validclient_idis permitted.Enforced per-operation scopes on the Micropub endpoint.
POSTentry create now requirescreate(the legacy aliaspostis still accepted);POST action=updaterequiresupdate;POST action=deleterequiresdelete;POST action=undeleterequiresundelete;GET ?q=sourcerequiresupdate.GET ?q=config,GET ?q=syndicate-to, andGETwith noqonly require an authenticated token (no scope gate). Storedscopeis split on whitespace and compared as an exact token, so values likecreateXYZno longer satisfycreate(previously a substring match). Scope failures still return HTTP 403 with the plain-text bodyauthorization error. Closes the previously over-permissive scope check that allowed acreate-only token to reach update/delete/undelete code paths.Normalized IndieAuth scope strings before consent display and auth-code storage by splitting on whitespace, removing duplicate tokens while preserving first-seen order, and joining with single spaces. Unknown scopes are intentionally preserved. The token endpoint now issues the stored auth-code scope and rejects any submitted
scopethat normalizes differently with HTTP 400invalid_grantand content typeapplication/x-www-form-urlencoded, without creating or reissuing a token.Implemented the Micropub source query on
GET /indieweb/micropub/?q=source&url=.... The query now uses the configuredMicropubContentHandler.get_entry(url, user)method after the existingupdatescope check, returns full source content as{"type": [...], "properties": {...}}, and supports the W3Cproperties[]=NAMEfilter form by returning only existing requested properties as{"properties": {...}}. Missing or unknownurlvalues return400 invalid_request; unexpected handler exceptions return500and are logged vialogger.exception. This completes the source-query gap left by the partial editing-support slice.Implemented the Micropub
update,delete, andundeleteactions onPOST /indieweb/micropub/by dispatching into the configuredMicropubContentHandler.POST action=updateis JSON-only (per Micropub §3.7); the body must include at least one ofreplace,add, ordeleteand values inside each operation must be arrays per §3.4 — empty bodies, scalar operation values, and non-conformantdeleteshapes are rejected with400 invalid_requestrather than papered over by the handler.POST action=deleteandPOST action=undeleteaccept either form-encoded or JSON bodies, both with a requiredurl. Successful updates and undeletes return204 No Content, or201 Createdwith aLocationheader when the configured handler relocates the entry; successful deletes return204 No Content(delete cannot relocate because the handler interface returnsNoneon delete). Unknown URLs (the handler raisesValueError), missingurl, malformed JSON, non-object JSON bodies, and form-encodedaction=updaterequests return400 invalid_request; unexpected handler exceptions return500(logged vialogger.exception). Per-operation scope enforcement and the existing403 authorization errorand403 invalid_clientshapes are unchanged.Hardened the Micropub endpoint against malformed JSON bodies. A
POSTwithContent-Type: application/jsonwhose body fails to parse (including invalid UTF-8 bytes such asb"\xff"that surface asUnicodeDecodeErrorrather thanjson.JSONDecodeError), or whose body parses to anything other than a JSON object, is now rejected with400 invalid_requestbefore scope or action dispatch. Previously such requests fell through to the create path and could silently create an empty entry when the token hadcreatescope, or escape as a500for invalid UTF-8.Webmention receive-side target verification now parses source-page
hrefattributes and compares common canonical URL variants instead of relying on rawhrefstring matching. Fragments are ignored; scheme/host case, a leadingwww., one non-root trailing slash, and query-parameter ordering are normalized for matching. Microformats2 target matching uses the same policy for reply/like/repost classification while storedWebmention.source_urlandWebmention.target_urlremain the submitted values.Webmention receive-side reprocessing now clears stale verification timestamps when a source can no longer be verified. Existing Webmentions whose source returns
410 Goneor whose HTML no longer links to the submitted target are markedfailedwithverified_at=NULLwhile preserving submitted URLs and previously parsed author/content fields. Other processor failure paths also clearverified_atwhen they mark a rowfailed, and spam reclassification clearsverified_atwhen a row is markedspam. A later valid source can be reprocessed back toverifiedwith a fresh timestamp.Webmention receive and send HTTP requests now follow redirects explicitly with a limit of 5 redirects and only continue to
http/httpsURLs. Receive-side source fetches parse the final response URL as the microformats2 base while preserving submitted source/target URLs; send-side endpoint discovery resolves relative endpoints against the final target page URL; source-content fetches follow the same policy; endpointPOSTdelivery preserves the Webmention form payload across followed redirects.Webmention receive-side author extraction now completes the local/same-page authorship fallback chain. Explicit
h-entryauthor data remains highest priority; URL-valued authors still resolve to matching same-pageh-carditems or fall back to URL-as-name when unmatched. Entries without explicit authors can now userel=authorlinks that point to anh-cardalready present in the fetched document, including same-page fragment references, and then a single unambiguous page-levelh-cardfallback. Relative author and photo URLs resolve against the final fetched source URL after redirects. Remote author-page fetching remains unsupported.Webmention receive-side author
h-cardlookup now uses the same conservative canonical URL matching policy already used for target verification. Explicit URL-valuedp-authorreferences and localrel=authorlinks can now match same-pageh-cardu-urlvalues across supported variants such as scheme/host case, a leadingwww., one non-root trailing slash, ignored fragments, and query-parameter ordering. Unmatched explicit author URLs still fall back to URL-as-name, and no remote author fetching was added.
0.5.3 (2025-10-28)¶
Fixed webmention author extraction when author is referenced as a URL string
Implemented partial microformats2 authorship algorithm to resolve author URLs to h-cards on the same page
Added recursive search for h-cards and h-entries in nested structures (e.g., h-feeds)
Fixed regression where missing h-cards would result in empty author names
Author URL now used as fallback name when matching h-card cannot be found
Previously displayed the author URL as the name when parsing feed.city-style webmentions
Now correctly extracts author name and photo from matching h-card on the page
Note: Does not yet fetch remote author URLs or follow rel=author links (full authorship algorithm)
Added a justfile with recipes for dependency install, testing, and type checking
0.5.2 (2025-07-27)¶
Fixed JSON copy/paste issue in Django admin for h_card field
Changed admin form to use CharField with custom widget instead of JSONField to prevent double-encoding
Added proper JSON formatting and validation in Profile admin interface
Added tests for admin JSON widget functionality
0.5.1 (2025-07-26)¶
Fixed h-card template to properly handle photo data from mf2py parser
Added automatic property name normalization (converts hyphens to underscores for Django template compatibility)
Added webmention integration to use local Profile data when author is a local user
Added automatic synchronization of Profile fields (name, photo_url, url) with h_card JSON data
Added URL and email validation for h_card data to prevent invalid data storage
Enhanced h_card normalization to properly handle nested objects
Added h_card structure validation in admin interface
Fixed all mypy type checking issues
Updated tests for consistency with implementation
0.5.0 (2025-07-25)¶
Added h-card support with Profile model for user profile data
Added flexible JSON storage for all h-card properties
Added h_card template tag for rendering h-card microformats
Added Profile admin interface with JSON editing support
Added h-card parsing and validation utilities
Added comprehensive test suite for h-card functionality
Updated documentation with h-card usage examples
0.4.3 (2025-07-11)¶
Fixed
webmention_counttemplate tag to always return integers for consistent template comparisonsPreviously returned string when used directly but integer when used with
asvariable assignmentAdded comprehensive tests for the webmention_count fix
0.4.2 (2025-07-10)¶
Added Django admin integration for Webmention, Token, and Auth models
Added comprehensive admin test suite
Webmention admin includes filters, search, and organized fieldsets for easy management
Token and Auth admin are read-only for security purposes
0.4.1 (2025-07-10)¶
Fixed Webmention endpoint to return Location header with HTTP 201 status per W3C specification
Added WebmentionStatusView to provide webmention status information at the Location URL
Fixed compatibility with webmention.rocks test suite
0.4.0 (2025-07-10)¶
MAJOR: Added complete Webmention support (W3C Recommendation compliance)
Added Webmention model for storing incoming and outgoing webmentions
Added WebmentionEndpoint view for receiving webmentions
Added WebmentionProcessor for validating and parsing webmentions with microformats2
Added WebmentionSender for discovering endpoints and sending webmentions
Added pluggable interfaces for URL resolution, spam checking, and comment integration
Added Django template tags for displaying webmentions (
webmentions_for,webmention_count,webmention_endpoint_link)Added management command
send_webmentionsfor sending webmentions from the command lineAdded comprehensive test suite for Webmention functionality (35 new tests)
Added detailed Webmention documentation with integration examples
Added CSS styling and templates for different webmention types (likes, reposts, replies, mentions)
Added Django signals for webmention processing (
webmention_received)Replaced requests library with httpx for better async support and HTTP/2 features
Updated dependencies to use httpx instead of requests
0.3.5 (2025-06-29)¶
Fixed Authorization header handling to check for HTTP_AUTHORIZATION (Django’s standard header format)
Maintained backward compatibility with test client Authorization format
Added tests to verify both Authorization header formats work correctly
0.3.4 (2025-06-29)¶
Fixed Token model unique constraint that prevented multiple clients from obtaining tokens for the same user
Removed incorrect unique=True from Token.me field (kept unique_together constraint)
0.3.3 (2025-06-29)¶
Fixed micropub authorization to accept “create” scope (standard Micropub) in addition to legacy “post” scope
Added debug logging for token authentication failures
0.3.2 (2025-06-29)¶
Fixed KeyError in TokenView when ‘me’ parameter is missing - the token endpoint now correctly handles optional parameters according to IndieAuth spec
Improved token endpoint error responses to use proper IndieAuth error codes (invalid_request, invalid_grant)
Added redirect_uri verification for enhanced security
Implemented one-time use of authorization codes to prevent replay attacks
Added proper content-type headers to token endpoint responses
0.3.1 (2025-06-28)¶
Added merge migration to resolve parallel migration branches
0.3.0 (2025-06-28)¶
MAJOR: Implemented fully functional Micropub endpoint with content creation
Added pluggable content handler system for Micropub integration
Added
MicropubContentHandlerabstract base class for custom implementationsAdded
InMemoryMicropubHandlerfor testing and developmentAdded support for both form-encoded and JSON Micropub requests
Implemented Micropub query endpoints (
?q=config,?q=syndicate-to)Added comprehensive test suite for Micropub functionality (19 new tests)
Added detailed Micropub documentation with integration examples
Added example content handlers demonstrating various integration patterns
Updated type hints to use modern Python syntax (
list,dictinstead ofList,Dict)BREAKING: Removed old Micropub property methods that were implementation details
Added comprehensive documentation for IndieAuth implementation including consent screen
Added test suite for IndieAuth consent screen functionality (14 new tests)
Fixed MyPy type errors in AuthView for better type safety
Updated development guidelines with “Definition of Done” criteria
0.2.0 (2025-06-16)¶
Fixed Read the Docs build by adding missing dependencies to docs/requirements.txt
Added coverage configuration to exclude migrations from coverage reports
Cleaned up duplicate documentation files (removed outdated .txt versions)
Added type annotations to models.py and views.py
Added mypy configuration with django-stubs for static type checking
Added documentation for running mypy in development.rst
Added comprehensive API reference documentation with examples
Added usage tutorial with client-side implementation examples
Added configuration guide documenting all settings and options
Added concepts documentation explaining IndieWeb protocols with Mermaid diagrams
Updated CONTRIBUTING.rst to reflect current development workflow (uv, ruff, pytest)
Documented Micropub handler architecture (in-memory default plus configurable handler via
INDIEWEB_MICROPUB_HANDLER)Converted all tests from unittest to pytest style
Added __str__ method to Token model
Added docstrings to all model and view classes
BREAKING: Removed unnecessary dependencies: - Replaced django-model-utils TimeStampedModel with explicit timestamp fields - Replaced django-braces AccessMixin with direct login redirect - Removed setuptools (not needed at runtime with modern packaging) - Replaced pytz with Python’s built-in datetime.timezone.utc
Package now only depends on Django itself
0.1.0 (2025-06-13)¶
Migrated from flit to uv build backend
Moved package from top-level to src layout
Replaced black, isort, and flake8 with ruff
Added Python 3.13 support
Dropped Python 3.9 support (minimum is now 3.10)
Updated pre-commit hooks
Consolidated dev dependencies into single group
Added comprehensive documentation with Sphinx and Furo theme
Updated documentation structure for Read the Docs
Fixed Django settings configuration for tests
0.0.8 (unreleased)¶
Development version (not released)
0.0.7 (2023-01-07)¶
Added migration for auto field
Updated pre-commit hooks
0.0.6 (2022-11-05)¶
Use flit and pyproject.toml instead of setup.py
Support recent Django versions
Even better package infrastructure
0.0.5 (2019-05-19)¶
Auth endpoint works with https://pin13.net/login/ o/
Use black for code formatting
Better package infrastructure
Require python >= 3.6
0.0.4 (2016-06-14)¶
exempt csrf checking
0.0.3 (2016-06-13)¶
added migrations
0.0.2 (2016-05-15)¶
Auth and Token endpoints with some tests.
0.0.1 (2016-05-14)¶
First release on PyPI.