스쟝
The new timeline - an AI-assisted recommended timeline has arrived at SiliconBeest Software.
It is heavy, so (future) admins should look into details and after that to choose whichever turning on or off. It is NOT recommended to turn it on on every and all services, so that's why the default option is OFF.
This can change in the future. Under this line, is generated by AI
The recommendation pipeline
Each page follows the same high-level process:
Authenticate the user and verify that the feature and rate-limit guard are enabled. Build or restore a bounded interest query for the user. Retrieve a rolling window of recent, viewable candidate posts from D1. Normalize and sanitize candidate text before inference. Ask BGE-M3 to score every candidate against the interest query. Validate the model response, diversify the ranking, and return one page. Store short-lived continuation state so the next page remains stable and contains no duplicates.
flowchart LR A["Public activity and followed tags"] --> B["Interest query"] C["Recent public posts"] --> D["Visibility and relationship filters"] E["Home-feed-eligible posts"] --> D D --> F["Sanitized candidate contexts"] B --> G["BGE-M3 semantic scoring"] F --> G G --> H["Response validation and diversity rules"] H --> I["Recommended page"] I --> J["Account-bound continuation cursor"]
- Building the user interest query
Personalization is based on followed tags and a small history of eligible public activity. D1 keeps at most the latest 30 recommendation signals for each account:
posted: a public original status authored by the user reposted: a repost of a public original status liked: a like on a public original status
These signals are deliberately asymmetric. Authored posts have weight 3, reposts have weight 2, and likes have weight 1 when tags and languages are ranked. Posting or reposting is therefore treated as a stronger expression of interest than a lightweight reaction.
Private, unlisted, direct, deleted, and repost-wrapper statuses are not stored as activity-profile sources. When a recommendation is generated, retained activity records are joined back to the current status rows. A signal is used only if its source is still a public, non-deleted original; a posted signal must also still belong to the user. This live check prevents stale background data from leaking into the profile after a deletion or visibility change.
The query can contain:
up to 12 followed or activity-derived tags; up to 5 languages observed in public activity; counts of posted, reposted, and liked signals; and normalized snippets of up to 140 characters from recent public activity.
URLs, email addresses, and mentions are removed from these snippets. The final query is capped at 7,000 characters and explicitly instructs the model to favor relevant, informative, and varied posts without inventing interests that are not supported by the available signals.
This design also provides a useful cold-start behavior. A user with no recorded activity can still receive a ranking based on followed tags. If neither activity nor followed tags exists, the model receives only the general instruction to rank recent visible posts for relevance and variety.
- Constructing the candidate window
The ranker does not search the entire status database. For each page, D1 assembles a recent rolling window from two sources:
up to 250 recent public original statuses; and up to 100 recent statuses that could appear in the user's Home timeline.
After the two sources are merged, normalized, deduplicated, and permission-checked, at most 350 candidates are sent to the ranking stage. The user's own posts are eligible, but only the latest eligible original is admitted so that self-authored content cannot dominate the candidate pool.
Home candidates may include reposts. A repost wrapper is normalized to its original status so the model ranks the actual content rather than duplicate wrappers. Both the wrapper and the original must still satisfy the relevant Home-timeline, visibility, account-state, and relationship rules.
Direct statuses are always excluded. The remaining checks include:
status visibility and deletion state; suspended or silenced account state; follows and the show_reblogs preference; user mutes and blocks in both directions; domain blocks; and mention-based visibility where applicable.
These checks happen before text is sent to Workers AI. The model therefore receives only content that the requesting user is authorized to view. However, the candidate set is not necessarily public-only: an unlisted or followers-only post can be processed when it is genuinely eligible for that user's Home timeline. Operators should account for this behavior in their privacy notice before enabling the feature.
- Preparing model input
Each candidate becomes a compact text context containing its language, up to eight tags, and a normalized body. A context is limited to 400 characters. HTML is converted to plain text, whitespace is normalized, and obvious URLs, email addresses, and mentions are removed.
The default model is Cloudflare's multilingual BGE-M3 reranker. It receives one interest query and an ordered array of candidate contexts. Its response must contain one finite score for every candidate index.
This is semantic scoring rather than text generation. The model does not write posts, summaries, or explanations. It estimates how closely each candidate context matches the user's interest query.
- Ranking and diversification
Model output is treated as untrusted input. The response is accepted only when it is an exact permutation of the candidate set: every candidate index must appear once, all indexes must be in range, and every score must be finite. A partial, duplicated, malformed, or out-of-range response causes recommendation generation to fail explicitly.
Valid results are sorted by descending model score. SiliconBeest then applies two deterministic diversity mechanisms:
Local reshuffling. Candidates are grouped into relevance windows of three and reordered within each group using the page seed. A refresh can therefore feel different without allowing a low-scoring tail item to jump into the highest relevance group. Author diversity. The first two posts from an author remain in the primary ranking. Additional posts from the same author move to an overflow section after posts from less-represented authors.
The API returns 30 statuses by default and clamps an explicitly requested page size to the range of 1 through 40.
A simplified version of the ranking logic looks like this:
query = build_interest_query(followed_tags, latest_30_public_activities) window = fetch_public_candidates(250) + fetch_home_candidates(100) window = normalize_boosts_deduplicate_and_filter_permissions(window)
contexts = sanitize_and_truncate(window, 400_chars_each) scores = bge_m3(query, contexts) validate_exact_score_for_every_candidate(scores)
ranked = sort_by_score_descending(scores) ranked = seeded_shuffle_within_groups(ranked, group_size=3) ranked = move_third_and_later_posts_per_author_to_overflow(ranked) return ranked[0:page_limit]
- Stable pagination and refresh behavior
A fresh request fixes three properties for the lifetime of that recommendation session:
an upper time boundary; the generated interest query; and a random seed used for stable sampling and local diversity.
The fixed time boundary prevents posts created after the refresh from appearing halfway through the same feed. Returned status IDs are added to an exclusion set. On the next page, D1 excludes those IDs before rebuilding the candidate window, while unselected recent candidates remain eligible and older posts replenish the window. The 350-candidate limit is therefore a per-page ranking bound, not a total feed-length limit. Pagination can continue beyond 350 posts until no eligible candidates remain.
Continuation state is stored in KV for five minutes and is bound to the requesting account. The opaque cursor represents the fixed boundary, query, seed, page number, and displayed or invalidated IDs. Replaying a successfully generated cursor can return a memoized page without another model call. Candidate text and model scores are not stored as separate recommendation records.
Refreshing starts a new session with a new boundary and seed, clears the visible feed and its exclusion set, and rebuilds the interest query from the latest D1 activity history. It does not erase that history. The Recommended feed has no live streaming connection; newly created posts become eligible on a later refresh rather than being inserted into the current snapshot.
- API and client behavior
The endpoint is:
POST /api/v1/timelines/recommended
The request requires authentication and the read:statuses scope. A fresh request omits cursor; subsequent requests use the opaque cursor from the response's Link header. Both initial and continuation requests use POST so browser or proxy prefetching cannot accidentally trigger inference through an ordinary cacheable GET.
The frontend keeps Recommended in its own timeline store, route, and navigation destination. It may prefetch the next cursor page to make infinite scrolling smooth, but it does not merge recommended statuses into Home. Refreshing Recommended also leaves Home untouched.
- Failure, privacy, and cost boundaries
The system fails closed around AI ranking. If inference is unavailable or the model returns an invalid structure, the API returns a structured failure and the UI asks the user to refresh and try again. It does not silently fall back to a chronological or Home feed, because such a fallback would mislabel a non-AI result as an AI recommendation.
Rate limiting is applied per account before each initial or continuation page request. A rejected request returns 429; if an enabled rate-limit binding is unavailable, the endpoint returns 503. A missing or expired cursor returns 404, and GET returns 405 without generating a page.
The persistence boundaries are intentionally narrow:
D1 stores only the latest 30 activity references per user, not copied status text. KV stores account-bound pagination state and short-lived memoized pages for five minutes. Candidate contexts and model scores are not persisted separately. No Vectorize index is created.
The main privacy boundary is simple: authorization happens before AI, but authorized non-public Home content may still be sent to Cloudflare for scoring. The main cost boundary is also explicit: every newly generated non-empty page can invoke the model, including continuation pages. The bounded candidate window, short inputs, POST-only API, memoized retries, and native rate limiter keep that work controlled.
Summary
SiliconBeest's AI-recommended timeline combines deterministic access control with bounded semantic ranking. D1 decides what the user may see, recent public activity and followed tags describe what the user may care about, and BGE-M3 decides how eligible candidates should be ordered. Seeded local reshuffling and author limits improve variety, while an account-bound rolling cursor provides stable, duplicate-free pagination.
The resulting feed is personalized without turning AI into an authorization system, without replacing the Home timeline, and without building a permanent embedding index of user or status content.