Bridges help users connect to external content (RSS, ActivityPub) but
are not real Org Social accounts. They no longer appear in /feeds/,
cannot be registered via POST, are skipped by both feed discovery
tasks, and do not count in /stats/. A data migration removes bridge
URLs already registered as feeds.
Expose external accounts as virtual Org Social feeds that any client
can follow with a plain #+FOLLOW: line:
- /bridge/activitypub/@{user}@{instance}/ bridges a Mastodon or any
ActivityPub account (WebFinger, actor and paginated outbox; only
public top-level notes, with CW, hashtags, language and attachments)
- /bridge/rss/?url={feed} bridges any RSS/Atom feed (entry title as
*** sub-heading, converted body and link to the original article)
Registration is implicit on first GET. Bridged data is stored in the
existing Profile/Post tables so /profile/, /search/ and the rest of
the API work on bridged feeds. Active bridges are refreshed every 15
minutes; bridges unrequested for 90 days are cleaned up.
Remote HTML is converted to Org text, escaping headline-like lines so
external content cannot inject posts. Fetches enforce the Webmention
SSRF protections, a 10s timeout and a 5MB size cap. Bridged posts
never queue Webmentions nor publish notifications.
Implements the sender side of https://www.w3.org/TR/webmention/. During
feed scans, external URLs found in new posts (and links added by edits)
are queued as OutgoingWebmention rows; a periodic task discovers each
target's endpoint and delivers the notification. The unique
(source, target) pair guarantees a webmention is sent at most once, no
matter how many times a feed is rescanned. Targets without an endpoint
are marked permanently, failures retry with exponential backoff, and
endpoints resolving to loopback/private addresses are rejected.
Receiving webmentions is out of scope: plain text feeds cannot
advertise an endpoint.
- Drop malformed #+BIRTHDAY values (not YYYY-MM-DD) instead of letting
them reach the Profile DateField and abort the whole feed scan
- Use a (connect, read) timeout of (3.05, 5) when fetching feeds so dead
hosts are dropped faster without penalizing slow-but-alive servers
- Add parser and scan_feeds regression tests
Sync generators blocked uvicorn's event loop preventing chunks from
being flushed. Converts to async generators with redis.asyncio so
streaming works correctly under ASGI. Adds /sse/ nginx location with
proxy_buffering off and 1h read timeout.
Without ?feed=, the endpoint subscribes to all notification channels via
Redis psubscribe("notifications:*") and adds target_feed to each event.
With ?feed=, behavior is unchanged. Updates README and root _links.
New GET /profile/?feed={url} endpoint that returns the list of feed URLs
that follow the given profile, using the existing Follow model.
Includes cache support, 400/404 error handling, and 8 unit tests.
Added test coverage for:
- Basic 301 redirect detection and parsing
- Feed validation with redirects
- Feed URL updates when only old URL exists
- Feed merging when both old and new URLs exist
- Mention migration without UNIQUE constraint errors
The mention migration test specifically validates the fix for the
production bug where feeds with 301 redirects were failing due to
duplicate (post, mentioned_profile) combinations.
All 23 parser tests pass successfully.
The feed redirect merge logic was attempting a bulk update of mentions,
which failed when duplicate (post, mentioned_profile) combinations existed.
This caused feeds with 301 redirects to fail merging properly.
Now mentions are migrated individually, checking for existing duplicates
before updating, similar to how Follow and PollVote relationships are handled.
This fixes the UNIQUE constraint errors seen in production for feeds like:
- thesolarprincess.github.io -> thesolarprincess.site
- haiverin.scot -> www.haiverin.scot
- teoten.com -> www.teoten.com
New features from Org Social v1.6:
- Add LOCATION, BIRTHDAY, LANGUAGE, PINNED fields to Profile model
- Support post ID in header (** 2025-05-01T12:00:00+0100)
- Header ID takes priority over property drawer ID
- Parse and store all new v1.6 metadata fields
Changes:
- Updated Profile model with 4 new fields
- Updated parser to extract new metadata fields
- Updated parser to support ID in post headers
- Updated tasks.py to save new profile fields
- Added database migration 0010
- Added 3 new tests for v1.6 features
- Renamed SKILL.md to CLAUDE.MD
All tests passing (58/58)
The SSE endpoint was using pubsub.listen() which blocks indefinitely
waiting for messages. This prevented heartbeats from being sent when
there was no activity, causing connections to timeout and close.
Changed to use pubsub.get_message(timeout=1) in a while loop, which
allows heartbeats to be sent every 30 seconds even when there are no
new notifications, keeping connections alive.
This fixes the issue where SSE connections would break and users
wouldn't receive real-time notifications.
When merging duplicate feeds with redirects, Follow relationships
can generate UNIQUE constraint errors if the relationship already
exists between the same profiles.
This fix:
- Iterates Follow relationships individually
- Checks for existing relationships before updating
- Deletes duplicates safely
- Adds comprehensive error handling
Similar approach to PollVote fix.
Resolves issue with feeds like haiverin.scot redirect.
Signed-off-by: Andros Fenollosa <hi@andros.dev>
This commit implements automatic cleanup of feeds that haven't been successfully fetched in 3 days.
Changes:
- Add last_successful_fetch field to Feed model to track when feeds are successfully fetched (HTTP 200)
- Update parse_org_social() and validate_org_social_feed() to record successful fetches
- Add new periodic task cleanup_stale_feeds() that runs every 3 days and deletes inactive feeds
- Add comprehensive test suite (9 tests, all passing)
- Migration safely handles existing feeds by setting initial last_successful_fetch to protect them
Safety features:
- Feeds with last_successful_fetch = NULL are never deleted (protects legacy feeds)
- Only feeds older than 3 days are deleted
- Extensive logging of cleanup operations
- Initial migration sets timestamp for all existing feeds to prevent accidental deletion
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit introduces three new REST API endpoints to the Org Social Relay:
1. /reactions/ - Retrieves reactions (posts with mood emojis) to a user's posts
2. /replies-to/ - Gets direct replies to a user's posts (excluding reactions and poll votes)
3. /notifications/ - Unified endpoint combining mentions, reactions, and replies with optional type filtering
Implementation details:
- All endpoints follow the existing API pattern with caching, validation, and error handling
- Comprehensive test suites with 35 passing tests
- Updated README with full documentation and examples
- Integrated into Django settings and URL configuration
- Code formatted with Ruff
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Changed tag search from substring match to exact word match
- Tags like 'emacs' now won't match 'notemacs' or 'emacs-lisp'
- Search remains case insensitive
- Added tests for exact word matching and case insensitivity
- Add group field to Post model
- Store group name directly in posts during feed scanning
- Filter group messages by post.group field
- Remove GroupMember dependency from views
- Update tests to use direct group field
- Simpler and more efficient approach
🤖 Generated with [Claude Code](https://claude.com/claude-code)