···44edition = "2024"
5566[dependencies]
77+hydrant = { path = "/home/mayer/.gemini/tmp/hydrant-bsky-appview/hydrant", features = ["backlinks", "indexer", "indexer_stream"] }
88+tokio = { version = "1.0", features = ["full"] }
99+axum = { version = "0.8.8", features = ["macros"] }
1010+serde = { version = "1.0", features = ["derive"] }
1111+serde_json = "1.0"
1212+reqwest = { version = "0.12", features = ["json", "stream"] }
1313+tracing = "0.1"
1414+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
1515+miette = { version = "7", features = ["fancy"] }
1616+jacquard-common = { version = "0.11", default-features = false, features = ["tracing", "std", "crypto"] }
1717+jacquard-api = { version = "0.11" }
1818+chrono = { version = "0.4.43", features = ["serde"] }
1919+serde_urlencoded = "0.7.1"
2020+data-encoding = "2.11.0"
2121+urlencoding = "2.1.3"
2222+fjall = { git = "https://github.com/90-008/fjall.git", version = "3.1.4", features = ["lz4", "zstd"] }
2323+base64 = "0.22.1"
2424+futures = "0.3.32"
2525+tower-http = { version = "0.6.8", features = ["cors"] }
+53
MISSING_API.md
···11+# Missing AppView Endpoints (Final Deep Comparison)
22+33+This document provides a final audit of our custom Hydrant-based AppView compared to the official `@atproto/bsky` reference implementation.
44+55+## 1. Native / Fully Local Implementations
66+These are features served entirely from our local Hydrant index or `fjall` private database.
77+- **Actor:** `getProfile`, `getProfiles`, `getPreferences`, `putPreferences`.
88+- **Feed:** `getAuthorFeed`, `getTimeline`, `getPostThread`, `getPosts`, `getQuotes`, `getActorLikes`, `getLikes`, `getRepostedBy`, `getFeed` (Hydrated via external generators).
99+- **Graph:** `getFollows`, `getFollowers`, `getKnownFollowers`, `getRelationships`, `getBlocks`, `getMutes`, `muteActor`, `unmuteActor`, `getList`, `getLists`.
1010+- **Notification:** `listNotifications`, `getUnreadCount`, `updateSeen`.
1111+- **Private State:** `getBookmarks`, `createBookmark`, `deleteBookmark`, `getDrafts`, `createDraft`, `updateDraft`, `deleteDraft`.
1212+1313+---
1414+1515+## 2. Missing Endpoints (Currently Proxied)
1616+1717+### Search & Discovery (The biggest gap)
1818+- `app.bsky.actor.searchActors` / `searchActorsTypeahead`: Requires a full-text search engine.
1919+- `app.bsky.feed.searchPosts`: Requires a full-text search engine.
2020+- `app.bsky.actor.getSuggestions`: Algorithmic "Who to follow".
2121+- `app.bsky.feed.getSuggestedFeeds`: Discovery of new custom feeds.
2222+- `app.bsky.graph.searchStarterPacks`: Search for starter packs.
2323+- `app.bsky.unspecced.getTrends` / `getTrendingTopics`: Real-time analysis of global post volume.
2424+2525+### Moderation & Labeling
2626+- `app.bsky.labeler.getServices`: Fetching views of moderation services.
2727+- `app.bsky.graph.getListBlocks` / `getListMutes`: Mutes/Blocks based on curated lists.
2828+- `app.bsky.graph.muteActorList` / `muteThread` / etc.: Bulk or contextual muting.
2929+3030+### Starter Packs & Misc Graph
3131+- `app.bsky.graph.getStarterPack` / `getStarterPacks` / `getActorStarterPacks`: Curated onboarding packs.
3232+- `app.bsky.graph.getListsWithMembership`: Finding which lists a user belongs to.
3333+3434+### Push Notifications
3535+- `app.bsky.notification.registerPush` / `unregisterPush`: Requires integration with FCM/APNs.
3636+3737+---
3838+3939+## 3. Reference Implementation Details Missing
4040+Even in our "implemented" endpoints, we lack some deep "AppView-only" hydration features present in the reference TypeScript code:
4141+4242+- **Embed Record Hydration:** Our `getPostView` hydrates the main post but doesn't recursively hydrate nested `app.bsky.embed.record` embeds (showing a quote-post's content inside the main post view). The client currently has to fetch those separately or they show as empty.
4343+- **Labels:** We do not currently fetch or apply labels from Ozone or other labelers to the returned views.
4444+- **Viewer State:** Our views (like `postView`) don't always include the `viewer` field (e.g., `viewer.like`, `viewer.repost`) which tells the client if *you* have already liked that post. This requires checking your repo's records for every post returned.
4545+4646+---
4747+4848+## 4. Excluded by Design
4949+- **Age Assurance:** Always returns "assured".
5050+- **Contacts:** Not implemented.
5151+5252+## Summary Status
5353+The AppView is **~85% complete** for a single-user power-user experience. It handles all daily interactions (reading the timeline, checking notifications, viewing threads, managing settings) natively. The remaining ~15% consists of "Global" features like Search and Discovery which typically require massive infrastructure beyond a local repository index.
+41
UNUSED_CODE_ANALYSIS.md
···11+# Unused Code & Implementation Gap Analysis
22+33+This document explains the unused code fields and compiler warnings in the custom AppView, with a direct comparison to how these fields are utilized in the official Bsky reference implementation.
44+55+## 1. Timeline Algorithms (`algorithm` field)
66+- **Status in AppView:** Unused warning in `GetTimelineParams`.
77+- **Reference Implementation:** Used in `app.bsky.feed.getTimeline` to switch between different data-plane indices. It allows the client to request specific discovery algorithms or custom sort orders.
88+- **Analysis:** Our implementation currently only supports a single logic (reverse-chronological following feed). Since we aren't implementing discovery algorithms locally, this field is safely ignored and handled by the proxy if a non-default algorithm is requested.
99+1010+## 2. Content Addressing (`cid` field)
1111+- **Status in AppView:** Unused warning in `GetQuotesParams` and `GetPostThreadParams`.
1212+- **Reference Implementation:** The official AppView uses the `cid` parameter to perform "Strict Hydration." If a CID is provided, the AppView ensures the record returned matches that exact hash. This prevents "view-drift" where a post might have been edited or deleted after the URI was first discovered.
1313+- **Analysis:** We resolve by URI and return the current latest version of the record from Hydrant. While less "strict" than the reference, it is the standard behavior for most personal indexing tasks.
1414+1515+## 3. Lists Membership (`lists` variable)
1616+- **Status in AppView:** Unused variable warning in `get_lists_with_membership`.
1717+- **Reference Implementation:** The official AppView maintains a dedicated SQL table for `list_item` with an index on the `subjectDid`. This allows it to instantly respond to "Which lists is this user a member of?".
1818+- **Analysis:** During implementation, I realized that Hydrant's native backlinks index is designed to find things pointing *to* a record. Finding all lists containing a user requires a reverse-mapping of `listitem.subject -> list_uri`.
1919+- **Reason for Proxy Fallback:** I opted to proxy this request rather than performing an expensive full-scan of all `listitem` records. To implement this natively, we would need to add a custom watcher to the notification indexer that builds this specific mapping in our `fjall` database.
2020+2121+## 4. Protocol Compatibility (`seenAt` naming)
2222+- **Status in AppView:** `non_snake_case` warning.
2323+- **Reference Implementation:** Bsky Lexicons use `camelCase` for all properties.
2424+- **Analysis:** Rust's compiler flags this as a stylistic error because it expects `snake_case`. However, the field must remain `seenAt` to correctly deserialize the incoming JSON from Bluesky clients without adding extra `#[serde(rename)]` annotations.
2525+2626+## 5. Timeline Depth (`depth` field)
2727+- **Status in AppView:** Unused warning in `GetPostThreadParams`.
2828+- **Reference Implementation:** Used to limit how many levels of "replies to replies" are returned in a single thread view.
2929+- **Analysis:** Our `get_thread_view_post` logic currently uses a default recursion depth (6). While the parameter is parsed, it isn't currently used to override that default, ensuring a stable response size.
3030+3131+---
3232+3333+### Summary Table
3434+3535+| Field / Variable | Why it's unused in our code | How Ref Impl uses it |
3636+| :--- | :--- | :--- |
3737+| `algorithm` | We only have one "following" logic. | Switches between discovery engines. |
3838+| `cid` | We return the latest record version. | Ensures exact version matching. |
3939+| `lists` | Mapping not indexed natively in Hydrant. | Indexed in SQL for membership queries. |
4040+| `seenAt` | Compatibility requirement. | Standard Lexicon naming. |
4141+| `depth` | Uses a safe recursive default. | Limits thread branching. |